on xenial help sign in
Merged launcher-icon-wobble branch and solved all conflicts.
commita63f3372906bcb23d8e697d7434b1ca60a7761aa authorMirco Müller <mirco.mueller@canonical.com> dateThu 23 Jul 2015, 12:31 (11 years ago) parent0b22f55e198a75
71 files changed+2956−421
debian/changelog+34 debian/control+5−3 plugins/AccountsService/AccountsService.cpp+31−42 plugins/AccountsService/AccountsService.h+4−3 plugins/AccountsService/AccountsServiceDBusAdaptor.cpp+13−11 plugins/AccountsService/AccountsServiceDBusAdaptor.h+3−2 plugins/Greeter/Unity/Launcher/CMakeLists.txt+1−1 plugins/Greeter/Unity/Launcher/launcheritem.cpp+14 plugins/Greeter/Unity/Launcher/launcheritem.h+4 plugins/Greeter/Unity/Launcher/launchermodelas.cpp+7 plugins/Greeter/Unity/Launcher/launchermodelas.h+1 plugins/Lights/Lights.cpp+1−1 plugins/Ubuntu/SystemImage/SystemImage.cpp+7−8 plugins/Ubuntu/SystemImage/SystemImage.h+1−5 plugins/Unity/Connectivity/Connectivity.cpp+5−5 plugins/Unity/Connectivity/Connectivity.h+1 plugins/Unity/DashCommunicator/dbusdashcommunicatorservice.h+1−1 plugins/Unity/DashCommunicator/plugin.cpp+1 plugins/Unity/Launcher/CMakeLists.txt+1−1 plugins/Unity/Launcher/dbusinterface.cpp+23−13 plugins/Unity/Launcher/dbusinterface.h+2−1 plugins/Unity/Launcher/launcheritem.cpp+23 plugins/Unity/Launcher/launcheritem.h+3 plugins/Unity/Launcher/launchermodel.cpp+31 plugins/Unity/Launcher/launchermodel.h+2 plugins/Unity/Session/dbusunitysessionservice.cpp+47−55 plugins/Unity/Session/dbusunitysessionservice.h+11−8 plugins/Wizard/System.cpp+13−9 po/ar.po+76−61 po/ga.po+697 po/sw.po+86−80 qml/Dash/Previews/PreviewComment.qml+83 qml/Dash/Previews/PreviewCommentInput.qml+66 qml/Dash/Previews/PreviewWidgetFactory.qml+2 qml/Launcher/LauncherDelegate.qml+86 qml/Launcher/LauncherPanel.qml+74−3 qml/Notifications/Notifications.qml+1−1 qml/Panel/IndicatorItem.qml+1−1 qml/Panel/Indicators/IndicatorBase.qml−1 qml/Tutorial/TutorialLeft.qml+16−6 tests/autopilot/unity8/settings_wizard/__init__.py+507 tests/autopilot/unity8/settings_wizard/fixture_setup.py+58 tests/autopilot/unity8/settings_wizard/ofono_helper.py+116 tests/autopilot/unity8/settings_wizard/tests/__init__.py tests/autopilot/unity8/settings_wizard/tests/test_settings_wizard.py+79 tests/mocks/QMenuModel/QDBusActionGroup.qml+2−2 tests/mocks/QMenuModel/actiondata.h+3−1 tests/mocks/Unity/Launcher/CMakeLists.txt+1−1 tests/mocks/Unity/Launcher/MockLauncherItem.cpp+19 tests/mocks/Unity/Launcher/MockLauncherItem.h+3 tests/mocks/Unity/Launcher/MockLauncherModel.cpp+40 tests/mocks/Unity/Launcher/MockLauncherModel.h+3 tests/plugins/AccountsService/client.cpp+10−20 tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt+1−1 tests/plugins/LightDM/CMakeLists.txt+6 tests/plugins/Unity/Launcher/CMakeLists.txt+1−1 tests/plugins/Unity/Launcher/launchermodeltest.cpp+23−7 tests/plugins/Unity/Session/sessionbackendtest.cpp+27−40 tests/qmltests/CMakeLists.txt+2 tests/qmltests/Dash/Previews/tst_PreviewComment.qml+84 tests/qmltests/Dash/Previews/tst_PreviewCommentInput.qml+85 tests/qmltests/Dash/Previews/tst_PreviewWidgetFactory.qml+2 tests/qmltests/Launcher/tst_Launcher.qml+168−24 tests/qmltests/Tutorial/tst_Tutorial.qml+31−1 tests/qmltests/tst_ShellWithPin.qml+1−1 tests/scripts/README+1 tests/scripts/alert-launcher-icon.sh+3 tools/CMakeLists.txt+1 tools/scopetool.cpp+6 tools/unix-signal-handler.cpp+141 tools/unix-signal-handler.h+54
debian/changelog+34
@@ -1,3 +1,37 @@
1+unity8 (8.10+15.10.20150717-0ubuntu1) wily; urgency=medium
2+
3+ [ Albert Astals ]
4+ * Ensure tutorial doesn't let user drag the launcher past screen edge.
5+ (LP: #1454882)
6+
7+ [ Albert Astals Cid ]
8+ * Fix build with ninja
9+
10+ [ Andrea Cimitan ]
11+ * Add PreviewSocialComment
12+
13+ [ Daniel d'Andrada ]
14+ * Ensure tutorial doesn't let user drag the launcher past screen edge.
15+ (LP: #1454882)
16+ * Update tst_ShellWithPin
17+
18+ [ Josh Arenson ]
19+ * Add a basic autopilot test for the SettingsWizard
20+
21+ [ Lukáš Tinkl ]
22+ * Avoid sync DBUS calls
23+
24+ [ Marcus Tomlinson ]
25+ * Handle TERM, HUP, and INT signals in unity-scope-tool (LP: #1470464)
26+
27+ [ Michael Zanetti ]
28+ * Add PreviewSocialComment
29+
30+ [ Nick Dedekind ]
31+ * Fixed qml warnings in 'make tryShell'
32+
33+ -- CI Train Bot <ci-train-bot@canonical.com> Fri, 17 Jul 2015 15:53:38 +0000
34+
135unity8 (8.10+15.10.20150713-0ubuntu1) wily; urgency=medium
236
337 [ Albert Astals Cid ]
debian/control+5−3
@@ -30,7 +30,7 @@ Build-Depends: cmake,
3030 libqt5xmlpatterns5-dev,
3131 libsystemsettings-dev,
3232 libudev-dev,
33− libunity-api-dev (>= 7.97),
33+ libunity-api-dev (>= 7.98),
3434 libusermetricsoutput1-dev,
3535 libxcb1-dev,
3636 pkg-config,
@@ -101,7 +101,7 @@ Depends: gsettings-desktop-schemas,
101101 qtdeclarative5-ubuntu-telephony0.1,
102102 qtdeclarative5-ubuntu-web-plugin,
103103 ubuntu-system-settings,
104− unity-launcher-impl-4,
104+ unity-launcher-impl-7,
105105 unity8-common (= ${source:Version}),
106106 unity8-private (= ${binary:Version}),
107107 unity8-private | unity-launcher-impl,
@@ -148,6 +148,7 @@ Depends: autopilot-qt5 (>= 1.4),
148148 gir1.2-notify-0.7,
149149 libqt5test5,
150150 libqt5widgets5,
151+ ofono-phonesim,
151152 python3-autopilot,
152153 python3-evdev,
153154 python3-fixtures,
@@ -158,6 +159,7 @@ Depends: autopilot-qt5 (>= 1.4),
158159 unity8 (= ${source:Version}),
159160 unity8-fake-env (= ${source:Version}),
160161 url-dispatcher-tools,
162+ xvfb,
161163 ${misc:Depends},
162164 ${python3:Depends},
163165 ${shlibs:Depends},
@@ -189,7 +191,7 @@ Depends: accountsservice-ubuntu-schemas (>= 0.0.3),
189191 ${misc:Depends},
190192 ${shlibs:Depends},
191193Provides: unity-launcher-impl,
192− unity-launcher-impl-4,
194+ unity-launcher-impl-7,
193195Description: Unity 8 private libs
194196 The Unity 8 shell is the primary user interface for Ubuntu devices.
195197 .
plugins/AccountsService/AccountsService.cpp+31−42
@@ -23,10 +23,9 @@
2323#include <QStringList>
2424#include <QDebug>
2525
26−AccountsService::AccountsService(QObject* parent)
27− : QObject(parent),
26+AccountsService::AccountsService(QObject* parent, const QString &user)
27+ : QObject(parent),
2828 m_service(new AccountsServiceDBusAdaptor(this)),
29− m_user(""),
3029 m_demoEdges(false),
3130 m_enableLauncherWhileLocked(false),
3231 m_enableIndicatorsWhileLocked(false),
@@ -36,12 +35,10 @@ AccountsService::AccountsService(QObject* parent)
3635 m_hereEnabled(false),
3736 m_hereLicensePath() // null means not set yet
3837{
39− connect(m_service, SIGNAL(propertiesChanged(const QString &, const QString &, const QStringList &)),
40− this, SLOT(propertiesChanged(const QString &, const QString &, const QStringList &)));
41− connect(m_service, SIGNAL(maybeChanged(const QString &)),
42− this, SLOT(maybeChanged(const QString &)));
38+ connect(m_service, &AccountsServiceDBusAdaptor::propertiesChanged, this, &AccountsService::onPropertiesChanged);
39+ connect(m_service, &AccountsServiceDBusAdaptor::maybeChanged, this, &AccountsService::onMaybeChanged);
4340
44− setUser(qgetenv("USER"));
41+ setUser(!user.isEmpty() ? user : QString::fromUtf8(qgetenv("USER")));
4542}
4643
4744QString AccountsService::user() const
@@ -173,19 +170,18 @@ void AccountsService::updateEnableLauncherWhileLocked(bool async)
173170 connect(watcher, &QDBusPendingCallWatcher::finished,
174171 this, [this](QDBusPendingCallWatcher* watcher) {
175172
176− QDBusPendingReply<QDBusVariant> reply = *watcher;
173+ QDBusPendingReply<QVariant> reply = *watcher;
174+ watcher->deleteLater();
177175 if (reply.isError()) {
178176 qWarning() << "Failed to get 'EnableLauncherWhileLocked' property - " << reply.error().message();
179− watcher->deleteLater();
180177 return;
181178 }
182179
183− auto enableLauncherWhileLocked = reply.value().variant().toBool();
180+ const bool enableLauncherWhileLocked = reply.value().toBool();
184181 if (m_enableLauncherWhileLocked != enableLauncherWhileLocked) {
185182 m_enableLauncherWhileLocked = enableLauncherWhileLocked;
186183 Q_EMIT enableLauncherWhileLockedChanged();
187184 }
188− watcher->deleteLater();
189185 });
190186 if (!async) {
191187 watcher->waitForFinished();
@@ -203,19 +199,18 @@ void AccountsService::updateEnableIndicatorsWhileLocked(bool async)
203199 connect(watcher, &QDBusPendingCallWatcher::finished,
204200 this, [this](QDBusPendingCallWatcher* watcher) {
205201
206− QDBusPendingReply<QDBusVariant> reply = *watcher;
202+ QDBusPendingReply<QVariant> reply = *watcher;
203+ watcher->deleteLater();
207204 if (reply.isError()) {
208205 qWarning() << "Failed to get 'EnableIndicatorsWhileLocked' property - " << reply.error().message();
209− watcher->deleteLater();
210206 return;
211207 }
212208
213− auto enableIndicatorsWhileLocked = reply.value().variant().toBool();
209+ const bool enableIndicatorsWhileLocked = reply.value().toBool();
214210 if (m_enableIndicatorsWhileLocked != enableIndicatorsWhileLocked) {
215211 m_enableIndicatorsWhileLocked = enableIndicatorsWhileLocked;
216212 Q_EMIT enableIndicatorsWhileLockedChanged();
217213 }
218− watcher->deleteLater();
219214 });
220215 if (!async) {
221216 watcher->waitForFinished();
@@ -233,19 +228,18 @@ void AccountsService::updateBackgroundFile(bool async)
233228 connect(watcher, &QDBusPendingCallWatcher::finished,
234229 this, [this](QDBusPendingCallWatcher* watcher) {
235230
236− QDBusPendingReply<QDBusVariant> reply = *watcher;
231+ QDBusPendingReply<QVariant> reply = *watcher;
232+ watcher->deleteLater();
237233 if (reply.isError()) {
238234 qWarning() << "Failed to get 'BackgroundFile' property - " << reply.error().message();
239− watcher->deleteLater();
240235 return;
241236 }
242237
243− auto backgroundFile = reply.value().variant().toString();
238+ const QString backgroundFile = reply.value().toString();
244239 if (m_backgroundFile != backgroundFile) {
245240 m_backgroundFile = backgroundFile;
246241 Q_EMIT backgroundFileChanged();
247242 }
248− watcher->deleteLater();
249243 });
250244 if (!async) {
251245 watcher->waitForFinished();
@@ -263,19 +257,18 @@ void AccountsService::updateStatsWelcomeScreen(bool async)
263257 connect(watcher, &QDBusPendingCallWatcher::finished,
264258 this, [this](QDBusPendingCallWatcher* watcher) {
265259
266− QDBusPendingReply<QDBusVariant> reply = *watcher;
260+ QDBusPendingReply<QVariant> reply = *watcher;
261+ watcher->deleteLater();
267262 if (reply.isError()) {
268263 qWarning() << "Failed to get 'StatsWelcomeScreen' property - " << reply.error().message();
269− watcher->deleteLater();
270264 return;
271265 }
272266
273− auto statsWelcomeScreen = reply.value().variant().toBool();
267+ const bool statsWelcomeScreen = reply.value().toBool();
274268 if (m_statsWelcomeScreen != statsWelcomeScreen) {
275269 m_statsWelcomeScreen = statsWelcomeScreen;
276270 Q_EMIT statsWelcomeScreenChanged();
277271 }
278− watcher->deleteLater();
279272 });
280273 if (!async) {
281274 watcher->waitForFinished();
@@ -293,19 +286,18 @@ void AccountsService::updatePasswordDisplayHint(bool async)
293286 connect(watcher, &QDBusPendingCallWatcher::finished,
294287 this, [this](QDBusPendingCallWatcher* watcher) {
295288
296− QDBusPendingReply<QDBusVariant> reply = *watcher;
289+ QDBusPendingReply<QVariant> reply = *watcher;
290+ watcher->deleteLater();
297291 if (reply.isError()) {
298292 qWarning() << "Failed to get 'PasswordDisplayHint' property - " << reply.error().message();
299− watcher->deleteLater();
300293 return;
301294 }
302295
303− PasswordDisplayHint passwordDisplayHint = (PasswordDisplayHint)reply.value().variant().toInt();
296+ const PasswordDisplayHint passwordDisplayHint = (PasswordDisplayHint)reply.value().toInt();
304297 if (m_passwordDisplayHint != passwordDisplayHint) {
305298 m_passwordDisplayHint = passwordDisplayHint;
306299 Q_EMIT passwordDisplayHintChanged();
307300 }
308− watcher->deleteLater();
309301 });
310302 if (!async) {
311303 watcher->waitForFinished();
@@ -323,19 +315,18 @@ void AccountsService::updateFailedLogins(bool async)
323315 connect(watcher, &QDBusPendingCallWatcher::finished,
324316 this, [this](QDBusPendingCallWatcher* watcher) {
325317
326− QDBusPendingReply<QDBusVariant> reply = *watcher;
318+ QDBusPendingReply<QVariant> reply = *watcher;
319+ watcher->deleteLater();
327320 if (reply.isError()) {
328321 qWarning() << "Failed to get 'FailedLogins' property - " << reply.error().message();
329− watcher->deleteLater();
330322 return;
331323 }
332324
333− uint failedLogins = reply.value().variant().toUInt();
325+ const uint failedLogins = reply.value().toUInt();
334326 if (m_failedLogins != failedLogins) {
335327 m_failedLogins = failedLogins;
336328 Q_EMIT failedLoginsChanged();
337329 }
338− watcher->deleteLater();
339330 });
340331 if (!async) {
341332 watcher->waitForFinished();
@@ -353,19 +344,18 @@ void AccountsService::updateHereEnabled(bool async)
353344 connect(watcher, &QDBusPendingCallWatcher::finished,
354345 this, [this](QDBusPendingCallWatcher* watcher) {
355346
356− QDBusPendingReply<QDBusVariant> reply = *watcher;
347+ QDBusPendingReply<QVariant> reply = *watcher;
348+ watcher->deleteLater();
357349 if (reply.isError()) {
358350 qWarning() << "Failed to get 'LicenseAccepted' property - " << reply.error().message();
359− watcher->deleteLater();
360351 return;
361352 }
362353
363− auto hereEnabled = reply.value().variant().toBool();
354+ const bool hereEnabled = reply.value().toBool();
364355 if (m_hereEnabled != hereEnabled) {
365356 m_hereEnabled = hereEnabled;
366357 Q_EMIT hereEnabledChanged();
367358 }
368− watcher->deleteLater();
369359 });
370360 if (!async) {
371361 watcher->waitForFinished();
@@ -383,14 +373,14 @@ void AccountsService::updateHereLicensePath(bool async)
383373 connect(watcher, &QDBusPendingCallWatcher::finished,
384374 this, [this](QDBusPendingCallWatcher* watcher) {
385375
386− QDBusPendingReply<QDBusVariant> reply = *watcher;
376+ QDBusPendingReply<QVariant> reply = *watcher;
377+ watcher->deleteLater();
387378 if (reply.isError()) {
388379 qWarning() << "Failed to get 'LicenseBasePath' property - " << reply.error().message();
389− watcher->deleteLater();
390380 return;
391381 }
392382
393− auto hereLicensePath = reply.value().variant().toString();
383+ QString hereLicensePath = reply.value().toString();
394384 if (hereLicensePath.isEmpty() || !QFile::exists(hereLicensePath))
395385 hereLicensePath = "";
396386
@@ -398,7 +388,6 @@ void AccountsService::updateHereLicensePath(bool async)
398388 m_hereLicensePath = hereLicensePath;
399389 Q_EMIT hereLicensePathChanged();
400390 }
401− watcher->deleteLater();
402391 });
403392 if (!async) {
404393 watcher->waitForFinished();
@@ -421,7 +410,7 @@ void AccountsService::setFailedLogins(uint failedLogins)
421410 }
422411}
423412
424−void AccountsService::propertiesChanged(const QString &user, const QString &interface, const QStringList &changed)
413+void AccountsService::onPropertiesChanged(const QString &user, const QString &interface, const QStringList &changed)
425414{
426415 if (m_user != user) {
427416 return;
@@ -459,7 +448,7 @@ void AccountsService::propertiesChanged(const QString &user, const QString &inte
459448 }
460449}
461450
462−void AccountsService::maybeChanged(const QString &user)
451+void AccountsService::onMaybeChanged(const QString &user)
463452{
464453 if (m_user != user) {
465454 return;
plugins/AccountsService/AccountsService.h+4−3
@@ -72,7 +72,8 @@ public:
7272 Numeric,
7373 };
7474
75− explicit AccountsService(QObject *parent = 0);
75+ explicit AccountsService(QObject *parent = 0, const QString & user = QString());
76+ ~AccountsService() = default;
7677
7778 QString user() const;
7879 void setUser(const QString &user);
@@ -103,8 +104,8 @@ Q_SIGNALS:
103104 void hereLicensePathChanged();
104105
105106private Q_SLOTS:
106− void propertiesChanged(const QString &user, const QString &interface, const QStringList &changed);
107− void maybeChanged(const QString &user);
107+ void onPropertiesChanged(const QString &user, const QString &interface, const QStringList &changed);
108+ void onMaybeChanged(const QString &user);
108109
109110private:
110111 void updateDemoEdges(bool async = true);
plugins/AccountsService/AccountsServiceDBusAdaptor.cpp+13−11
@@ -21,35 +21,35 @@
2121#include <QDBusConnectionInterface>
2222#include <QDBusMessage>
2323#include <QDBusVariant>
24+#include <QDebug>
2425
2526AccountsServiceDBusAdaptor::AccountsServiceDBusAdaptor(QObject* parent)
2627 : QObject(parent),
2728 m_accountsManager(nullptr),
28− m_users(),
2929 m_ignoreNextChanged(false)
3030{
3131 QDBusConnection connection = QDBusConnection::SM_BUSNAME();
3232 QDBusConnectionInterface *interface = connection.interface();
3333 interface->startService("org.freedesktop.Accounts");
3434 m_accountsManager = new QDBusInterface("org.freedesktop.Accounts",
35− "/org/freedesktop/Accounts",
36− "org.freedesktop.Accounts",
37− connection, this);
35+ "/org/freedesktop/Accounts",
36+ "org.freedesktop.Accounts",
37+ connection, this);
3838}
3939
4040QVariant AccountsServiceDBusAdaptor::getUserProperty(const QString &user, const QString &interface, const QString &property)
4141{
4242 QDBusInterface *iface = getUserInterface(user);
4343 if (iface != nullptr && iface->isValid()) {
44− QDBusMessage answer = iface->call("Get", interface, property);
45− if (answer.type() == QDBusMessage::ReplyMessage) {
46− return answer.arguments()[0].value<QDBusVariant>().variant();
44+ QDBusReply<QVariant> answer = iface->call("Get", interface, property);
45+ if (answer.isValid()) {
46+ return answer;
4747 }
4848 }
4949 return QVariant();
5050}
5151
52−QDBusPendingReply<QDBusVariant> AccountsServiceDBusAdaptor::getUserPropertyAsync(const QString &user, const QString &interface, const QString &property)
52+QDBusPendingReply<QVariant> AccountsServiceDBusAdaptor::getUserPropertyAsync(const QString &user, const QString &interface, const QString &property)
5353{
5454 QDBusInterface *iface = getUserInterface(user);
5555 if (iface != nullptr && iface->isValid()) {
@@ -116,9 +116,9 @@ QDBusInterface *AccountsServiceDBusAdaptor::getUserInterface(const QString &user
116116{
117117 QDBusInterface *iface = m_users.value(user);
118118 if (iface == nullptr && m_accountsManager->isValid()) {
119− QDBusMessage answer = m_accountsManager->call("FindUserByName", user);
120− if (answer.type() == QDBusMessage::ReplyMessage) {
121− QString path = answer.arguments()[0].value<QDBusObjectPath>().path();
119+ QDBusReply<QDBusObjectPath> answer = m_accountsManager->asyncCall("FindUserByName", user);
120+ if (answer.isValid()) {
121+ const QString path = answer.value().path();
122122
123123 iface = new QDBusInterface("org.freedesktop.Accounts",
124124 path,
@@ -148,6 +148,8 @@ QDBusInterface *AccountsServiceDBusAdaptor::getUserInterface(const QString &user
148148 SLOT(propertiesChangedSlot(QString, QVariantMap, QStringList)));
149149
150150 m_users.insert(user, iface);
151+ } else {
152+ qWarning() << "Couldn't get user interface" << answer.error().name() << answer.error().message();
151153 }
152154 }
153155 return iface;
plugins/AccountsService/AccountsServiceDBusAdaptor.h+3−2
@@ -33,13 +33,14 @@ class AccountsServiceDBusAdaptor: public QObject, public QDBusContext
3333
3434public:
3535 explicit AccountsServiceDBusAdaptor(QObject *parent = 0);
36+ ~AccountsServiceDBusAdaptor() = default;
3637
3738 Q_INVOKABLE QVariant getUserProperty(const QString &user, const QString &interface, const QString &property);
38− Q_INVOKABLE QDBusPendingReply<QDBusVariant> getUserPropertyAsync(const QString &user, const QString &interface, const QString &property);
39+ Q_INVOKABLE QDBusPendingReply<QVariant> getUserPropertyAsync(const QString &user, const QString &interface, const QString &property);
3940
4041 template <typename T>
4142 inline T getUserProperty(const QString &user, const QString &interface, const QString &property) {
42− QVariant variant = getUserProperty(user, interface, property);
43+ const QVariant variant = getUserProperty(user, interface, property);
4344 if (variant.isValid() && variant.canConvert<QDBusArgument>()) {
4445 return qdbus_cast<T>(variant.value<QDBusArgument>());
4546 }
plugins/Greeter/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,4 +1,4 @@
1−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
1+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
22pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
33pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
44
plugins/Greeter/Unity/Launcher/launcheritem.cpp+14
@@ -31,6 +31,7 @@ LauncherItem::LauncherItem(const QString &appId, const QString &name, const QStr
3131 m_count(0),
3232 m_countVisible(false),
3333 m_focused(false),
34+ m_alerting(false),
3435 m_quickList(new QuickListModel(this))
3536{
3637 QuickListEntry nameAction;
@@ -165,6 +166,19 @@ void LauncherItem::setFocused(bool focused)
165166 }
166167}
167168
169+bool LauncherItem::alerting() const
170+{
171+ return m_alerting;
172+}
173+
174+void LauncherItem::setAlerting(bool alerting)
175+{
176+ if (m_alerting != alerting) {
177+ m_alerting = alerting;
178+ Q_EMIT alertingChanged(alerting);
179+ }
180+}
181+
168182unity::shell::launcher::QuickListModelInterface *LauncherItem::quickList() const
169183{
170184 return m_quickList;
plugins/Greeter/Unity/Launcher/launcheritem.h+4
@@ -41,6 +41,7 @@ public:
4141 int count() const override;
4242 bool countVisible() const override;
4343 bool focused() const override;
44+ bool alerting() const override;
4445
4546 unity::shell::launcher::QuickListModelInterface *quickList() const override;
4647
@@ -54,6 +55,8 @@ private:
5455 void setCount(int count);
5556 void setCountVisible(bool countVisible);
5657 void setFocused(bool focused);
58+ void setAlerting(bool alerting);
59+
5760
5861private:
5962 QString m_appId;
@@ -66,6 +69,7 @@ private:
6669 int m_count;
6770 bool m_countVisible;
6871 bool m_focused;
72+ bool m_alerting;
6973 QuickListModel *m_quickList;
7074
7175 friend class LauncherModel;
plugins/Greeter/Unity/Launcher/launchermodelas.cpp+7
@@ -72,6 +72,13 @@ QVariant LauncherModel::data(const QModelIndex &index, int role) const
7272 return QVariant();
7373}
7474
75+void LauncherModel::setAlerting(const QString &appId, bool alerting)
76+{
77+ Q_UNUSED(appId)
78+ Q_UNUSED(alerting)
79+ qWarning() << "This is a read only implementation. Cannot set alert-state of items.";
80+}
81+
7582unity::shell::launcher::LauncherItemInterface *LauncherModel::get(int index) const
7683{
7784 if (index < 0 || index >= m_list.count()) {
plugins/Greeter/Unity/Launcher/launchermodelas.h+1
@@ -41,6 +41,7 @@ public:
4141
4242 QVariant data(const QModelIndex &index, int role) const override;
4343
44+ Q_INVOKABLE void setAlerting(const QString &appId, bool alerting) override;
4445 Q_INVOKABLE unity::shell::launcher::LauncherItemInterface* get(int index) const override;
4546 Q_INVOKABLE void move(int oldIndex, int newIndex) override;
4647 Q_INVOKABLE void pin(const QString &appId, int index = -1) override;
plugins/Lights/Lights.cpp+1−1
@@ -71,7 +71,7 @@ void Lights::setColor(const QColor &color)
7171 if (m_color != color) {
7272 m_color = color;
7373 Q_EMIT colorChanged(m_color);
74− // FIXME: update the collor if the light is already on
74+ // FIXME: update the color if the light is already on
7575 }
7676}
7777
plugins/Ubuntu/SystemImage/SystemImage.cpp+7−8
@@ -16,19 +16,18 @@
1616
1717#include "SystemImage.h"
1818#include <QDBusConnection>
19−#include <QDBusInterface>
19+#include <QDBusPendingCall>
2020
2121SystemImage::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))
22+ : QObject(parent)
2823{
2924}
3025
3126void SystemImage::factoryReset()
3227{
33− m_interface->call("FactoryReset");
28+ const QDBusMessage msg = QDBusMessage::createMethodCall("com.canonical.SystemImage",
29+ "/Service",
30+ "com.canonical.SystemImage",
31+ "FactoryReset");
32+ QDBusConnection::systemBus().asyncCall(msg);
3433}
plugins/Ubuntu/SystemImage/SystemImage.h+1−5
@@ -19,8 +19,6 @@
1919
2020#include <QObject>
2121
22−class QDBusInterface;
23−
2422class SystemImage : public QObject
2523{
2624 Q_OBJECT
@@ -28,11 +26,9 @@ class SystemImage : public QObject
2826
2927public:
3028 explicit SystemImage(QObject *parent = 0);
29+ ~SystemImage() = default;
3130
3231 Q_INVOKABLE void factoryReset();
33−
34−private:
35− QDBusInterface *m_interface;
3632};
3733
3834#endif // SYSTEMIMAGE_H
plugins/Unity/Connectivity/Connectivity.cpp+5−5
@@ -16,7 +16,6 @@
1616
1717#include "Connectivity.h"
1818#include <QDBusConnection>
19−#include <QDBusInterface>
2019#include <QDBusPendingCall>
2120
2221Connectivity::Connectivity(QObject *parent)
@@ -26,8 +25,9 @@ Connectivity::Connectivity(QObject *parent)
2625
2726void Connectivity::unlockAllModems()
2827{
29− QDBusInterface iface("com.ubuntu.connectivity1",
30− "/com/ubuntu/connectivity1/Private",
31− "com.ubuntu.connectivity1.Private");
32− iface.asyncCall("UnlockAllModems");
28+ const QDBusMessage msg = QDBusMessage::createMethodCall("com.ubuntu.connectivity1",
29+ "/com/ubuntu/connectivity1/Private",
30+ "com.ubuntu.connectivity1.Private",
31+ "UnlockAllModems");
32+ QDBusConnection::sessionBus().asyncCall(msg);
3333}
plugins/Unity/Connectivity/Connectivity.h+1
@@ -26,6 +26,7 @@ class Connectivity : public QObject
2626
2727public:
2828 explicit Connectivity(QObject *parent = 0);
29+ ~Connectivity() = default;
2930
3031 Q_INVOKABLE void unlockAllModems();
3132};
plugins/Unity/DashCommunicator/dbusdashcommunicatorservice.h+1−1
@@ -36,4 +36,4 @@ public Q_SLOTS:
3636
3737};
3838
39−#endif // DBUSUNITYSESSIONSERVICE_H
39+#endif // DBUSDASHCOMMUNICATORSERVICE_H
plugins/Unity/DashCommunicator/plugin.cpp+1
@@ -25,6 +25,7 @@
2525
2626void DashCommunicatorPlugin::registerTypes(const char *uri)
2727{
28+ // @uri Unity.DashCommunicator
2829 Q_ASSERT(uri == QStringLiteral("Unity.DashCommunicator"));
2930 qmlRegisterType<DashCommunicatorService>(uri, 0, 1, "DashCommunicatorService");
3031 qmlRegisterType<DashCommunicator>(uri, 0, 1, "DashCommunicator");
plugins/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,4 +1,4 @@
1−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
1+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
22pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
33pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
44
plugins/Unity/Launcher/dbusinterface.cpp+23−13
@@ -32,10 +32,6 @@ DBusInterface::DBusInterface(LauncherModel *parent):
3232{
3333}
3434
35−DBusInterface::~DBusInterface()
36−{
37−}
38−
3935QString DBusInterface::introspect(const QString &path) const
4036{
4137 /* This case we should just list the nodes */
@@ -67,6 +63,7 @@ QString DBusInterface::introspect(const QString &path) const
6763 "<property name=\"count\" type=\"i\" access=\"readwrite\" />"
6864 "<property name=\"countVisible\" type=\"b\" access=\"readwrite\" />"
6965 "<property name=\"progress\" type=\"i\" access=\"readwrite\" />"
66+ "<method name=\"Alert\" />"
7067 "</interface>";
7168 return nodeiface;
7269}
@@ -127,6 +124,20 @@ bool DBusInterface::handleMessage(const QDBusMessage& message, const QDBusConnec
127124 return false;
128125 }
129126
127+ /* Break down the path to just the app id */
128+ bool validpath = true;
129+ QString pathtemp = message.path();
130+ if (!pathtemp.startsWith("/com/canonical/Unity/Launcher/")) {
131+ validpath = false;
132+ }
133+ pathtemp.remove("/com/canonical/Unity/Launcher/");
134+ if (pathtemp.indexOf('/') >= 0) {
135+ validpath = false;
136+ }
137+
138+ /* Find ourselves an appid */
139+ QString appid = decodeAppId(pathtemp);
140+
130141 // First handle methods of the Launcher interface
131142 if (message.interface() == "com.canonical.Unity.Launcher") {
132143 if (message.member() == "Refresh") {
@@ -134,6 +145,13 @@ bool DBusInterface::handleMessage(const QDBusMessage& message, const QDBusConnec
134145 Q_EMIT refreshCalled();
135146 return connection.send(reply);
136147 }
148+ } else if (message.interface() == "com.canonical.Unity.Launcher.Item") {
149+ // Handle methods of the Launcher-Item interface
150+ if (message.member() == "Alert" && validpath) {
151+ QDBusMessage reply = message.createReply();
152+ Q_EMIT alertCalled(appid);
153+ return connection.send(reply);
154+ }
137155 }
138156
139157 // Now handle dynamic properties (for launcher emblems)
@@ -145,18 +163,10 @@ bool DBusInterface::handleMessage(const QDBusMessage& message, const QDBusConnec
145163 return false;
146164 }
147165
148− /* Break down the path to just the app id */
149− QString pathtemp = message.path();
150− if (!pathtemp.startsWith("/com/canonical/Unity/Launcher/")) {
151− return false;
152− }
153− pathtemp.remove("/com/canonical/Unity/Launcher/");
154− if (pathtemp.indexOf('/') >= 0) {
166+ if (!validpath) {
155167 return false;
156168 }
157169
158− /* Find ourselves an appid */
159− QString appid = decodeAppId(pathtemp);
160170 int index = m_launcherModel->findApplication(appid);
161171 LauncherItem *item = static_cast<LauncherItem*>(m_launcherModel->get(index));
162172
plugins/Unity/Launcher/dbusinterface.h+2−1
@@ -27,7 +27,7 @@ class DBusInterface: public UnityDBusVirtualObject
2727 Q_OBJECT
2828public:
2929 DBusInterface(LauncherModel *parent);
30− ~DBusInterface();
30+ ~DBusInterface() = default;
3131
3232 // QDBusVirtualObject implementaition
3333 QString introspect (const QString &path) const override;
@@ -38,6 +38,7 @@ Q_SIGNALS:
3838 void countVisibleChanged(const QString &appId, bool countVisible);
3939 void progressChanged(const QString &appId, int progress);
4040 void refreshCalled();
41+ void alertCalled(const QString &appId);
4142
4243private:
4344 static QString decodeAppId(const QString& path);
plugins/Unity/Launcher/launcheritem.cpp+23
@@ -34,6 +34,7 @@ LauncherItem::LauncherItem(const QString &appId, const QString &name, const QStr
3434 m_count(0),
3535 m_countVisible(false),
3636 m_focused(false),
37+ m_alerting(false),
3738 m_quickList(new QuickListModel(this))
3839{
3940 QuickListEntry nameAction;
@@ -150,6 +151,9 @@ void LauncherItem::setCount(int count)
150151 if (m_count != count) {
151152 m_count = count;
152153 Q_EMIT countChanged(count);
154+ if (m_countVisible) {
155+ setAlerting(true);
156+ }
153157 }
154158}
155159
@@ -163,6 +167,9 @@ void LauncherItem::setCountVisible(bool countVisible)
163167 if (m_countVisible != countVisible) {
164168 m_countVisible = countVisible;
165169 Q_EMIT countVisibleChanged(countVisible);
170+ if (countVisible) {
171+ setAlerting(true);
172+ }
166173 }
167174}
168175
@@ -175,10 +182,26 @@ void LauncherItem::setFocused(bool focused)
175182{
176183 if (m_focused != focused) {
177184 m_focused = focused;
185+ if (focused) {
186+ setAlerting(false);
187+ }
178188 Q_EMIT focusedChanged(focused);
179189 }
180190}
181191
192+bool LauncherItem::alerting() const
193+{
194+ return m_alerting;
195+}
196+
197+void LauncherItem::setAlerting(bool alerting)
198+{
199+ if (m_alerting != alerting) {
200+ m_alerting = alerting;
201+ Q_EMIT alertingChanged(alerting);
202+ }
203+}
204+
182205unity::shell::launcher::QuickListModelInterface *LauncherItem::quickList() const
183206{
184207 return m_quickList;
plugins/Unity/Launcher/launcheritem.h+3
@@ -44,6 +44,7 @@ public:
4444 int count() const override;
4545 bool countVisible() const override;
4646 bool focused() const override;
47+ bool alerting() const override;
4748
4849 unity::shell::launcher::QuickListModelInterface *quickList() const override;
4950
@@ -57,6 +58,7 @@ private:
5758 void setCount(int count);
5859 void setCountVisible(bool countVisible);
5960 void setFocused(bool focused);
61+ void setAlerting(bool alerting);
6062
6163private:
6264 QString m_appId;
@@ -69,6 +71,7 @@ private:
6971 int m_count;
7072 bool m_countVisible;
7173 bool m_focused;
74+ bool m_alerting;
7275 QuickListModel *m_quickList;
7376
7477 friend class LauncherModel;
plugins/Unity/Launcher/launchermodel.cpp+31
@@ -42,6 +42,7 @@ LauncherModel::LauncherModel(QObject *parent):
4242 connect(m_dbusIface, &DBusInterface::countVisibleChanged, this, &LauncherModel::countVisibleChanged);
4343 connect(m_dbusIface, &DBusInterface::progressChanged, this, &LauncherModel::progressChanged);
4444 connect(m_dbusIface, &DBusInterface::refreshCalled, this, &LauncherModel::refresh);
45+ connect(m_dbusIface, &DBusInterface::alertCalled, this, &LauncherModel::alert);
4546
4647 connect(m_settings, &GSettings::changed, this, &LauncherModel::refresh);
4748
@@ -83,11 +84,25 @@ QVariant LauncherModel::data(const QModelIndex &index, int role) const
8384 return item->progress();
8485 case RoleFocused:
8586 return item->focused();
87+ case RoleAlerting:
88+ return item->alerting();
8689 }
8790
8891 return QVariant();
8992}
9093
94+void LauncherModel::setAlerting(const QString &appId, bool alerting) {
95+ int index = findApplication(appId);
96+ if (index >= 0) {
97+ QModelIndex modelIndex = this->index(index);
98+ LauncherItem *item = m_list.at(index);
99+ if (!item->focused()) {
100+ item->setAlerting(alerting);
101+ Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RoleAlerting);
102+ }
103+ }
104+}
105+
91106unity::shell::launcher::LauncherItemInterface *LauncherModel::get(int index) const
92107{
93108 if (index < 0 || index >= m_list.count()) {
@@ -335,6 +350,9 @@ void LauncherModel::countChanged(const QString &appId, int count)
335350 if (idx >= 0) {
336351 LauncherItem *item = m_list.at(idx);
337352 item->setCount(count);
353+ if (item->countVisible()) {
354+ setAlerting(item->appId(), true);
355+ }
338356 Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleCount);
339357 m_asAdapter->syncItems(m_list);
340358 }
@@ -346,6 +364,9 @@ void LauncherModel::countVisibleChanged(const QString &appId, int countVisible)
346364 if (idx >= 0) {
347365 LauncherItem *item = m_list.at(idx);
348366 item->setCountVisible(countVisible);
367+ if (countVisible) {
368+ setAlerting(item->appId(), true);
369+ }
349370 Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleCountVisible);
350371
351372 // If countVisible goes to false, and the item is neither pinned nor recent we can drop it
@@ -454,6 +475,16 @@ void LauncherModel::refresh()
454475 m_asAdapter->syncItems(m_list);
455476}
456477
478+void LauncherModel::alert(const QString &appId)
479+{
480+ int idx = findApplication(appId);
481+ if (idx >= 0) {
482+ LauncherItem *item = m_list.at(idx);
483+ setAlerting(item->appId(), true);
484+ Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleAlerting);
485+ }
486+}
487+
457488void LauncherModel::applicationAdded(const QModelIndex &parent, int row)
458489{
459490 Q_UNUSED(parent);
plugins/Unity/Launcher/launchermodel.h+2
@@ -45,6 +45,7 @@ public:
4545
4646 QVariant data(const QModelIndex &index, int role) const override;
4747
48+ Q_INVOKABLE void setAlerting(const QString &appId, bool alerting) override;
4849 Q_INVOKABLE unity::shell::launcher::LauncherItemInterface* get(int index) const override;
4950 Q_INVOKABLE void move(int oldIndex, int newIndex) override;
5051 Q_INVOKABLE void pin(const QString &appId, int index = -1) override;
@@ -63,6 +64,7 @@ public:
6364public Q_SLOTS:
6465 void requestRemove(const QString &appId) override;
6566 Q_INVOKABLE void refresh();
67+ Q_INVOKABLE void alert(const QString &appId);
6668
6769private:
6870 void storeAppList();
plugins/Unity/Session/dbusunitysessionservice.cpp+47−55
@@ -18,77 +18,69 @@
1818#include "dbusunitysessionservice.h"
1919
2020// Qt
21−#include <QDBusConnection>
22−#include <QDBusInterface>
21+#include <QDBusPendingCall>
2322
2423DBusUnitySessionService::DBusUnitySessionService()
2524 : UnityDBusObject("/com/canonical/Unity/Session", "com.canonical.Unity")
2625{
2726}
2827
29−DBusUnitySessionService::~DBusUnitySessionService()
30−{
31−}
32−
3328void DBusUnitySessionService::Logout()
3429{
35− Q_EMIT logoutReady();
30+ // TODO ask the apps to quit and then emit the signal
31+ Q_EMIT logoutReady();
3632}
3733
3834void DBusUnitySessionService::EndSession()
3935{
40− QDBusConnection connection = QDBusConnection::sessionBus();
41− QDBusInterface iface1 ("com.ubuntu.Upstart",
42− "/com/ubuntu/Upstart",
43− "com.ubuntu.Upstart0_6",
44− connection);
45−
46− iface1.call("EndSession");
36+ const QDBusMessage msg = QDBusMessage::createMethodCall("com.ubuntu.Upstart",
37+ "/com/ubuntu/Upstart",
38+ "com.ubuntu.Upstart0_6",
39+ "EndSession");
40+ QDBusConnection::sessionBus().asyncCall(msg);
4741}
4842
4943void DBusUnitySessionService::RequestLogout()
5044{
51− Q_EMIT logoutRequested(false);
45+ Q_EMIT logoutRequested(false);
5246}
5347
5448void DBusUnitySessionService::Reboot()
5549{
56− QDBusConnection connection = QDBusConnection::systemBus();
57− QDBusInterface iface1 ("org.freedesktop.login1",
58− "/org/freedesktop/login1",
59− "org.freedesktop.login1.Manager",
60− connection);
61−
62− iface1.call("Reboot", false);
50+ QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.login1",
51+ "/org/freedesktop/login1",
52+ "org.freedesktop.login1.Manager",
53+ "Reboot");
54+ msg << false;
55+ QDBusConnection::systemBus().asyncCall(msg);
6356}
6457
6558void DBusUnitySessionService::RequestReboot()
6659{
67− Q_EMIT rebootRequested(false);
60+ Q_EMIT rebootRequested(false);
6861}
6962
7063void DBusUnitySessionService::Shutdown()
7164{
72− QDBusConnection connection = QDBusConnection::systemBus();
73− QDBusInterface iface1 ("org.freedesktop.login1",
74− "/org/freedesktop/login1",
75− "org.freedesktop.login1.Manager",
76− connection);
77−
78− iface1.call("PowerOff", false);
65+ QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.login1",
66+ "/org/freedesktop/login1",
67+ "org.freedesktop.login1.Manager",
68+ "PowerOff");
69+ msg << false;
70+ QDBusConnection::systemBus().asyncCall(msg);
7971}
8072
8173void DBusUnitySessionService::RequestShutdown()
8274{
83− Q_EMIT shutdownRequested(false);
75+ Q_EMIT shutdownRequested(false);
8476}
8577
8678enum class Action : unsigned
8779{
88− LOGOUT = 0,
89− SHUTDOWN,
90− REBOOT,
91− NONE
80+ LOGOUT = 0,
81+ SHUTDOWN,
82+ REBOOT,
83+ NONE
9284};
9385
9486DBusGnomeSessionManagerWrapper::DBusGnomeSessionManagerWrapper()
@@ -96,38 +88,38 @@ DBusGnomeSessionManagerWrapper::DBusGnomeSessionManagerWrapper()
9688{
9789}
9890
99−DBusGnomeSessionManagerWrapper::~DBusGnomeSessionManagerWrapper()
91+void DBusGnomeSessionManagerWrapper::performAsyncCall(const QString &method)
10092{
93+ const QDBusMessage msg = QDBusMessage::createMethodCall("com.canonical.Unity",
94+ "/com/canonical/Unity/Session",
95+ "com.canonical.Unity.Session",
96+ method);
97+ QDBusConnection::sessionBus().asyncCall(msg);
10198}
10299
103100void DBusGnomeSessionManagerWrapper::Open(const unsigned type, const unsigned arg_1, const unsigned max_wait, const QList<QDBusObjectPath> &inhibitors)
104101{
105− Q_UNUSED(arg_1);
106− Q_UNUSED(max_wait);
107− Q_UNUSED(inhibitors);
108−
109− QDBusConnection connection = QDBusConnection::sessionBus();
110− QDBusInterface iface1 ("com.canonical.Unity",
111− "/com/canonical/Unity/Session",
112− "com.canonical.Unity.Session",
113− connection);
102+ Q_UNUSED(arg_1);
103+ Q_UNUSED(max_wait);
104+ Q_UNUSED(inhibitors);
114105
115− Action action = (Action)type;
106+ const Action action = (Action)type;
116107
117− switch (action)
118− {
108+ switch (action)
109+ {
119110 case Action::LOGOUT:
120− iface1.call("RequestLogout");
121− break;
111+ performAsyncCall("RequestLogout");
112+ break;
122113
123114 case Action::REBOOT:
124− iface1.call("RequestShutdown");
125− break;
115+ performAsyncCall("RequestReboot");
116+ break;
126117
127118 case Action::SHUTDOWN:
128− break;
119+ performAsyncCall("RequestShutdown");
120+ break;
129121
130122 default:
131− break;
132− }
123+ break;
124+ }
133125}
plugins/Unity/Session/dbusunitysessionservice.h+11−8
@@ -38,7 +38,7 @@ class DBusUnitySessionService : public UnityDBusObject
3838
3939public:
4040 DBusUnitySessionService();
41− ~DBusUnitySessionService();
41+ ~DBusUnitySessionService() = default;
4242
4343 // For use in QML. Javascript doesn't accept functions beginning with capital letters
4444 Q_INVOKABLE void logout() { Logout(); }
@@ -90,7 +90,7 @@ public Q_SLOTS:
9090 /**
9191 * Logout the system.
9292 *
93− * This method directly logout the system without user's confirmation.
93+ * This method directly logs out the system without user's confirmation.
9494 * Ordinary applications should avoid calling this method. Please call
9595 * RequestLogout() to ask the user to decide logout or not.
9696 */
@@ -99,7 +99,7 @@ public Q_SLOTS:
9999 /**
100100 * Reboot the system.
101101 *
102− * This method directly reboot the system without user's confirmation.
102+ * This method directly reboots the system without user's confirmation.
103103 * Ordinary applications should avoid calling this method. Please call
104104 * RequestReboot() to ask the user to decide reboot or not.
105105 */
@@ -108,7 +108,7 @@ public Q_SLOTS:
108108 /**
109109 * Shutdown the system.
110110 *
111− * This method directly shutdown the system without user's confirmation.
111+ * This method directly shuts down the system without user's confirmation.
112112 * Ordinary applications should avoid calling this method. Please call
113113 * RequestShutdown() to ask the user to decide shutdown or not.
114114 */
@@ -117,7 +117,7 @@ public Q_SLOTS:
117117 /**
118118 * Issue a logout request.
119119 *
120− * This method emit the logoutRequested signal to the shell with a boolean
120+ * This method emits the logoutRequested signal to the shell with a boolean
121121 * which indicates if there's any inhibitors. The shell should receive
122122 * this signal and display a dialog to ask the user to confirm the logout
123123 * action. If confirmed, shell can call Logout() method to logout.
@@ -127,7 +127,7 @@ public Q_SLOTS:
127127 /**
128128 * Issue a reboot request.
129129 *
130− * This method emit the rebootRequested signal to the shell with a boolean
130+ * This method emits the rebootRequested signal to the shell with a boolean
131131 * which indicates if there's any inhibitors. The shell should receive
132132 * this signal and display a dialog to ask the user to confirm the reboot
133133 * action. If confirmed, shell can call Reboot() method to reboot
@@ -137,7 +137,7 @@ public Q_SLOTS:
137137 /**
138138 * Issue a shutdown request.
139139 *
140− * This method emit the shutdownRequested signal to the shell with a
140+ * This method emits the shutdownRequested signal to the shell with a
141141 * boolean which indicates if there's any inhibitors.
142142 * The shell should receive
143143 * this signal and display a dialog to ask the user to confirm the reboot
@@ -161,9 +161,12 @@ class DBusGnomeSessionManagerWrapper : public UnityDBusObject
161161
162162public:
163163 DBusGnomeSessionManagerWrapper();
164− ~DBusGnomeSessionManagerWrapper();
164+ ~DBusGnomeSessionManagerWrapper() = default;
165165
166166public Q_SLOTS:
167167 Q_SCRIPTABLE void Open(const unsigned int type, const unsigned int arg_1, const unsigned int max_wait, const QList<QDBusObjectPath> &inhibitors);
168+
169+private:
170+ void performAsyncCall(const QString &method);
168171};
169172#endif // DBUSUNITYSESSIONSERVICE_H
plugins/Wizard/System.cpp+13−9
@@ -16,7 +16,9 @@
1616
1717#include "System.h"
1818
19−#include <QDBusInterface>
19+#include <QDBusPendingCall>
20+#include <QDBusMessage>
21+#include <QDBusConnection>
2022#include <QDBusMetaType>
2123#include <QDir>
2224#include <QFile>
@@ -65,21 +67,23 @@ void System::setWizardEnabled(bool enabled)
6567void System::setSessionVariable(const QString &variable, const QString &value)
6668{
6769 // We need to update both upstart's and DBus's environment
68− QProcess::execute(QString("initctl set-env --global %1=%2").arg(variable, value));
69−
70− QDBusInterface iface("org.freedesktop.DBus",
71− "/org/freedesktop/DBus",
72− "org.freedesktop.DBus",
73− QDBusConnection::sessionBus());
70+ QProcess::startDetached(QStringLiteral("initctl set-env --global %1=%2").arg(variable, value));
7471
7572 QMap<QString,QString> valueMap;
7673 valueMap.insert(variable, value);
77− iface.call("UpdateActivationEnvironment", QVariant::fromValue(valueMap));
74+
75+ QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.DBus",
76+ "/org/freedesktop/DBus",
77+ "org.freedesktop.DBus",
78+ "UpdateActivationEnvironment");
79+
80+ msg << QVariant::fromValue(valueMap);
81+ QDBusConnection::sessionBus().asyncCall(msg);
7882}
7983
8084void System::updateSessionLanguage(const QString &locale)
8185{
82− QString language = locale.split(".")[0];
86+ const QString language = locale.split(".")[0];
8387
8488 setSessionVariable("LANGUAGE", language);
8589 setSessionVariable("LANG", locale);
po/ar.po+76−61
@@ -8,7 +8,7 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-06-22 12:00+0200\n"
11−"PO-Revision-Date: 2015-07-14 11:45+0000\n"
11+"PO-Revision-Date: 2015-07-17 12:36+0000\n"
1212"Last-Translator: Ibrahim Saed <ibraheem5000@gmail.com>\n"
1313"Language-Team: Arabic <ar@li.org>\n"
1414"MIME-Version: 1.0\n"
@@ -16,7 +16,7 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n % 100 >= "
1818"3 && n % 100 <= 10 ? 3 : n % 100 >= 11 && n % 100 <= 99 ? 4 : 5;\n"
19−"X-Launchpad-Export-Date: 2015-07-16 05:44+0000\n"
19+"X-Launchpad-Export-Date: 2015-07-21 05:35+0000\n"
2020"X-Generator: Launchpad (build 17628)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:112
@@ -364,19 +364,19 @@ msgstr "أدخِل الرمز السرّي"
364364#: qml/Greeter/NarrowView.qml:118
365365#, qt-format
366366msgid "Sorry, incorrect %1"
367−msgstr ""
367+msgstr "نأسف، %1 غير صحيحية"
368368
369369#: qml/Greeter/NarrowView.qml:119
370370msgid "Sorry, incorrect passphrase"
371−msgstr ""
371+msgstr "نأسف، عبارة السّر غير صحيحية"
372372
373373#: qml/Greeter/NarrowView.qml:120
374374msgid "Please re-enter"
375−msgstr ""
375+msgstr "برجاء إعادة إدخالها"
376376
377377#: qml/Greeter/NarrowView.qml:121
378378msgid "Sorry, incorrect passcode"
379−msgstr ""
379+msgstr "نأسف، الرمز السري غير صحيح"
380380
381381#: qml/Notifications/NotificationMenuItemFactory.qml:128
382382msgid "Show password"
@@ -384,7 +384,7 @@ msgstr "عرض كلمة السر"
384384
385385#: qml/Panel/ActiveCallHint.qml:79
386386msgid "Tap to return to call..."
387−msgstr ""
387+msgstr "انقر للعودة إلى المكالمة..."
388388
389389#: qml/Panel/ActiveCallHint.qml:92
390390msgid "Conference"
@@ -398,52 +398,64 @@ msgstr ""
398398#, qt-format
399399msgid "%1 hour"
400400msgid_plural "%1 hours"
401−msgstr[0] ""
402−msgstr[1] ""
401+msgstr[0] "أقل من ساعة (%1)"
402+msgstr[1] "ساعة واحدة (%1)"
403+msgstr[2] "ساعتان (%1)"
404+msgstr[3] "%1 ساعات"
405+msgstr[4] "%1 ساعة"
406+msgstr[5] "%1 ساعة"
403407
404408#: qml/Panel/Indicators/MenuItemFactory.qml:772
405409#, qt-format
406410msgid "%1 minute"
407411msgid_plural "%1 minutes"
408−msgstr[0] ""
409−msgstr[1] ""
412+msgstr[0] "أقل من دقيقة (%1)"
413+msgstr[1] "دقيقة واحدة (%1)"
414+msgstr[2] "دقيقتان (%1)"
415+msgstr[3] "%1 دقائق"
416+msgstr[4] "%1 دقيقة"
417+msgstr[5] "%1 دقيقة"
410418
411419#: qml/Panel/Indicators/MenuItemFactory.qml:777
412420#, qt-format
413421msgid "%1 second"
414422msgid_plural "%1 seconds"
415−msgstr[0] ""
416−msgstr[1] ""
423+msgstr[0] "أقل من ثانية (%1)"
424+msgstr[1] "ثانية واحدة (%1)"
425+msgstr[2] "ثانيتان (%1)"
426+msgstr[3] "%1 ثوان"
427+msgstr[4] "%1 ثانية"
428+msgstr[5] "%1 ثانية"
417429
418430#: qml/Panel/Indicators/MenuItemFactory.qml:780
419431msgid "0 seconds"
420−msgstr ""
432+msgstr "أقل من ثانية"
421433
422434#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
423435#: qml/Panel/Indicators/MenuItemFactory.qml:782
424436#, qt-format
425437msgid "%1 remaining"
426−msgstr ""
438+msgstr "باق %1"
427439
428440#: qml/Panel/Indicators/MenuItemFactory.qml:788
429441msgid "In queue…"
430−msgstr ""
442+msgstr "في الانتظار..."
431443
432444#: qml/Panel/Indicators/MenuItemFactory.qml:792
433445msgid "Downloading"
434−msgstr ""
446+msgstr "التنزيل"
435447
436448#: qml/Panel/Indicators/MenuItemFactory.qml:794
437449msgid "Paused, tap to resume"
438−msgstr ""
450+msgstr "مُلبث، انقر للاستئناف"
439451
440452#: qml/Panel/Indicators/MenuItemFactory.qml:796
441453msgid "Canceled"
442−msgstr ""
454+msgstr "تم إلغائه"
443455
444456#: qml/Panel/Indicators/MenuItemFactory.qml:798
445457msgid "Finished"
446−msgstr ""
458+msgstr "انتهى"
447459
448460#: qml/Panel/Indicators/MenuItemFactory.qml:800
449461msgid "Failed, tap to retry"
@@ -460,36 +472,36 @@ msgstr ""
460472
461473#: qml/Panel/Indicators/RoamingIndication.qml:27
462474msgid "Roaming"
463−msgstr ""
475+msgstr "التجوال"
464476
465477#: qml/Tutorial/TutorialBottomFinish.qml:24
466478msgid "This action does different things for different apps"
467−msgstr ""
479+msgstr "هذا الإجراء يقوم بأشياء مختلفة على التطبيقات المختلفة"
468480
469481#: qml/Tutorial/TutorialBottomFinish.qml:25
470482msgid "Tap here to finish."
471−msgstr ""
483+msgstr "انقر هنا للإنهاء."
472484
473485#: qml/Tutorial/TutorialBottom.qml:28
474486msgid "Open special menus"
475−msgstr ""
487+msgstr "فتح قوائم خاصة"
476488
477489#: qml/Tutorial/TutorialBottom.qml:29
478490msgid "Swipe up from the bottom edge."
479−msgstr ""
491+msgstr "اسحب لأعلى من الحافة السفلى."
480492
481493#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
482494#: qml/Tutorial/TutorialRight.qml:144
483495msgid "Tap here to continue."
484−msgstr ""
496+msgstr "انقر هنا للمتابعة."
485497
486498#: qml/Tutorial/TutorialContent.qml:81
487499msgid "Click here to finish."
488−msgstr ""
500+msgstr "انقر هنا للإنهاء."
489501
490502#: qml/Tutorial/TutorialLeftFinish.qml:24
491503msgid "These are the shortcuts to favorite apps"
492−msgstr ""
504+msgstr "هذه اختصارات للتطبيقات المُفضلة"
493505
494506#: qml/Tutorial/TutorialLeft.qml:26
495507msgid "Open the launcher"
@@ -497,7 +509,7 @@ msgstr ""
497509
498510#: qml/Tutorial/TutorialLeft.qml:27
499511msgid "Short swipe from the left edge."
500−msgstr ""
512+msgstr "اسحب سحبة قصيرة من الحافة اليسرى."
501513
502514#: qml/Tutorial/TutorialPage.qml:178
503515msgid "You almost got it!"
@@ -505,7 +517,7 @@ msgstr ""
505517
506518#: qml/Tutorial/TutorialPage.qml:195
507519msgid "Try again."
508−msgstr ""
520+msgstr "حاول مجددًا."
509521
510522#: qml/Tutorial/TutorialRight.qml:31
511523msgid "To view open apps"
@@ -513,138 +525,141 @@ msgstr ""
513525
514526#: qml/Tutorial/TutorialRight.qml:32
515527msgid "Long swipe from the right edge."
516−msgstr ""
528+msgstr "سحبة طويلة من الحافة اليمنى."
517529
518530#: qml/Tutorial/TutorialRight.qml:122
519531msgid "View all your running tasks."
520−msgstr ""
532+msgstr "عرض جميع المهام النشطة."
521533
522534#: qml/Wizard/Page.qml:89
523535msgctxt "Button: Go back one page in the Wizard"
524536msgid "Back"
525−msgstr ""
537+msgstr "عودة"
526538
527539#: qml/Wizard/Pages/here-terms.qml:27
528540msgid "Terms & Conditions"
529−msgstr ""
541+msgstr "الشروط والأحكام"
530542
531543#: qml/Wizard/Pages/passwd-confirm.qml:49
532544msgid "Confirm passphrase"
533−msgstr ""
545+msgstr "تأكيد العبارة السرية"
534546
535547#: qml/Wizard/Pages/passwd-confirm.qml:50
536548msgid "Confirm passcode"
537−msgstr ""
549+msgstr "تأكيد الرمز السري"
538550
539551#: qml/Wizard/Pages/passwd-confirm.qml:53
540552#: qml/Wizard/Pages/passwd-confirm.qml:54
541553msgid "Please try again."
542−msgstr ""
554+msgstr "حاول مجددا."
543555
544556#: qml/Wizard/Pages/passwd-confirm.qml:82 qml/Wizard/Pages/passwd-set.qml:90
545557#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
546558#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
547559#: qml/Wizard/Pages/60-reporting.qml:50
548560msgid "Continue"
549−msgstr ""
561+msgstr "متابعة"
550562
551563#: qml/Wizard/Pages/passwd-set.qml:61
552564msgid "Choose your passcode"
553−msgstr ""
565+msgstr "اختر الرمز السري خاصتك"
554566
555567#: qml/Wizard/Pages/passwd-set.qml:68
556568msgid "Passphrase must be 4 characters long"
557−msgstr ""
569+msgstr "يجب أن تحتوي العبارة السرية على 4 أحرف على الأقل"
558570
559571#: qml/Wizard/Pages/10-welcome.qml:27
560572msgid "Hi!"
561−msgstr ""
573+msgstr "أهلا!"
562574
563575#: qml/Wizard/Pages/10-welcome.qml:44
564576msgid "Welcome to your Ubuntu phone."
565−msgstr ""
577+msgstr "مرحبا بك في أوبونتو للهواتف."
566578
567579#: qml/Wizard/Pages/10-welcome.qml:52
568580msgid "Let’s get started."
569−msgstr ""
581+msgstr "هيا بنا نبدأ."
570582
571583#: qml/Wizard/Pages/20-sim.qml:25
572584msgid "Add a SIM card and restart your device"
573−msgstr ""
585+msgstr "أدخِل شريحة SIM ثم أعِد تشغيل الجهاز"
574586
575587#: qml/Wizard/Pages/20-sim.qml:59
576588msgid "Without it, you won’t be able to make calls or use text messaging."
577589msgstr ""
590+"بدونها لن تتمكن من إجراء أي مكالمات هاتفية أو استخدام الرسائل النصية."
578591
579592#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
580593msgid "Skip"
581−msgstr ""
594+msgstr "تخطي"
582595
583596#: qml/Wizard/Pages/30-passwd-type.qml:39
584597msgid "Lock security"
585−msgstr ""
598+msgstr "قِفل الأمان"
586599
587600#: qml/Wizard/Pages/30-passwd-type.qml:74
588601msgid "Please select how you’d like to unlock your phone."
589−msgstr ""
602+msgstr "رجاءً اختر الطريقة التي ترغب بأن توصد هاتفك بها."
590603
591604#: qml/Wizard/Pages/30-passwd-type.qml:97
592605msgctxt "Label: Type of security method"
593606msgid "Swipe"
594−msgstr ""
607+msgstr "سحبة"
595608
596609#: qml/Wizard/Pages/30-passwd-type.qml:98
597610msgctxt "Label: Description of security method"
598611msgid "No security"
599−msgstr ""
612+msgstr "بدون أمان"
600613
601614#: qml/Wizard/Pages/30-passwd-type.qml:100
602615msgctxt "Label: Type of security method"
603616msgid "Passcode"
604−msgstr ""
617+msgstr "رمز سري"
605618
606619#: qml/Wizard/Pages/30-passwd-type.qml:101
607620msgctxt "Label: Description of security method"
608621msgid "4 digits only"
609−msgstr ""
622+msgstr "4 أرقام فقط"
610623
611624#: qml/Wizard/Pages/30-passwd-type.qml:103
612625msgctxt "Label: Type of security method"
613626msgid "Passphrase"
614−msgstr ""
627+msgstr "عبارة سرية"
615628
616629#: qml/Wizard/Pages/30-passwd-type.qml:104
617630msgctxt "Label: Description of security method"
618631msgid "Numbers and letters"
619−msgstr ""
632+msgstr "أرقام وحروف"
620633
621634#: qml/Wizard/Pages/40-wifi.qml:29
622635msgid "Connect to Wi‑Fi"
623−msgstr ""
636+msgstr "اتصل بالواي فاي"
624637
625638#: qml/Wizard/Pages/40-wifi.qml:168
626639msgid "Available networks…"
627−msgstr ""
640+msgstr "الشبكات المتاحة..."
628641
629642#: qml/Wizard/Pages/40-wifi.qml:169
630643msgid "No available networks."
631−msgstr ""
644+msgstr "لا توجد شبكات متاحة."
632645
633646#: qml/Wizard/Pages/50-location.qml:27
634647msgid "Location"
635−msgstr ""
648+msgstr "الموقع"
636649
637650#: qml/Wizard/Pages/50-location.qml:62
638651msgid "Let the phone detect your location:"
639−msgstr ""
652+msgstr "دع هاتفك يحدد موقعك:"
640653
641654#: qml/Wizard/Pages/50-location.qml:69
642655msgid "Using GPS only (less accurate)"
643−msgstr ""
656+msgstr "باستخدام نظام تحديد المواقع (GPS) فقط (أقل دقّة)"
644657
645658#: qml/Wizard/Pages/50-location.qml:86
646659msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
647660msgstr ""
661+"باستخدام نظام تحديد المواقع (GPS)، والواي فاي، وبيانات الشبكة الخلوية "
662+"(مستحسن)"
648663
649664#. Translators: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
650665#: qml/Wizard/Pages/50-location.qml:103
@@ -659,11 +674,11 @@ msgstr ""
659674
660675#: qml/Wizard/Pages/50-location.qml:124
661676msgid "You can change your mind later in <b>System Settings</b>."
662−msgstr ""
677+msgstr "يمكنك تغيير رأيك في وقت لاحق عن طريق <b>إعدادات النظام</b>."
663678
664679#: qml/Wizard/Pages/60-reporting.qml:24
665680msgid "Improving your experience"
666−msgstr ""
681+msgstr "تحسين تجربتك"
667682
668683#: qml/Wizard/Pages/60-reporting.qml:36
669684msgid ""
po/ga.ponew file+697
@@ -0,0 +1,697 @@
1+# Irish translation for unity8
2+# Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015
3+# This file is distributed under the same license as the unity8 package.
4+# FIRST AUTHOR <EMAIL@ADDRESS>, 2015.
5+#
6+msgid ""
7+msgstr ""
8+"Project-Id-Version: unity8\n"
9+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10+"POT-Creation-Date: 2015-06-22 12:00+0200\n"
11+"PO-Revision-Date: 2015-07-20 16:25+0000\n"
12+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13+"Language-Team: Irish <ga@li.org>\n"
14+"MIME-Version: 1.0\n"
15+"Content-Type: text/plain; charset=UTF-8\n"
16+"Content-Transfer-Encoding: 8bit\n"
17+"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n==2 ? 1 : 2;\n"
18+"X-Launchpad-Export-Date: 2015-07-21 05:35+0000\n"
19+"X-Generator: Launchpad (build 17628)\n"
20+
21+#: plugins/LightDM/Greeter.cpp:112
22+msgid "Password: "
23+msgstr "Focal Faire: "
24+
25+#: plugins/Unity/Launcher/launcheritem.cpp:45
26+#: plugins/Unity/Launcher/launcheritem.cpp:100
27+msgid "Pin shortcut"
28+msgstr ""
29+
30+#: plugins/Unity/Launcher/launcheritem.cpp:100
31+msgid "Unpin shortcut"
32+msgstr ""
33+
34+#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
35+#. This format string is used for showing, on a 12-hour clock, times that happen yesterday.
36+#. (\u2003 is a unicode em space which is slightly wider than a normal space.)
37+#. en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 1:00 PM"
38+#: plugins/Utils/relativetimeformatter.cpp:157
39+msgid "Yesterday %l:%M %p"
40+msgstr ""
41+
42+#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
43+#. This format string is used for showing, on a 12-hour clock, times that happened today.
44+#. en_US example: "%l:%M %p" --> "1:00 PM"
45+#: plugins/Utils/relativetimeformatter.cpp:164
46+msgid "%l:%M %p"
47+msgstr ""
48+
49+#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
50+#. This format string is used for showing, on a 12-hour clock, events/appointments that happen tomorrow.
51+#. (\u2003 is a unicode em space which is slightly wider than a normal space.)
52+#. en_US example: "Tomorrow\u2003%l:%M %p" --> "Tomorrow 1:00 PM"
53+#: plugins/Utils/relativetimeformatter.cpp:172
54+msgid "Tomorrow %l:%M %p"
55+msgstr ""
56+
57+#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
58+#. This format string is used for showing, on a 12-hour clock, times that happened in the last week.
59+#. (\u2003 is a unicode em space which is slightly wider than a normal space.)
60+#. en_US example: "%a\u2003%l:%M %p" --> "Fri 1:00 PM"
61+#: plugins/Utils/relativetimeformatter.cpp:181
62+msgid "%a %l:%M %p"
63+msgstr ""
64+
65+#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
66+#. This format string is used for showing, on a 12-hour clock, times that happened before a week from now.
67+#. (\u2003 is a unicode em space which is slightly wider than a normal space.)
68+#. en_US example: "%a %d %b\u2003%l:%M %p" --> "Fri Oct 31 1:00 PM"
69+#. en_GB example: "%a %b %d\u2003%l:%M %p" --> "Fri 31 Oct 1:00 PM"
70+#: plugins/Utils/relativetimeformatter.cpp:190
71+msgid "%a %d %b %l:%M %p"
72+msgstr ""
73+
74+#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
75+#. This format string is used for showing, on a 24-hour clock, times that happen yesterday.
76+#. (\u2003 is a unicode em space which is slightly wider than a normal space.)
77+#. en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 13:00"
78+#: plugins/Utils/relativetimeformatter.cpp:201
79+msgid "Yesterday %H:%M"
80+msgstr ""
81+
82+#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
83+#. This format string is used for showing, on a 24-hour clock, times that happened today.
84+#. en_US example: "%H:%M" --> "13:00"
85+#: plugins/Utils/relativetimeformatter.cpp:208
86+msgid "%H:%M"
87+msgstr ""
88+
89+#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
90+#. This format string is used for showing, on a 24-hour clock, events/appointments that happen tomorrow.
91+#. (\u2003 is a unicode em space which is slightly wider than a normal space.)
92+#. en_US example: "Tomorrow\u2003%l:%M %p" --> "Tomorrow 13:00"
93+#: plugins/Utils/relativetimeformatter.cpp:216
94+msgid "Tomorrow %H:%M"
95+msgstr ""
96+
97+#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
98+#. This format string is used for showing, on a 24-hour clock, times that happened in the last week.
99+#. (\u2003 is a unicode em space which is slightly wider than a normal space.)
100+#. en_US example: "%a\u2003%H:%M" --> "Fri 13:00"
101+#: plugins/Utils/relativetimeformatter.cpp:225
102+msgid "%a %H:%M"
103+msgstr ""
104+
105+#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
106+#. This format string is used for showing, on a 24-hour clock, times that happened before a week from now.
107+#. (\u2003 is a unicode em space which is slightly wider than a normal space.)
108+#. en_US example: "%a %d %b\u2003%H:%M" --> "Fri Oct 31 13:00"
109+#. en_GB example: "%a %b %d\u2003%H:%M" --> "Fri 31 Oct 13:00"
110+#: plugins/Utils/relativetimeformatter.cpp:234
111+msgid "%a %d %b %H:%M"
112+msgstr ""
113+
114+#: qml/Components/DelayedLockscreen.qml:49
115+msgid "Device Locked"
116+msgstr ""
117+
118+#: qml/Components/DelayedLockscreen.qml:64
119+msgid "You have been locked out due to too many failed passphrase attempts."
120+msgstr ""
121+
122+#: qml/Components/DelayedLockscreen.qml:65
123+msgid "You have been locked out due to too many failed passcode attempts."
124+msgstr ""
125+
126+#: qml/Components/DelayedLockscreen.qml:74
127+#, qt-format
128+msgid "Please wait %1 minute and then try again…"
129+msgid_plural "Please wait %1 minutes and then try again…"
130+msgstr[0] ""
131+msgstr[1] ""
132+
133+#: qml/Components/Dialogs.qml:69
134+msgctxt "Title: Lock/Log out dialog"
135+msgid "Log out"
136+msgstr ""
137+
138+#: qml/Components/Dialogs.qml:70
139+msgid "Are you sure you want to log out?"
140+msgstr ""
141+
142+#: qml/Components/Dialogs.qml:72
143+msgctxt "Button: Lock the system"
144+msgid "Lock"
145+msgstr ""
146+
147+#: qml/Components/Dialogs.qml:79
148+msgctxt "Button: Log out from the system"
149+msgid "Log Out"
150+msgstr ""
151+
152+#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
153+#: qml/Components/Dialogs.qml:173
154+msgid "Cancel"
155+msgstr ""
156+
157+#: qml/Components/Dialogs.qml:98
158+msgctxt "Title: Reboot/Shut down dialog"
159+msgid "Shut down"
160+msgstr ""
161+
162+#: qml/Components/Dialogs.qml:99
163+msgid "Are you sure you want to shut down?"
164+msgstr ""
165+
166+#: qml/Components/Dialogs.qml:101
167+msgctxt "Button: Reboot the system"
168+msgid "Reboot"
169+msgstr ""
170+
171+#: qml/Components/Dialogs.qml:109
172+msgctxt "Button: Shut down the system"
173+msgid "Shut down"
174+msgstr ""
175+
176+#: qml/Components/Dialogs.qml:129
177+msgctxt "Title: Reboot dialog"
178+msgid "Reboot"
179+msgstr ""
180+
181+#: qml/Components/Dialogs.qml:130
182+msgid "Are you sure you want to reboot?"
183+msgstr ""
184+
185+#: qml/Components/Dialogs.qml:132
186+msgid "No"
187+msgstr ""
188+
189+#: qml/Components/Dialogs.qml:138
190+msgid "Yes"
191+msgstr ""
192+
193+#: qml/Components/Dialogs.qml:152
194+msgctxt "Title: Power off/Restart dialog"
195+msgid "Power"
196+msgstr ""
197+
198+#: qml/Components/Dialogs.qml:153
199+msgid ""
200+"Are you sure you would like\n"
201+"to power off?"
202+msgstr ""
203+
204+#: qml/Components/Dialogs.qml:155
205+msgctxt "Button: Power off the system"
206+msgid "Power off"
207+msgstr ""
208+
209+#: qml/Components/Dialogs.qml:164
210+msgctxt "Button: Restart the system"
211+msgid "Restart"
212+msgstr ""
213+
214+#: qml/Components/Lockscreen.qml:245
215+msgid "Return to Call"
216+msgstr ""
217+
218+#: qml/Components/Lockscreen.qml:245
219+msgid "Emergency Call"
220+msgstr ""
221+
222+#: qml/Components/Lockscreen.qml:277
223+msgid "OK"
224+msgstr ""
225+
226+#: qml/Dash/GenericScopeView.qml:532 qml/Dash/GenericScopeView.qml:690
227+msgid "See less"
228+msgstr ""
229+
230+#: qml/Dash/GenericScopeView.qml:532
231+msgid "See all"
232+msgstr ""
233+
234+#: qml/Dash/GenericScopeView.qml:594
235+msgctxt "Label: Hint for dash search line edit"
236+msgid "Search"
237+msgstr ""
238+
239+#: qml/Dash/PageHeader.qml:269
240+msgctxt "Button: Open the Ubuntu Store"
241+msgid "Store"
242+msgstr ""
243+
244+#: qml/Dash/PageHeader.qml:276
245+msgctxt "Button: Start a search in the current dash scope"
246+msgid "Search"
247+msgstr ""
248+
249+#: qml/Dash/PageHeader.qml:286
250+msgctxt "Button: Show the current dash scope settings"
251+msgid "Settings"
252+msgstr ""
253+
254+#: qml/Dash/PageHeader.qml:293
255+msgid "Remove from Favorites"
256+msgstr ""
257+
258+#: qml/Dash/PageHeader.qml:293
259+msgid "Add to Favorites"
260+msgstr ""
261+
262+#: qml/Dash/Previews/PreviewActionCombo.qml:35
263+msgid "More..."
264+msgstr ""
265+
266+#: qml/Dash/Previews/PreviewActionCombo.qml:35
267+msgid "Less..."
268+msgstr ""
269+
270+#: qml/Dash/Previews/PreviewRatingInput.qml:84
271+msgid "Rate this"
272+msgstr ""
273+
274+#: qml/Dash/Previews/PreviewRatingInput.qml:129
275+msgid "Add a review"
276+msgstr ""
277+
278+#: qml/Dash/Previews/PreviewRatingInput.qml:169
279+msgid "Send"
280+msgstr ""
281+
282+#: qml/Dash/PullToRefreshScopeStyle.qml:55
283+msgid "Pull to refresh…"
284+msgstr ""
285+
286+#: qml/Dash/PullToRefreshScopeStyle.qml:60
287+msgid "Release to refresh…"
288+msgstr ""
289+
290+#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
291+msgid "Enable location data"
292+msgstr ""
293+
294+#: qml/Dash/ScopesList.qml:67
295+msgid "Manage"
296+msgstr ""
297+
298+#: qml/Dash/ScopesList.qml:113
299+msgid "Home"
300+msgstr ""
301+
302+#: qml/Dash/ScopesList.qml:114
303+msgid "Also installed"
304+msgstr ""
305+
306+#: qml/Greeter/CoverPage.qml:107
307+msgid "Unlock"
308+msgstr ""
309+
310+#: qml/Greeter/LoginList.qml:236
311+msgid "Retry"
312+msgstr ""
313+
314+#: qml/Greeter/LoginList.qml:237
315+msgid "Tap to unlock"
316+msgstr ""
317+
318+#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
319+msgid "Sorry, incorrect passphrase."
320+msgstr ""
321+
322+#: qml/Greeter/NarrowView.qml:54 qml/Wizard/Pages/passwd-confirm.qml:54
323+msgid "Sorry, incorrect passcode."
324+msgstr ""
325+
326+#: qml/Greeter/NarrowView.qml:55
327+msgid "This will be your last attempt."
328+msgstr ""
329+
330+#: qml/Greeter/NarrowView.qml:57
331+msgid ""
332+"If passphrase is entered incorrectly, your phone will conduct a factory "
333+"reset and all personal data will be deleted."
334+msgstr ""
335+
336+#: qml/Greeter/NarrowView.qml:58
337+msgid ""
338+"If passcode is entered incorrectly, your phone will conduct a factory reset "
339+"and all personal data will be deleted."
340+msgstr ""
341+
342+#: qml/Greeter/NarrowView.qml:115
343+#, qt-format
344+msgid "Enter %1"
345+msgstr ""
346+
347+#: qml/Greeter/NarrowView.qml:116 qml/Wizard/Pages/passwd-set.qml:60
348+msgid "Enter passphrase"
349+msgstr ""
350+
351+#: qml/Greeter/NarrowView.qml:117
352+msgid "Enter passcode"
353+msgstr ""
354+
355+#: qml/Greeter/NarrowView.qml:118
356+#, qt-format
357+msgid "Sorry, incorrect %1"
358+msgstr ""
359+
360+#: qml/Greeter/NarrowView.qml:119
361+msgid "Sorry, incorrect passphrase"
362+msgstr ""
363+
364+#: qml/Greeter/NarrowView.qml:120
365+msgid "Please re-enter"
366+msgstr ""
367+
368+#: qml/Greeter/NarrowView.qml:121
369+msgid "Sorry, incorrect passcode"
370+msgstr ""
371+
372+#: qml/Notifications/NotificationMenuItemFactory.qml:128
373+msgid "Show password"
374+msgstr ""
375+
376+#: qml/Panel/ActiveCallHint.qml:79
377+msgid "Tap to return to call..."
378+msgstr ""
379+
380+#: qml/Panel/ActiveCallHint.qml:92
381+msgid "Conference"
382+msgstr ""
383+
384+#: qml/Panel/Indicators/MenuItemFactory.qml:639
385+msgid "Nothing is playing"
386+msgstr ""
387+
388+#: qml/Panel/Indicators/MenuItemFactory.qml:768
389+#, qt-format
390+msgid "%1 hour"
391+msgid_plural "%1 hours"
392+msgstr[0] ""
393+msgstr[1] ""
394+
395+#: qml/Panel/Indicators/MenuItemFactory.qml:772
396+#, qt-format
397+msgid "%1 minute"
398+msgid_plural "%1 minutes"
399+msgstr[0] ""
400+msgstr[1] ""
401+
402+#: qml/Panel/Indicators/MenuItemFactory.qml:777
403+#, qt-format
404+msgid "%1 second"
405+msgid_plural "%1 seconds"
406+msgstr[0] ""
407+msgstr[1] ""
408+
409+#: qml/Panel/Indicators/MenuItemFactory.qml:780
410+msgid "0 seconds"
411+msgstr ""
412+
413+#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
414+#: qml/Panel/Indicators/MenuItemFactory.qml:782
415+#, qt-format
416+msgid "%1 remaining"
417+msgstr ""
418+
419+#: qml/Panel/Indicators/MenuItemFactory.qml:788
420+msgid "In queue…"
421+msgstr ""
422+
423+#: qml/Panel/Indicators/MenuItemFactory.qml:792
424+msgid "Downloading"
425+msgstr ""
426+
427+#: qml/Panel/Indicators/MenuItemFactory.qml:794
428+msgid "Paused, tap to resume"
429+msgstr ""
430+
431+#: qml/Panel/Indicators/MenuItemFactory.qml:796
432+msgid "Canceled"
433+msgstr ""
434+
435+#: qml/Panel/Indicators/MenuItemFactory.qml:798
436+msgid "Finished"
437+msgstr ""
438+
439+#: qml/Panel/Indicators/MenuItemFactory.qml:800
440+msgid "Failed, tap to retry"
441+msgstr ""
442+
443+#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
444+msgctxt "Label: Hint in message indicator line edit"
445+msgid "Reply"
446+msgstr ""
447+
448+#: qml/Panel/Indicators/ModemInfoItem.qml:105
449+msgid "Unlock SIM"
450+msgstr ""
451+
452+#: qml/Panel/Indicators/RoamingIndication.qml:27
453+msgid "Roaming"
454+msgstr ""
455+
456+#: qml/Tutorial/TutorialBottomFinish.qml:24
457+msgid "This action does different things for different apps"
458+msgstr ""
459+
460+#: qml/Tutorial/TutorialBottomFinish.qml:25
461+msgid "Tap here to finish."
462+msgstr ""
463+
464+#: qml/Tutorial/TutorialBottom.qml:28
465+msgid "Open special menus"
466+msgstr ""
467+
468+#: qml/Tutorial/TutorialBottom.qml:29
469+msgid "Swipe up from the bottom edge."
470+msgstr ""
471+
472+#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
473+#: qml/Tutorial/TutorialRight.qml:144
474+msgid "Tap here to continue."
475+msgstr ""
476+
477+#: qml/Tutorial/TutorialContent.qml:81
478+msgid "Click here to finish."
479+msgstr ""
480+
481+#: qml/Tutorial/TutorialLeftFinish.qml:24
482+msgid "These are the shortcuts to favorite apps"
483+msgstr ""
484+
485+#: qml/Tutorial/TutorialLeft.qml:26
486+msgid "Open the launcher"
487+msgstr ""
488+
489+#: qml/Tutorial/TutorialLeft.qml:27
490+msgid "Short swipe from the left edge."
491+msgstr ""
492+
493+#: qml/Tutorial/TutorialPage.qml:178
494+msgid "You almost got it!"
495+msgstr ""
496+
497+#: qml/Tutorial/TutorialPage.qml:195
498+msgid "Try again."
499+msgstr ""
500+
501+#: qml/Tutorial/TutorialRight.qml:31
502+msgid "To view open apps"
503+msgstr ""
504+
505+#: qml/Tutorial/TutorialRight.qml:32
506+msgid "Long swipe from the right edge."
507+msgstr ""
508+
509+#: qml/Tutorial/TutorialRight.qml:122
510+msgid "View all your running tasks."
511+msgstr ""
512+
513+#: qml/Wizard/Page.qml:89
514+msgctxt "Button: Go back one page in the Wizard"
515+msgid "Back"
516+msgstr ""
517+
518+#: qml/Wizard/Pages/here-terms.qml:27
519+msgid "Terms & Conditions"
520+msgstr ""
521+
522+#: qml/Wizard/Pages/passwd-confirm.qml:49
523+msgid "Confirm passphrase"
524+msgstr ""
525+
526+#: qml/Wizard/Pages/passwd-confirm.qml:50
527+msgid "Confirm passcode"
528+msgstr ""
529+
530+#: qml/Wizard/Pages/passwd-confirm.qml:53
531+#: qml/Wizard/Pages/passwd-confirm.qml:54
532+msgid "Please try again."
533+msgstr ""
534+
535+#: qml/Wizard/Pages/passwd-confirm.qml:82 qml/Wizard/Pages/passwd-set.qml:90
536+#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
537+#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
538+#: qml/Wizard/Pages/60-reporting.qml:50
539+msgid "Continue"
540+msgstr ""
541+
542+#: qml/Wizard/Pages/passwd-set.qml:61
543+msgid "Choose your passcode"
544+msgstr ""
545+
546+#: qml/Wizard/Pages/passwd-set.qml:68
547+msgid "Passphrase must be 4 characters long"
548+msgstr ""
549+
550+#: qml/Wizard/Pages/10-welcome.qml:27
551+msgid "Hi!"
552+msgstr ""
553+
554+#: qml/Wizard/Pages/10-welcome.qml:44
555+msgid "Welcome to your Ubuntu phone."
556+msgstr ""
557+
558+#: qml/Wizard/Pages/10-welcome.qml:52
559+msgid "Let’s get started."
560+msgstr ""
561+
562+#: qml/Wizard/Pages/20-sim.qml:25
563+msgid "Add a SIM card and restart your device"
564+msgstr ""
565+
566+#: qml/Wizard/Pages/20-sim.qml:59
567+msgid "Without it, you won’t be able to make calls or use text messaging."
568+msgstr ""
569+
570+#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
571+msgid "Skip"
572+msgstr ""
573+
574+#: qml/Wizard/Pages/30-passwd-type.qml:39
575+msgid "Lock security"
576+msgstr ""
577+
578+#: qml/Wizard/Pages/30-passwd-type.qml:74
579+msgid "Please select how you’d like to unlock your phone."
580+msgstr ""
581+
582+#: qml/Wizard/Pages/30-passwd-type.qml:97
583+msgctxt "Label: Type of security method"
584+msgid "Swipe"
585+msgstr ""
586+
587+#: qml/Wizard/Pages/30-passwd-type.qml:98
588+msgctxt "Label: Description of security method"
589+msgid "No security"
590+msgstr ""
591+
592+#: qml/Wizard/Pages/30-passwd-type.qml:100
593+msgctxt "Label: Type of security method"
594+msgid "Passcode"
595+msgstr ""
596+
597+#: qml/Wizard/Pages/30-passwd-type.qml:101
598+msgctxt "Label: Description of security method"
599+msgid "4 digits only"
600+msgstr ""
601+
602+#: qml/Wizard/Pages/30-passwd-type.qml:103
603+msgctxt "Label: Type of security method"
604+msgid "Passphrase"
605+msgstr ""
606+
607+#: qml/Wizard/Pages/30-passwd-type.qml:104
608+msgctxt "Label: Description of security method"
609+msgid "Numbers and letters"
610+msgstr ""
611+
612+#: qml/Wizard/Pages/40-wifi.qml:29
613+msgid "Connect to Wi‑Fi"
614+msgstr ""
615+
616+#: qml/Wizard/Pages/40-wifi.qml:168
617+msgid "Available networks…"
618+msgstr ""
619+
620+#: qml/Wizard/Pages/40-wifi.qml:169
621+msgid "No available networks."
622+msgstr ""
623+
624+#: qml/Wizard/Pages/50-location.qml:27
625+msgid "Location"
626+msgstr ""
627+
628+#: qml/Wizard/Pages/50-location.qml:62
629+msgid "Let the phone detect your location:"
630+msgstr ""
631+
632+#: qml/Wizard/Pages/50-location.qml:69
633+msgid "Using GPS only (less accurate)"
634+msgstr ""
635+
636+#: qml/Wizard/Pages/50-location.qml:86
637+msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
638+msgstr ""
639+
640+#. Translators: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
641+#: qml/Wizard/Pages/50-location.qml:103
642+msgid ""
643+"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
644+"conditions</a>."
645+msgstr ""
646+
647+#: qml/Wizard/Pages/50-location.qml:112
648+msgid "Not at all"
649+msgstr ""
650+
651+#: qml/Wizard/Pages/50-location.qml:124
652+msgid "You can change your mind later in <b>System Settings</b>."
653+msgstr ""
654+
655+#: qml/Wizard/Pages/60-reporting.qml:24
656+msgid "Improving your experience"
657+msgstr ""
658+
659+#: qml/Wizard/Pages/60-reporting.qml:36
660+msgid ""
661+"Your phone is set up to automatically report errors to Canonical and its "
662+"partners, the makers of the operating system."
663+msgstr ""
664+
665+#: qml/Wizard/Pages/60-reporting.qml:43
666+msgid ""
667+"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
668+"Privacy</b>"
669+msgstr ""
670+
671+#: qml/Wizard/Pages/80-finished.qml:24
672+msgid "All done"
673+msgstr ""
674+
675+#: qml/Wizard/Pages/80-finished.qml:39
676+msgid "Nice work!"
677+msgstr ""
678+
679+#: qml/Wizard/Pages/80-finished.qml:46
680+msgid "Your phone is now ready to use."
681+msgstr ""
682+
683+#: qml/Wizard/Pages/80-finished.qml:53
684+msgid "Finish"
685+msgstr ""
686+
687+#. Translators: This is the arrow for "Back" buttons
688+#: qml/Wizard/StackButton.qml:39
689+#, qt-format
690+msgid "〈 %1"
691+msgstr ""
692+
693+#. Translators: This is the arrow for "Forward" buttons
694+#: qml/Wizard/StackButton.qml:42
695+#, qt-format
696+msgid "%1 〉"
697+msgstr ""
po/sw.po+86−80
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-06-22 12:00+0200\n"
11−"PO-Revision-Date: 2015-07-10 20:22+0000\n"
11+"PO-Revision-Date: 2015-07-17 20:02+0000\n"
1212"Last-Translator: Valentine <mwangi_valentino@yahoo.com>\n"
1313"Language-Team: Swahili <sw@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-11 05:33+0000\n"
19−"X-Generator: Launchpad (build 17620)\n"
18+"X-Launchpad-Export-Date: 2015-07-21 05:35+0000\n"
19+"X-Generator: Launchpad (build 17628)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -25,11 +25,11 @@ msgstr "Neno siri "
2525#: plugins/Unity/Launcher/launcheritem.cpp:45
2626#: plugins/Unity/Launcher/launcheritem.cpp:100
2727msgid "Pin shortcut"
28−msgstr ""
28+msgstr "Njia ya mkato ya nambari ya siri"
2929
3030#: plugins/Unity/Launcher/launcheritem.cpp:100
3131msgid "Unpin shortcut"
32−msgstr ""
32+msgstr "Toa njia ya mkato ya nambari ya siri"
3333
3434#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
3535#. This format string is used for showing, on a 12-hour clock, times that happen yesterday.
@@ -60,7 +60,7 @@ msgstr "Kesho %l:%M %p"
6060#. en_US example: "%a\u2003%l:%M %p" --> "Fri 1:00 PM"
6161#: plugins/Utils/relativetimeformatter.cpp:181
6262msgid "%a %l:%M %p"
63−msgstr ""
63+msgstr "%a %l:%M %p"
6464
6565#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
6666#. This format string is used for showing, on a 12-hour clock, times that happened before a week from now.
@@ -118,10 +118,12 @@ msgstr "kifaa kimefungwa"
118118#: qml/Components/DelayedLockscreen.qml:64
119119msgid "You have been locked out due to too many failed passphrase attempts."
120120msgstr ""
121+"Huwezi kulogi kwa sababu umenoa kubonyeza vizuri neno la siri mara nyingi."
121122
122123#: qml/Components/DelayedLockscreen.qml:65
123124msgid "You have been locked out due to too many failed passcode attempts."
124125msgstr ""
126+"Huwezi kulogi kwa sababu umenoa kubonyeza vizuri neno la siri mara nyingi."
125127
126128#: qml/Components/DelayedLockscreen.qml:74
127129#, qt-format
@@ -193,7 +195,7 @@ msgstr "Ndio"
193195#: qml/Components/Dialogs.qml:152
194196msgctxt "Title: Power off/Restart dialog"
195197msgid "Power"
196−msgstr ""
198+msgstr "Zima"
197199
198200#: qml/Components/Dialogs.qml:153
199201msgid ""
@@ -213,7 +215,7 @@ msgstr "Zima na uwashe"
213215
214216#: qml/Components/Lockscreen.qml:245
215217msgid "Return to Call"
216−msgstr ""
218+msgstr "Rudi nyuma upige simu"
217219
218220#: qml/Components/Lockscreen.qml:245
219221msgid "Emergency Call"
@@ -269,11 +271,11 @@ msgstr "Kidogo..."
269271
270272#: qml/Dash/Previews/PreviewRatingInput.qml:84
271273msgid "Rate this"
272−msgstr ""
274+msgstr "Pea kiwango"
273275
274276#: qml/Dash/Previews/PreviewRatingInput.qml:129
275277msgid "Add a review"
276−msgstr ""
278+msgstr "Ongeza pitio"
277279
278280#: qml/Dash/Previews/PreviewRatingInput.qml:169
279281msgid "Send"
@@ -281,11 +283,11 @@ msgstr "Tuma"
281283
282284#: qml/Dash/PullToRefreshScopeStyle.qml:55
283285msgid "Pull to refresh…"
284−msgstr ""
286+msgstr "Vuta unawarishe"
285287
286288#: qml/Dash/PullToRefreshScopeStyle.qml:60
287289msgid "Release to refresh…"
288−msgstr ""
290+msgstr "Achilia unawarishe"
289291
290292#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
291293msgid "Enable location data"
@@ -317,27 +319,31 @@ msgstr "Gusa ufungue"
317319
318320#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
319321msgid "Sorry, incorrect passphrase."
320−msgstr ""
322+msgstr "Samahani, neno siri si hilo"
321323
322324#: qml/Greeter/NarrowView.qml:54 qml/Wizard/Pages/passwd-confirm.qml:54
323325msgid "Sorry, incorrect passcode."
324−msgstr ""
326+msgstr "Samahani, neno siri si hilo"
325327
326328#: qml/Greeter/NarrowView.qml:55
327329msgid "This will be your last attempt."
328−msgstr ""
330+msgstr "Hii ndiyo nafasi ya mwisho"
329331
330332#: qml/Greeter/NarrowView.qml:57
331333msgid ""
332334"If passphrase is entered incorrectly, your phone will conduct a factory "
333335"reset and all personal data will be deleted."
334336msgstr ""
337+"Kama utabonyeza neno la siri lisilofaa, simu yako itajiweka upya na data "
338+"yako yote itafutwa."
335339
336340#: qml/Greeter/NarrowView.qml:58
337341msgid ""
338342"If passcode is entered incorrectly, your phone will conduct a factory reset "
339343"and all personal data will be deleted."
340344msgstr ""
345+"Kama utabonyeza neno la siri lisilofaa, simu yako itajiweka upya na data "
346+"yako yote itafutwa."
341347
342348#: qml/Greeter/NarrowView.qml:115
343349#, qt-format
@@ -350,75 +356,75 @@ msgstr "Bonyeza neno siri"
350356
351357#: qml/Greeter/NarrowView.qml:117
352358msgid "Enter passcode"
353−msgstr ""
359+msgstr "Bonyeza neno la siri"
354360
355361#: qml/Greeter/NarrowView.qml:118
356362#, qt-format
357363msgid "Sorry, incorrect %1"
358−msgstr ""
364+msgstr "Samahani, %1 halidhihirishi"
359365
360366#: qml/Greeter/NarrowView.qml:119
361367msgid "Sorry, incorrect passphrase"
362−msgstr ""
368+msgstr "Samahani, neno la siri si hilo"
363369
364370#: qml/Greeter/NarrowView.qml:120
365371msgid "Please re-enter"
366−msgstr ""
372+msgstr "Tafadhali bonyeza tena"
367373
368374#: qml/Greeter/NarrowView.qml:121
369375msgid "Sorry, incorrect passcode"
370−msgstr ""
376+msgstr "Samahani, neno la siri si hilo"
371377
372378#: qml/Notifications/NotificationMenuItemFactory.qml:128
373379msgid "Show password"
374−msgstr ""
380+msgstr "Onyesha neno la siri"
375381
376382#: qml/Panel/ActiveCallHint.qml:79
377383msgid "Tap to return to call..."
378−msgstr ""
384+msgstr "Gusa urudi ili upige simu"
379385
380386#: qml/Panel/ActiveCallHint.qml:92
381387msgid "Conference"
382−msgstr ""
388+msgstr "Mkutano"
383389
384390#: qml/Panel/Indicators/MenuItemFactory.qml:639
385391msgid "Nothing is playing"
386−msgstr ""
392+msgstr "Hakuna linalocheza"
387393
388394#: qml/Panel/Indicators/MenuItemFactory.qml:768
389395#, qt-format
390396msgid "%1 hour"
391397msgid_plural "%1 hours"
392−msgstr[0] ""
393−msgstr[1] ""
398+msgstr[0] "Saa %1"
399+msgstr[1] "Mass %1"
394400
395401#: qml/Panel/Indicators/MenuItemFactory.qml:772
396402#, qt-format
397403msgid "%1 minute"
398404msgid_plural "%1 minutes"
399−msgstr[0] ""
400−msgstr[1] ""
405+msgstr[0] "Dakika %1"
406+msgstr[1] "Dakika %1"
401407
402408#: qml/Panel/Indicators/MenuItemFactory.qml:777
403409#, qt-format
404410msgid "%1 second"
405411msgid_plural "%1 seconds"
406−msgstr[0] ""
407−msgstr[1] ""
412+msgstr[0] "Sekunde %1"
413+msgstr[1] "Sekunde %1"
408414
409415#: qml/Panel/Indicators/MenuItemFactory.qml:780
410416msgid "0 seconds"
411−msgstr ""
417+msgstr "sekunde 0"
412418
413419#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
414420#: qml/Panel/Indicators/MenuItemFactory.qml:782
415421#, qt-format
416422msgid "%1 remaining"
417−msgstr ""
423+msgstr "%1 imebaki"
418424
419425#: qml/Panel/Indicators/MenuItemFactory.qml:788
420426msgid "In queue…"
421−msgstr ""
427+msgstr "Kwenye foleni"
422428
423429#: qml/Panel/Indicators/MenuItemFactory.qml:792
424430msgid "Downloading"
@@ -426,204 +432,204 @@ msgstr ""
426432
427433#: qml/Panel/Indicators/MenuItemFactory.qml:794
428434msgid "Paused, tap to resume"
429−msgstr ""
435+msgstr "Imepumzishwa, gusa uendelee"
430436
431437#: qml/Panel/Indicators/MenuItemFactory.qml:796
432438msgid "Canceled"
433−msgstr ""
439+msgstr "Imeghairishwa"
434440
435441#: qml/Panel/Indicators/MenuItemFactory.qml:798
436442msgid "Finished"
437−msgstr ""
443+msgstr "Imemaliza"
438444
439445#: qml/Panel/Indicators/MenuItemFactory.qml:800
440446msgid "Failed, tap to retry"
441−msgstr ""
447+msgstr "Imefeli, gusa urudie"
442448
443449#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
444450msgctxt "Label: Hint in message indicator line edit"
445451msgid "Reply"
446−msgstr ""
452+msgstr "Jibu"
447453
448454#: qml/Panel/Indicators/ModemInfoItem.qml:105
449455msgid "Unlock SIM"
450−msgstr ""
456+msgstr "Fungua SIM"
451457
452458#: qml/Panel/Indicators/RoamingIndication.qml:27
453459msgid "Roaming"
454−msgstr ""
460+msgstr "Uzururaji"
455461
456462#: qml/Tutorial/TutorialBottomFinish.qml:24
457463msgid "This action does different things for different apps"
458−msgstr ""
464+msgstr "Hatua hii inafanya vitu tofauti kwenye programu tofauti"
459465
460466#: qml/Tutorial/TutorialBottomFinish.qml:25
461467msgid "Tap here to finish."
462−msgstr ""
468+msgstr "Gusa hapa umalize"
463469
464470#: qml/Tutorial/TutorialBottom.qml:28
465471msgid "Open special menus"
466−msgstr ""
472+msgstr "Fungua orodha maalum"
467473
468474#: qml/Tutorial/TutorialBottom.qml:29
469475msgid "Swipe up from the bottom edge."
470−msgstr ""
476+msgstr "Vuta juu kutoka chini"
471477
472478#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
473479#: qml/Tutorial/TutorialRight.qml:144
474480msgid "Tap here to continue."
475−msgstr ""
481+msgstr "Gusa hapa uendelee."
476482
477483#: qml/Tutorial/TutorialContent.qml:81
478484msgid "Click here to finish."
479−msgstr ""
485+msgstr "Click hapa umalize."
480486
481487#: qml/Tutorial/TutorialLeftFinish.qml:24
482488msgid "These are the shortcuts to favorite apps"
483−msgstr ""
489+msgstr "Hizi ni njia za mkato za programu ulizopenda"
484490
485491#: qml/Tutorial/TutorialLeft.qml:26
486492msgid "Open the launcher"
487−msgstr ""
493+msgstr "Fungua launcher"
488494
489495#: qml/Tutorial/TutorialLeft.qml:27
490496msgid "Short swipe from the left edge."
491−msgstr ""
497+msgstr "Vuta kidogo kutoka kushoto."
492498
493499#: qml/Tutorial/TutorialPage.qml:178
494500msgid "You almost got it!"
495−msgstr ""
501+msgstr "Karibu upate!"
496502
497503#: qml/Tutorial/TutorialPage.qml:195
498504msgid "Try again."
499−msgstr ""
505+msgstr "Jaribu tena."
500506
501507#: qml/Tutorial/TutorialRight.qml:31
502508msgid "To view open apps"
503−msgstr ""
509+msgstr "Kuona fungua programu"
504510
505511#: qml/Tutorial/TutorialRight.qml:32
506512msgid "Long swipe from the right edge."
507−msgstr ""
513+msgstr "Vuta kabisa kutoka kulia."
508514
509515#: qml/Tutorial/TutorialRight.qml:122
510516msgid "View all your running tasks."
511−msgstr ""
517+msgstr "Ona kazi zote zinazoendelea."
512518
513519#: qml/Wizard/Page.qml:89
514520msgctxt "Button: Go back one page in the Wizard"
515521msgid "Back"
516−msgstr ""
522+msgstr "Nyuma"
517523
518524#: qml/Wizard/Pages/here-terms.qml:27
519525msgid "Terms & Conditions"
520−msgstr ""
526+msgstr "Suala na Masharti"
521527
522528#: qml/Wizard/Pages/passwd-confirm.qml:49
523529msgid "Confirm passphrase"
524−msgstr ""
530+msgstr "Thibitisha neno la siri"
525531
526532#: qml/Wizard/Pages/passwd-confirm.qml:50
527533msgid "Confirm passcode"
528−msgstr ""
534+msgstr "Thibitisha neno la siri"
529535
530536#: qml/Wizard/Pages/passwd-confirm.qml:53
531537#: qml/Wizard/Pages/passwd-confirm.qml:54
532538msgid "Please try again."
533−msgstr ""
539+msgstr "Jaribu tena."
534540
535541#: qml/Wizard/Pages/passwd-confirm.qml:82 qml/Wizard/Pages/passwd-set.qml:90
536542#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
537543#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
538544#: qml/Wizard/Pages/60-reporting.qml:50
539545msgid "Continue"
540−msgstr ""
546+msgstr "Endelea"
541547
542548#: qml/Wizard/Pages/passwd-set.qml:61
543549msgid "Choose your passcode"
544−msgstr ""
550+msgstr "Chagua neno lako la siri"
545551
546552#: qml/Wizard/Pages/passwd-set.qml:68
547553msgid "Passphrase must be 4 characters long"
548−msgstr ""
554+msgstr "Neno la siri ni lazima liwe na urefu wa herufi 4"
549555
550556#: qml/Wizard/Pages/10-welcome.qml:27
551557msgid "Hi!"
552−msgstr ""
558+msgstr "Jambo!"
553559
554560#: qml/Wizard/Pages/10-welcome.qml:44
555561msgid "Welcome to your Ubuntu phone."
556−msgstr ""
562+msgstr "Karibu kwenye simu yako ya Ubuntu."
557563
558564#: qml/Wizard/Pages/10-welcome.qml:52
559565msgid "Let’s get started."
560−msgstr ""
566+msgstr "Tuanze pamoja."
561567
562568#: qml/Wizard/Pages/20-sim.qml:25
563569msgid "Add a SIM card and restart your device"
564−msgstr ""
570+msgstr "Ongeza kadi ya SIM halafu uanzishe upya kifaa chako"
565571
566572#: qml/Wizard/Pages/20-sim.qml:59
567573msgid "Without it, you won’t be able to make calls or use text messaging."
568−msgstr ""
574+msgstr "Bila hiyo, hutaweza kupiga simu ama kutuma teksti."
569575
570576#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
571577msgid "Skip"
572−msgstr ""
578+msgstr "Ruka"
573579
574580#: qml/Wizard/Pages/30-passwd-type.qml:39
575581msgid "Lock security"
576−msgstr ""
582+msgstr "Funga usalama"
577583
578584#: qml/Wizard/Pages/30-passwd-type.qml:74
579585msgid "Please select how you’d like to unlock your phone."
580−msgstr ""
586+msgstr "Tafadhali chagua vile ungependa kufunga simu."
581587
582588#: qml/Wizard/Pages/30-passwd-type.qml:97
583589msgctxt "Label: Type of security method"
584590msgid "Swipe"
585−msgstr ""
591+msgstr "Vuta"
586592
587593#: qml/Wizard/Pages/30-passwd-type.qml:98
588594msgctxt "Label: Description of security method"
589595msgid "No security"
590−msgstr ""
596+msgstr "Hakuna usalama"
591597
592598#: qml/Wizard/Pages/30-passwd-type.qml:100
593599msgctxt "Label: Type of security method"
594600msgid "Passcode"
595−msgstr ""
601+msgstr "Neno la siri"
596602
597603#: qml/Wizard/Pages/30-passwd-type.qml:101
598604msgctxt "Label: Description of security method"
599605msgid "4 digits only"
600−msgstr ""
606+msgstr "Herufi nne peke yake"
601607
602608#: qml/Wizard/Pages/30-passwd-type.qml:103
603609msgctxt "Label: Type of security method"
604610msgid "Passphrase"
605−msgstr ""
611+msgstr "Kaulisiri"
606612
607613#: qml/Wizard/Pages/30-passwd-type.qml:104
608614msgctxt "Label: Description of security method"
609615msgid "Numbers and letters"
610−msgstr ""
616+msgstr "Nambari na herufi"
611617
612618#: qml/Wizard/Pages/40-wifi.qml:29
613619msgid "Connect to Wi‑Fi"
614−msgstr ""
620+msgstr "Tumia Wi-fi"
615621
616622#: qml/Wizard/Pages/40-wifi.qml:168
617623msgid "Available networks…"
618−msgstr ""
624+msgstr "Mitandao inayopatikana"
619625
620626#: qml/Wizard/Pages/40-wifi.qml:169
621627msgid "No available networks."
622−msgstr ""
628+msgstr "Hakuna mitandao inayopatikana"
623629
624630#: qml/Wizard/Pages/50-location.qml:27
625631msgid "Location"
626−msgstr ""
632+msgstr "Mahali"
627633
628634#: qml/Wizard/Pages/50-location.qml:62
629635msgid "Let the phone detect your location:"
qml/Dash/Previews/PreviewComment.qmlnew file+83
@@ -0,0 +1,83 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import Ubuntu.Components 1.2
19+
20+/*! \brief Preview widget for comments.
21+
22+ This widget shows an (optional) avatar contained in widgetData["source"]
23+ along with a label that comes from widgetData["author"],
24+ a (optional) subtitle from widgetData["subtitle"] and the comment widgetData["comment"].
25+*/
26+
27+PreviewWidget {
28+ id: root
29+ implicitHeight: Math.max(avatar.height, column.implicitHeight)
30+
31+ UbuntuShape {
32+ id: avatar
33+ objectName: "avatar"
34+ anchors {
35+ left: parent.left
36+ top: parent.top
37+ }
38+ width: units.gu(6)
39+ height: width
40+ source: Image {
41+ source: widgetData["source"]
42+ }
43+ radius: "medium"
44+ opacity: source.status === Image.Ready ? 1 : 0
45+ visible: widgetData["source"] !== ""
46+ }
47+
48+ Column {
49+ id: column
50+ objectName: "column"
51+ anchors {
52+ left: avatar.visible ? avatar.right : parent.left
53+ right: parent.right
54+ top: parent.top
55+ topMargin: units.gu(0.5)
56+ leftMargin: avatar.visible ? units.gu(1) : 0
57+ }
58+ spacing: units.gu(0.24)
59+
60+ Label {
61+ width: parent.width
62+ text: widgetData["author"] || ""
63+ fontSize: "small"
64+ maximumLineCount: 1
65+ elide: Text.ElideRight
66+ }
67+ Label {
68+ objectName: "subtitle"
69+ width: parent.width
70+ visible: text !== ""
71+ text: widgetData["subtitle"] || ""
72+ fontSize: "xx-small"
73+ maximumLineCount: 1
74+ elide: Text.ElideRight
75+ }
76+ Label {
77+ width: parent.width
78+ text: widgetData["comment"] || ""
79+ fontSize: "small"
80+ wrapMode: Text.Wrap
81+ }
82+ }
83+}
qml/Dash/Previews/PreviewCommentInput.qmlnew file+66
@@ -0,0 +1,66 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import Ubuntu.Components 1.1
19+import "../../Components"
20+
21+/*! \brief Preview widget for commenting.
22+
23+ The widget can show a field to enter a comment.
24+ It is possible to customise the submit button's label by setting widgetData["submit-label"]
25+ The successeful submit emits triggered(widgetId, "commented", data),
26+ with data being {"comment": comment}.
27+*/
28+
29+PreviewWidget {
30+ id: root
31+ implicitHeight: Math.max(commentTextArea.implicitHeight, submitButton.implicitHeight)
32+
33+ function submit() {
34+ var data = { "comment": commentTextArea.text };
35+ triggered(root.widgetId, "commented", data);
36+ }
37+
38+ TextArea {
39+ id: commentTextArea
40+ objectName: "commentTextArea"
41+ anchors {
42+ top: parent.top
43+ left: parent.left
44+ right: submitButton.left
45+ rightMargin: units.gu(1)
46+ }
47+ autoSize: true
48+ }
49+
50+ Button {
51+ id: submitButton
52+ objectName: "submitButton"
53+
54+ readonly property bool readyToSubmit: commentTextArea.text.trim().length > 0
55+
56+ anchors {
57+ top: parent.top
58+ right: parent.right
59+ }
60+ color: readyToSubmit ? Theme.palette.selected.base : Theme.palette.normal.base
61+ text: widgetData["submit-label"] || i18n.tr("Send")
62+ onClicked: {
63+ if (readyToSubmit) root.submit()
64+ }
65+ }
66+}
qml/Dash/Previews/PreviewWidgetFactory.qml+2
@@ -50,6 +50,8 @@ Loader {
5050 switch (widgetType) {
5151 case "actions": return "PreviewActions.qml";
5252 case "audio": return "PreviewAudioPlayback.qml";
53+ case "comment": return "PreviewComment.qml";
54+ case "comment-input": return "PreviewCommentInput.qml";
5355 case "expandable": return "PreviewExpandable.qml";
5456 case "gallery": return "PreviewImageGallery.qml";
5557 case "header": return "PreviewHeader.qml";
qml/Launcher/LauncherDelegate.qml+86
@@ -27,6 +27,8 @@ Item {
2727 property bool itemFocused: false
2828 property real maxAngle: 0
2929 property bool inverted: false
30+ property bool alerting: false
31+ readonly property alias wiggling: wiggleAnim.running
3032
3133 readonly property int effectiveHeight: Math.cos(angle * Math.PI / 180) * itemHeight
3234 readonly property real foldedHeight: Math.cos(maxAngle * Math.PI / 180) * itemHeight
@@ -39,6 +41,82 @@ Item {
3941 property real offset: 0
4042 property real itemOpacity: 1
4143 property real brightness: 0
44+ property double maxWiggleAngle: 5.0
45+
46+ QtObject {
47+ id: priv
48+
49+ readonly property int wiggleDuration: UbuntuAnimation.SnapDuration
50+ property real wiggleAngle: 0
51+ }
52+
53+ SequentialAnimation {
54+ id: wiggleAnim
55+
56+ running: alerting
57+ loops: 1
58+ alwaysRunToEnd: true
59+
60+ NumberAnimation {
61+ target: priv
62+ property: "wiggleAngle"
63+ from: 0
64+ to: maxWiggleAngle
65+ duration: priv.wiggleDuration
66+ easing.type: Easing.InQuad
67+ }
68+
69+ NumberAnimation {
70+ target: priv
71+ property: "wiggleAngle"
72+ from: maxWiggleAngle
73+ to: -maxWiggleAngle
74+ duration: priv.wiggleDuration
75+ easing.type: Easing.InOutQuad
76+ }
77+
78+ NumberAnimation {
79+ target: priv
80+ property: "wiggleAngle"
81+ from: -maxWiggleAngle
82+ to: maxWiggleAngle
83+ duration: priv.wiggleDuration
84+ easing.type: Easing.InOutQuad
85+ }
86+
87+ NumberAnimation {
88+ target: priv
89+ property: "wiggleAngle"
90+ from: maxWiggleAngle
91+ to: -maxWiggleAngle
92+ duration: priv.wiggleDuration
93+ easing.type: Easing.InOutQuad
94+ }
95+
96+ NumberAnimation {
97+ target: priv
98+ property: "wiggleAngle"
99+ from: -maxWiggleAngle
100+ to: maxWiggleAngle
101+ duration: priv.wiggleDuration
102+ easing.type: Easing.InOutQuad
103+ }
104+
105+ NumberAnimation {
106+ target: priv
107+ property: "wiggleAngle"
108+ from: maxWiggleAngle
109+ to: 0
110+ duration: priv.wiggleDuration
111+ easing.type: Easing.OutQuad
112+ }
113+
114+ UbuntuNumberAnimation {
115+ target: root
116+ property: "alerting"
117+ to: 0
118+ }
119+ }
42120
43121 Item {
44122 id: iconItem
@@ -167,6 +245,14 @@ Item {
167245 }
168246
169247 transform: [
248+ // The rotation about the icon's center/z-axis for the wiggle
249+ // needs to happen here too, because there's no other way to
250+ // align the wiggle with the icon-folding otherwise
251+ Rotation {
252+ axis { x: 0; y: 0; z: 1 }
253+ origin { x: iconItem.width / 2; y: iconItem.height / 2; z: 0 }
254+ angle: priv.wiggleAngle
255+ },
170256 // Rotating 3 times at top/bottom because that increases the perspective.
171257 // This is a hack, but as QML does not support real 3D coordinates
172258 // getting a higher perspective can only be done by a hack. This is the most
qml/Launcher/LauncherPanel.qml+74−3
@@ -86,6 +86,7 @@ Rectangle {
8686 height: parent.height - dashItem.height - parent.spacing*2
8787
8888 Item {
89+ id: launcherListViewItem
8990 anchors.fill: parent
9091 clip: true
9192
@@ -109,12 +110,21 @@ Rectangle {
109110 preferredHighlightBegin: (height - itemHeight) / 2
110111 preferredHighlightEnd: (height + itemHeight) / 2
111112
113+ // for the single peeking icon, when alert-state is set on delegate
114+ property int peekingIndex: -1
115+
112116 // The size of the area the ListView is extended to make sure items are not
113117 // destroyed when dragging them outside the list. This needs to be at least
114118 // itemHeight to prevent folded items from disappearing and DragArea limits
115119 // need to be smaller than this size to avoid breakage.
116120 property int extensionSize: 0
117121
122+ // These properties are needed to facilitate implicitly unfolding the
123+ // launcher to the peeking icon, if it is not already fully flat
124+ // and in view for the user
125+ property real moveToIndexYFrom
126+ property real moveToIndexYTo
127+
118128 // Setting extensionSize after the list has been populated because it has
119129 // the potential to mess up with the intial positioning in combination
120130 // with snapping to the center of the list. This catches all the cases
@@ -128,6 +138,14 @@ Rectangle {
128138 flick(0, clickFlickSpeed)
129139 }
130140
141+ UbuntuNumberAnimation {
142+ id: moveToIndexAnimation
143+ target: launcherListView
144+ property: "contentY"
145+ from: launcherListView.moveToIndexYFrom
146+ to: launcherListView.moveToIndexYTo
147+ }
148+
131149 // The height of the area where icons start getting folded
132150 property int foldingStartHeight: units.gu(6.5)
133151 // The height of the area where the items reach the final folding angle
@@ -180,10 +198,63 @@ Rectangle {
180198 progress: model.progress
181199 itemFocused: model.focused
182200 inverted: root.inverted
201+ alerting: model.alerting
183202 z: -Math.abs(offset)
184203 maxAngle: 55
185204 property bool dragging: false
186205
206+ SequentialAnimation {
207+ id: peekingAnimation
208+
209+ // revealing
210+ PropertyAction { target: root; property: "visible"; value: (launcher.visibleWidth === 0) ? 1 : 0 }
211+ PropertyAction { target: launcherListViewItem; property: "clip"; value: 0 }
212+
213+ UbuntuNumberAnimation {
214+ target: launcherDelegate
215+ alwaysRunToEnd: true
216+ loops: 1
217+ properties: "x"
218+ to: (units.gu(.5) + launcherListView.width * .5) * (root.inverted ? -1 : 1)
219+ duration: UbuntuAnimation.BriskDuration
220+ }
221+
222+ // hiding
223+ UbuntuNumberAnimation {
224+ target: launcherDelegate
225+ alwaysRunToEnd: true
226+ loops: 1
227+ properties: "x"
228+ to: 0
229+ duration: UbuntuAnimation.BriskDuration
230+ }
231+
232+ PropertyAction { target: launcherListViewItem; property: "clip"; value: 1 }
233+ PropertyAction { target: root; property: "visible"; value: (launcher.visibleWidth === 0) ? 0 : 1 }
234+ }
235+
236+ onAlertingChanged: {
237+ if(alerting) {
238+ if (!dragging && (launcherListView.peekingIndex === -1 || launcher.visibleWidth > 0)) {
239+ launcherListView.moveToIndexYFrom = launcherListView.contentY
240+ launcherListView.positionViewAtIndex(index, ListView.Center)
241+ launcherListView.moveToIndexYTo = launcherListView.contentY
242+ moveToIndexAnimation.start()
243+ if (!dragging && launcher.state !== "visible") {
244+ peekingAnimation.start()
245+ }
246+ }
247+
248+ if (launcherListView.peekingIndex === -1) {
249+ launcherListView.peekingIndex = index
250+ }
251+ } else {
252+ if (launcherListView.peekingIndex === index) {
253+ launcherListView.peekingIndex = -1
254+ }
255+ }
256+ }
257+
187258 ThinDivider {
188259 id: dropIndicator
189260 objectName: "dropIndicator"
@@ -521,7 +592,7 @@ Rectangle {
521592 Image {
522593 anchors {
523594 left: parent.left
524− leftMargin: (quickList.item.width - units.gu(1)) / 2 - width / 2
595+ leftMargin: quickList.item !== undefined ? (quickList.item.width - units.gu(1)) / 2 - width / 2 : 0
525596 verticalCenter: parent.verticalCenter
526597 verticalCenterOffset: (parent.height / 2 + units.dp(3)) * (quickList.offset > 0 ? 1 : -1) * (root.inverted ? 1 : -1)
527598 }
@@ -564,9 +635,9 @@ Rectangle {
564635
565636 // internal
566637 property int itemCenter: item ? root.mapFromItem(quickList.item).y + (item.height / 2) : units.gu(1)
567− property int offset: itemCenter + (item.height/2) + height + units.gu(1) > parent.height ?
638+ property int offset: item !== undefined ? (itemCenter + (item.height/2) + height + units.gu(1) > parent.height ?
568639 -(item.height/2) - height - units.gu(.5) :
569− (item.height/2) + units.gu(.5)
640+ (item.height/2) + units.gu(.5)) : 0
570641
571642 Column {
572643 id: quickListColumn
qml/Notifications/Notifications.qml+1−1
@@ -33,7 +33,7 @@ ListView {
3333 id: snapDecisionProxyModel
3434 objectName: "snapDecisionProxyModel"
3535
36− model: notificationList.model
36+ model: notificationList.model ? notificationList.model : null
3737 filterRole: UnityNotifications.ModelInterface != undefined ? UnityNotifications.ModelInterface.RoleType : 0
3838 filterRegExp: RegExp(UnityNotifications.Notification.SnapDecision)
3939 }
qml/Panel/IndicatorItem.qml+1−1
@@ -234,7 +234,7 @@ IndicatorDelegate {
234234 ]
235235 }
236236
237− onRootActionStateChanged: {
237+ rootActionState.onUpdated: {
238238 if (rootActionState == undefined) {
239239 title = "";
240240 leftLabel = "";
qml/Panel/Indicators/IndicatorBase.qml−1
@@ -43,6 +43,5 @@ Item {
4343 ModelActionRootState {
4444 id: rootAction
4545 menu: menuModel ? menuModel : null
46− onUpdated: indicatorItem.rootActionStateChanged()
4746 }
4847}
qml/Tutorial/TutorialLeft.qml+16−6
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014,2015 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -46,24 +46,34 @@ TutorialPage {
4646
4747 SequentialAnimation {
4848 id: teaseAnimation
49+ objectName: "teaseAnimation"
4950 paused: running && root.paused
5051 running: !slider.active && root.launcher.visibleWidth === 0 && root.shown
5152 loops: Animation.Infinite
53+ property real bounce: 0
54+ readonly property real maxBounce: units.gu(2)
5255
5356 UbuntuNumberAnimation {
54− target: root.launcher
55− property: "x"
56− to: units.gu(2)
57+ target: teaseAnimation
58+ property: "bounce"
59+ to: teaseAnimation.maxBounce
5760 duration: UbuntuAnimation.SleepyDuration
5861 }
5962 UbuntuNumberAnimation {
60− target: root.launcher
61− property: "x"
63+ target: teaseAnimation
64+ property: "bounce"
6265 to: 0
6366 duration: UbuntuAnimation.SleepyDuration
6467 }
6568 }
6669
70+ Binding {
71+ target: root.launcher
72+ when: root.shown
73+ property: "x"
74+ value: Math.min(root.launcher.panelWidth - root.launcher.visibleWidth, teaseAnimation.bounce)
75+ }
76+
6777 Timer {
6878 id: finishTimer
6979 interval: 1
tests/autopilot/unity8/settings_wizard/__init__.pynew file+507
@@ -0,0 +1,507 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2015 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+import logging
21+import re
22+
23+import autopilot
24+from autopilot import introspection
25+from ubuntuuitoolkit import UbuntuUIToolkitCustomProxyObjectBase
26+
27+from contextlib import contextmanager
28+
29+
30+logger = logging.getLogger(__name__)
31+
32+
33+@contextmanager
34+def override_proxy_timeout(proxy, timeout_seconds):
35+ original_timeout = proxy._poll_time
36+ try:
37+ proxy._poll_time = timeout_seconds
38+ yield proxy
39+ finally:
40+ proxy._poll_time = original_timeout
41+
42+
43+def get_wizard(current_page):
44+ return current_page.get_root_instance().select_single(Wizard)
45+
46+
47+class Wizard(UbuntuUIToolkitCustomProxyObjectBase):
48+ """High-level helper to navigate through the pages of the wizard"""
49+
50+ def get_language_page(self):
51+ return self.wait_select_single(
52+ objectName='languagePage', visible='True')
53+
54+ def get_current_page(self):
55+ return self.wait_select_single('Page', visible='True')
56+
57+ def get_sim_page(self):
58+ return self.wait_select_single(
59+ objectName='simPage', visible='True')
60+
61+ def get_password_page(self):
62+ return self.wait_select_single(
63+ objectName='passwdPage', visible='True')
64+
65+ def get_password_entry_page(self):
66+ return self.wait_select_single(
67+ objectName='passwdSetPage', visible='True')
68+
69+ def get_confirm_password_page(self):
70+ return self.wait_select_single(
71+ objectName='passwdConfirmPage', visible='True')
72+
73+ def get_wifi_connect_page(self):
74+ return self.wait_select_single(
75+ objectName='wifiPage', visible='True')
76+
77+ def get_reporting_page(self):
78+ return self.wait_select_single(
79+ objectName='reportingPage', visible='True')
80+
81+ def get_finished_page(self):
82+ return self.wait_select_single(
83+ objectName='finishedPage', visible='True')
84+
85+
86+class WizardLanguagePage(UbuntuUIToolkitCustomProxyObjectBase):
87+ """Helper class to interact with the language welcome page"""
88+
89+ # This class was renamed from LanguagePage to WizardLanguagePage because
90+ # there is a class with the same name in the system settings custom proxy
91+ # objects. Reported in http://pad.lv/1422904. --elopio - 2015-02-17
92+
93+ @classmethod
94+ def validate_dbus_object(cls, path, state):
95+ name = introspection.get_classname_from_path(path)
96+ if name == b'Page':
97+ if state['objectName'][1] == 'languagePage':
98+ return True
99+ return False
100+
101+ def _get_language_button(self):
102+ return self.select_single(
103+ 'ComboButton', objectName='languageCombo')
104+
105+ def _get_continue_button(self):
106+ return self.select_single('StackButton', text='Continue')
107+
108+ def _swipe_to_language(self, list, language):
109+ """Swipe to the chosen language in the given list"""
110+ list.swipe_to_top()
111+ while not list.atYEnd:
112+ try:
113+ item = list.select_single(
114+ 'LabelVisual', text=language, visible='True')
115+ item.swipe_into_view()
116+ return item
117+ except introspection.dbus.StateNotFoundError:
118+ list.swipe_to_show_more_below()
119+ # item could not be found
120+ raise introspection.dbus.StateNotFoundError
121+
122+ @autopilot.logging.log_action(logger.info)
123+ def select_language(self, language):
124+ """Select a different language from the list"""
125+ combo_button = self._get_language_button()
126+ self.pointing_device.click_object(combo_button)
127+ language_list = combo_button.wait_select_single(
128+ 'UbuntuListView11', visible='True')
129+ item = self._swipe_to_language(language_list, language)
130+ self.pointing_device.click_object(item)
131+
132+ def get_selected_language(self):
133+ return self._get_language_button().get_properties()['text']
134+
135+ @autopilot.logging.log_action(logger.info)
136+ def continue_(self):
137+ self.pointing_device.click_object(self._get_continue_button())
138+ wizard = get_wizard(self)
139+ next_page = wizard.get_current_page()
140+ sim_inserted = True
141+ if next_page.objectName == 'simPage':
142+ # no sim is inserted
143+ next_page = wizard.get_sim_page()
144+ sim_inserted = False
145+ else:
146+ # sim is inserted
147+ next_page = wizard.get_password_page()
148+ return sim_inserted, next_page
149+
150+
151+class SimPage(UbuntuUIToolkitCustomProxyObjectBase):
152+ """Helper class to interact with the no sim notification page"""
153+
154+ @classmethod
155+ def validate_dbus_object(cls, path, state):
156+ name = introspection.get_classname_from_path(path)
157+ if name == b'Page':
158+ if state['objectName'][1] == 'simPage':
159+ return True
160+ return False
161+
162+ def _get_back_button(self):
163+ return self.select_single('StackButton', text='Back')
164+
165+ def _get_skip_button(self):
166+ return self.select_single('StackButton', text='Skip')
167+
168+ @autopilot.logging.log_action(logger.info)
169+ def back(self):
170+ self.pointing_device.click_object(self._get_back_button())
171+ return get_wizard(self).get_languge_page()
172+
173+ @autopilot.logging.log_action(logger.info)
174+ def skip(self):
175+ self.pointing_device.click_object(self._get_skip_button())
176+ return get_wizard(self).get_password_page()
177+
178+
179+class PasswordPage(UbuntuUIToolkitCustomProxyObjectBase):
180+ """Helper class to interact with the password security page"""
181+
182+ SECURITY_SWIPE = 'Swipe'
183+ SECURITY_PASSCODE = 'Passcode'
184+ SECURITY_PASSPHRASE = 'Passphrase'
185+
186+ @classmethod
187+ def validate_dbus_object(cls, path, state):
188+ name = introspection.get_classname_from_path(path)
189+ if name == b'Page':
190+ if state['objectName'][1] == 'passwdPage':
191+ return True
192+ return False
193+
194+ def _get_option_name(self, formatted_name):
195+ # the option name is formatted as follows:
196+ # need to read the name between bold tags
197+ # '<b>Passcode</b> (4 digits only)'
198+ formatted_pattern = re.compile(r'<b>(.*?)</b>.*',
199+ re.IGNORECASE | re.DOTALL)
200+ name = None
201+ match = formatted_pattern.search(formatted_name)
202+ if match:
203+ name = match.group(1)
204+ return name
205+
206+ def _get_all_options(self):
207+ return self.select_many('OptionSelectorDelegate')
208+
209+ def _get_continue_button(self):
210+ return self.select_single('StackButton', text='Continue')
211+
212+ def _get_back_button(self):
213+ return self.select_single('StackButton', text='Back')
214+
215+ def _get_selected_option(self):
216+ return self.select_single(
217+ 'OptionSelectorDelegate', visible='True', selected='True')
218+
219+ def get_selected_security_option(self):
220+ name_fmt = self._get_selected_option().get_properties()['subText']
221+ return self._get_option_name(name_fmt)
222+
223+ @autopilot.logging.log_action(logger.info)
224+ def select_security_option(self, selected_option):
225+ options = self._get_all_options()
226+ for option in options:
227+ name = self._get_option_name(option.get_properties()['subText'])
228+ if name == selected_option:
229+ self.pointing_device.click_object(option)
230+
231+ @autopilot.logging.log_action(logger.info)
232+ def back(self, sim_inserted):
233+ self.pointing_device.click_object(self._get_back_button())
234+ wizard = get_wizard(self)
235+ if sim_inserted:
236+ page = wizard.get_languge_page()
237+ else:
238+ page = wizard.get_sim_page()
239+ return page
240+
241+ @autopilot.logging.log_action(logger.info)
242+ def continue_(self):
243+ option = self.get_selected_security_option()
244+ self.pointing_device.click_object(self._get_continue_button())
245+ wizard = get_wizard(self)
246+ if option == self.SECURITY_SWIPE:
247+ next_page = wizard.get_wifi_connect_page()
248+ else:
249+ next_page = wizard.get_password_entry_page()
250+ return next_page
251+
252+
253+class PasswordEntryPage(UbuntuUIToolkitCustomProxyObjectBase):
254+ """Helper class to interact with the pin entry page"""
255+
256+ @classmethod
257+ def validate_dbus_object(cls, path, state):
258+ name = introspection.get_classname_from_path(path)
259+ if name == b'Page':
260+ if state['objectName'][1] == 'passwdSetPage':
261+ return True
262+ return False
263+
264+ def _get_pinpad_button(self, char):
265+ return self.select_single('PinPadButton', text=char)
266+
267+ def _get_continue_button(self):
268+ return self.select_single('StackButton', text='Continue')
269+
270+ def _get_back_button(self):
271+ return self.select_single('StackButton', text='Back')
272+
273+ def _get_text_field(self):
274+ return self.select_single('QQuickTextInput')
275+
276+ @autopilot.logging.log_action(logger.info)
277+ def enter_pin(self, pin):
278+ for char in pin:
279+ self.pointing_device.click_object(self._get_pinpad_button(char))
280+ return get_wizard(self).get_confirm_password_page()
281+
282+ @autopilot.logging.log_action(logger.info)
283+ def enter_text(self, text):
284+ self.pointing_device.click_object(self._get_text_field())
285+ autopilot.input.Keyboard.create().type(text)
286+
287+ @autopilot.logging.log_action(logger.info)
288+ def back(self):
289+ self.pointing_device.click_object(self._get_back_button())
290+ return get_wizard(self).get_password_page()
291+
292+ @autopilot.logging.log_action(logger.info)
293+ def continue_(self):
294+ autopilot.input.Keyboard.create().press_and_release('Enter')
295+ return get_wizard(self).get_confirm_password_page()
296+
297+
298+class ConfirmPasswordPage(UbuntuUIToolkitCustomProxyObjectBase):
299+ """Helper class to interact with the pin confirmation page"""
300+
301+ @classmethod
302+ def validate_dbus_object(cls, path, state):
303+ name = introspection.get_classname_from_path(path)
304+ if name == b'Page':
305+ if state['objectName'][1] == 'passwdConfirmPage':
306+ return True
307+ return False
308+
309+ def _get_pinpad_button(self, char):
310+ return self.select_single('PinPadButton', text=char)
311+
312+ def _get_continue_button(self):
313+ return self.select_single('StackButton', text='Continue')
314+
315+ def _get_back_button(self):
316+ return self.select_single('StackButton', text='Back')
317+
318+ def _get_text_field(self):
319+ return self.select_single('QQuickTextInput')
320+
321+ @autopilot.logging.log_action(logger.info)
322+ def enter_pin(self, pin):
323+ for char in pin:
324+ self.pointing_device.click_object(self._get_pinpad_button(char))
325+ return get_wizard(self).get_wifi_connect_page()
326+
327+ @autopilot.logging.log_action(logger.info)
328+ def enter_text(self, text):
329+ self.pointing_device.click_object(self._get_text_field())
330+ autopilot.input.Keyboard.create().type(text)
331+
332+ @autopilot.logging.log_action(logger.info)
333+ def back(self):
334+ self.pointing_device.click_object(self._get_back_button())
335+ return get_wizard(self).get_password_page()
336+
337+ @autopilot.logging.log_action(logger.info)
338+ def continue_(self):
339+ autopilot.input.Keyboard.create().press_and_release('Enter')
340+ return get_wizard(self).get_wifi_connect_page()
341+
342+
343+class WifiConnectPage(UbuntuUIToolkitCustomProxyObjectBase):
344+ """Helper class to interact with the Wi-Fi network list page"""
345+
346+ @classmethod
347+ def validate_dbus_object(cls, path, state):
348+ name = introspection.get_classname_from_path(path)
349+ if name == b'Page':
350+ if state['objectName'][1] == 'wifiPage':
351+ return True
352+ return False
353+
354+ def _get_all_networks(self):
355+ try:
356+ networks = self.select_many('Standard', objectName='accessPoint',
357+ visible='True')
358+ except:
359+ networks = []
360+ return networks
361+
362+ def _get_network(self, ssid):
363+ return self.wait_select_single(
364+ 'Standard', objectName='accessPoint', text=ssid, visible='True')
365+
366+ def _get_network_checkbox(self, ssid):
367+ return self._get_network(ssid).select_single(
368+ 'CheckBox', visible='True')
369+
370+ def _get_notification(self, unity):
371+ logger.info('Waiting longer for notification object')
372+ with override_proxy_timeout(unity, 30):
373+ return unity.wait_select_single(
374+ Notification, objectName='notification1', visible='True')
375+
376+ def _get_back_button(self):
377+ return self.wait_select_single(
378+ 'StackButton', text='Back', visible='True')
379+
380+ def _get_skip_button(self):
381+ return self.wait_select_single(
382+ 'StackButton', text='Skip', visible='True')
383+
384+ def _get_continue_button(self):
385+ return self.wait_select_single(
386+ 'StackButton', text='Continue', visible='True')
387+
388+ def is_any_network_checked(self):
389+ networks = self._get_all_networks()
390+ for network in networks:
391+ checkbox = network.select_single('CheckBox', visible='True')
392+ if checkbox.get_properties()['checked']:
393+ return True
394+ return False
395+
396+ def is_any_network_found(self):
397+ num_neworks = len(self._get_all_networks())
398+ return True if num_neworks > 0 else False
399+
400+ def is_network_checked(self, ssid):
401+ return self._get_network_checkbox(ssid).get_properties()['checked']
402+
403+ @autopilot.logging.log_action(logger.info)
404+ def select_network(self, unity, ssid):
405+ self.pointing_device.click_object(self._get_network_checkbox(ssid))
406+ return PasswordNotification(self._get_notification(unity))
407+
408+ @autopilot.logging.log_action(logger.info)
409+ def back(self):
410+ self.pointing_device.click_object(self._get_back_button())
411+ return get_wizard(self).get_password_page()
412+
413+ @autopilot.logging.log_action(logger.info)
414+ def skip(self):
415+ self.pointing_device.click_object(self._get_skip_button())
416+ return get_wizard(self).get_reporting_page()
417+
418+ @autopilot.logging.log_action(logger.info)
419+ def continue_(self):
420+ self.pointing_device.click_object(self._get_continue_button())
421+ return get_wizard(self).get_reporting_page()
422+
423+
424+class Notification(UbuntuUIToolkitCustomProxyObjectBase):
425+ """Base class for notification objects"""
426+ pass
427+
428+
429+class PasswordNotification(Notification):
430+ """Helper class to interact with the password entry notification"""
431+
432+ def __init__(self, notification):
433+ self.notification = notification
434+
435+ def _get_connect_button(self):
436+ return self.notification.wait_select_single(
437+ 'Button', text='Connect', objectName='notify_button0')
438+
439+ def _get_cancel_buttoon(self):
440+ return self.notification.wait_select_single(
441+ 'Button', text='Cancel', objectName='notify_button1')
442+
443+ def _get_text_field(self):
444+ return self.notification.wait_select_single('TextField')
445+
446+ @autopilot.logging.log_action(logger.info)
447+ def enter_text(self, text):
448+ self._get_text_field().write(text)
449+
450+ @autopilot.logging.log_action(logger.info)
451+ def connect(self):
452+ self.notification.pointing_device.click_object(
453+ self._get_connect_button())
454+
455+ @autopilot.logging.log_action(logger.info)
456+ def cancel(self):
457+ self.notification.pointing_device.click_object(
458+ self._get_cancel_buttoon())
459+
460+
461+class ReportingPage(UbuntuUIToolkitCustomProxyObjectBase):
462+ """Helper class to interact with the reporting page"""
463+
464+ @classmethod
465+ def validate_dbus_object(cls, path, state):
466+ name = introspection.get_classname_from_path(path)
467+ if name == b'Page':
468+ if state['objectName'][1] == 'reportingPage':
469+ return True
470+ return False
471+
472+ def _get_continue_button(self):
473+ return self.select_single('StackButton', text='Continue')
474+
475+ def _get_back_button(self):
476+ return self.select_single('StackButton', text='Back')
477+
478+ @autopilot.logging.log_action(logger.info)
479+ def back(self):
480+ self.pointing_device.click_object(self._get_back_button())
481+ return get_wizard(self).get_wifi_connect_page()
482+
483+ @autopilot.logging.log_action(logger.info)
484+ def continue_(self):
485+ self.pointing_device.click_object(self._get_continue_button())
486+ return get_wizard(self).get_finished_page()
487+
488+
489+class FinishedPage(UbuntuUIToolkitCustomProxyObjectBase):
490+ """Helper class to interact with the finished page"""
491+
492+ @classmethod
493+ def validate_dbus_object(cls, path, state):
494+ name = introspection.get_classname_from_path(path)
495+ if name == b'Page':
496+ if state['objectName'][1] == 'finishedPage':
497+ return True
498+ return False
499+
500+ def _get_finish_button(self):
501+ return self.wait_select_single(
502+ 'StackButton', text='Finish', visible='True')
503+
504+ @autopilot.logging.log_action(logger.info)
505+ def finish(self):
506+ self.pointing_device.click_object(self._get_finish_button())
507+ self.wait_until_destroyed()
tests/autopilot/unity8/settings_wizard/fixture_setup.pynew file+58
@@ -0,0 +1,58 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2015 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+"""Set up and clean up fixtures for the Unity SettingsWizard tests."""
21+
22+import fixtures
23+import os
24+
25+from os.path import expanduser
26+
27+
28+class SettingsWizard(fixtures.Fixture):
29+ WIZARD_FILE = expanduser("~") + \
30+ '/.config/ubuntu-system-settings/wizard-has-run'
31+
32+ def __init__(self, enable):
33+ super().__init__()
34+ self.enable = enable
35+
36+ def setUp(self):
37+ super().setUp()
38+ original_state = self.is_settings_wizard_enabled()
39+ self.addCleanup(self.set_settings_wizard, original_state)
40+ if self.enable != original_state:
41+ self.set_settings_wizard(self.enable)
42+
43+ def is_settings_wizard_enabled(self):
44+ return not os.path.exists(self.WIZARD_FILE)
45+
46+ def set_settings_wizard(self, enabled):
47+ if enabled:
48+ self.enable_settings_wizard()
49+ else:
50+ self.disable_settings_wizard()
51+
52+ def enable_settings_wizard(self):
53+ if os.path.exists(self.WIZARD_FILE):
54+ os.remove(self.WIZARD_FILE)
55+
56+ def disable_settings_wizard(self):
57+ if not os.path.exists(self.WIZARD_FILE):
58+ open(self.WIZARD_FILE, 'a').close()
tests/autopilot/unity8/settings_wizard/ofono_helper.pynew file+116
@@ -0,0 +1,116 @@
1+#!/usr/bin/python -tt
2+
3+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
4+#
5+# Unity Indicators Autopilot Test Suite
6+# Copyright (C) 2014, 2015 Canonical
7+#
8+# This program is free software: you can redistribute it and/or modify
9+# it under the terms of the GNU General Public License as published by
10+# the Free Software Foundation, either version 3 of the License, or
11+# (at your option) any later version.
12+#
13+# This program is distributed in the hope that it will be useful,
14+# but WITHOUT ANY WARRANTY; without even the implied warranty of
15+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+# GNU General Public License for more details.
17+#
18+# You should have received a copy of the GNU General Public License
19+# along with this program. If not, see <http://www.gnu.org/licenses/>.
20+
21+
22+from __future__ import absolute_import
23+
24+import dbus
25+import subprocess
26+from time import sleep
27+
28+
29+class OfonoManager(object):
30+
31+ def __init__(self):
32+ self.system_bus = dbus.SystemBus()
33+ self.ofono = self.system_bus.get_object('org.ofono', '/')
34+
35+ self.real_manager = dbus.Interface(
36+ self.ofono,
37+ 'org.ofono.Manager'
38+ )
39+
40+ def _get_sim_manager(self):
41+ modems = self.real_manager.GetModems()
42+ path = modems[0][0]
43+ return dbus.Interface(self.system_bus.get_object('org.ofono', path),
44+ 'org.ofono.SimManager')
45+
46+ def physical_sim_is_present(self):
47+ sim_manager = self._get_sim_manager()
48+ properties = sim_manager.GetProperties()
49+ return properties["Present"] == 1
50+
51+
52+class PhonesimManager(object):
53+
54+ def __init__(self, sims, exe=None):
55+ if exe is None:
56+ self.phonesim_exe = '/usr/bin/ofono-phonesim'
57+ else:
58+ self.phonesim_exe = exe
59+ self.sims = sims
60+ self.sim_processes = {}
61+ self.system_bus = dbus.SystemBus()
62+ self.ofono = self.system_bus.get_object('org.ofono', '/')
63+
64+ self.phonesim_manager = dbus.Interface(
65+ self.ofono,
66+ 'org.ofono.phonesim.Manager'
67+ )
68+
69+ def start_phonesim_processes(self):
70+ for simname, simport, conffile in self.sims:
71+ cmd = ['/usr/bin/xvfb-run',
72+ '-a',
73+ self.phonesim_exe,
74+ '-p',
75+ str(simport),
76+ conffile]
77+
78+ p = subprocess.Popen(cmd)
79+ self.sim_processes[simname] = p
80+ # give the processes some time to start
81+ sleep(3)
82+
83+ def shutdown(self):
84+ for p in self.sim_processes.values():
85+ p.kill()
86+ self.sim_processes = {}
87+
88+ def reset_ofono(self):
89+ self.phonesim_manager.Reset()
90+
91+ def remove_all_ofono(self):
92+ self.phonesim_manager.RemoveAll()
93+
94+ def add_ofono(self, name):
95+ for simname, simport, _ in self.sims:
96+ if name == simname:
97+ self.phonesim_manager.Add(simname, '127.0.0.1', str(simport))
98+ return
99+ raise RuntimeError('Tried to add unknown modem %s.' % name)
100+
101+ def power_on(self, sim_name):
102+ sim = self.system_bus.get_object('org.ofono', '/'+sim_name)
103+ modem = dbus.Interface(sim, dbus_interface='org.ofono.Modem')
104+ modem.SetProperty('Powered', True)
105+ sleep(1)
106+
107+ def power_off(self, sim_name):
108+ sim = self.system_bus.get_object('org.ofono', '/'+sim_name)
109+ modem = dbus.Interface(sim, dbus_interface='org.ofono.Modem')
110+ modem.SetProperty('Powered', False)
111+ sleep(1)
112+
113+ def get_required_pin(self, sim_name):
114+ sim = self.system_bus.get_object('org.ofono', '/'+sim_name)
115+ interface = dbus.Interface(sim, dbus_interface='org.ofono.SimManager')
116+ return str(interface.GetProperties()['PinRequired'])
tests/autopilot/unity8/settings_wizard/tests/__init__.pynew file

No changes to the content.

tests/autopilot/unity8/settings_wizard/tests/test_settings_wizard.pynew file+79
@@ -0,0 +1,79 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2015 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+from unity8.settings_wizard import fixture_setup, Wizard
21+from unity8.shell import tests
22+
23+DEFAULT_LANGUAGE = 'English (United States)'
24+DEFAULT_PHONESIM_CONFIG_FILE = '/usr/share/phonesim/default.xml'
25+DEFAULT_SECURITY_METHOD = 'Passcode'
26+
27+
28+class SkipThroughSettingsWizardTestCase(tests.UnityTestCase):
29+ """ Autopilot test for completing settings wizard """
30+
31+ def setUp(self):
32+ super().setUp()
33+ self.wizard_helper = self.useFixture(
34+ fixture_setup.SettingsWizard(True))
35+ self.unity = self.launch_unity()
36+ self.wizard = self._get_settings_wizard()
37+
38+ def _get_settings_wizard(self):
39+ return self.unity.wait_select_single(Wizard)
40+
41+ def _test_language_page(self):
42+ """ Get the language page, check the default language and continue """
43+ language_page = self.wizard.get_language_page()
44+ default_selection = language_page.get_selected_language()
45+ self.assertEqual(default_selection, DEFAULT_LANGUAGE)
46+ return language_page.continue_()
47+
48+ def _test_password_page(self, password_page):
49+ """ Check default selection for password type and change
50+ password type to swipe to keep this test as uncomplicated
51+ as possible """
52+ default_selection = password_page.get_selected_security_option()
53+ self.assertEqual(default_selection, DEFAULT_SECURITY_METHOD)
54+ password_page.select_security_option('Swipe')
55+ return password_page.continue_()
56+
57+ def _test_reporting_page(self, reporting_page):
58+ return reporting_page.continue_()
59+
60+ def _test_wifi_connect_page(self, wifi_connect_page):
61+ if wifi_connect_page.is_any_network_checked() or not \
62+ wifi_connect_page.is_any_network_found():
63+ return wifi_connect_page.continue_()
64+ else:
65+ return wifi_connect_page.skip()
66+
67+ def test_skipping_through_wizard(self):
68+ """ Most basic test of the settings wizard. Skip all skipable pages """
69+ sim_inserted, next_page = self._test_language_page()
70+ if sim_inserted:
71+ password_page = next_page
72+ else:
73+ password_page = next_page.skip()
74+ wifi_connect_page = self._test_password_page(password_page)
75+ reporting_page = self._test_wifi_connect_page(wifi_connect_page)
76+ finish_page = self._test_reporting_page(reporting_page)
77+ finish_page.finish()
78+ self.assertFalse(
79+ self.wizard_helper.is_settings_wizard_enabled())
tests/mocks/QMenuModel/QDBusActionGroup.qml+2−2
@@ -41,8 +41,8 @@ QtObject {
4141 signal activated
4242
4343 property string actionName: \"" + actionName + "\"
44− property bool valid: ActionData.data[actionName] != undefined ? ActionData.data[actionName].valid : false
45− property var state: ActionData.data[actionName] != undefined ? ActionData.data[actionName].state : undefined
44+ property bool valid: ActionData.data.hasOwnProperty(actionName) ? ActionData.data[actionName].valid : false
45+ property var state: ActionData.data.hasOwnProperty(actionName) ? ActionData.data[actionName].state : undefined
4646
4747 function activate() {
4848 activated();
tests/mocks/QMenuModel/actiondata.h+3−1
@@ -31,7 +31,9 @@ class Q_DECL_EXPORT ActionData : public QObject
3131public:
3232 ActionData(QObject* parent = 0)
3333 : QObject(parent)
34− {}
34+ , m_data(QVariantMap())
35+ {
36+ }
3537
3638 QVariant data() const { return m_data; }
3739 void setData(const QVariant& data)
tests/mocks/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,4 +1,4 @@
1−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
1+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
22
33include_directories(
44 ${CMAKE_CURRENT_SOURCE_DIR}
tests/mocks/Unity/Launcher/MockLauncherItem.cpp+19
@@ -37,6 +37,7 @@ MockLauncherItem::MockLauncherItem(const QString &appId, const QString& desktopF
3737 m_count(0),
3838 m_countVisible(false),
3939 m_focused(false),
40+ m_alerting(false),
4041 m_quickList(new MockQuickListModel(this))
4142{
4243
@@ -129,6 +130,9 @@ void MockLauncherItem::setCount(int count)
129130 {
130131 m_count = count;
131132 Q_EMIT countChanged(count);
133+ if (m_countVisible) {
134+ setAlerting(true);
135+ }
132136 }
133137}
134138
@@ -142,6 +146,9 @@ void MockLauncherItem::setCountVisible(bool countVisible)
142146 if (m_countVisible != countVisible) {
143147 m_countVisible = countVisible;
144148 Q_EMIT countVisibleChanged(countVisible);
149+ if (countVisible) {
150+ setAlerting(true);
151+ }
145152 }
146153}
147154
@@ -159,6 +166,18 @@ void MockLauncherItem::setFocused(bool focused)
159166 }
160167}
161168
169+bool MockLauncherItem::alerting() const
170+{
171+ return m_alerting;
172+}
173+
174+void MockLauncherItem::setAlerting(bool alerting)
175+{
176+ if (m_alerting != alerting) {
177+ m_alerting = alerting;
178+ Q_EMIT alertingChanged(alerting);
179+ }
180+}
162181
163182unity::shell::launcher::QuickListModelInterface *MockLauncherItem::quickList() const
164183{
tests/mocks/Unity/Launcher/MockLauncherItem.h+3
@@ -44,6 +44,7 @@ public:
4444 int count() const override;
4545 bool countVisible() const override;
4646 bool focused() const override;
47+ bool alerting() const override;
4748
4849 unity::shell::launcher::QuickListModelInterface *quickList() const override;
4950
@@ -55,6 +56,7 @@ private:
5556 void setCount(int count);
5657 void setCountVisible(bool countVisible);
5758 void setFocused(bool focused);
59+ void setAlerting(bool alerting);
5860
5961 QString m_appId;
6062 QString m_desktopFile;
@@ -67,6 +69,7 @@ private:
6769 int m_count;
6870 bool m_countVisible;
6971 bool m_focused;
72+ bool m_alerting;
7073 MockQuickListModel *m_quickList;
7174
7275 friend class MockLauncherModel;
tests/mocks/Unity/Launcher/MockLauncherModel.cpp+40
@@ -34,6 +34,7 @@ MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(pa
3434 item = new MockLauncherItem("gallery-app", "/usr/share/applications/gallery-app.desktop", "Gallery", "gallery", this);
3535 item->setProgress(50);
3636 item->setCountVisible(true);
37+ item->setAlerting(false);
3738 m_list.append(item);
3839 item = new MockLauncherItem("music-app", "/usr/share/applications/music-app.desktop", "Music", "soundcloud", this);
3940 m_list.append(item);
@@ -43,25 +44,30 @@ MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(pa
4344 item = new MockLauncherItem("webbrowser-app", "/usr/share/applications/webbrowser-app.desktop", "Browser", "browser", this);
4445 item->setCount(1);
4546 item->setCountVisible(true);
47+ item->setAlerting(false);
4648 m_list.append(item);
4749 item = new MockLauncherItem("twitter-webapp", "/usr/share/applications/twitter-webapp.desktop", "Twitter", "twitter", this);
4850 item->setCount(12);
4951 item->setCountVisible(true);
52+ item->setAlerting(false);
5053 item->setPinned(true);
5154 m_list.append(item);
5255 item = new MockLauncherItem("gmail-webapp", "/usr/share/applications/gmail-webapp.desktop", "GMail", "gmail", this);
5356 item->setCount(123);
5457 item->setCountVisible(true);
58+ item->setAlerting(false);
5559 m_list.append(item);
5660 item = new MockLauncherItem("ubuntu-weather-app", "/usr/share/applications/ubuntu-weather-app.desktop", "Weather", "weather", this);
5761 item->setCount(1234567890);
5862 item->setCountVisible(true);
63+ item->setAlerting(false);
5964 item->setPinned(true);
6065 m_list.append(item);
6166 item = new MockLauncherItem("notes-app", "/usr/share/applications/notes-app.desktop", "Notepad", "notepad", this);
6267 item->setProgress(50);
6368 item->setCount(5);
6469 item->setCountVisible(true);
70+ item->setAlerting(false);
6571 item->setFocused(true);
6672 item->setPinned(true);
6773 m_list.append(item);
@@ -105,6 +111,8 @@ QVariant MockLauncherModel::data(const QModelIndex& index, int role) const
105111 return item->countVisible();
106112 case RoleFocused:
107113 return item->focused();
114+ case RoleAlerting:
115+ return item->alerting();
108116 }
109117
110118 return QVariant();
@@ -119,6 +127,18 @@ unity::shell::launcher::LauncherItemInterface *MockLauncherModel::get(int index)
119127 return m_list.at(index);
120128}
121129
130+void MockLauncherModel::setAlerting(const QString &appId, bool alerting) {
131+ int index = findApp(appId);
132+ if (index >= 0) {
133+ QModelIndex modelIndex = this->index(index);
134+ MockLauncherItem *item = m_list.at(index);
135+ if (!item->focused()) {
136+ item->setAlerting(alerting);
137+ Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RoleAlerting);
138+ }
139+ }
140+}
141+
122142void MockLauncherModel::move(int oldIndex, int newIndex)
123143{
124144 // Make sure its not moved outside the lists
@@ -236,6 +256,26 @@ void MockLauncherModel::emitHint()
236256 Q_EMIT hint();
237257}
238258
259+void MockLauncherModel::setCount(const QString &appId, int count)
260+{
261+ int index = findApp(appId);
262+ if (index >= 0) {
263+ m_list.at(index)->setCount(count);
264+ QModelIndex modelIndex = this->index(index);
265+ Q_EMIT dataChanged(modelIndex, modelIndex);
266+ }
267+}
268+
269+void MockLauncherModel::setCountVisible(const QString &appId, bool countVisible)
270+{
271+ int index = findApp(appId);
272+ if (index >= 0) {
273+ m_list.at(index)->setCountVisible(countVisible);
274+ QModelIndex modelIndex = this->index(index);
275+ Q_EMIT dataChanged(modelIndex, modelIndex);
276+ }
277+}
278+
239279unity::shell::application::ApplicationManagerInterface *MockLauncherModel::applicationManager() const
240280{
241281 return nullptr;
tests/mocks/Unity/Launcher/MockLauncherModel.h+3
@@ -38,6 +38,7 @@ public:
3838
3939 QVariant data(const QModelIndex& index, int role) const override;
4040
41+ Q_INVOKABLE void setAlerting(const QString &appId, bool alerting) override;
4142 Q_INVOKABLE unity::shell::launcher::LauncherItemInterface *get(int index) const override;
4243 Q_INVOKABLE void move(int oldIndex, int newIndex) override;
4344 Q_INVOKABLE void pin(const QString &appId, int index = -1) override;
@@ -55,6 +56,8 @@ public:
5556 // For testing
5657 Q_INVOKABLE void emitHint();
5758 Q_INVOKABLE void setProgress(const QString &appId, int progress);
59+ Q_INVOKABLE void setCount(const QString &appId, int count);
60+ Q_INVOKABLE void setCountVisible(const QString &appId, bool countVisible);
5861
5962Q_SIGNALS:
6063 void quickListTriggered(const QString &appId, int index);
tests/plugins/AccountsService/client.cpp+10−20
@@ -94,8 +94,7 @@ private Q_SLOTS:
9494
9595 void testGetSetService()
9696 {
97− AccountsService session;
98− session.setUser(QTest::currentTestFunction());
97+ AccountsService session(this, QTest::currentTestFunction());
9998
10099 QCOMPARE(session.demoEdges(), false);
101100 session.setDemoEdges(true);
@@ -112,8 +111,7 @@ private Q_SLOTS:
112111
113112 void testAsynchornousChangeForDemoEdges()
114113 {
115− AccountsService session;
116− session.setUser(QTest::currentTestFunction());
114+ AccountsService session(this, QTest::currentTestFunction());
117115
118116 QCOMPARE(session.demoEdges(), false);
119117 ASSERT_DBUS_CALL(m_userInterface->call("Set",
@@ -125,8 +123,7 @@ private Q_SLOTS:
125123
126124 void testAsynchornousChangeForFailedLogins()
127125 {
128− AccountsService session;
129− session.setUser(QTest::currentTestFunction());
126+ AccountsService session(this, QTest::currentTestFunction());
130127
131128 QCOMPARE(session.failedLogins(), (uint)0);
132129 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
@@ -138,8 +135,7 @@ private Q_SLOTS:
138135
139136 void testAsynchornousChangeForStatsWelcomeScreen()
140137 {
141− AccountsService session;
142− session.setUser(QTest::currentTestFunction());
138+ AccountsService session(this, QTest::currentTestFunction());
143139
144140 QCOMPARE(session.statsWelcomeScreen(), true);
145141 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
@@ -151,8 +147,7 @@ private Q_SLOTS:
151147
152148 void testAsynchornousChangeForStatsEnableLauncherWhileLocked()
153149 {
154− AccountsService session;
155− session.setUser(QTest::currentTestFunction());
150+ AccountsService session(this, QTest::currentTestFunction());
156151
157152 QCOMPARE(session.enableLauncherWhileLocked(), true);
158153 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
@@ -164,8 +159,7 @@ private Q_SLOTS:
164159
165160 void testAsynchornousChangeForStatsEnableIndicatorsWhileLocked()
166161 {
167− AccountsService session;
168− session.setUser(QTest::currentTestFunction());
162+ AccountsService session(this, QTest::currentTestFunction());
169163
170164 QCOMPARE(session.enableIndicatorsWhileLocked(), true);
171165 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
@@ -177,8 +171,7 @@ private Q_SLOTS:
177171
178172 void testAsynchornousChangeForStatsPasswordDisplayHint()
179173 {
180− AccountsService session;
181− session.setUser(QTest::currentTestFunction());
174+ AccountsService session(this, QTest::currentTestFunction());
182175
183176 QCOMPARE(session.passwordDisplayHint(), AccountsService::Keyboard);
184177 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
@@ -190,8 +183,7 @@ private Q_SLOTS:
190183
191184 void testAsynchornousChangeForStatsLicenseAccepted()
192185 {
193− AccountsService session;
194− session.setUser(QTest::currentTestFunction());
186+ AccountsService session(this, QTest::currentTestFunction());
195187
196188 QCOMPARE(session.hereEnabled(), false);
197189 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
@@ -203,8 +195,7 @@ private Q_SLOTS:
203195
204196 void testAsynchornousChangeForLicenseBasePath()
205197 {
206− AccountsService session;
207− session.setUser(QTest::currentTestFunction());
198+ AccountsService session(this, QTest::currentTestFunction());
208199
209200 QCOMPARE(session.hereLicensePath(), QString());
210201 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
@@ -216,8 +207,7 @@ private Q_SLOTS:
216207
217208 void testAsynchornousChangeForStatsBackgroundFile()
218209 {
219− AccountsService session;
220− session.setUser(QTest::currentTestFunction());
210+ AccountsService session(this, QTest::currentTestFunction());
221211
222212 QCOMPARE(session.backgroundFile(), QString());
223213 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,4 +1,4 @@
1−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
1+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
22pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
33
44include_directories(
tests/plugins/LightDM/CMakeLists.txt+6
@@ -9,6 +9,9 @@ add_executable(GreeterDBusTestExec
99 dbus.cpp
1010 ${CMAKE_SOURCE_DIR}/plugins/LightDM/Greeter.cpp
1111 )
12+# This add_dependencies is needed since we're linking with with -L and -l below
13+# make seems to work but ninja breaks without it
14+add_dependencies(GreeterDBusTestExec MockLightDM)
1215qt5_use_modules(GreeterDBusTestExec Core DBus Quick Test)
1316target_link_libraries(GreeterDBusTestExec
1417 -L${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm
@@ -44,6 +47,9 @@ add_executable(GreeterUsersModelTestExec
4447 ${CMAKE_SOURCE_DIR}/plugins/LightDM/UsersModel.cpp
4548 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
4649 )
50+# This add_dependencies is needed since we're linking with with -L and -l below
51+# make seems to work but ninja breaks without it
52+add_dependencies(GreeterUsersModelTestExec MockLightDM)
4753qt5_use_modules(GreeterUsersModelTestExec Core Test)
4854target_link_libraries(GreeterUsersModelTestExec
4955 -L${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm
tests/plugins/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,5 +1,5 @@
11pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
2−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
2+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
33
44include_directories(
55 ${CMAKE_CURRENT_SOURCE_DIR}
tests/plugins/Unity/Launcher/launchermodeltest.cpp+23−7
@@ -368,11 +368,18 @@ private Q_SLOTS:
368368 }
369369
370370 void testCountEmblems() {
371+ QSignalSpy spy(launcherModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)));
372+
371373 // Call GetAll on abs-icon
372374 QDBusInterface interface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher/abs_2Dicon", "org.freedesktop.DBus.Properties");
373375 QDBusReply<QVariantMap> reply = interface.call("GetAll");
374376 QVariantMap map = reply.value();
375377
378+ // Check that the alerting-status is still false, and the item on the upper side of the API
379+ int index = launcherModel->findApplication("abs-icon");
380+ QCOMPARE(index >= 0, true);
381+ QVERIFY(launcherModel->get(index)->alerting() == false);
382+
376383 // Make sure GetAll returns a map with count and countVisible props
377384 QCOMPARE(map.contains("count"), true);
378385 QCOMPARE(map.contains("countVisible"), true);
@@ -393,13 +400,8 @@ private Q_SLOTS:
393400 QCOMPARE(map.value("count").toInt(), 55);
394401 QCOMPARE(map.value("countVisible").toBool(), true);
395402
396− // Now the item on the upper side of the API
397− int index = launcherModel->findApplication("abs-icon");
398− QCOMPARE(index >= 0, true);
399−
400− // And make sure values have changed there as well
401− QCOMPARE(launcherModel->get(index)->countVisible(), true);
402− QCOMPARE(launcherModel->get(index)->count(), 55);
403+ // Finally check, that the change to "count" implicitly also set the alerting-state to true
404+ QVERIFY(launcherModel->get(index)->alerting() == true);
403405 }
404406
405407 void testCountEmblemAddsRemovesItem_data() {
@@ -459,6 +461,20 @@ private Q_SLOTS:
459461 QCOMPARE(index == -1, !isRunning && !isPinned && !startWhenVisible);
460462 }
461463
464+ void testAlert() {
465+ // Check that the alerting-status is still false
466+ int index = launcherModel->findApplication("abs-icon");
467+ QCOMPARE(index >= 0, true);
468+ QVERIFY(launcherModel->get(index)->alerting() == false);
469+
470+ // Call Alert() on "abs-icon"
471+ QDBusInterface interface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher/abs_2Dicon", "com.canonical.Unity.Launcher.Item");
472+ interface.call("Alert");
473+
474+ // Check that the alerting-status is now true
475+ QVERIFY(launcherModel->get(index)->alerting() == true);
476+ }
477+
462478 void testRefreshAfterDeletedDesktopFiles_data() {
463479 QTest::addColumn<bool>("deleted");
464480 QTest::newRow("have .desktop files") << false;
tests/plugins/Unity/Session/sessionbackendtest.cpp+27−40
@@ -1,5 +1,5 @@
11/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2015 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU Lesser General Public License as published by
@@ -15,6 +15,7 @@
1515 *
1616 * Authors:
1717 * Ying-Chun Liu (PaulLiu) <paul.liu@canonical.com>
18+ * Lukáš Tinkl <lukas.tinkl@canonical.com>
1819 */
1920
2021#include <QtTest>
@@ -49,17 +50,22 @@ private Q_SLOTS:
4950
5051 void testUnitySessionLogoutRequested_data() {
5152 QTest::addColumn<QString>("method");
53+ QTest::addColumn<QString>("signal");
5254
53− QTest::newRow("Logout") << "RequestLogout";
55+ QTest::newRow("Logout") << "RequestLogout" << "logoutRequested(bool)";
56+ QTest::newRow("Reboot") << "RequestReboot" << "rebootRequested(bool)";
57+ QTest::newRow("Shutdown") << "RequestShutdown" << "shutdownRequested(bool)";
5458 }
5559
5660 void testUnitySessionLogoutRequested() {
5761 QFETCH(QString, method);
62+ QFETCH(QString, signal);
5863
5964 DBusUnitySessionService dbusUnitySessionService;
6065 QCoreApplication::processEvents(); // to let the service register on DBus
6166
62− QSignalSpy spy(&dbusUnitySessionService, SIGNAL(logoutRequested(bool)));
67+ // .. because QSignalSpy checks the signal signature like this: "if (((aSignal[0] - '0') & 0x03) != QSIGNAL_CODE)"
68+ QSignalSpy spy(&dbusUnitySessionService, signal.prepend(QSIGNAL_CODE).toLocal8Bit().constData());
6369
6470 QDBusReply<void> reply = dbusUnitySession->call(method);
6571 QCOMPARE(reply.isValid(), true);
@@ -67,51 +73,34 @@ private Q_SLOTS:
6773 QCOMPARE(spy.count(), 1);
6874 }
6975
70− void testGnomeSessionWrapperLogoutRequested() {
71− DBusUnitySessionService dbusUnitySessionService;
72− QCoreApplication::processEvents(); // to let the service register on DBus
73−
74− // Spy on the logoutRequested signal on the /com/canonical/Unity/Session object
75− // as proof we are actually calling the actual method.
76− QSignalSpy spy(&dbusUnitySessionService, SIGNAL(logoutRequested(bool)));
76+ void testGnomeSessionWrapper_data() {
77+ QTest::addColumn<uint>("method");
78+ QTest::addColumn<QString>("signal");
7779
78− DBusGnomeSessionManagerWrapper dbusGnomeSessionManagerWrapper;
79− QCoreApplication::processEvents(); // to let the service register on DBus
80−
81− QDBusInterface dbusGnomeSessionWrapper ("com.canonical.Unity",
82− "/org/gnome/SessionManager/EndSessionDialog",
83− "org.gnome.SessionManager.EndSessionDialog",
84− QDBusConnection::sessionBus());
85−
86− QCOMPARE(dbusGnomeSessionWrapper.isValid(), true);
87−
88− // Set the QVariant as a QList<QDBusObjectPath> type
89− QDbusList var;
90− QVariant inhibitors;
91− inhibitors.setValue(var);
92−
93− QDBusReply<void> reply = dbusGnomeSessionWrapper.call("Open", (unsigned)Action::LOGOUT, (unsigned)0, (unsigned)0, inhibitors);
94− QCOMPARE(reply.isValid(), true);
95−
96− // Make sure we see the signal being emitted.
97− QCOMPARE(spy.count(), 1);
80+ QTest::newRow("Logout") << (uint)Action::LOGOUT << "logoutRequested(bool)";
81+ QTest::newRow("Shutdown") << (unsigned)Action::SHUTDOWN << "shutdownRequested(bool)";
82+ QTest::newRow("Reboot") << (unsigned)Action::REBOOT << "rebootRequested(bool)";
9883 }
9984
100− void testGnomeSessionWrapperShutdownRequested() {
85+ void testGnomeSessionWrapper() {
86+ QFETCH(uint, method);
87+ QFETCH(QString, signal);
88+
10189 DBusUnitySessionService dbusUnitySessionService;
10290 QCoreApplication::processEvents(); // to let the service register on DBus
10391
104− // Spy on the shutdownRequested signal on the /com/canonical/Unity/Session object
92+ // Spy on the given signal on the /com/canonical/Unity/Session object
10593 // as proof we are actually calling the actual method.
106− QSignalSpy spy(&dbusUnitySessionService, SIGNAL(shutdownRequested(bool)));
94+ // .. because QSignalSpy checks the signal signature like this: "if (((aSignal[0] - '0') & 0x03) != QSIGNAL_CODE)"
95+ QSignalSpy spy(&dbusUnitySessionService, signal.prepend(QSIGNAL_CODE).toLocal8Bit().constData());
10796
10897 DBusGnomeSessionManagerWrapper dbusGnomeSessionManagerWrapper;
10998 QCoreApplication::processEvents(); // to let the service register on DBus
11099
111− QDBusInterface dbusGnomeSessionWrapper ("com.canonical.Unity",
112− "/org/gnome/SessionManager/EndSessionDialog",
113− "org.gnome.SessionManager.EndSessionDialog",
114− QDBusConnection::sessionBus());
100+ QDBusInterface dbusGnomeSessionWrapper("com.canonical.Unity",
101+ "/org/gnome/SessionManager/EndSessionDialog",
102+ "org.gnome.SessionManager.EndSessionDialog",
103+ QDBusConnection::sessionBus());
115104
116105 QCOMPARE(dbusGnomeSessionWrapper.isValid(), true);
117106
@@ -120,9 +109,7 @@ private Q_SLOTS:
120109 QVariant inhibitors;
121110 inhibitors.setValue(var);
122111
123− // * Reboot action translates to the shutdown signal due to some weird idiosyncracy
124− // in the indicator-session/Unity interaction. *
125− QDBusReply<void> reply = dbusGnomeSessionWrapper.call("Open", (unsigned)Action::REBOOT, (unsigned)0, (unsigned)0, inhibitors);
112+ QDBusReply<void> reply = dbusGnomeSessionWrapper.call("Open", method, (unsigned)0, (unsigned)0, inhibitors);
126113 QCOMPARE(reply.isValid(), true);
127114
128115 // Make sure we see the signal being emitted.
tests/qmltests/CMakeLists.txt+2
@@ -27,6 +27,8 @@ add_unity8_qmltest(Dash PreviewListView)
2727add_unity8_qmltest(Dash/Previews Preview)
2828add_unity8_qmltest(Dash/Previews PreviewActions)
2929add_unity8_qmltest(Dash/Previews PreviewAudioPlayback)
30+add_unity8_qmltest(Dash/Previews PreviewComment)
31+add_unity8_qmltest(Dash/Previews PreviewCommentInput)
3032add_unity8_qmltest(Dash/Previews PreviewExpandable)
3133add_unity8_qmltest(Dash/Previews PreviewHeader)
3234add_unity8_qmltest(Dash/Previews PreviewImageGallery)
tests/qmltests/Dash/Previews/tst_PreviewComment.qmlnew file+84
@@ -0,0 +1,84 @@
1+/*
2+ * Copyright 2015 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 Ubuntu.Components 1.1
20+import "../../../../qml/Dash/Previews"
21+import Unity.Test 0.1 as UT
22+
23+Rectangle {
24+ id: root
25+ width: units.gu(40)
26+ height: units.gu(80)
27+ color: Theme.palette.selected.background
28+
29+ property var comment: {
30+ "author": "Claire Thompson",
31+ "subtitle": "28/04/2015 3:48pm",
32+ "comment": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
33+ "source": "../../graphics/avatars/amanda@12.png"
34+ }
35+
36+ property var commentNoImage: {
37+ "author": "Claire Thompson",
38+ "subtitle": "28/04/2015 3:48pm",
39+ "comment": "Been quite a while since I really liked one.",
40+ "source": ""
41+ }
42+
43+ property var commentNoSubtitle: {
44+ "author": "Claire Thompson",
45+ "comment": "Been quite a while since I really liked one.",
46+ "source": "../../graphics/avatars/amanda@12.png"
47+ }
48+
49+ PreviewComment {
50+ id: previewComment
51+ anchors.left: parent.left
52+ anchors.right: parent.right
53+ widgetData: comment
54+ widgetId: "previewComment"
55+ }
56+
57+ UT.UnityTestCase {
58+ name: "PreviewCommentTest"
59+ when: windowShown
60+
61+ function init() {
62+ previewComment.widgetData = comment;
63+ }
64+
65+ function test_AnchorsNoAvatar() {
66+ var column = findChild(previewComment, "column");
67+ var avatar = findChild(previewComment, "avatar");
68+
69+ compare(previewComment.widgetData, comment);
70+ compare(avatar.visible, true);
71+ compare(column.anchors.left, avatar.anchors.right);
72+
73+ previewComment.widgetData = commentNoImage;
74+ tryCompare(avatar, "visible", false);
75+ compare(column.anchors.left, previewComment.anchors.left);
76+ }
77+
78+ function test_OptionalSubtitle() {
79+ var subtitle = findChild(previewComment, "subtitle");
80+ previewComment.widgetData = commentNoSubtitle;
81+ tryCompare(subtitle, "visible", false);
82+ }
83+ }
84+}
tests/qmltests/Dash/Previews/tst_PreviewCommentInput.qmlnew file+85
@@ -0,0 +1,85 @@
1+/*
2+ * Copyright 2015 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 Ubuntu.Components 1.1
20+import "../../../../qml/Dash/Previews"
21+import Unity.Test 0.1 as UT
22+
23+Rectangle {
24+ id: root
25+ width: units.gu(40)
26+ height: units.gu(80)
27+ color: Theme.palette.selected.background
28+
29+ property string validInputText: "Great!"
30+ property string invalidInputText: ""
31+
32+ property var widgetDataNewLabels: { "submit-label": "TestSubmitLabel" }
33+
34+ PreviewCommentInput {
35+ id: previewCommentInput
36+ anchors.left: parent.left
37+ anchors.right: parent.right
38+ widgetData: widgetDataNewLabels
39+ widgetId: "previewCommentInput"
40+ }
41+
42+ SignalSpy {
43+ id: spy
44+ signalName: "triggered"
45+ }
46+
47+ UT.UnityTestCase {
48+ name: "PreviewCommentInputTest"
49+ when: windowShown
50+
51+ property var commentTextArea: findChild(previewCommentInput, "commentTextArea")
52+ property var submitButton: findChild(previewCommentInput, "submitButton")
53+
54+ function init() {
55+ commentTextArea.text = "";
56+ }
57+
58+ function test_labels() {
59+ previewCommentInput.widgetData = widgetDataNewLabels;
60+ compare(submitButton.text, widgetDataNewLabels["submit-label"]);
61+ }
62+
63+ function test_submit_data() {
64+ return [
65+ {tag: "empty review", inputText: invalidInputText, emitted: false},
66+ {tag: "filled review", inputText: validInputText, emitted: true},
67+ ];
68+ }
69+
70+ function test_submit(data) {
71+ spy.clear();
72+ spy.target = previewCommentInput;
73+
74+ commentTextArea.text = data.inputText;
75+ mouseClick(submitButton);
76+ if (!data.emitted) {
77+ compare(spy.count, 0);
78+ } else {
79+ compare(spy.count, 1);
80+ var args = spy.signalArguments[0];
81+ compare(args[2]["comment"], data.inputText);
82+ }
83+ }
84+ }
85+}
tests/qmltests/Dash/Previews/tst_PreviewWidgetFactory.qml+2
@@ -77,6 +77,8 @@ Rectangle {
7777 return [
7878 { tag: "Actions", type: "actions", source: "PreviewActions.qml" },
7979 { tag: "Audio", type: "audio", source: "PreviewAudioPlayback.qml" },
80+ { tag: "Comment", type: "comment", source: "PreviewComment.qml" },
81+ { tag: "Comment Input", type: "comment-input", source: "PreviewCommentInput.qml" },
8082 { tag: "Expandable", type: "expandable", source: "PreviewExpandable.qml" },
8183 { tag: "Gallery", type: "gallery", source: "PreviewImageGallery.qml" },
8284 { tag: "Header", type: "header", source: "PreviewHeader.qml" },
tests/qmltests/Launcher/tst_Launcher.qml+168−24
@@ -95,30 +95,55 @@ Item {
9595 }
9696
9797 Row {
98− spacing: units.gu(1)
99−
100− Button {
101− text: "35% bar"
102− onClicked: LauncherModel.setProgress(LauncherModel.get(parseInt(appIdEntry.displayText)).appId, 35)
103− Layout.fillWidth: true
104− }
105−
106− TextArea {
107− id: appIdEntry
108− anchors.verticalCenter: parent.verticalCenter
109− width: units.gu(4)
110− height: units.gu(4)
111− autoSize: true
112− text: "2"
113− maximumLineCount: 1
114− }
115−
116− Button {
117− text: "no bar"
118− onClicked: LauncherModel.setProgress(LauncherModel.get(parseInt(appIdEntry.displayText)).appId, -1)
119− Layout.fillWidth: true
120− }
121− }
98+ spacing: units.gu(1)
99+
100+ Button {
101+ text: "35% bar"
102+ onClicked: LauncherModel.setProgress(LauncherModel.get(parseInt(appIdEntryBar.displayText)).appId, 35)
103+ Layout.fillWidth: true
104+ }
105+
106+ TextArea {
107+ id: appIdEntryBar
108+ anchors.verticalCenter: parent.verticalCenter
109+ width: units.gu(4)
110+ height: units.gu(4)
111+ autoSize: true
112+ text: "2"
113+ maximumLineCount: 1
114+ }
115+
116+ Button {
117+ text: "no bar"
118+ onClicked: LauncherModel.setProgress(LauncherModel.get(parseInt(appIdEntryBar.displayText)).appId, -1)
119+ Layout.fillWidth: true
120+ }
121+ }
122+
123+ Row {
124+ spacing: units.gu(1)
125+
126+ Button {
127+ text: "set alert"
128+ onClicked: LauncherModel.setAlerting(LauncherModel.get(parseInt(appIdEntryAlert.displayText)).appId, true)
129+ }
130+
131+ TextArea {
132+ id: appIdEntryAlert
133+ anchors.verticalCenter: parent.verticalCenter
134+ width: units.gu(5)
135+ height: units.gu(4)
136+ autoSize: true
137+ text: "2"
138+ maximumLineCount: 1
139+ Layout.fillWidth: true
140+ }
141+
142+ Button {
143+ text: "unset alert"
144+ onClicked: LauncherModel.setAlerting(LauncherModel.get(parseInt(appIdEntryAlert.displayText)).appId, false)
145+ }
146+ }
122147 }
123148
124149 SignalSpy {
@@ -195,6 +220,16 @@ Item {
195220 tryCompare(panel, "x", -panel.width, 1000);
196221 }
197222
223+ function waitForWiggleToStart(appIcon) {
224+ verify(appIcon != undefined)
225+ tryCompare(appIcon, "wiggling", true, 1000, "wiggle-anim should not be in stopped state")
226+ }
227+
228+ function waitForWiggleToStop(appIcon) {
229+ verify(appIcon != undefined)
230+ tryCompare(appIcon, "wiggling", false, 1000, "wiggle-anim should not be in running state")
231+ }
232+
198233 function positionLauncherListAtBeginning() {
199234 var listView = testCase.findChild(launcherLoader.item, "launcherListView");
200235 listView.contentY = -listView.topMargin;
@@ -673,5 +708,114 @@ Item {
673708 compare(findChild(item, "progressOverlay").visible, true)
674709 LauncherModel.setProgress(LauncherModel.get(0).appId, 0)
675710 }
711+
712+ function test_alertPeekingIcon() {
713+ var listView = findChild(launcher, "launcherListView")
714+ verify(listView != undefined)
715+ LauncherModel.setAlerting(LauncherModel.get(5).appId, true)
716+ tryCompare(listView, "peekingIndex", 5, 1000, "Wrong appId set as peeking-index")
717+ LauncherModel.setAlerting(LauncherModel.get(5).appId, false)
718+ tryCompare(listView, "peekingIndex", -1, 1000, "peeking-index should be -1")
719+ }
720+
721+ function test_alertHidingIcon() {
722+ var listView = findChild(launcher, "launcherListView")
723+ verify(listView != undefined)
724+ var appIcon6 = findChild(launcher, "launcherDelegate6")
725+ verify(appIcon6 != undefined)
726+ LauncherModel.setAlerting(LauncherModel.get(6).appId, true)
727+ waitForWiggleToStart(appIcon6)
728+ LauncherModel.setAlerting(LauncherModel.get(6).appId, false)
729+ waitForWiggleToStop(appIcon6)
730+ tryCompare(appIcon6, "x", 0, 1000, "x-value of appId #6 should not be non-zero")
731+ waitForRendering(listView)
732+ }
733+
734+ function test_alertIgnoreFocusedApp() {
735+ LauncherModel.setAlerting(LauncherModel.get(0).appId, true)
736+ compare(LauncherModel.get(0).alerting, false, "Focused app should not have the alert-state set")
737+ }
738+
739+ function test_alertOnlyOnePeekingIcon() {
740+ var listView = findChild(launcher, "launcherListView")
741+ verify(listView != undefined)
742+ LauncherModel.setAlerting(LauncherModel.get(3).appId, true)
743+ LauncherModel.setAlerting(LauncherModel.get(1).appId, true)
744+ LauncherModel.setAlerting(LauncherModel.get(5).appId, true)
745+ tryCompare(listView, "peekingIndex", 3, 1000, "Wrong appId set as peeking-index")
746+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
747+ LauncherModel.setAlerting(LauncherModel.get(3).appId, false)
748+ LauncherModel.setAlerting(LauncherModel.get(5).appId, false)
749+ tryCompare(listView, "peekingIndex", -1, 1000, "peeking-index should be -1")
750+ waitForRendering(listView)
751+ }
752+
753+ function test_alertMultipleApps() {
754+ LauncherModel.setAlerting(LauncherModel.get(1).appId, true)
755+ LauncherModel.setAlerting(LauncherModel.get(3).appId, true)
756+ LauncherModel.setAlerting(LauncherModel.get(5).appId, true)
757+ LauncherModel.setAlerting(LauncherModel.get(7).appId, true)
758+ compare(LauncherModel.get(1).alerting, true, "Alert-state of appId #1 should not be false")
759+ compare(LauncherModel.get(3).alerting, true, "Alert-state of appId #3 should not be false")
760+ compare(LauncherModel.get(5).alerting, true, "Alert-state of appId #5 should not be false")
761+ compare(LauncherModel.get(7).alerting, true, "Alert-state of appId #7 should not be false")
762+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
763+ LauncherModel.setAlerting(LauncherModel.get(3).appId, false)
764+ LauncherModel.setAlerting(LauncherModel.get(5).appId, false)
765+ LauncherModel.setAlerting(LauncherModel.get(7).appId, false)
766+ compare(LauncherModel.get(1).alerting, false, "Alert-state of appId #1 should not be true")
767+ compare(LauncherModel.get(3).alerting, false, "Alert-state of appId #1 should not be true")
768+ compare(LauncherModel.get(5).alerting, false, "Alert-state of appId #1 should not be true")
769+ compare(LauncherModel.get(7).alerting, false, "Alert-state of appId #1 should not be true")
770+ }
771+
772+ function test_alertMoveIconIntoView() {
773+ dragLauncherIntoView();
774+ var appIcon1 = findChild(launcher, "launcherDelegate1");
775+ var appIcon7 = findChild(launcher, "launcherDelegate7");
776+ LauncherModel.setAlerting(LauncherModel.get(1).appId, true)
777+ tryCompare(appIcon1, "angle", 0, 1000, "angle of appId #1 should not be non-zero")
778+ waitForWiggleToStart(appIcon1)
779+ LauncherModel.setAlerting(LauncherModel.get(7).appId, true)
780+ tryCompare(appIcon7, "angle", 0, 1000, "angle of appId #7 should not be non-zero")
781+ waitForWiggleToStart(appIcon7)
782+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
783+ waitForWiggleToStop(appIcon1)
784+ LauncherModel.setAlerting(LauncherModel.get(7).appId, false)
785+ waitForWiggleToStop(appIcon7)
786+ }
787+
788+ function test_alertWigglePeekDrag() {
789+ var appIcon5 = findChild(launcher, "launcherDelegate5");
790+ var listView = findChild(launcher, "launcherListView")
791+ verify(listView != undefined)
792+ LauncherModel.setAlerting(LauncherModel.get(5).appId, true)
793+ tryCompare(listView, "peekingIndex", 5, 1000, "Wrong appId set as peeking-index")
794+ waitForWiggleToStart(appIcon5)
795+ tryCompare(appIcon5, "wiggling", true, 1000, "appId #6 should not be still")
796+ dragLauncherIntoView();
797+ tryCompare(listView, "peekingIndex", -1, 1000, "peeking-index should be -1")
798+ LauncherModel.setAlerting(LauncherModel.get(5).appId, false)
799+ waitForWiggleToStop(appIcon5)
800+ tryCompare(appIcon5, "wiggling", false, 1000, "appId #1 should not be wiggling")
801+ }
802+
803+ function test_alertViaCountAndCountVisible() {
804+ dragLauncherIntoView();
805+ var appIcon1 = findChild(launcher, "launcherDelegate1")
806+ var oldCount = LauncherModel.get(1).count
807+ LauncherModel.setCount(LauncherModel.get(1).appId, 42)
808+ tryCompare(appIcon1, "wiggling", false, 1000, "appId #1 should be still")
809+ LauncherModel.setCountVisible(LauncherModel.get(1).appId, 1)
810+ tryCompare(appIcon1, "wiggling", true, 1000, "appId #1 should not be still")
811+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
812+ waitForWiggleToStop(appIcon1)
813+ LauncherModel.setCount(LauncherModel.get(1).appId, 4711)
814+ tryCompare(appIcon1, "wiggling", true, 1000, "appId #1 should not be still")
815+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
816+ waitForWiggleToStop(appIcon1)
817+ LauncherModel.setCountVisible(LauncherModel.get(1).appId, 0)
818+ LauncherModel.setCount(LauncherModel.get(1).appId, oldCount)
819+ }
676820 }
677821}
tests/qmltests/Tutorial/tst_Tutorial.qml+31−1
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013,2014 Canonical, Ltd.
2+ * Copyright (C) 2013,2014,2015 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -319,6 +319,36 @@ Item {
319319 tryCompare(left, "shown", true); // and we should still be on left
320320 }
321321
322+ function test_launcherNoDragGap() {
323+ // See bug 1454882, where if you dragged the launcher while it was
324+ // visible, you could pull it further than the edge of the screen.
325+
326+ var left = goToPage("tutorialLeft");
327+ var launcher = findChild(shell, "launcher");
328+ var teaseAnimation = findInvisibleChild(left, "teaseAnimation");
329+
330+ // Wait for launcher to be really out there
331+ tryCompareFunction(function() {return launcher.x > teaseAnimation.maxBounce/2}, true);
332+ verify(teaseAnimation.running);
333+
334+ // Start a drag, make sure animation stops
335+ touchFlick(shell, 0, halfHeight, units.gu(4), halfHeight, true, false);
336+ verify(!teaseAnimation.running);
337+ verify(launcher.visibleWidth > 0);
338+ verify(launcher.x > 0);
339+ compare(launcher.x, teaseAnimation.bounce);
340+
341+ // Continue drag, make sure we don't create a gap on the left hand side
342+ touchFlick(shell, units.gu(4), halfHeight, shell.width, halfHeight, false, false);
343+ verify(!teaseAnimation.running);
344+ compare(launcher.visibleWidth, launcher.panelWidth);
345+ compare(launcher.x, 0);
346+
347+ // Finish and make sure we continue animation
348+ touchFlick(shell, shell.width, halfHeight, shell.width, halfHeight, false, true);
349+ tryCompare(teaseAnimation, "running", true);
350+ }
351+
322352 function test_spread() {
323353 // Unfortunately, most of what we want to test of the spread is
324354 // "did it render correctly?" but that's hard to test. So instead,
tests/qmltests/tst_ShellWithPin.qml+1−1
@@ -313,7 +313,7 @@ Item {
313313 tryCompare(panel, "fullscreenMode", false)
314314 tryCompare(indicators, "available", true)
315315 tryCompare(launcher, "available", true)
316− tryCompare(stage, "spreadEnabled", true)
316+ tryCompare(stage, "spreadEnabled", false)
317317 }
318318
319319 function test_emergencyCallCrash() {
tests/scripts/README+1
@@ -2,6 +2,7 @@ This directory holds a few handy shell-scripts to simplify runtime-testing of DB
22
33launcher-icon related:
44 * list-launcher-icons.sh - get all icons of unity8-launcher
5+ * alert-launcher-icon.sh - trigger alert/wiggle of unfocused launcher-icon
56 * get-progress.sh - get progress-value of a launcher-icon
67 * set-progress.sh - set progress-value of a launcher-icon
78 * set-count.sh - set the count of a launcher-icon
tests/scripts/alert-launcher-icon.shnew file+3
@@ -0,0 +1,3 @@
1+#!/bin/sh
2+
3+qdbus com.canonical.Unity.Launcher /com/canonical/Unity/Launcher/$1 com.canonical.Unity.Launcher.Item.Alert
tools/CMakeLists.txt+1
@@ -1,6 +1,7 @@
11add_executable(${SCOPE_TOOL}
22 scopetool.cpp
33 registry-tracker.cpp
4+ unix-signal-handler.cpp
45 )
56
67qt5_use_modules(${SCOPE_TOOL} Qml Quick)
tools/scopetool.cpp+6
@@ -32,6 +32,7 @@
3232// local
3333#include <paths.h>
3434#include "registry-tracker.h"
35+#include "unix-signal-handler.h"
3536
3637
3738int main(int argc, char *argv[])
@@ -96,6 +97,11 @@ int main(int argc, char *argv[])
9697
9798 view->show();
9899
100+ UnixSignalHandler signal_handler([]{
101+ QGuiApplication::exit(0);
102+ });
103+ signal_handler.setupUnixSignalHandlers();
104+
99105 int result = application->exec();
100106
101107 delete view;
tools/unix-signal-handler.cppnew file+141
@@ -0,0 +1,141 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU General Public License version 3, as published
6+ * by the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but
9+ * WITHOUT ANY WARRANTY; without even the implied warranties of
10+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11+ * PURPOSE. See the GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License along
14+ * with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Marcus Tomlinson <marcus.tomlinson@canonical.com>
17+ */
18+
19+#include "unix-signal-handler.h"
20+
21+#include <QDebug>
22+
23+#include <csignal>
24+#include <sys/socket.h>
25+#include <unistd.h>
26+
27+int UnixSignalHandler::sighupFd[2];
28+int UnixSignalHandler::sigintFd[2];
29+int UnixSignalHandler::sigtermFd[2];
30+
31+UnixSignalHandler::UnixSignalHandler(const std::function<void()>& f, QObject *parent)
32+ : QObject(parent),
33+ m_func(f)
34+{
35+ if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sighupFd))
36+ {
37+ qFatal("Couldn't create HUP socketpair");
38+ }
39+ if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigintFd))
40+ {
41+ qFatal("Couldn't create INT socketpair");
42+ }
43+ if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigtermFd))
44+ {
45+ qFatal("Couldn't create TERM socketpair");
46+ }
47+
48+ m_socketNotifierHup = new QSocketNotifier(sighupFd[1], QSocketNotifier::Read, this);
49+ connect(m_socketNotifierHup, &QSocketNotifier::activated, this, &UnixSignalHandler::handleSigHup);
50+ m_socketNotifierInt = new QSocketNotifier(sigintFd[1], QSocketNotifier::Read, this);
51+ connect(m_socketNotifierInt, &QSocketNotifier::activated, this, &UnixSignalHandler::handleSigInt);
52+ m_socketNotifierTerm = new QSocketNotifier(sigtermFd[1], QSocketNotifier::Read, this);
53+ connect(m_socketNotifierTerm, &QSocketNotifier::activated, this, &UnixSignalHandler::handleSigTerm);
54+}
55+
56+void UnixSignalHandler::hupSignalHandler(int)
57+{
58+ char a = 1;
59+ ::write(sighupFd[0], &a, sizeof(a));
60+}
61+
62+void UnixSignalHandler::intSignalHandler(int)
63+{
64+ char a = 1;
65+ ::write(sigintFd[0], &a, sizeof(a));
66+}
67+
68+void UnixSignalHandler::termSignalHandler(int)
69+{
70+ char a = 1;
71+ ::write(sigtermFd[0], &a, sizeof(a));
72+}
73+
74+int UnixSignalHandler::setupUnixSignalHandlers()
75+{
76+ struct sigaction sighup, sigint, sigterm;
77+
78+ sighup.sa_handler = UnixSignalHandler::hupSignalHandler;
79+ sigemptyset(&sighup.sa_mask);
80+ sighup.sa_flags = 0;
81+ sighup.sa_flags |= SA_RESTART;
82+
83+ if (sigaction(SIGHUP, &sighup, 0) > 0)
84+ {
85+ return 1;
86+ }
87+
88+ sigint.sa_handler = UnixSignalHandler::intSignalHandler;
89+ sigemptyset(&sigint.sa_mask);
90+ sigint.sa_flags = 0;
91+ sigint.sa_flags |= SA_RESTART;
92+
93+ if (sigaction(SIGINT, &sigint, 0) > 0)
94+ {
95+ return 2;
96+ }
97+
98+ sigterm.sa_handler = UnixSignalHandler::termSignalHandler;
99+ sigemptyset(&sigterm.sa_mask);
100+ sigterm.sa_flags |= SA_RESTART;
101+
102+ if (sigaction(SIGTERM, &sigterm, 0) > 0)
103+ {
104+ return 3;
105+ }
106+
107+ return 0;
108+}
109+
110+void UnixSignalHandler::handleSigHup()
111+{
112+ m_socketNotifierHup->setEnabled(false);
113+ char tmp;
114+ ::read(sighupFd[1], &tmp, sizeof(tmp));
115+
116+ m_func();
117+
118+ m_socketNotifierHup->setEnabled(true);
119+}
120+
121+void UnixSignalHandler::handleSigInt()
122+{
123+ m_socketNotifierInt->setEnabled(false);
124+ char tmp;
125+ ::read(sigintFd[1], &tmp, sizeof(tmp));
126+
127+ m_func();
128+
129+ m_socketNotifierInt->setEnabled(true);
130+}
131+
132+void UnixSignalHandler::handleSigTerm()
133+{
134+ m_socketNotifierTerm->setEnabled(false);
135+ char tmp;
136+ ::read(sigtermFd[1], &tmp, sizeof(tmp));
137+
138+ m_func();
139+
140+ m_socketNotifierTerm->setEnabled(true);
141+}
tools/unix-signal-handler.hnew file+54
@@ -0,0 +1,54 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU General Public License version 3, as published
6+ * by the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but
9+ * WITHOUT ANY WARRANTY; without even the implied warranties of
10+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11+ * PURPOSE. See the GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License along
14+ * with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Marcus Tomlinson <marcus.tomlinson@canonical.com>
17+ */
18+
19+#pragma once
20+
21+#include <QObject>
22+#include <QSocketNotifier>
23+
24+#include <functional>
25+
26+class UnixSignalHandler: public QObject {
27+Q_OBJECT
28+
29+public:
30+ UnixSignalHandler(const std::function<void()>& f, QObject *parent = 0);
31+ ~UnixSignalHandler() = default;
32+
33+ static int setupUnixSignalHandlers();
34+
35+protected Q_SLOTS:
36+ void handleSigHup();
37+ void handleSigInt();
38+ void handleSigTerm();
39+
40+protected:
41+ static void hupSignalHandler(int unused);
42+ static void intSignalHandler(int unused);
43+ static void termSignalHandler(int unused);
44+
45+ static int sighupFd[2];
46+ static int sigintFd[2];
47+ static int sigtermFd[2];
48+
49+ std::function<void()> m_func;
50+
51+ QSocketNotifier *m_socketNotifierHup;
52+ QSocketNotifier *m_socketNotifierInt;
53+ QSocketNotifier *m_socketNotifierTerm;
54+};