@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 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 General Public License as published by
@@ -12,8 +12,6 @@
1212 *
1313 * You should have received a copy of the GNU General Public License
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Author: Michael Terry <michael.terry@canonical.com>
1715 */
1816
1917#include "AccountsService.h"
@@ -24,18 +22,55 @@
2422#include <QStringList>
2523#include <QDebug>
2624
25+#define IFACE_ACCOUNTS_USER QStringLiteral("org.freedesktop.Accounts.User")
26+#define IFACE_LOCATION_HERE QStringLiteral("com.ubuntu.location.providers.here.AccountsService")
27+#define IFACE_UBUNTU_INPUT QStringLiteral("com.ubuntu.AccountsService.Input")
28+#define IFACE_UBUNTU_SECURITY QStringLiteral("com.ubuntu.AccountsService.SecurityPrivacy")
29+#define IFACE_UBUNTU_SECURITY_OLD QStringLiteral("com.ubuntu.touch.AccountsService.SecurityPrivacy")
30+#define IFACE_UNITY QStringLiteral("com.canonical.unity.AccountsService")
31+#define IFACE_UNITY_PRIVATE QStringLiteral("com.canonical.unity.AccountsService.Private")
32+
33+#define PROP_BACKGROUND_FILE QStringLiteral("BackgroundFile")
34+#define PROP_DEMO_EDGES QStringLiteral("demo-edges")
35+#define PROP_DEMO_EDGES_COMPLETED QStringLiteral("DemoEdgesCompleted")
36+#define PROP_EMAIL QStringLiteral("Email")
37+#define PROP_ENABLE_INDICATORS_WHILE_LOCKED QStringLiteral("EnableIndicatorsWhileLocked")
38+#define PROP_ENABLE_LAUNCHER_WHILE_LOCKED QStringLiteral("EnableLauncherWhileLocked")
39+#define PROP_FAILED_LOGINS QStringLiteral("FailedLogins")
40+#define PROP_LICENSE_ACCEPTED QStringLiteral("LicenseAccepted")
41+#define PROP_LICENSE_BASE_PATH QStringLiteral("LicenseBasePath")
42+#define PROP_MOUSE_CURSOR_SPEED QStringLiteral("MouseCursorSpeed")
43+#define PROP_MOUSE_DOUBLE_CLICK_SPEED QStringLiteral("MouseDoubleClickSpeed")
44+#define PROP_MOUSE_PRIMARY_BUTTON QStringLiteral("MousePrimaryButton")
45+#define PROP_MOUSE_SCROLL_SPEED QStringLiteral("MouseScrollSpeed")
46+#define PROP_PASSWORD_DISPLAY_HINT QStringLiteral("PasswordDisplayHint")
47+#define PROP_REAL_NAME QStringLiteral("RealName")
48+#define PROP_STATS_WELCOME_SCREEN QStringLiteral("StatsWelcomeScreen")
49+#define PROP_TOUCHPAD_CURSOR_SPEED QStringLiteral("TouchpadCursorSpeed")
50+#define PROP_TOUCHPAD_DISABLE_WHILE_TYPING QStringLiteral("TouchpadDisableWhileTyping")
51+#define PROP_TOUCHPAD_DISABLE_WITH_MOUSE QStringLiteral("TouchpadDisableWithMouse")
52+#define PROP_TOUCHPAD_DOUBLE_CLICK_SPEED QStringLiteral("TouchpadDoubleClickSpeed")
53+#define PROP_TOUCHPAD_PRIMARY_BUTTON QStringLiteral("TouchpadPrimaryButton")
54+#define PROP_TOUCHPAD_SCROLL_SPEED QStringLiteral("TouchpadScrollSpeed")
55+#define PROP_TOUCHPAD_TAP_TO_CLICK QStringLiteral("TouchpadTapToClick")
56+#define PROP_TOUCHPAD_TWO_FINGER_SCROLL QStringLiteral("TouchpadTwoFingerScroll")
57+
58+
59+QVariant primaryButtonConverter(const QVariant &value)
60+{
61+ QString stringValue = value.toString();
62+ if (stringValue == "left") {
63+ return QVariant::fromValue(0);
64+ } else if (stringValue == "right") {
65+ return QVariant::fromValue(1); // Mir is less clear on this -- any non-zero value is the same
66+ } else {
67+ return QVariant::fromValue(0); // default to left
68+ }
69+}
70+
2771AccountsService::AccountsService(QObject* parent, const QString &user)
28− : QObject(parent),
29− m_service(new AccountsServiceDBusAdaptor(this)),
30− m_demoEdges(false),
31− m_demoEdgesCompleted(),
32− m_enableLauncherWhileLocked(false),
33− m_enableIndicatorsWhileLocked(false),
34− m_statsWelcomeScreen(false),
35− m_passwordDisplayHint(Keyboard),
36− m_failedLogins(0),
37− m_hereEnabled(false),
38− m_hereLicensePath() // null means not set yet
72+ : QObject(parent)
73+ , m_service(new AccountsServiceDBusAdaptor(this))
3974{
4075 m_unityInput = new QDBusInterface(QStringLiteral("com.canonical.Unity.Input"),
4176 QStringLiteral("/com/canonical/Unity/Input"),
@@ -45,6 +80,46 @@ AccountsService::AccountsService(QObject* parent, const QString &user)
4580 connect(m_service, &AccountsServiceDBusAdaptor::propertiesChanged, this, &AccountsService::onPropertiesChanged);
4681 connect(m_service, &AccountsServiceDBusAdaptor::maybeChanged, this, &AccountsService::onMaybeChanged);
4782
83+ registerProperty(IFACE_ACCOUNTS_USER, PROP_BACKGROUND_FILE, QStringLiteral("backgroundFileChanged"));
84+ registerProperty(IFACE_ACCOUNTS_USER, PROP_EMAIL, QStringLiteral("emailChanged"));
85+ registerProperty(IFACE_ACCOUNTS_USER, PROP_REAL_NAME, QStringLiteral("realNameChanged"));
86+ registerProperty(IFACE_LOCATION_HERE, PROP_LICENSE_ACCEPTED, QStringLiteral("hereEnabledChanged"));
87+ registerProperty(IFACE_LOCATION_HERE, PROP_LICENSE_BASE_PATH, QStringLiteral("hereLicensePathChanged"));
88+ registerProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_LAUNCHER_WHILE_LOCKED, QStringLiteral("enableLauncherWhileLockedChanged"));
89+ registerProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_INDICATORS_WHILE_LOCKED, QStringLiteral("enableIndicatorsWhileLockedChanged"));
90+ registerProperty(IFACE_UBUNTU_SECURITY, PROP_PASSWORD_DISPLAY_HINT, QStringLiteral("passwordDisplayHintChanged"));
91+ registerProperty(IFACE_UBUNTU_SECURITY_OLD, PROP_STATS_WELCOME_SCREEN, QStringLiteral("statsWelcomeScreenChanged"));
92+ registerProperty(IFACE_UNITY, PROP_DEMO_EDGES, QStringLiteral("demoEdgesChanged"));
93+ registerProperty(IFACE_UNITY, PROP_DEMO_EDGES_COMPLETED, QStringLiteral("demoEdgesCompletedChanged"));
94+ registerProperty(IFACE_UNITY_PRIVATE, PROP_FAILED_LOGINS, QStringLiteral("failedLoginsChanged"));
95+
96+ registerProxy(IFACE_UBUNTU_INPUT, PROP_MOUSE_CURSOR_SPEED,
97+ m_unityInput, QStringLiteral("setMouseCursorSpeed"));
98+ registerProxy(IFACE_UBUNTU_INPUT, PROP_MOUSE_DOUBLE_CLICK_SPEED,
99+ m_unityInput, QStringLiteral("setMouseDoubleClickSpeed"));
100+ registerProxy(IFACE_UBUNTU_INPUT, PROP_MOUSE_PRIMARY_BUTTON,
101+ m_unityInput, QStringLiteral("setMousePrimaryButton"),
102+ primaryButtonConverter);
103+ registerProxy(IFACE_UBUNTU_INPUT, PROP_MOUSE_SCROLL_SPEED,
104+ m_unityInput, QStringLiteral("setMouseScrollSpeed"));
105+ registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_CURSOR_SPEED,
106+ m_unityInput, QStringLiteral("setTouchpadCursorSpeed"));
107+ registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_SCROLL_SPEED,
108+ m_unityInput, QStringLiteral("setTouchpadScrollSpeed"));
109+ registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_DISABLE_WHILE_TYPING,
110+ m_unityInput, QStringLiteral("setTouchpadDisableWhileTyping"));
111+ registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_DISABLE_WITH_MOUSE,
112+ m_unityInput, QStringLiteral("setTouchpadDisableWithMouse"));
113+ registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_DOUBLE_CLICK_SPEED,
114+ m_unityInput, QStringLiteral("setTouchpadDoubleClickSpeed"));
115+ registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_PRIMARY_BUTTON,
116+ m_unityInput, QStringLiteral("setTouchpadPrimaryButton"),
117+ primaryButtonConverter);
118+ registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_TAP_TO_CLICK,
119+ m_unityInput, QStringLiteral("setTouchpadTapToClick"));
120+ registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_TWO_FINGER_SCROLL,
121+ m_unityInput, QStringLiteral("setTouchpadTwoFingerScroll"));
122+
48123 setUser(!user.isEmpty() ? user : QString::fromUtf8(qgetenv("USER")));
49124}
50125
@@ -58,455 +133,236 @@ void AccountsService::setUser(const QString &user)
58133 if (user.isEmpty() || m_user == user)
59134 return;
60135
136+ bool wasEmpty = m_user.isEmpty();
137+
61138 m_user = user;
62139 Q_EMIT userChanged();
63140
64− updateDemoEdgesCompleted(false);
65− updateDemoEdges(false);
66− updateEnableLauncherWhileLocked(false);
67− updateEnableIndicatorsWhileLocked(false);
68− updateBackgroundFile(false);
69− updateMouseCursorSpeed();
70− updateTouchpadCursorSpeed();
71− updateStatsWelcomeScreen(false);
72− updatePasswordDisplayHint(false);
73− updateFailedLogins(false);
74− updateHereEnabled(false);
75− updateHereLicensePath(false);
141+ // Do the first update synchronously, as a cheap way to block rendering
142+ // until we have the right values on bootup.
143+ refresh(!wasEmpty);
76144}
77145
78146bool AccountsService::demoEdges() const
79147{
80− return m_demoEdges;
148+ auto value = getProperty(IFACE_UNITY, PROP_DEMO_EDGES);
149+ return value.toBool();
81150}
82151
83152void AccountsService::setDemoEdges(bool demoEdges)
84153{
85− if (m_demoEdges != demoEdges) {
86− m_demoEdges = demoEdges;
87− m_service->setUserPropertyAsync(m_user, QStringLiteral("com.canonical.unity.AccountsService"), QStringLiteral("demo-edges"), demoEdges);
88−
89− Q_EMIT demoEdgesChanged();
90− }
154+ setProperty(IFACE_UNITY, PROP_DEMO_EDGES, demoEdges);
91155}
92156
93157QStringList AccountsService::demoEdgesCompleted() const
94158{
95− return m_demoEdgesCompleted;
159+ auto value = getProperty(IFACE_UNITY, PROP_DEMO_EDGES_COMPLETED);
160+ return value.toStringList();
96161}
97162
98163void AccountsService::markDemoEdgeCompleted(const QString &edge)
99164{
100− if (!m_demoEdgesCompleted.contains(edge)) {
101− m_demoEdgesCompleted << edge;
102− m_service->setUserPropertyAsync(m_user, "com.canonical.unity.AccountsService", "DemoEdgesCompleted", m_demoEdgesCompleted);
103−
104− Q_EMIT demoEdgesCompletedChanged();
165+ auto currentList = demoEdgesCompleted();
166+ if (!currentList.contains(edge)) {
167+ setProperty(IFACE_UNITY, PROP_DEMO_EDGES_COMPLETED, currentList << edge);
105168 }
106169}
107170
108171bool AccountsService::enableLauncherWhileLocked() const
109172{
110− return m_enableLauncherWhileLocked;
173+ auto value = getProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_LAUNCHER_WHILE_LOCKED);
174+ return value.toBool();
111175}
112176
113177bool AccountsService::enableIndicatorsWhileLocked() const
114178{
115− return m_enableIndicatorsWhileLocked;
179+ auto value = getProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_INDICATORS_WHILE_LOCKED);
180+ return value.toBool();
116181}
117182
118183QString AccountsService::backgroundFile() const
119184{
120− return m_backgroundFile;
185+ auto value = getProperty(IFACE_ACCOUNTS_USER, PROP_BACKGROUND_FILE);
186+ return value.toString();
121187}
122188
123189bool AccountsService::statsWelcomeScreen() const
124190{
125− return m_statsWelcomeScreen;
191+ auto value = getProperty(IFACE_UBUNTU_SECURITY_OLD, PROP_STATS_WELCOME_SCREEN);
192+ return value.toBool();
126193}
127194
128195AccountsService::PasswordDisplayHint AccountsService::passwordDisplayHint() const
129196{
130− return m_passwordDisplayHint;
197+ auto value = getProperty(IFACE_UBUNTU_SECURITY, PROP_PASSWORD_DISPLAY_HINT);
198+ return (PasswordDisplayHint)value.toInt();
131199}
132200
133201bool AccountsService::hereEnabled() const
134202{
135− return m_hereEnabled;
203+ auto value = getProperty(IFACE_LOCATION_HERE, PROP_LICENSE_ACCEPTED);
204+ return value.toBool();
136205}
137206
138207void AccountsService::setHereEnabled(bool enabled)
139208{
140− if (m_hereEnabled != enabled) {
141− m_hereEnabled = enabled;
142− m_service->setUserPropertyAsync(m_user, QStringLiteral("com.ubuntu.location.providers.here.AccountsService"), QStringLiteral("LicenseAccepted"), enabled);
143−
144− Q_EMIT hereEnabledChanged();
145− }
209+ setProperty(IFACE_LOCATION_HERE, PROP_LICENSE_ACCEPTED, enabled);
146210}
147211
148212QString AccountsService::hereLicensePath() const
149213{
150− return m_hereLicensePath;
214+ auto value = getProperty(IFACE_LOCATION_HERE, PROP_LICENSE_BASE_PATH);
215+ QString hereLicensePath = value.toString();
216+ if (hereLicensePath.isEmpty() || !QFile::exists(hereLicensePath))
217+ hereLicensePath = QStringLiteral("");
218+ return hereLicensePath;
151219}
152220
153221bool AccountsService::hereLicensePathValid() const
154222{
155− return !m_hereLicensePath.isNull();
223+ auto value = getProperty(IFACE_LOCATION_HERE, PROP_LICENSE_BASE_PATH);
224+ return !value.toString().isNull();
156225}
157226
158−void AccountsService::updateDemoEdges(bool async)
227+QString AccountsService::realName() const
159228{
160− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
161− QStringLiteral("com.canonical.unity.AccountsService"),
162− QStringLiteral("demo-edges"));
163− QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
164−
165− connect(watcher, &QDBusPendingCallWatcher::finished,
166− this, [this](QDBusPendingCallWatcher* watcher) {
167−
168− QDBusPendingReply<QDBusVariant> reply = *watcher;
169− watcher->deleteLater();
170− if (reply.isError()) {
171− qWarning() << "Failed to get 'demo-edges' property - " << reply.error().message();
172− return;
173− }
174−
175− auto demoEdges = reply.value().variant().toBool();
176− if (m_demoEdges != demoEdges) {
177− m_demoEdges = demoEdges;
178− Q_EMIT demoEdgesChanged();
179− }
180− });
181− if (!async) {
182− watcher->waitForFinished();
183− delete watcher;
184− }
229+ auto value = getProperty(IFACE_ACCOUNTS_USER, PROP_REAL_NAME);
230+ return value.toString();
185231}
186232
187−void AccountsService::updateDemoEdgesCompleted(bool async)
233+void AccountsService::setRealName(const QString &realName)
188234{
189− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
190− "com.canonical.unity.AccountsService",
191− "DemoEdgesCompleted");
192− QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
193−
194− connect(watcher, &QDBusPendingCallWatcher::finished,
195− this, [this](QDBusPendingCallWatcher* watcher) {
196−
197− QDBusPendingReply<QDBusVariant> reply = *watcher;
198− watcher->deleteLater();
199− if (reply.isError()) {
200− qWarning() << "Failed to get 'DemoEdgesCompleted' property - " << reply.error().message();
201− return;
202− }
203−
204− auto demoEdgesCompleted = reply.value().variant().toStringList();
205− if (m_demoEdgesCompleted != demoEdgesCompleted) {
206− m_demoEdgesCompleted = demoEdgesCompleted;
207− Q_EMIT demoEdgesCompletedChanged();
208− }
209− });
210− if (!async) {
211− watcher->waitForFinished();
212− delete watcher;
213− }
235+ setProperty(IFACE_ACCOUNTS_USER, PROP_REAL_NAME, realName);
214236}
215237
216−void AccountsService::updateEnableLauncherWhileLocked(bool async)
238+QString AccountsService::email() const
217239{
218− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
219− QStringLiteral("com.ubuntu.AccountsService.SecurityPrivacy"),
220− QStringLiteral("EnableLauncherWhileLocked"));
221− QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
222−
223− connect(watcher, &QDBusPendingCallWatcher::finished,
224− this, [this](QDBusPendingCallWatcher* watcher) {
225−
226− QDBusPendingReply<QVariant> reply = *watcher;
227− watcher->deleteLater();
228− if (reply.isError()) {
229− qWarning() << "Failed to get 'EnableLauncherWhileLocked' property - " << reply.error().message();
230− return;
231− }
232−
233− const bool enableLauncherWhileLocked = reply.value().toBool();
234− if (m_enableLauncherWhileLocked != enableLauncherWhileLocked) {
235− m_enableLauncherWhileLocked = enableLauncherWhileLocked;
236− Q_EMIT enableLauncherWhileLockedChanged();
237− }
238− });
239− if (!async) {
240− watcher->waitForFinished();
241− delete watcher;
242− }
240+ auto value = getProperty(IFACE_ACCOUNTS_USER, PROP_EMAIL);
241+ return value.toString();
243242}
244243
245−void AccountsService::updateEnableIndicatorsWhileLocked(bool async)
244+void AccountsService::setEmail(const QString &email)
246245{
247− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
248− QStringLiteral("com.ubuntu.AccountsService.SecurityPrivacy"),
249− QStringLiteral("EnableIndicatorsWhileLocked"));
250− QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
251−
252− connect(watcher, &QDBusPendingCallWatcher::finished,
253− this, [this](QDBusPendingCallWatcher* watcher) {
254−
255− QDBusPendingReply<QVariant> reply = *watcher;
256− watcher->deleteLater();
257− if (reply.isError()) {
258− qWarning() << "Failed to get 'EnableIndicatorsWhileLocked' property - " << reply.error().message();
259− return;
260− }
261−
262− const bool enableIndicatorsWhileLocked = reply.value().toBool();
263− if (m_enableIndicatorsWhileLocked != enableIndicatorsWhileLocked) {
264− m_enableIndicatorsWhileLocked = enableIndicatorsWhileLocked;
265− Q_EMIT enableIndicatorsWhileLockedChanged();
266− }
267− });
268− if (!async) {
269− watcher->waitForFinished();
270− delete watcher;
271− }
246+ setProperty(IFACE_ACCOUNTS_USER, PROP_EMAIL, email);
272247}
273248
274−void AccountsService::updateBackgroundFile(bool async)
249+uint AccountsService::failedLogins() const
275250{
276− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
277− QStringLiteral("org.freedesktop.Accounts.User"),
278− QStringLiteral("BackgroundFile"));
279− QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
280−
281− connect(watcher, &QDBusPendingCallWatcher::finished,
282− this, [this](QDBusPendingCallWatcher* watcher) {
283−
284− QDBusPendingReply<QVariant> reply = *watcher;
285− watcher->deleteLater();
286− if (reply.isError()) {
287− qWarning() << "Failed to get 'BackgroundFile' property - " << reply.error().message();
288− return;
289− }
290−
291− const QString backgroundFile = reply.value().toString();
292− if (m_backgroundFile != backgroundFile) {
293− m_backgroundFile = backgroundFile;
294− Q_EMIT backgroundFileChanged();
295− }
296− });
297− if (!async) {
298− watcher->waitForFinished();
299− delete watcher;
300− }
251+ return getProperty(IFACE_UNITY_PRIVATE, PROP_FAILED_LOGINS).toUInt();
301252}
302253
303−void AccountsService::updateMouseCursorSpeed()
254+void AccountsService::setFailedLogins(uint failedLogins)
304255{
305− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
306− QStringLiteral("com.ubuntu.AccountsService.Input"),
307− QStringLiteral("MouseCursorSpeed"));
308− QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
309−
310− connect(watcher, &QDBusPendingCallWatcher::finished,
311− this, [this](QDBusPendingCallWatcher* watcher) {
312−
313− QDBusPendingReply<QVariant> reply = *watcher;
314− watcher->deleteLater();
315− if (reply.isError()) {
316− qWarning() << "Failed to get 'MouseCursorSpeed' property - " << reply.error().message();
317− return;
318− }
319−
320− // Merely proxy this along to USC. We don't care about keeping a copy
321− // or exporting it internally.
322− m_unityInput->asyncCall(QStringLiteral("setMouseCursorSpeed"), reply.value());
323− });
256+ setProperty(IFACE_UNITY_PRIVATE, PROP_FAILED_LOGINS, failedLogins);
324257}
325258
326−void AccountsService::updateTouchpadCursorSpeed()
327−{
328− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
329− QStringLiteral("com.ubuntu.AccountsService.Input"),
330− QStringLiteral("TouchpadCursorSpeed"));
331− QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
332−
333− connect(watcher, &QDBusPendingCallWatcher::finished,
334− this, [this](QDBusPendingCallWatcher* watcher) {
335−
336− QDBusPendingReply<QVariant> reply = *watcher;
337− watcher->deleteLater();
338− if (reply.isError()) {
339− qWarning() << "Failed to get 'TouchpadCursorSpeed' property - " << reply.error().message();
340− return;
341− }
259+// ====================================================
260+// Everything below this line is generic helper methods
261+// ====================================================
342262
343− // Merely proxy this along to USC. We don't care about keeping a copy
344− // or exporting it internally.
345− m_unityInput->asyncCall(QStringLiteral("setTouchpadCursorSpeed"), reply.value());
346− });
263+void AccountsService::emitChangedForProperty(const QString &interface, const QString &property)
264+{
265+ QString signalName = m_properties[interface][property].signal;
266+ QMetaObject::invokeMethod(this, signalName.toUtf8().data());
347267}
348268
349−void AccountsService::updateStatsWelcomeScreen(bool async)
269+QVariant AccountsService::getProperty(const QString &interface, const QString &property) const
350270{
351− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
352− QStringLiteral("com.ubuntu.touch.AccountsService.SecurityPrivacy"),
353− QStringLiteral("StatsWelcomeScreen"));
354− QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
355−
356− connect(watcher, &QDBusPendingCallWatcher::finished,
357− this, [this](QDBusPendingCallWatcher* watcher) {
358−
359− QDBusPendingReply<QVariant> reply = *watcher;
360− watcher->deleteLater();
361− if (reply.isError()) {
362− qWarning() << "Failed to get 'StatsWelcomeScreen' property - " << reply.error().message();
363− return;
364− }
365−
366− const bool statsWelcomeScreen = reply.value().toBool();
367− if (m_statsWelcomeScreen != statsWelcomeScreen) {
368− m_statsWelcomeScreen = statsWelcomeScreen;
369− Q_EMIT statsWelcomeScreenChanged();
370− }
371− });
372− if (!async) {
373− watcher->waitForFinished();
374− delete watcher;
375− }
271+ return m_properties[interface][property].value;
376272}
377273
378−void AccountsService::updatePasswordDisplayHint(bool async)
274+void AccountsService::setProperty(const QString &interface, const QString &property, const QVariant &value)
379275{
380− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
381− QStringLiteral("com.ubuntu.AccountsService.SecurityPrivacy"),
382− QStringLiteral("PasswordDisplayHint"));
383− QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
384−
385− connect(watcher, &QDBusPendingCallWatcher::finished,
386− this, [this](QDBusPendingCallWatcher* watcher) {
387−
388− QDBusPendingReply<QVariant> reply = *watcher;
389− watcher->deleteLater();
390− if (reply.isError()) {
391− qWarning() << "Failed to get 'PasswordDisplayHint' property - " << reply.error().message();
392− return;
393− }
394−
395− const PasswordDisplayHint passwordDisplayHint = (PasswordDisplayHint)reply.value().toInt();
396− if (m_passwordDisplayHint != passwordDisplayHint) {
397− m_passwordDisplayHint = passwordDisplayHint;
398− Q_EMIT passwordDisplayHintChanged();
399− }
400− });
401− if (!async) {
402− watcher->waitForFinished();
403− delete watcher;
276+ if (m_properties[interface][property].value != value) {
277+ m_properties[interface][property].value = value;
278+ m_service->setUserPropertyAsync(m_user, interface, property, value);
279+ emitChangedForProperty(interface, property);
404280 }
405281}
406282
407−void AccountsService::updateFailedLogins(bool async)
283+void AccountsService::updateCache(const QString &interface, const QString &property, const QVariant &value)
408284{
409− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
410− QStringLiteral("com.canonical.unity.AccountsService.Private"),
411− QStringLiteral("FailedLogins"));
412− QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
413−
414− connect(watcher, &QDBusPendingCallWatcher::finished,
415− this, [this](QDBusPendingCallWatcher* watcher) {
285+ PropertyInfo &info = m_properties[interface][property];
416286
417− QDBusPendingReply<QVariant> reply = *watcher;
418− watcher->deleteLater();
419− if (reply.isError()) {
420− qWarning() << "Failed to get 'FailedLogins' property - " << reply.error().message();
421− return;
287+ if (info.proxyInterface) {
288+ QVariant finalValue;
289+ if (info.proxyConverter) {
290+ finalValue = info.proxyConverter(value);
291+ } else {
292+ finalValue = value;
422293 }
294+ info.proxyInterface->asyncCall(info.proxyMethod, finalValue);
295+ return; // don't bother saving a copy
296+ }
423297
424− const uint failedLogins = reply.value().toUInt();
425− if (m_failedLogins != failedLogins) {
426− m_failedLogins = failedLogins;
427− Q_EMIT failedLoginsChanged();
428− }
429− });
430− if (!async) {
431− watcher->waitForFinished();
432− delete watcher;
298+ if (info.value != value) {
299+ info.value = value;
300+ emitChangedForProperty(interface, property);
433301 }
434302}
435303
436−void AccountsService::updateHereEnabled(bool async)
304+void AccountsService::updateProperty(const QString &interface, const QString &property)
437305{
438306 QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
439− QStringLiteral("com.ubuntu.location.providers.here.AccountsService"),
440− QStringLiteral("LicenseAccepted"));
307+ interface,
308+ property);
441309 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
442310
443311 connect(watcher, &QDBusPendingCallWatcher::finished,
444− this, [this](QDBusPendingCallWatcher* watcher) {
312+ this, [this, interface, property](QDBusPendingCallWatcher* watcher) {
445313
446314 QDBusPendingReply<QVariant> reply = *watcher;
447315 watcher->deleteLater();
448316 if (reply.isError()) {
449− qWarning() << "Failed to get 'LicenseAccepted' property - " << reply.error().message();
317+ qWarning() << "Failed to get '" << property << "' property:" << reply.error().message();
450318 return;
451319 }
452320
453− const bool hereEnabled = reply.value().toBool();
454− if (m_hereEnabled != hereEnabled) {
455− m_hereEnabled = hereEnabled;
456− Q_EMIT hereEnabledChanged();
457− }
321+ updateCache(interface, property, reply.value());
458322 });
459− if (!async) {
460− watcher->waitForFinished();
461− delete watcher;
462− }
463323}
464324
465−void AccountsService::updateHereLicensePath(bool async)
325+void AccountsService::updateAllProperties(const QString &interface, bool async)
466326{
467− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
468− QStringLiteral("com.ubuntu.location.providers.here.AccountsService"),
469− QStringLiteral("LicenseBasePath"));
327+ QDBusPendingCall pendingReply = m_service->getAllPropertiesAsync(m_user,
328+ interface);
470329 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
471330
472331 connect(watcher, &QDBusPendingCallWatcher::finished,
473− this, [this](QDBusPendingCallWatcher* watcher) {
332+ this, [this, interface](QDBusPendingCallWatcher* watcher) {
474333
475− QDBusPendingReply<QVariant> reply = *watcher;
334+ QDBusPendingReply< QHash<QString, QVariant> > reply = *watcher;
476335 watcher->deleteLater();
477336 if (reply.isError()) {
478− qWarning() << "Failed to get 'LicenseBasePath' property - " << reply.error().message();
337+ qWarning() << "Failed to get all properties for" << interface << ":" << reply.error().message();
479338 return;
480339 }
481340
482− QString hereLicensePath = reply.value().toString();
483− if (hereLicensePath.isEmpty() || !QFile::exists(hereLicensePath))
484− hereLicensePath = QLatin1String("");
485−
486− if (m_hereLicensePath.isNull() || m_hereLicensePath != hereLicensePath) {
487− m_hereLicensePath = hereLicensePath;
488− Q_EMIT hereLicensePathChanged();
341+ auto valueHash = reply.value();
342+ auto i = valueHash.constBegin();
343+ while (i != valueHash.constEnd()) {
344+ updateCache(interface, i.key(), i.value());
345+ ++i;
489346 }
490347 });
491348 if (!async) {
492349 watcher->waitForFinished();
493− delete watcher;
494350 }
495351}
496352
497−uint AccountsService::failedLogins() const
353+void AccountsService::registerProxy(const QString &interface, const QString &property, QDBusInterface *iface, const QString &method, ProxyConverter converter)
498354{
499− return m_failedLogins;
355+ registerProperty(interface, property, nullptr);
356+
357+ m_properties[interface][property].proxyInterface = iface;
358+ m_properties[interface][property].proxyMethod = method;
359+ m_properties[interface][property].proxyConverter = converter;
500360}
501361
502−void AccountsService::setFailedLogins(uint failedLogins)
362+void AccountsService::registerProperty(const QString &interface, const QString &property, const QString &signal)
503363{
504− if (m_failedLogins != failedLogins) {
505− m_failedLogins = failedLogins;
506− m_service->setUserPropertyAsync(m_user, QStringLiteral("com.canonical.unity.AccountsService.Private"), QStringLiteral("FailedLogins"), failedLogins);
507−
508− Q_EMIT failedLoginsChanged();
509− }
364+ m_properties[interface][property] = PropertyInfo();
365+ m_properties[interface][property].signal = signal;
510366}
511367
512368void AccountsService::onPropertiesChanged(const QString &user, const QString &interface, const QStringList &changed)
@@ -515,45 +371,13 @@ void AccountsService::onPropertiesChanged(const QString &user, const QString &in
515371 return;
516372 }
517373
518− if (interface == QLatin1String("com.canonical.unity.AccountsService")) {
519− if (changed.contains(QStringLiteral("demo-edges"))) {
520− updateDemoEdges();
521− }
522− if (changed.contains(QStringLiteral("DemoEdgesCompleted"))) {
523− updateDemoEdgesCompleted();
524− }
525− } else if (interface == QLatin1String("com.canonical.unity.AccountsService.Private")) {
526− if (changed.contains(QStringLiteral("FailedLogins"))) {
527− updateFailedLogins();
528− }
529− } else if (interface == QLatin1String("com.ubuntu.AccountsService.Input")) {
530− if (changed.contains(QStringLiteral("MouseCursorSpeed"))) {
531− updateMouseCursorSpeed();
532− }
533− if (changed.contains(QStringLiteral("TouchpadCursorSpeed"))) {
534− updateTouchpadCursorSpeed();
535− }
536− } else if (interface == QLatin1String("com.ubuntu.touch.AccountsService.SecurityPrivacy")) {
537− if (changed.contains(QStringLiteral("StatsWelcomeScreen"))) {
538− updateStatsWelcomeScreen();
539− }
540− } else if (interface == QLatin1String("com.ubuntu.AccountsService.SecurityPrivacy")) {
541− if (changed.contains(QStringLiteral("PasswordDisplayHint"))) {
542− updatePasswordDisplayHint();
543− }
544− if (changed.contains(QStringLiteral("EnableLauncherWhileLocked"))) {
545− updateEnableLauncherWhileLocked();
546− }
547− if (changed.contains(QStringLiteral("EnableIndicatorsWhileLocked"))) {
548− updateEnableIndicatorsWhileLocked();
549− }
550− } else if (interface == QLatin1String("com.ubuntu.location.providers.here.AccountsService")) {
551− if (changed.contains(QStringLiteral("LicenseAccepted"))) {
552− updateHereEnabled();
553− }
554− if (changed.contains(QStringLiteral("LicenseBasePath"))) {
555− updateHereLicensePath();
374+ auto propHash = m_properties.value(interface);
375+ auto i = propHash.constBegin();
376+ while (i != propHash.constEnd()) {
377+ if (changed.contains(i.key())) {
378+ updateProperty(interface, i.key());
556379 }
380+ ++i;
557381 }
558382}
559383
@@ -563,6 +387,20 @@ void AccountsService::onMaybeChanged(const QString &user)
563387 return;
564388 }
565389
566− // Standard properties might have changed
567− updateBackgroundFile();
390+ // Any of the standard properties might have changed!
391+ auto propHash = m_properties.value(IFACE_ACCOUNTS_USER);
392+ auto i = propHash.constBegin();
393+ while (i != propHash.constEnd()) {
394+ updateProperty(IFACE_ACCOUNTS_USER, i.key());
395+ ++i;
396+ }
397+}
398+
399+void AccountsService::refresh(bool async)
400+{
401+ auto i = m_properties.constBegin();
402+ while (i != m_properties.constEnd()) {
403+ updateAllProperties(i.key(), async);
404+ ++i;
405+ }
568406}