@@ -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,17 +22,54 @@
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_EMAIL QStringLiteral("Email")
36+#define PROP_ENABLE_INDICATORS_WHILE_LOCKED QStringLiteral("EnableIndicatorsWhileLocked")
37+#define PROP_ENABLE_LAUNCHER_WHILE_LOCKED QStringLiteral("EnableLauncherWhileLocked")
38+#define PROP_FAILED_LOGINS QStringLiteral("FailedLogins")
39+#define PROP_LICENSE_ACCEPTED QStringLiteral("LicenseAccepted")
40+#define PROP_LICENSE_BASE_PATH QStringLiteral("LicenseBasePath")
41+#define PROP_MOUSE_CURSOR_SPEED QStringLiteral("MouseCursorSpeed")
42+#define PROP_MOUSE_DOUBLE_CLICK_SPEED QStringLiteral("MouseDoubleClickSpeed")
43+#define PROP_MOUSE_PRIMARY_BUTTON QStringLiteral("MousePrimaryButton")
44+#define PROP_MOUSE_SCROLL_SPEED QStringLiteral("MouseScrollSpeed")
45+#define PROP_PASSWORD_DISPLAY_HINT QStringLiteral("PasswordDisplayHint")
46+#define PROP_REAL_NAME QStringLiteral("RealName")
47+#define PROP_STATS_WELCOME_SCREEN QStringLiteral("StatsWelcomeScreen")
48+#define PROP_TOUCHPAD_CURSOR_SPEED QStringLiteral("TouchpadCursorSpeed")
49+#define PROP_TOUCHPAD_DISABLE_WHILE_TYPING QStringLiteral("TouchpadDisableWhileTyping")
50+#define PROP_TOUCHPAD_DISABLE_WITH_MOUSE QStringLiteral("TouchpadDisableWithMouse")
51+#define PROP_TOUCHPAD_DOUBLE_CLICK_SPEED QStringLiteral("TouchpadDoubleClickSpeed")
52+#define PROP_TOUCHPAD_PRIMARY_BUTTON QStringLiteral("TouchpadPrimaryButton")
53+#define PROP_TOUCHPAD_SCROLL_SPEED QStringLiteral("TouchpadScrollSpeed")
54+#define PROP_TOUCHPAD_TAP_TO_CLICK QStringLiteral("TouchpadTapToClick")
55+#define PROP_TOUCHPAD_TWO_FINGER_SCROLL QStringLiteral("TouchpadTwoFingerScroll")
56+
57+
58+QVariant primaryButtonConverter(const QVariant &value)
59+{
60+ QString stringValue = value.toString();
61+ if (stringValue == "left") {
62+ return QVariant::fromValue(0);
63+ } else if (stringValue == "right") {
64+ return QVariant::fromValue(1); // Mir is less clear on this -- any non-zero value is the same
65+ } else {
66+ return QVariant::fromValue(0); // default to left
67+ }
68+}
69+
2770AccountsService::AccountsService(QObject* parent, const QString &user)
28− : QObject(parent),
29− m_service(new AccountsServiceDBusAdaptor(this)),
30− m_demoEdges(false),
31− m_enableLauncherWhileLocked(false),
32− m_enableIndicatorsWhileLocked(false),
33− m_statsWelcomeScreen(false),
34− m_passwordDisplayHint(Keyboard),
35− m_failedLogins(0),
36− m_hereEnabled(false),
37− m_hereLicensePath() // null means not set yet
71+ : QObject(parent)
72+ , m_service(new AccountsServiceDBusAdaptor(this))
3873{
3974 m_unityInput = new QDBusInterface(QStringLiteral("com.canonical.Unity.Input"),
4075 QStringLiteral("/com/canonical/Unity/Input"),
@@ -44,6 +79,45 @@ AccountsService::AccountsService(QObject* parent, const QString &user)
4479 connect(m_service, &AccountsServiceDBusAdaptor::propertiesChanged, this, &AccountsService::onPropertiesChanged);
4580 connect(m_service, &AccountsServiceDBusAdaptor::maybeChanged, this, &AccountsService::onMaybeChanged);
4681
82+ registerProperty(IFACE_ACCOUNTS_USER, PROP_BACKGROUND_FILE, QStringLiteral("backgroundFileChanged"));
83+ registerProperty(IFACE_ACCOUNTS_USER, PROP_EMAIL, QStringLiteral("emailChanged"));
84+ registerProperty(IFACE_ACCOUNTS_USER, PROP_REAL_NAME, QStringLiteral("realNameChanged"));
85+ registerProperty(IFACE_LOCATION_HERE, PROP_LICENSE_ACCEPTED, QStringLiteral("hereEnabledChanged"));
86+ registerProperty(IFACE_LOCATION_HERE, PROP_LICENSE_BASE_PATH, QStringLiteral("hereLicensePathChanged"));
87+ registerProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_LAUNCHER_WHILE_LOCKED, QStringLiteral("enableLauncherWhileLockedChanged"));
88+ registerProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_INDICATORS_WHILE_LOCKED, QStringLiteral("enableIndicatorsWhileLockedChanged"));
89+ registerProperty(IFACE_UBUNTU_SECURITY, PROP_PASSWORD_DISPLAY_HINT, QStringLiteral("passwordDisplayHintChanged"));
90+ registerProperty(IFACE_UBUNTU_SECURITY_OLD, PROP_STATS_WELCOME_SCREEN, QStringLiteral("statsWelcomeScreenChanged"));
91+ registerProperty(IFACE_UNITY, PROP_DEMO_EDGES, QStringLiteral("demoEdgesChanged"));
92+ registerProperty(IFACE_UNITY_PRIVATE, PROP_FAILED_LOGINS, QStringLiteral("failedLoginsChanged"));
93+
94+ registerProxy(IFACE_UBUNTU_INPUT, PROP_MOUSE_CURSOR_SPEED,
95+ m_unityInput, QStringLiteral("setMouseCursorSpeed"));
96+ registerProxy(IFACE_UBUNTU_INPUT, PROP_MOUSE_DOUBLE_CLICK_SPEED,
97+ m_unityInput, QStringLiteral("setMouseDoubleClickSpeed"));
98+ registerProxy(IFACE_UBUNTU_INPUT, PROP_MOUSE_PRIMARY_BUTTON,
99+ m_unityInput, QStringLiteral("setMousePrimaryButton"),
100+ primaryButtonConverter);
101+ registerProxy(IFACE_UBUNTU_INPUT, PROP_MOUSE_SCROLL_SPEED,
102+ m_unityInput, QStringLiteral("setMouseScrollSpeed"));
103+ registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_CURSOR_SPEED,
104+ m_unityInput, QStringLiteral("setTouchpadCursorSpeed"));
105+ registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_SCROLL_SPEED,
106+ m_unityInput, QStringLiteral("setTouchpadScrollSpeed"));
107+ registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_DISABLE_WHILE_TYPING,
108+ m_unityInput, QStringLiteral("setTouchpadDisableWhileTyping"));
109+ registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_DISABLE_WITH_MOUSE,
110+ m_unityInput, QStringLiteral("setTouchpadDisableWithMouse"));
111+ registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_DOUBLE_CLICK_SPEED,
112+ m_unityInput, QStringLiteral("setTouchpadDoubleClickSpeed"));
113+ registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_PRIMARY_BUTTON,
114+ m_unityInput, QStringLiteral("setTouchpadPrimaryButton"),
115+ primaryButtonConverter);
116+ registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_TAP_TO_CLICK,
117+ m_unityInput, QStringLiteral("setTouchpadTapToClick"));
118+ registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_TWO_FINGER_SCROLL,
119+ m_unityInput, QStringLiteral("setTouchpadTwoFingerScroll"));
120+
47121 setUser(!user.isEmpty() ? user : QString::fromUtf8(qgetenv("USER")));
48122}
49123
@@ -57,410 +131,222 @@ void AccountsService::setUser(const QString &user)
57131 if (user.isEmpty() || m_user == user)
58132 return;
59133
134+ bool wasEmpty = m_user.isEmpty();
135+
60136 m_user = user;
61137 Q_EMIT userChanged();
62138
63− updateDemoEdges(false);
64− updateEnableLauncherWhileLocked(false);
65− updateEnableIndicatorsWhileLocked(false);
66− updateBackgroundFile(false);
67− updateMouseCursorSpeed();
68− updateTouchpadCursorSpeed();
69− updateStatsWelcomeScreen(false);
70− updatePasswordDisplayHint(false);
71− updateFailedLogins(false);
72− updateHereEnabled(false);
73− updateHereLicensePath(false);
139+ // Do the first update synchronously, as a cheap way to block rendering
140+ // until we have the right values on bootup.
141+ refresh(!wasEmpty);
74142}
75143
76144bool AccountsService::demoEdges() const
77145{
78− return m_demoEdges;
146+ auto value = getProperty(IFACE_UNITY, PROP_DEMO_EDGES);
147+ return value.toBool();
79148}
80149
81150void AccountsService::setDemoEdges(bool demoEdges)
82151{
83− if (m_demoEdges != demoEdges) {
84− m_demoEdges = demoEdges;
85− m_service->setUserPropertyAsync(m_user, QStringLiteral("com.canonical.unity.AccountsService"), QStringLiteral("demo-edges"), demoEdges);
86−
87− Q_EMIT demoEdgesChanged();
88− }
152+ setProperty(IFACE_UNITY, PROP_DEMO_EDGES, demoEdges);
89153}
90154
91155bool AccountsService::enableLauncherWhileLocked() const
92156{
93− return m_enableLauncherWhileLocked;
157+ auto value = getProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_LAUNCHER_WHILE_LOCKED);
158+ return value.toBool();
94159}
95160
96161bool AccountsService::enableIndicatorsWhileLocked() const
97162{
98− return m_enableIndicatorsWhileLocked;
163+ auto value = getProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_INDICATORS_WHILE_LOCKED);
164+ return value.toBool();
99165}
100166
101167QString AccountsService::backgroundFile() const
102168{
103− return m_backgroundFile;
169+ auto value = getProperty(IFACE_ACCOUNTS_USER, PROP_BACKGROUND_FILE);
170+ return value.toString();
104171}
105172
106173bool AccountsService::statsWelcomeScreen() const
107174{
108− return m_statsWelcomeScreen;
175+ auto value = getProperty(IFACE_UBUNTU_SECURITY_OLD, PROP_STATS_WELCOME_SCREEN);
176+ return value.toBool();
109177}
110178
111179AccountsService::PasswordDisplayHint AccountsService::passwordDisplayHint() const
112180{
113− return m_passwordDisplayHint;
181+ auto value = getProperty(IFACE_UBUNTU_SECURITY, PROP_PASSWORD_DISPLAY_HINT);
182+ return (PasswordDisplayHint)value.toInt();
114183}
115184
116185bool AccountsService::hereEnabled() const
117186{
118− return m_hereEnabled;
187+ auto value = getProperty(IFACE_LOCATION_HERE, PROP_LICENSE_ACCEPTED);
188+ return value.toBool();
119189}
120190
121191void AccountsService::setHereEnabled(bool enabled)
122192{
123− if (m_hereEnabled != enabled) {
124− m_hereEnabled = enabled;
125− m_service->setUserPropertyAsync(m_user, QStringLiteral("com.ubuntu.location.providers.here.AccountsService"), QStringLiteral("LicenseAccepted"), enabled);
126−
127− Q_EMIT hereEnabledChanged();
128− }
193+ setProperty(IFACE_LOCATION_HERE, PROP_LICENSE_ACCEPTED, enabled);
129194}
130195
131196QString AccountsService::hereLicensePath() const
132197{
133− return m_hereLicensePath;
198+ auto value = getProperty(IFACE_LOCATION_HERE, PROP_LICENSE_BASE_PATH);
199+ QString hereLicensePath = value.toString();
200+ if (hereLicensePath.isEmpty() || !QFile::exists(hereLicensePath))
201+ hereLicensePath = QStringLiteral("");
202+ return hereLicensePath;
134203}
135204
136205bool AccountsService::hereLicensePathValid() const
137206{
138− return !m_hereLicensePath.isNull();
207+ auto value = getProperty(IFACE_LOCATION_HERE, PROP_LICENSE_BASE_PATH);
208+ return !value.toString().isNull();
139209}
140210
141−void AccountsService::updateDemoEdges(bool async)
211+QString AccountsService::realName() const
142212{
143− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
144− QStringLiteral("com.canonical.unity.AccountsService"),
145− QStringLiteral("demo-edges"));
146− QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
147−
148− connect(watcher, &QDBusPendingCallWatcher::finished,
149− this, [this](QDBusPendingCallWatcher* watcher) {
150−
151− QDBusPendingReply<QDBusVariant> reply = *watcher;
152− watcher->deleteLater();
153− if (reply.isError()) {
154− qWarning() << "Failed to get 'demo-edges' property - " << reply.error().message();
155− return;
156− }
157−
158− auto demoEdges = reply.value().variant().toBool();
159− if (m_demoEdges != demoEdges) {
160− m_demoEdges = demoEdges;
161− Q_EMIT demoEdgesChanged();
162− }
163− });
164− if (!async) {
165− watcher->waitForFinished();
166− delete watcher;
167− }
213+ auto value = getProperty(IFACE_ACCOUNTS_USER, PROP_REAL_NAME);
214+ return value.toString();
168215}
169216
170−void AccountsService::updateEnableLauncherWhileLocked(bool async)
217+void AccountsService::setRealName(const QString &realName)
171218{
172− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
173− QStringLiteral("com.ubuntu.AccountsService.SecurityPrivacy"),
174− QStringLiteral("EnableLauncherWhileLocked"));
175− QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
176−
177− connect(watcher, &QDBusPendingCallWatcher::finished,
178− this, [this](QDBusPendingCallWatcher* watcher) {
179−
180− QDBusPendingReply<QVariant> reply = *watcher;
181− watcher->deleteLater();
182− if (reply.isError()) {
183− qWarning() << "Failed to get 'EnableLauncherWhileLocked' property - " << reply.error().message();
184− return;
185− }
186−
187− const bool enableLauncherWhileLocked = reply.value().toBool();
188− if (m_enableLauncherWhileLocked != enableLauncherWhileLocked) {
189− m_enableLauncherWhileLocked = enableLauncherWhileLocked;
190− Q_EMIT enableLauncherWhileLockedChanged();
191− }
192− });
193− if (!async) {
194− watcher->waitForFinished();
195− delete watcher;
196− }
219+ setProperty(IFACE_ACCOUNTS_USER, PROP_REAL_NAME, realName);
197220}
198221
199−void AccountsService::updateEnableIndicatorsWhileLocked(bool async)
222+QString AccountsService::email() const
200223{
201− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
202− QStringLiteral("com.ubuntu.AccountsService.SecurityPrivacy"),
203− QStringLiteral("EnableIndicatorsWhileLocked"));
204− QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
205−
206− connect(watcher, &QDBusPendingCallWatcher::finished,
207− this, [this](QDBusPendingCallWatcher* watcher) {
208−
209− QDBusPendingReply<QVariant> reply = *watcher;
210− watcher->deleteLater();
211− if (reply.isError()) {
212− qWarning() << "Failed to get 'EnableIndicatorsWhileLocked' property - " << reply.error().message();
213− return;
214− }
215−
216− const bool enableIndicatorsWhileLocked = reply.value().toBool();
217− if (m_enableIndicatorsWhileLocked != enableIndicatorsWhileLocked) {
218− m_enableIndicatorsWhileLocked = enableIndicatorsWhileLocked;
219− Q_EMIT enableIndicatorsWhileLockedChanged();
220− }
221− });
222− if (!async) {
223− watcher->waitForFinished();
224− delete watcher;
225− }
224+ auto value = getProperty(IFACE_ACCOUNTS_USER, PROP_EMAIL);
225+ return value.toString();
226226}
227227
228−void AccountsService::updateBackgroundFile(bool async)
228+void AccountsService::setEmail(const QString &email)
229229{
230− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
231− QStringLiteral("org.freedesktop.Accounts.User"),
232− QStringLiteral("BackgroundFile"));
233− QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
234−
235− connect(watcher, &QDBusPendingCallWatcher::finished,
236− this, [this](QDBusPendingCallWatcher* watcher) {
237−
238− QDBusPendingReply<QVariant> reply = *watcher;
239− watcher->deleteLater();
240− if (reply.isError()) {
241− qWarning() << "Failed to get 'BackgroundFile' property - " << reply.error().message();
242− return;
243− }
244−
245− const QString backgroundFile = reply.value().toString();
246− if (m_backgroundFile != backgroundFile) {
247− m_backgroundFile = backgroundFile;
248− Q_EMIT backgroundFileChanged();
249− }
250− });
251− if (!async) {
252− watcher->waitForFinished();
253− delete watcher;
254− }
230+ setProperty(IFACE_ACCOUNTS_USER, PROP_EMAIL, email);
255231}
256232
257−void AccountsService::updateMouseCursorSpeed()
233+uint AccountsService::failedLogins() const
258234{
259− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
260− QStringLiteral("com.ubuntu.AccountsService.Input"),
261− QStringLiteral("MouseCursorSpeed"));
262− QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
263−
264− connect(watcher, &QDBusPendingCallWatcher::finished,
265− this, [this](QDBusPendingCallWatcher* watcher) {
266−
267− QDBusPendingReply<QVariant> reply = *watcher;
268− watcher->deleteLater();
269− if (reply.isError()) {
270− qWarning() << "Failed to get 'MouseCursorSpeed' property - " << reply.error().message();
271− return;
272− }
273−
274− // Merely proxy this along to USC. We don't care about keeping a copy
275− // or exporting it internally.
276− m_unityInput->asyncCall(QStringLiteral("setMouseCursorSpeed"), reply.value());
277− });
235+ return getProperty(IFACE_UNITY_PRIVATE, PROP_FAILED_LOGINS).toUInt();
278236}
279237
280−void AccountsService::updateTouchpadCursorSpeed()
238+void AccountsService::setFailedLogins(uint failedLogins)
281239{
282− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
283− QStringLiteral("com.ubuntu.AccountsService.Input"),
284− QStringLiteral("TouchpadCursorSpeed"));
285− QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
286−
287− connect(watcher, &QDBusPendingCallWatcher::finished,
288− this, [this](QDBusPendingCallWatcher* watcher) {
240+ setProperty(IFACE_UNITY_PRIVATE, PROP_FAILED_LOGINS, failedLogins);
241+}
289242
290− QDBusPendingReply<QVariant> reply = *watcher;
291− watcher->deleteLater();
292− if (reply.isError()) {
293− qWarning() << "Failed to get 'TouchpadCursorSpeed' property - " << reply.error().message();
294− return;
295− }
243+// ====================================================
244+// Everything below this line is generic helper methods
245+// ====================================================
296246
297− // Merely proxy this along to USC. We don't care about keeping a copy
298− // or exporting it internally.
299− m_unityInput->asyncCall(QStringLiteral("setTouchpadCursorSpeed"), reply.value());
300− });
247+void AccountsService::emitChangedForProperty(const QString &interface, const QString &property)
248+{
249+ QString signalName = m_properties[interface][property].signal;
250+ QMetaObject::invokeMethod(this, signalName.toUtf8().data());
301251}
302252
303−void AccountsService::updateStatsWelcomeScreen(bool async)
253+QVariant AccountsService::getProperty(const QString &interface, const QString &property) const
304254{
305− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
306− QStringLiteral("com.ubuntu.touch.AccountsService.SecurityPrivacy"),
307− QStringLiteral("StatsWelcomeScreen"));
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 'StatsWelcomeScreen' property - " << reply.error().message();
317− return;
318− }
319−
320− const bool statsWelcomeScreen = reply.value().toBool();
321− if (m_statsWelcomeScreen != statsWelcomeScreen) {
322− m_statsWelcomeScreen = statsWelcomeScreen;
323− Q_EMIT statsWelcomeScreenChanged();
324− }
325− });
326− if (!async) {
327− watcher->waitForFinished();
328− delete watcher;
329− }
255+ return m_properties[interface][property].value;
330256}
331257
332−void AccountsService::updatePasswordDisplayHint(bool async)
258+void AccountsService::setProperty(const QString &interface, const QString &property, const QVariant &value)
333259{
334− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
335− QStringLiteral("com.ubuntu.AccountsService.SecurityPrivacy"),
336− QStringLiteral("PasswordDisplayHint"));
337− QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
338−
339− connect(watcher, &QDBusPendingCallWatcher::finished,
340− this, [this](QDBusPendingCallWatcher* watcher) {
341−
342− QDBusPendingReply<QVariant> reply = *watcher;
343− watcher->deleteLater();
344− if (reply.isError()) {
345− qWarning() << "Failed to get 'PasswordDisplayHint' property - " << reply.error().message();
346− return;
347− }
348−
349− const PasswordDisplayHint passwordDisplayHint = (PasswordDisplayHint)reply.value().toInt();
350− if (m_passwordDisplayHint != passwordDisplayHint) {
351− m_passwordDisplayHint = passwordDisplayHint;
352− Q_EMIT passwordDisplayHintChanged();
353− }
354− });
355− if (!async) {
356− watcher->waitForFinished();
357− delete watcher;
260+ if (m_properties[interface][property].value != value) {
261+ m_properties[interface][property].value = value;
262+ m_service->setUserPropertyAsync(m_user, interface, property, value);
263+ emitChangedForProperty(interface, property);
358264 }
359265}
360266
361−void AccountsService::updateFailedLogins(bool async)
267+void AccountsService::updateCache(const QString &interface, const QString &property, const QVariant &value)
362268{
363− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
364− QStringLiteral("com.canonical.unity.AccountsService.Private"),
365− QStringLiteral("FailedLogins"));
366− QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
367−
368− connect(watcher, &QDBusPendingCallWatcher::finished,
369− this, [this](QDBusPendingCallWatcher* watcher) {
370−
371− QDBusPendingReply<QVariant> reply = *watcher;
372− watcher->deleteLater();
373− if (reply.isError()) {
374− qWarning() << "Failed to get 'FailedLogins' property - " << reply.error().message();
375− return;
269+ PropertyInfo &info = m_properties[interface][property];
270+
271+ if (info.proxyInterface) {
272+ QVariant finalValue;
273+ if (info.proxyConverter) {
274+ finalValue = info.proxyConverter(value);
275+ } else {
276+ finalValue = value;
376277 }
278+ info.proxyInterface->asyncCall(info.proxyMethod, finalValue);
279+ return; // don't bother saving a copy
280+ }
377281
378− const uint failedLogins = reply.value().toUInt();
379− if (m_failedLogins != failedLogins) {
380− m_failedLogins = failedLogins;
381− Q_EMIT failedLoginsChanged();
382− }
383− });
384− if (!async) {
385− watcher->waitForFinished();
386− delete watcher;
282+ if (info.value != value) {
283+ info.value = value;
284+ emitChangedForProperty(interface, property);
387285 }
388286}
389287
390−void AccountsService::updateHereEnabled(bool async)
288+void AccountsService::updateProperty(const QString &interface, const QString &property)
391289{
392290 QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
393− QStringLiteral("com.ubuntu.location.providers.here.AccountsService"),
394− QStringLiteral("LicenseAccepted"));
291+ interface,
292+ property);
395293 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
396294
397295 connect(watcher, &QDBusPendingCallWatcher::finished,
398− this, [this](QDBusPendingCallWatcher* watcher) {
296+ this, [this, interface, property](QDBusPendingCallWatcher* watcher) {
399297
400298 QDBusPendingReply<QVariant> reply = *watcher;
401299 watcher->deleteLater();
402300 if (reply.isError()) {
403− qWarning() << "Failed to get 'LicenseAccepted' property - " << reply.error().message();
301+ qWarning() << "Failed to get '" << property << "' property:" << reply.error().message();
404302 return;
405303 }
406304
407− const bool hereEnabled = reply.value().toBool();
408− if (m_hereEnabled != hereEnabled) {
409− m_hereEnabled = hereEnabled;
410− Q_EMIT hereEnabledChanged();
411− }
305+ updateCache(interface, property, reply.value());
412306 });
413− if (!async) {
414− watcher->waitForFinished();
415− delete watcher;
416− }
417307}
418308
419−void AccountsService::updateHereLicensePath(bool async)
309+void AccountsService::updateAllProperties(const QString &interface, bool async)
420310{
421− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
422− QStringLiteral("com.ubuntu.location.providers.here.AccountsService"),
423− QStringLiteral("LicenseBasePath"));
311+ QDBusPendingCall pendingReply = m_service->getAllPropertiesAsync(m_user,
312+ interface);
424313 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
425314
426315 connect(watcher, &QDBusPendingCallWatcher::finished,
427− this, [this](QDBusPendingCallWatcher* watcher) {
316+ this, [this, interface](QDBusPendingCallWatcher* watcher) {
428317
429− QDBusPendingReply<QVariant> reply = *watcher;
318+ QDBusPendingReply< QHash<QString, QVariant> > reply = *watcher;
430319 watcher->deleteLater();
431320 if (reply.isError()) {
432− qWarning() << "Failed to get 'LicenseBasePath' property - " << reply.error().message();
321+ qWarning() << "Failed to get all properties for" << interface << ":" << reply.error().message();
433322 return;
434323 }
435324
436− QString hereLicensePath = reply.value().toString();
437− if (hereLicensePath.isEmpty() || !QFile::exists(hereLicensePath))
438− hereLicensePath = QLatin1String("");
439−
440− if (m_hereLicensePath.isNull() || m_hereLicensePath != hereLicensePath) {
441− m_hereLicensePath = hereLicensePath;
442− Q_EMIT hereLicensePathChanged();
325+ auto valueHash = reply.value();
326+ auto i = valueHash.constBegin();
327+ while (i != valueHash.constEnd()) {
328+ updateCache(interface, i.key(), i.value());
329+ ++i;
443330 }
444331 });
445332 if (!async) {
446333 watcher->waitForFinished();
447− delete watcher;
448334 }
449335}
450336
451−uint AccountsService::failedLogins() const
337+void AccountsService::registerProxy(const QString &interface, const QString &property, QDBusInterface *iface, const QString &method, ProxyConverter converter)
452338{
453− return m_failedLogins;
339+ registerProperty(interface, property, nullptr);
340+
341+ m_properties[interface][property].proxyInterface = iface;
342+ m_properties[interface][property].proxyMethod = method;
343+ m_properties[interface][property].proxyConverter = converter;
454344}
455345
456−void AccountsService::setFailedLogins(uint failedLogins)
346+void AccountsService::registerProperty(const QString &interface, const QString &property, const QString &signal)
457347{
458− if (m_failedLogins != failedLogins) {
459− m_failedLogins = failedLogins;
460− m_service->setUserPropertyAsync(m_user, QStringLiteral("com.canonical.unity.AccountsService.Private"), QStringLiteral("FailedLogins"), failedLogins);
461−
462− Q_EMIT failedLoginsChanged();
463− }
348+ m_properties[interface][property] = PropertyInfo();
349+ m_properties[interface][property].signal = signal;
464350}
465351
466352void AccountsService::onPropertiesChanged(const QString &user, const QString &interface, const QStringList &changed)
@@ -469,42 +355,13 @@ void AccountsService::onPropertiesChanged(const QString &user, const QString &in
469355 return;
470356 }
471357
472− if (interface == QLatin1String("com.canonical.unity.AccountsService")) {
473− if (changed.contains(QStringLiteral("demo-edges"))) {
474− updateDemoEdges();
475− }
476− } else if (interface == QLatin1String("com.canonical.unity.AccountsService.Private")) {
477− if (changed.contains(QStringLiteral("FailedLogins"))) {
478− updateFailedLogins();
479− }
480− } else if (interface == QLatin1String("com.ubuntu.AccountsService.Input")) {
481− if (changed.contains(QStringLiteral("MouseCursorSpeed"))) {
482− updateMouseCursorSpeed();
483− }
484− if (changed.contains(QStringLiteral("TouchpadCursorSpeed"))) {
485− updateTouchpadCursorSpeed();
486− }
487− } else if (interface == QLatin1String("com.ubuntu.touch.AccountsService.SecurityPrivacy")) {
488− if (changed.contains(QStringLiteral("StatsWelcomeScreen"))) {
489− updateStatsWelcomeScreen();
490− }
491− } else if (interface == QLatin1String("com.ubuntu.AccountsService.SecurityPrivacy")) {
492− if (changed.contains(QStringLiteral("PasswordDisplayHint"))) {
493− updatePasswordDisplayHint();
494− }
495− if (changed.contains(QStringLiteral("EnableLauncherWhileLocked"))) {
496− updateEnableLauncherWhileLocked();
497− }
498− if (changed.contains(QStringLiteral("EnableIndicatorsWhileLocked"))) {
499− updateEnableIndicatorsWhileLocked();
500− }
501− } else if (interface == QLatin1String("com.ubuntu.location.providers.here.AccountsService")) {
502− if (changed.contains(QStringLiteral("LicenseAccepted"))) {
503− updateHereEnabled();
504− }
505− if (changed.contains(QStringLiteral("LicenseBasePath"))) {
506− updateHereLicensePath();
358+ auto propHash = m_properties.value(interface);
359+ auto i = propHash.constBegin();
360+ while (i != propHash.constEnd()) {
361+ if (changed.contains(i.key())) {
362+ updateProperty(interface, i.key());
507363 }
364+ ++i;
508365 }
509366}
510367
@@ -514,6 +371,20 @@ void AccountsService::onMaybeChanged(const QString &user)
514371 return;
515372 }
516373
517− // Standard properties might have changed
518− updateBackgroundFile();
374+ // Any of the standard properties might have changed!
375+ auto propHash = m_properties.value(IFACE_ACCOUNTS_USER);
376+ auto i = propHash.constBegin();
377+ while (i != propHash.constEnd()) {
378+ updateProperty(IFACE_ACCOUNTS_USER, i.key());
379+ ++i;
380+ }
381+}
382+
383+void AccountsService::refresh(bool async)
384+{
385+ auto i = m_properties.constBegin();
386+ while (i != m_properties.constEnd()) {
387+ updateAllProperties(i.key(), async);
388+ ++i;
389+ }
519390}