@@ -22,17 +22,54 @@
2222#include <QStringList>
2323#include <QDebug>
2424
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+
2570AccountsService::AccountsService(QObject* parent, const QString &user)
26− : QObject(parent),
27− m_service(new AccountsServiceDBusAdaptor(this)),
28− m_demoEdges(false),
29− m_enableLauncherWhileLocked(false),
30− m_enableIndicatorsWhileLocked(false),
31− m_statsWelcomeScreen(false),
32− m_passwordDisplayHint(Keyboard),
33− m_failedLogins(0),
34− m_hereEnabled(false),
35− m_hereLicensePath() // null means not set yet
71+ : QObject(parent)
72+ , m_service(new AccountsServiceDBusAdaptor(this))
3673{
3774 m_unityInput = new QDBusInterface(QStringLiteral("com.canonical.Unity.Input"),
3875 QStringLiteral("/com/canonical/Unity/Input"),
@@ -42,6 +79,45 @@ AccountsService::AccountsService(QObject* parent, const QString &user)
4279 connect(m_service, &AccountsServiceDBusAdaptor::propertiesChanged, this, &AccountsService::onPropertiesChanged);
4380 connect(m_service, &AccountsServiceDBusAdaptor::maybeChanged, this, &AccountsService::onMaybeChanged);
4481
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+
45121 setUser(!user.isEmpty() ? user : QString::fromUtf8(qgetenv("USER")));
46122}
47123
@@ -55,490 +131,222 @@ void AccountsService::setUser(const QString &user)
55131 if (user.isEmpty() || m_user == user)
56132 return;
57133
134+ bool wasEmpty = m_user.isEmpty();
135+
58136 m_user = user;
59137 Q_EMIT userChanged();
60138
61− updateDemoEdges(false);
62− updateEnableLauncherWhileLocked(false);
63− updateEnableIndicatorsWhileLocked(false);
64− updateBackgroundFile(false);
65− updateMouseCursorSpeed();
66− updateTouchpadCursorSpeed();
67− updateStatsWelcomeScreen(false);
68− updatePasswordDisplayHint(false);
69− updateFailedLogins(false);
70− updateHereEnabled(false);
71− 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);
72142}
73143
74144bool AccountsService::demoEdges() const
75145{
76− return m_demoEdges;
146+ auto value = getProperty(IFACE_UNITY, PROP_DEMO_EDGES);
147+ return value.toBool();
77148}
78149
79150void AccountsService::setDemoEdges(bool demoEdges)
80151{
81− if (m_demoEdges != demoEdges) {
82− m_demoEdges = demoEdges;
83− m_service->setUserPropertyAsync(m_user, QStringLiteral("com.canonical.unity.AccountsService"), QStringLiteral("demo-edges"), demoEdges);
84−
85− Q_EMIT demoEdgesChanged();
86− }
152+ setProperty(IFACE_UNITY, PROP_DEMO_EDGES, demoEdges);
87153}
88154
89155bool AccountsService::enableLauncherWhileLocked() const
90156{
91− return m_enableLauncherWhileLocked;
157+ auto value = getProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_LAUNCHER_WHILE_LOCKED);
158+ return value.toBool();
92159}
93160
94161bool AccountsService::enableIndicatorsWhileLocked() const
95162{
96− return m_enableIndicatorsWhileLocked;
163+ auto value = getProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_INDICATORS_WHILE_LOCKED);
164+ return value.toBool();
97165}
98166
99167QString AccountsService::backgroundFile() const
100168{
101− return m_backgroundFile;
169+ auto value = getProperty(IFACE_ACCOUNTS_USER, PROP_BACKGROUND_FILE);
170+ return value.toString();
102171}
103172
104173bool AccountsService::statsWelcomeScreen() const
105174{
106− return m_statsWelcomeScreen;
175+ auto value = getProperty(IFACE_UBUNTU_SECURITY_OLD, PROP_STATS_WELCOME_SCREEN);
176+ return value.toBool();
107177}
108178
109179AccountsService::PasswordDisplayHint AccountsService::passwordDisplayHint() const
110180{
111− return m_passwordDisplayHint;
181+ auto value = getProperty(IFACE_UBUNTU_SECURITY, PROP_PASSWORD_DISPLAY_HINT);
182+ return (PasswordDisplayHint)value.toInt();
112183}
113184
114185bool AccountsService::hereEnabled() const
115186{
116− return m_hereEnabled;
187+ auto value = getProperty(IFACE_LOCATION_HERE, PROP_LICENSE_ACCEPTED);
188+ return value.toBool();
117189}
118190
119191void AccountsService::setHereEnabled(bool enabled)
120192{
121− if (m_hereEnabled != enabled) {
122− m_hereEnabled = enabled;
123− m_service->setUserPropertyAsync(m_user, QStringLiteral("com.ubuntu.location.providers.here.AccountsService"), QStringLiteral("LicenseAccepted"), enabled);
124−
125− Q_EMIT hereEnabledChanged();
126− }
193+ setProperty(IFACE_LOCATION_HERE, PROP_LICENSE_ACCEPTED, enabled);
127194}
128195
129196QString AccountsService::hereLicensePath() const
130197{
131− 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;
132203}
133204
134205bool AccountsService::hereLicensePathValid() const
135206{
136− return !m_hereLicensePath.isNull();
207+ auto value = getProperty(IFACE_LOCATION_HERE, PROP_LICENSE_BASE_PATH);
208+ return !value.toString().isNull();
137209}
138210
139211QString AccountsService::realName() const
140212{
141− return m_realName;
213+ auto value = getProperty(IFACE_ACCOUNTS_USER, PROP_REAL_NAME);
214+ return value.toString();
142215}
143216
144217void AccountsService::setRealName(const QString &realName)
145218{
146− if (realName != m_realName) {
147− m_realName = realName;
148− m_service->setUserPropertyAsync(m_user, "org.freedesktop.Accounts.User", "RealName", m_realName);
149− Q_EMIT realNameChanged();
150− }
219+ setProperty(IFACE_ACCOUNTS_USER, PROP_REAL_NAME, realName);
151220}
152221
153222QString AccountsService::email() const
154223{
155− return m_email;
224+ auto value = getProperty(IFACE_ACCOUNTS_USER, PROP_EMAIL);
225+ return value.toString();
156226}
157227
158228void AccountsService::setEmail(const QString &email)
159229{
160− if (email != m_email) {
161− m_email = email;
162− m_service->setUserPropertyAsync(m_user, "org.freedesktop.Accounts.User", "Email", m_email);
163− Q_EMIT emailChanged();
164− }
230+ setProperty(IFACE_ACCOUNTS_USER, PROP_EMAIL, email);
165231}
166232
167−void AccountsService::updateDemoEdges(bool async)
233+uint AccountsService::failedLogins() const
168234{
169− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
170− QStringLiteral("com.canonical.unity.AccountsService"),
171− QStringLiteral("demo-edges"));
172− QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
173−
174− connect(watcher, &QDBusPendingCallWatcher::finished,
175− this, [this](QDBusPendingCallWatcher* watcher) {
176−
177− QDBusPendingReply<QVariant> reply = *watcher;
178− watcher->deleteLater();
179− if (reply.isError()) {
180− qWarning() << "Failed to get 'demo-edges' property - " << reply.error().message();
181− return;
182− }
183−
184− const bool demoEdges = reply.value().toBool();
185− if (m_demoEdges != demoEdges) {
186− m_demoEdges = demoEdges;
187− Q_EMIT demoEdgesChanged();
188− }
189− });
190− if (!async) {
191− watcher->waitForFinished();
192− delete watcher;
193− }
235+ return getProperty(IFACE_UNITY_PRIVATE, PROP_FAILED_LOGINS).toUInt();
194236}
195237
196−void AccountsService::updateEnableLauncherWhileLocked(bool async)
238+void AccountsService::setFailedLogins(uint failedLogins)
197239{
198− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
199− QStringLiteral("com.ubuntu.AccountsService.SecurityPrivacy"),
200− QStringLiteral("EnableLauncherWhileLocked"));
201− QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
202−
203− connect(watcher, &QDBusPendingCallWatcher::finished,
204− this, [this](QDBusPendingCallWatcher* watcher) {
205−
206− QDBusPendingReply<QVariant> reply = *watcher;
207− watcher->deleteLater();
208− if (reply.isError()) {
209− qWarning() << "Failed to get 'EnableLauncherWhileLocked' property - " << reply.error().message();
210− return;
211− }
212−
213− const bool enableLauncherWhileLocked = reply.value().toBool();
214− if (m_enableLauncherWhileLocked != enableLauncherWhileLocked) {
215− m_enableLauncherWhileLocked = enableLauncherWhileLocked;
216− Q_EMIT enableLauncherWhileLockedChanged();
217− }
218− });
219− if (!async) {
220− watcher->waitForFinished();
221− delete watcher;
222− }
240+ setProperty(IFACE_UNITY_PRIVATE, PROP_FAILED_LOGINS, failedLogins);
223241}
224242
225−void AccountsService::updateEnableIndicatorsWhileLocked(bool async)
226−{
227− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
228− QStringLiteral("com.ubuntu.AccountsService.SecurityPrivacy"),
229− QStringLiteral("EnableIndicatorsWhileLocked"));
230− QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
231−
232− connect(watcher, &QDBusPendingCallWatcher::finished,
233− this, [this](QDBusPendingCallWatcher* watcher) {
243+// ====================================================
244+// Everything below this line is generic helper methods
245+// ====================================================
234246
235− QDBusPendingReply<QVariant> reply = *watcher;
236− watcher->deleteLater();
237− if (reply.isError()) {
238− qWarning() << "Failed to get 'EnableIndicatorsWhileLocked' property - " << reply.error().message();
239− return;
240− }
241−
242− const bool enableIndicatorsWhileLocked = reply.value().toBool();
243− if (m_enableIndicatorsWhileLocked != enableIndicatorsWhileLocked) {
244− m_enableIndicatorsWhileLocked = enableIndicatorsWhileLocked;
245− Q_EMIT enableIndicatorsWhileLockedChanged();
246− }
247− });
248− if (!async) {
249− watcher->waitForFinished();
250− delete watcher;
251− }
252−}
253−
254−void AccountsService::updateBackgroundFile(bool async)
247+void AccountsService::emitChangedForProperty(const QString &interface, const QString &property)
255248{
256− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
257− QStringLiteral("org.freedesktop.Accounts.User"),
258− QStringLiteral("BackgroundFile"));
259− QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
260−
261− connect(watcher, &QDBusPendingCallWatcher::finished,
262− this, [this](QDBusPendingCallWatcher* watcher) {
263−
264− QDBusPendingReply<QVariant> reply = *watcher;
265− watcher->deleteLater();
266− if (reply.isError()) {
267− qWarning() << "Failed to get 'BackgroundFile' property - " << reply.error().message();
268− return;
269− }
270−
271− const QString backgroundFile = reply.value().toString();
272− if (m_backgroundFile != backgroundFile) {
273− m_backgroundFile = backgroundFile;
274− Q_EMIT backgroundFileChanged();
275− }
276− });
277− if (!async) {
278− watcher->waitForFinished();
279− delete watcher;
280− }
249+ QString signalName = m_properties[interface][property].signal;
250+ QMetaObject::invokeMethod(this, signalName.toUtf8().data());
281251}
282252
283−void AccountsService::updateMouseCursorSpeed()
253+QVariant AccountsService::getProperty(const QString &interface, const QString &property) const
284254{
285− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
286− QStringLiteral("com.ubuntu.AccountsService.Input"),
287− QStringLiteral("MouseCursorSpeed"));
288− QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
289−
290− connect(watcher, &QDBusPendingCallWatcher::finished,
291− this, [this](QDBusPendingCallWatcher* watcher) {
292−
293− QDBusPendingReply<QVariant> reply = *watcher;
294− watcher->deleteLater();
295− if (reply.isError()) {
296− qWarning() << "Failed to get 'MouseCursorSpeed' property - " << reply.error().message();
297− return;
298− }
299−
300− // Merely proxy this along to USC. We don't care about keeping a copy
301− // or exporting it internally.
302− m_unityInput->asyncCall(QStringLiteral("setMouseCursorSpeed"), reply.value());
303− });
255+ return m_properties[interface][property].value;
304256}
305257
306−void AccountsService::updateTouchpadCursorSpeed()
258+void AccountsService::setProperty(const QString &interface, const QString &property, const QVariant &value)
307259{
308− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
309− QStringLiteral("com.ubuntu.AccountsService.Input"),
310− QStringLiteral("TouchpadCursorSpeed"));
311− QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
312−
313− connect(watcher, &QDBusPendingCallWatcher::finished,
314− this, [this](QDBusPendingCallWatcher* watcher) {
315−
316− QDBusPendingReply<QVariant> reply = *watcher;
317− watcher->deleteLater();
318− if (reply.isError()) {
319− qWarning() << "Failed to get 'TouchpadCursorSpeed' property - " << reply.error().message();
320− return;
321− }
322−
323− // Merely proxy this along to USC. We don't care about keeping a copy
324− // or exporting it internally.
325− m_unityInput->asyncCall(QStringLiteral("setTouchpadCursorSpeed"), reply.value());
326− });
327−}
328−
329−void AccountsService::updateStatsWelcomeScreen(bool async)
330−{
331− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
332− QStringLiteral("com.ubuntu.touch.AccountsService.SecurityPrivacy"),
333− QStringLiteral("StatsWelcomeScreen"));
334− QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
335−
336− connect(watcher, &QDBusPendingCallWatcher::finished,
337− this, [this](QDBusPendingCallWatcher* watcher) {
338−
339− QDBusPendingReply<QVariant> reply = *watcher;
340− watcher->deleteLater();
341− if (reply.isError()) {
342− qWarning() << "Failed to get 'StatsWelcomeScreen' property - " << reply.error().message();
343− return;
344− }
345−
346− const bool statsWelcomeScreen = reply.value().toBool();
347− if (m_statsWelcomeScreen != statsWelcomeScreen) {
348− m_statsWelcomeScreen = statsWelcomeScreen;
349− Q_EMIT statsWelcomeScreenChanged();
350− }
351− });
352− if (!async) {
353− watcher->waitForFinished();
354− 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);
355264 }
356265}
357266
358−void AccountsService::updatePasswordDisplayHint(bool async)
267+void AccountsService::updateCache(const QString &interface, const QString &property, const QVariant &value)
359268{
360− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
361− QStringLiteral("com.ubuntu.AccountsService.SecurityPrivacy"),
362− QStringLiteral("PasswordDisplayHint"));
363− QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
364−
365− connect(watcher, &QDBusPendingCallWatcher::finished,
366− this, [this](QDBusPendingCallWatcher* watcher) {
367−
368− QDBusPendingReply<QVariant> reply = *watcher;
369− watcher->deleteLater();
370− if (reply.isError()) {
371− qWarning() << "Failed to get 'PasswordDisplayHint' property - " << reply.error().message();
372− return;
373− }
269+ PropertyInfo &info = m_properties[interface][property];
374270
375− const PasswordDisplayHint passwordDisplayHint = (PasswordDisplayHint)reply.value().toInt();
376− if (m_passwordDisplayHint != passwordDisplayHint) {
377− m_passwordDisplayHint = passwordDisplayHint;
378− Q_EMIT passwordDisplayHintChanged();
271+ if (info.proxyInterface) {
272+ QVariant finalValue;
273+ if (info.proxyConverter) {
274+ finalValue = info.proxyConverter(value);
275+ } else {
276+ finalValue = value;
379277 }
380− });
381− if (!async) {
382− watcher->waitForFinished();
383− delete watcher;
278+ info.proxyInterface->asyncCall(info.proxyMethod, finalValue);
279+ return; // don't bother saving a copy
384280 }
385−}
386−
387−void AccountsService::updateFailedLogins(bool async)
388−{
389− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
390− QStringLiteral("com.canonical.unity.AccountsService.Private"),
391− QStringLiteral("FailedLogins"));
392− QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
393−
394− connect(watcher, &QDBusPendingCallWatcher::finished,
395− this, [this](QDBusPendingCallWatcher* watcher) {
396−
397− QDBusPendingReply<QVariant> reply = *watcher;
398− watcher->deleteLater();
399− if (reply.isError()) {
400− qWarning() << "Failed to get 'FailedLogins' property - " << reply.error().message();
401− return;
402− }
403281
404− const uint failedLogins = reply.value().toUInt();
405− if (m_failedLogins != failedLogins) {
406− m_failedLogins = failedLogins;
407− Q_EMIT failedLoginsChanged();
408− }
409− });
410− if (!async) {
411− watcher->waitForFinished();
412− delete watcher;
282+ if (info.value != value) {
283+ info.value = value;
284+ emitChangedForProperty(interface, property);
413285 }
414286}
415287
416−void AccountsService::updateHereEnabled(bool async)
288+void AccountsService::updateProperty(const QString &interface, const QString &property)
417289{
418290 QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
419− QStringLiteral("com.ubuntu.location.providers.here.AccountsService"),
420− QStringLiteral("LicenseAccepted"));
291+ interface,
292+ property);
421293 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
422294
423295 connect(watcher, &QDBusPendingCallWatcher::finished,
424− this, [this](QDBusPendingCallWatcher* watcher) {
296+ this, [this, interface, property](QDBusPendingCallWatcher* watcher) {
425297
426298 QDBusPendingReply<QVariant> reply = *watcher;
427299 watcher->deleteLater();
428300 if (reply.isError()) {
429− qWarning() << "Failed to get 'LicenseAccepted' property - " << reply.error().message();
301+ qWarning() << "Failed to get '" << property << "' property:" << reply.error().message();
430302 return;
431303 }
432304
433− const bool hereEnabled = reply.value().toBool();
434− if (m_hereEnabled != hereEnabled) {
435− m_hereEnabled = hereEnabled;
436− Q_EMIT hereEnabledChanged();
437− }
305+ updateCache(interface, property, reply.value());
438306 });
439− if (!async) {
440− watcher->waitForFinished();
441− delete watcher;
442− }
443307}
444308
445−void AccountsService::updateHereLicensePath(bool async)
309+void AccountsService::updateAllProperties(const QString &interface, bool async)
446310{
447− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
448− QStringLiteral("com.ubuntu.location.providers.here.AccountsService"),
449− QStringLiteral("LicenseBasePath"));
311+ QDBusPendingCall pendingReply = m_service->getAllPropertiesAsync(m_user,
312+ interface);
450313 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
451314
452315 connect(watcher, &QDBusPendingCallWatcher::finished,
453− this, [this](QDBusPendingCallWatcher* watcher) {
454−
455− QDBusPendingReply<QVariant> reply = *watcher;
456− watcher->deleteLater();
457− if (reply.isError()) {
458− qWarning() << "Failed to get 'LicenseBasePath' property - " << reply.error().message();
459− return;
460− }
461−
462− QString hereLicensePath = reply.value().toString();
463− if (hereLicensePath.isEmpty() || !QFile::exists(hereLicensePath))
464− hereLicensePath = QLatin1String("");
465−
466− if (m_hereLicensePath.isNull() || m_hereLicensePath != hereLicensePath) {
467− m_hereLicensePath = hereLicensePath;
468− Q_EMIT hereLicensePathChanged();
469− }
470− });
471− if (!async) {
472− watcher->waitForFinished();
473− delete watcher;
474− }
475−}
316+ this, [this, interface](QDBusPendingCallWatcher* watcher) {
476317
477−void AccountsService::updateRealName(bool async)
478−{
479− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user, "org.freedesktop.Accounts.User", "RealName");
480− QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
481−
482− connect(watcher, &QDBusPendingCallWatcher::finished,
483− this, [this](QDBusPendingCallWatcher* watcher) {
484− QDBusPendingReply<QVariant> reply = *watcher;
318+ QDBusPendingReply< QHash<QString, QVariant> > reply = *watcher;
485319 watcher->deleteLater();
486320 if (reply.isError()) {
487− qWarning() << "Failed to get 'RealName' property - " << reply.error().message();
321+ qWarning() << "Failed to get all properties for" << interface << ":" << reply.error().message();
488322 return;
489323 }
490324
491− const QString realName = reply.value().toString();
492− if (m_realName != realName) {
493− m_realName = realName;
494− Q_EMIT realNameChanged();
325+ auto valueHash = reply.value();
326+ auto i = valueHash.constBegin();
327+ while (i != valueHash.constEnd()) {
328+ updateCache(interface, i.key(), i.value());
329+ ++i;
495330 }
496331 });
497332 if (!async) {
498333 watcher->waitForFinished();
499− delete watcher;
500334 }
501335}
502336
503−void AccountsService::updateEmail(bool async)
337+void AccountsService::registerProxy(const QString &interface, const QString &property, QDBusInterface *iface, const QString &method, ProxyConverter converter)
504338{
505− QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user, "org.freedesktop.Accounts.User", "Email");
506− QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
507−
508− connect(watcher, &QDBusPendingCallWatcher::finished,
509− this, [this](QDBusPendingCallWatcher* watcher) {
510− QDBusPendingReply<QVariant> reply = *watcher;
511− watcher->deleteLater();
512− if (reply.isError()) {
513− qWarning() << "Failed to get 'Email' property - " << reply.error().message();
514− return;
515− }
339+ registerProperty(interface, property, nullptr);
516340
517− const QString email = reply.value().toString();
518− if (m_email != email) {
519− m_email = email;
520− Q_EMIT emailChanged();
521− }
522− });
523− if (!async) {
524− watcher->waitForFinished();
525− delete watcher;
526− }
341+ m_properties[interface][property].proxyInterface = iface;
342+ m_properties[interface][property].proxyMethod = method;
343+ m_properties[interface][property].proxyConverter = converter;
527344}
528345
529−uint AccountsService::failedLogins() const
346+void AccountsService::registerProperty(const QString &interface, const QString &property, const QString &signal)
530347{
531− return m_failedLogins;
532−}
533−
534−void AccountsService::setFailedLogins(uint failedLogins)
535−{
536− if (m_failedLogins != failedLogins) {
537− m_failedLogins = failedLogins;
538− m_service->setUserPropertyAsync(m_user, QStringLiteral("com.canonical.unity.AccountsService.Private"), QStringLiteral("FailedLogins"), failedLogins);
539−
540− Q_EMIT failedLoginsChanged();
541− }
348+ m_properties[interface][property] = PropertyInfo();
349+ m_properties[interface][property].signal = signal;
542350}
543351
544352void AccountsService::onPropertiesChanged(const QString &user, const QString &interface, const QStringList &changed)
@@ -547,42 +355,13 @@ void AccountsService::onPropertiesChanged(const QString &user, const QString &in
547355 return;
548356 }
549357
550− if (interface == QLatin1String("com.canonical.unity.AccountsService")) {
551− if (changed.contains(QStringLiteral("demo-edges"))) {
552− updateDemoEdges();
553− }
554− } else if (interface == QLatin1String("com.canonical.unity.AccountsService.Private")) {
555− if (changed.contains(QStringLiteral("FailedLogins"))) {
556− updateFailedLogins();
557− }
558− } else if (interface == QLatin1String("com.ubuntu.AccountsService.Input")) {
559− if (changed.contains(QStringLiteral("MouseCursorSpeed"))) {
560− updateMouseCursorSpeed();
561− }
562− if (changed.contains(QStringLiteral("TouchpadCursorSpeed"))) {
563− updateTouchpadCursorSpeed();
564− }
565− } else if (interface == QLatin1String("com.ubuntu.touch.AccountsService.SecurityPrivacy")) {
566− if (changed.contains(QStringLiteral("StatsWelcomeScreen"))) {
567− updateStatsWelcomeScreen();
568− }
569− } else if (interface == QLatin1String("com.ubuntu.AccountsService.SecurityPrivacy")) {
570− if (changed.contains(QStringLiteral("PasswordDisplayHint"))) {
571− updatePasswordDisplayHint();
572− }
573− if (changed.contains(QStringLiteral("EnableLauncherWhileLocked"))) {
574− updateEnableLauncherWhileLocked();
575− }
576− if (changed.contains(QStringLiteral("EnableIndicatorsWhileLocked"))) {
577− updateEnableIndicatorsWhileLocked();
578− }
579− } else if (interface == QLatin1String("com.ubuntu.location.providers.here.AccountsService")) {
580− if (changed.contains(QStringLiteral("LicenseAccepted"))) {
581− updateHereEnabled();
582− }
583− if (changed.contains(QStringLiteral("LicenseBasePath"))) {
584− 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());
585363 }
364+ ++i;
586365 }
587366}
588367
@@ -592,8 +371,20 @@ void AccountsService::onMaybeChanged(const QString &user)
592371 return;
593372 }
594373
595− // Standard properties might have changed
596− updateBackgroundFile();
597− updateRealName();
598− updateEmail();
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+ }
599390}