Merge lp:~mterry/unity8/launcher-items into lp:unity8

Proposed by Michael Terry
Status: Merged
Approved by: Michael Terry
Approved revision: 225
Merged at revision: 272
Proposed branch: lp:~mterry/unity8/launcher-items
Merge into: lp:unity8
Prerequisite: lp:~mterry/unity8/demo
Diff against target: 749 lines (+365/-73)
18 files modified
Launcher/LauncherDelegate.qml (+1/-1)
Shell.qml (+6/-0)
plugins/AccountsService/com.canonical.unity.AccountsService.xml (+7/-0)
plugins/AccountsService/plugin.cpp (+2/-0)
plugins/Unity/Launcher/CMakeLists.txt (+5/-1)
plugins/Unity/Launcher/backend/launcherbackend.cpp (+171/-61)
plugins/Unity/Launcher/backend/launcherbackend.h (+38/-5)
plugins/Unity/Launcher/launchermodel.cpp (+4/-3)
plugins/Unity/Launcher/launchermodel.h (+1/-1)
tests/mocks/Unity/Launcher/CMakeLists.txt (+2/-0)
tests/mocks/Unity/Launcher/MockLauncherItem.cpp (+1/-1)
tests/plugins/Unity/CMakeLists.txt (+1/-0)
tests/plugins/Unity/Launcher/CMakeLists.txt (+22/-0)
tests/plugins/Unity/Launcher/abs-icon.desktop (+3/-0)
tests/plugins/Unity/Launcher/launcherbackendtest.cpp (+94/-0)
tests/plugins/Unity/Launcher/no-icon.desktop (+2/-0)
tests/plugins/Unity/Launcher/no-name.desktop (+2/-0)
tests/plugins/Unity/Launcher/rel-icon.desktop (+3/-0)
To merge this branch: bzr merge lp:~mterry/unity8/launcher-items
Reviewer Review Type Date Requested Status
Michael Zanetti (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Albert Astals Cid (community) Needs Information
Omer Akram (community) Needs Fixing
Review via email: mp+181061@code.launchpad.net

Commit message

Implement launcher item backend via AccountsService.

Description of the change

Implement launcher item backend in AccountsService.

The launcher items are currently hardcoded. But there are various TODOs to make them actually be stored on the system and have the icons an names pulled from the desktop files. This branch does that.

The storage backend is AccountsService. This is so that the greeter (once split out into a separate process) can still access the list of launcher items to show for the currently selected user. (And I've added a bit of setUser API so that the Qml code can tell the launcher backend which user to act as.)

We now look up icons for launcher items from the system icon theme, instead of our own testing icons.

And I corrected what seemed like an oversight of the LauncherModel not telling the backend when items have been pinned.

Like the demo branch (on which this depends), you should actually install the deb file, rather than just ./run_on_device, since this modifies the system dbus interface for AccountsService.

Additionally, if you want to test setting the AccountsService value manually (note that this code does not listen for changes, so you'll have to restart unity8), use a command line like the following:

gdbus call --system --dest org.freedesktop.Accounts --object-path /org/freedesktop/Accounts/User32011 --method org.freedesktop.DBus.Properties.Set com.canonical.unity.AccountsService launcher-items "<['camera-app.desktop', '/usr/share/applications/phone-app.desktop']>"

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote :

172 + for (int i = 0; i < m_storedApps.size(); i++) {
173 + delete m_storedApps[i].settings;
174 + }

Just a hint (not really necessary to change)

Q_FOREACH(const LauncherBackendItem &item, m_storedApps) {
  delete item.settings;
}

Revision history for this message
Michael Zanetti (mzanetti) wrote :

Actually, now that I see you're iteration more often over all the items, I'd like you to change the loops to use Q_FOREACH. It's much nicer to read than the m_storedApplications[index] everywhere.

===

338 + // Mark all apps as pinned (since we just refreshed the set)
339 + for (int i = 0; i < m_storedApps.size(); i++) {
340 + m_storedApps[i].pinned = true;
341 + }

344 +void LauncherBackend::syncToAccounts()
345 +{
346 + auto pinnedItems = QStringList();
347 + for (int i = 0; i < m_storedApps.size(); i++) {
348 + if (m_storedApps[i].pinned) {
349 + pinnedItems.append(m_storedApps[i].settings->fileName());
350 + }
351 + }
352 +
353 + if (m_user != "" && m_accounts != nullptr) {
354 + m_accounts->setUserProperty(m_user, "launcher-items", QVariant(pinnedItems));
355 + }
356 +}

the pinned state needs to be stored and reloaded from the storage. Because we will also have non-pinned, persistent items, for example the recent applications.

===

Not really sure we need the tests with the .desktop files as the QSettings in there will be replaced with Antti's proper .desktop file parser. Anyways... for now there's no need to drop it.

===

The order of the items is not stored. Reordering them in the launcher is reverted after a restart of unity.

review: Needs Fixing
Revision history for this message
Michael Zanetti (mzanetti) wrote :

Also, we need a flag in the config that tells us if the config is just empty or if it is indeed uninitialized to avoid resetting to the default config if the user intentionally removes all items (for example to see only running/recent ones)

review: Needs Fixing
Revision history for this message
Michael Zanetti (mzanetti) wrote :

Sorry, while starting to work on top of this I realized another issue:

The backend is supposed to deliver information for this, regardless if the item is in the list:

    QString desktopFile(const QString &appId) const;
    QString displayName(const QString &appId) const;
    QString icon(const QString &appId) const;

review: Needs Fixing
Revision history for this message
Michael Zanetti (mzanetti) wrote :

and there is an API change in LauncherModel, which means we need to update lp:unity-api and its tests.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Omer Akram (om26er) wrote :

There is a conflict now. please merge trunk.

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote :

file:///usr/share/unity8/Shell.qml:470: ReferenceError: user is not defined

This causes some weird effect at startup that the width somehow is broken and everything is displaced on the Nexus 4.

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Michael Terry (mterry) wrote :

Fixed the user warning.

Revision history for this message
Michael Zanetti (mzanetti) :
review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Albert Astals Cid (aacid) wrote :

plugins/Unity/Launcher/backend/launcherbackend.cpp conflicts

Revision history for this message
Albert Astals Cid (aacid) wrote :

Reading

234 - // TODO: return real path instead of this hardcoded one
235 - return QLatin1String("/usr/share/applications/") + appId;
236 + QFileInfo fileInfo(QDir("/usr/share/applications"), appId);
237 + return fileInfo.absoluteFilePath();

maybe we want canonicalFilePath in there instead of absolute?

review: Needs Information
Revision history for this message
Michael Terry (mterry) wrote :

I tried canonicalFilePath, but that actually hits the disk and checks if the file exists. If it doesn't, it returns "". Which I didn't think we actually wanted.

Revision history for this message
Albert Astals Cid (aacid) wrote :

Ok, still

236 + QFileInfo fileInfo(QDir("/usr/share/applications"), appId);
237 + return fileInfo.absoluteFilePath();

looks like a really slow way of doing

return "/usr/share/applications/" + appdId;

no?

review: Needs Information
lp:~mterry/unity8/launcher-items updated
225. By Michael Terry

Merge from trunk

Revision history for this message
Michael Terry (mterry) wrote :

> return "/usr/share/applications/" + appdId;

That won't gracefully handle a full-path appId, which the current code allows.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Launcher/LauncherDelegate.qml'
2--- Launcher/LauncherDelegate.qml 2013-08-21 16:07:02 +0000
3+++ Launcher/LauncherDelegate.qml 2013-08-29 13:56:41 +0000
4@@ -60,7 +60,7 @@
5 id: iconImage
6 sourceSize.width: iconShape.width
7 sourceSize.height: iconShape.height
8- source: "../graphics/applicationIcons/" + root.iconName + ".png"
9+ source: root.iconName
10 }
11 }
12
13
14=== modified file 'Shell.qml'
15--- Shell.qml 2013-08-28 19:59:50 +0000
16+++ Shell.qml 2013-08-29 13:56:41 +0000
17@@ -19,6 +19,7 @@
18 import Ubuntu.Application 0.1
19 import Ubuntu.Components 0.1
20 import Ubuntu.Gestures 0.1
21+import Unity.Launcher 0.1
22 import LightDM 0.1 as LightDM
23 import Powerd 0.1
24 import "Dash"
25@@ -465,6 +466,11 @@
26 }
27
28 onUnlocked: greeter.hide()
29+ onSelected: {
30+ // Update launcher items for new user
31+ var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
32+ LauncherModel.setUser(user);
33+ }
34
35 onLeftTeaserPressedChanged: {
36 if (leftTeaserPressed) {
37
38=== modified file 'plugins/AccountsService/com.canonical.unity.AccountsService.xml'
39--- plugins/AccountsService/com.canonical.unity.AccountsService.xml 2013-08-12 18:41:45 +0000
40+++ plugins/AccountsService/com.canonical.unity.AccountsService.xml 2013-08-29 13:56:41 +0000
41@@ -6,6 +6,9 @@
42 <annotation name="org.freedesktop.Accounts.Authentication.ChangeOwn"
43 value="com.canonical.unity.AccountsService.ModifyOwnUser"/>
44
45+ <annotation name="org.freedesktop.Accounts.Authentication.ReadAny"
46+ value="com.canonical.unity.AccountsService.ModifyAnyUser"/>
47+
48 <annotation name="org.freedesktop.Accounts.Authentication.ChangeAny"
49 value="com.canonical.unity.AccountsService.ModifyAnyUser"/>
50
51@@ -13,5 +16,9 @@
52 <annotation name="org.freedesktop.Accounts.DefaultValue" value="true"/>
53 </property>
54
55+ <property name="launcher-items" type="aa{sv}" access="readwrite">
56+ <annotation name="org.freedesktop.Accounts.DefaultValue" value="[{'defaults': <true>}]"/>
57+ </property>
58+
59 </interface>
60 </node>
61
62=== modified file 'plugins/AccountsService/plugin.cpp'
63--- plugins/AccountsService/plugin.cpp 2013-08-22 14:54:08 +0000
64+++ plugins/AccountsService/plugin.cpp 2013-08-29 13:56:41 +0000
65@@ -19,6 +19,7 @@
66 #include "plugin.h"
67 #include "AccountsService.h"
68
69+#include <QDBusMetaType>
70 #include <QtQml>
71
72 static QObject *service_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
73@@ -31,5 +32,6 @@
74 void AccountsServicePlugin::registerTypes(const char *uri)
75 {
76 Q_ASSERT(uri == QLatin1String("AccountsService"));
77+ qDBusRegisterMetaType<QList<QVariantMap>>();
78 qmlRegisterSingletonType<AccountsService>(uri, 0, 1, "AccountsService", service_provider);
79 }
80
81=== modified file 'plugins/Unity/Launcher/CMakeLists.txt'
82--- plugins/Unity/Launcher/CMakeLists.txt 2013-07-16 08:32:38 +0000
83+++ plugins/Unity/Launcher/CMakeLists.txt 2013-08-29 13:56:41 +0000
84@@ -1,8 +1,11 @@
85 include(FindPkgConfig)
86 pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=2)
87
88+add_definitions(-DSM_BUSNAME=systemBus)
89+
90 include_directories(
91 ${CMAKE_CURRENT_SOURCE_DIR}
92+ ${CMAKE_SOURCE_DIR}/plugins/AccountsService
93 )
94
95 set(QMLLAUNCHERPLUGIN_SRC
96@@ -12,6 +15,7 @@
97 quicklistmodel.cpp
98 common/quicklistentry.cpp
99 backend/launcherbackend.cpp
100+ ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsService.cpp
101 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
102 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
103 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/QuickListModelInterface.h
104@@ -21,7 +25,7 @@
105 ${QMLLAUNCHERPLUGIN_SRC}
106 )
107
108-qt5_use_modules(UnityLauncher-qml Qml)
109+qt5_use_modules(UnityLauncher-qml DBus Qml)
110
111 # export the qmldir and qmltypes files
112 export_qmlfiles(Unity.Launcher Unity/Launcher)
113
114=== modified file 'plugins/Unity/Launcher/backend/launcherbackend.cpp'
115--- plugins/Unity/Launcher/backend/launcherbackend.cpp 2013-08-26 18:49:18 +0000
116+++ plugins/Unity/Launcher/backend/launcherbackend.cpp 2013-08-29 13:56:41 +0000
117@@ -2,6 +2,7 @@
118 * Copyright (C) 2013 Canonical, Ltd.
119 *
120 * Authors:
121+ * Michael Terry <michael.terry@canonical.com>
122 * Michael Zanetti <michael.zanetti@canonical.com>
123 *
124 * This program is free software; you can redistribute it and/or modify
125@@ -17,104 +18,111 @@
126 * along with this program. If not, see <http://www.gnu.org/licenses/>.
127 */
128
129+#include "AccountsService.h"
130 #include "launcherbackend.h"
131
132-#include <QHash>
133+#include <QDir>
134+#include <QDBusArgument>
135+#include <QFileInfo>
136
137-LauncherBackend::LauncherBackend(QObject *parent):
138- QObject(parent)
139+LauncherBackend::LauncherBackend(bool useStorage, QObject *parent):
140+ QObject(parent),
141+ m_accounts(nullptr)
142 {
143-
144- // TODO: load default pinned ones from default config, instead of hardcoding here...
145-
146- m_storedApps <<
147- QLatin1String("dialer-app.desktop") <<
148- QLatin1String("messaging-app.desktop") <<
149- QLatin1String("address-book-app.desktop") <<
150- QLatin1String("camera-app.desktop") <<
151- QLatin1String("gallery-app.desktop") <<
152- QLatin1String("facebook-webapp.desktop") <<
153- QLatin1String("webbrowser-app.desktop") <<
154- QLatin1String("twitter-webapp.desktop") <<
155- QLatin1String("gmail-webapp.desktop") <<
156- QLatin1String("ubuntu-weather-app.desktop") <<
157- QLatin1String("notes-app.desktop") <<
158- QLatin1String("calendar-app.desktop");
159-
160- // TODO: get stuff from desktop files instead this hardcoded map
161- m_displayNameMap.insert("dialer-app.desktop", "Dialer");
162- m_displayNameMap.insert("messaging-app.desktop", "Messaging");
163- m_displayNameMap.insert("address-book-app.desktop", "Contacts");
164- m_displayNameMap.insert("camera-app.desktop", "Camera");
165- m_displayNameMap.insert("gallery-app.desktop", "Gallery");
166- m_displayNameMap.insert("facebook-webapp.desktop", "Facebook");
167- m_displayNameMap.insert("webbrowser-app.desktop", "Browser");
168- m_displayNameMap.insert("twitter-webapp.desktop", "Twitter");
169- m_displayNameMap.insert("gmail-webapp.desktop", "GMail");
170- m_displayNameMap.insert("ubuntu-weather-app.desktop", "Weather");
171- m_displayNameMap.insert("notes-app.desktop", "Notes");
172- m_displayNameMap.insert("calendar-app.desktop", "Calendar");
173-
174- // TODO: get stuff from desktop files instead this hardcoded map
175- m_iconMap.insert("dialer-app.desktop", "phone-app");
176- m_iconMap.insert("messaging-app.desktop", "messages-app");
177- m_iconMap.insert("address-book-app.desktop", "contacts-app");
178- m_iconMap.insert("camera-app.desktop", "camera");
179- m_iconMap.insert("gallery-app.desktop", "gallery");
180- m_iconMap.insert("facebook-webapp.desktop", "facebook");
181- m_iconMap.insert("webbrowser-app.desktop", "browser");
182- m_iconMap.insert("twitter-webapp.desktop", "twitter");
183- m_iconMap.insert("gmail-webapp.desktop", "gmail");
184- m_iconMap.insert("ubuntu-weather-app.desktop", "weather");
185- m_iconMap.insert("notes-app.desktop", "notepad");
186- m_iconMap.insert("calendar-app.desktop", "calendar");
187-
188+ if (useStorage) {
189+ m_accounts = new AccountsService(this);
190+ }
191+ setUser(qgetenv("USER"));
192 }
193
194 LauncherBackend::~LauncherBackend()
195 {
196+ clearItems();
197+}
198
199+void LauncherBackend::clearItems()
200+{
201+ for (LauncherBackendItem &app: m_storedApps) {
202+ delete app.settings;
203+ }
204+ m_storedApps.clear();
205 }
206
207 QStringList LauncherBackend::storedApplications() const
208 {
209- return m_storedApps;
210+ auto files = QStringList();
211+ for (const LauncherBackendItem &app: m_storedApps) {
212+ files << app.settings->fileName();
213+ }
214+ return files;
215 }
216
217 void LauncherBackend::setStoredApplications(const QStringList &appIds)
218 {
219- m_storedApps = appIds;
220- // TODO: Cleanup pinned state from settings for apps not in list any more.
221+ // Record all existing pinned apps, so we can notice them in new list
222+ auto pinnedItems = QStringList();
223+ for (LauncherBackendItem &app: m_storedApps) {
224+ if (app.pinned) {
225+ pinnedItems.append(app.settings->fileName());
226+ }
227+ }
228+
229+ clearItems();
230+
231+ for (const QString &appId: appIds) {
232+ loadApp(makeAppDetails(appId, pinnedItems.contains(desktopFile(appId))));
233+ }
234+
235+ syncToAccounts();
236 }
237
238 QString LauncherBackend::desktopFile(const QString &appId) const
239 {
240- // TODO: return real path instead of this hardcoded one
241- return QLatin1String("/usr/share/applications/") + appId;
242+ QFileInfo fileInfo(QDir("/usr/share/applications"), appId);
243+ return fileInfo.absoluteFilePath();
244 }
245
246 QString LauncherBackend::displayName(const QString &appId) const
247 {
248- return m_displayNameMap.value(appId);
249+ auto desktopFile = parseDesktopFile(appId);
250+ auto displayName = desktopFile->value("Desktop Entry/Name").toString();
251+ delete desktopFile;
252+ return displayName;
253 }
254
255 QString LauncherBackend::icon(const QString &appId) const
256 {
257- return m_iconMap.value(appId);
258+ auto desktopFile = parseDesktopFile(appId);
259+ auto iconName = desktopFile->value("Desktop Entry/Icon").toString();
260+ delete desktopFile;
261+
262+ if (!iconName.isEmpty()) {
263+ QFileInfo iconFileInfo(iconName);
264+ if (iconFileInfo.isRelative()) {
265+ iconName = "image://gicon/" + iconName;
266+ }
267+ }
268+
269+ return iconName;
270 }
271
272 bool LauncherBackend::isPinned(const QString &appId) const
273 {
274- // TODO: return app's pinned state from settings
275- Q_UNUSED(appId)
276- return false;
277+ auto index = findItem(appId);
278+ if (index < 0) {
279+ return false;
280+ } else {
281+ return m_storedApps[index].pinned;
282+ }
283 }
284
285 void LauncherBackend::setPinned(const QString &appId, bool pinned)
286 {
287- // TODO: Store pinned state in settings.
288- Q_UNUSED(appId)
289- Q_UNUSED(pinned)
290+ auto index = findItem(appId);
291+ if (index >= 0 && !m_storedApps[index].pinned) {
292+ m_storedApps[index].pinned = pinned;
293+ syncToAccounts();
294+ }
295 }
296
297 QList<QuickListEntry> LauncherBackend::quickList(const QString &appId) const
298@@ -143,9 +151,111 @@
299 return 0;
300 }
301
302+void LauncherBackend::setUser(const QString &username)
303+{
304+ m_user = username;
305+ syncFromAccounts();
306+}
307+
308 void LauncherBackend::triggerQuickListAction(const QString &appId, const QString &quickListId)
309 {
310 // TODO: execute the given quicklist action
311 Q_UNUSED(appId)
312 Q_UNUSED(quickListId)
313 }
314+
315+void LauncherBackend::syncFromAccounts()
316+{
317+ QList<QVariantMap> apps;
318+ bool defaults = true;
319+
320+ clearItems();
321+
322+ if (m_user != "" && m_accounts != nullptr) {
323+ auto variant = m_accounts->getUserProperty(m_user, "launcher-items");
324+ apps = qdbus_cast<QList<QVariantMap>>(variant.value<QDBusArgument>());
325+ defaults = isDefaultsItem(apps);
326+ }
327+
328+ // TODO: load default pinned ones from default config, instead of hardcoding here...
329+ if (defaults) {
330+ apps <<
331+ makeAppDetails("dialer-app.desktop", true) <<
332+ makeAppDetails("messaging-app.desktop", true) <<
333+ makeAppDetails("address-book-app.desktop", true) <<
334+ makeAppDetails("camera-app.desktop", true) <<
335+ makeAppDetails("gallery-app.desktop", true) <<
336+ makeAppDetails("facebook-webapp.desktop", true) <<
337+ makeAppDetails("webbrowser-app.desktop", true) <<
338+ makeAppDetails("twitter-webapp.desktop", true) <<
339+ makeAppDetails("gmail-webapp.desktop", true) <<
340+ makeAppDetails("ubuntu-weather-app.desktop", true) <<
341+ makeAppDetails("notes-app.desktop", true) <<
342+ makeAppDetails("calendar-app.desktop", true);
343+ }
344+
345+ for (const QVariant &app: apps) {
346+ loadApp(app.toMap());
347+ }
348+}
349+
350+void LauncherBackend::syncToAccounts()
351+{
352+ if (m_user != "" && m_accounts != nullptr) {
353+ QList<QVariantMap> items;
354+
355+ for (LauncherBackendItem &app: m_storedApps) {
356+ items << makeAppDetails(app.settings->fileName(), app.pinned);
357+ }
358+
359+ m_accounts->setUserProperty(m_user, "launcher-items", QVariant::fromValue(items));
360+ }
361+}
362+
363+QSettings *LauncherBackend::parseDesktopFile(const QString &appId) const
364+{
365+ auto fullAppId = desktopFile(appId);
366+ return new QSettings(fullAppId, QSettings::IniFormat);
367+}
368+
369+void LauncherBackend::loadApp(const QVariantMap &details)
370+{
371+ auto appId = details.value("id").toString();
372+ auto isPinned = details.value("is-pinned").toBool();
373+
374+ if (appId.isEmpty()) {
375+ return;
376+ }
377+
378+ LauncherBackendItem item;
379+ item.settings = parseDesktopFile(appId);
380+ item.pinned = isPinned;
381+ m_storedApps.append(item);
382+}
383+
384+QVariantMap LauncherBackend::makeAppDetails(const QString &appId, bool pinned) const
385+{
386+ QVariantMap details;
387+ details.insert("id", appId);
388+ details.insert("is-pinned", pinned);
389+ return details;
390+}
391+
392+bool LauncherBackend::isDefaultsItem(const QList<QVariantMap> &apps) const
393+{
394+ // To differentiate between an empty list and a list that hasn't been set
395+ // yet (and should thus be populated with the defaults), we use a special
396+ // list of one item with the 'defaults' field set to true.
397+ return (apps.size() == 1 && apps[0].value("defaults").toBool());
398+}
399+
400+int LauncherBackend::findItem(const QString &appId) const
401+{
402+ auto fullAppId = desktopFile(appId);
403+ for (int i = 0; i < m_storedApps.size(); ++i) {
404+ if (m_storedApps[i].settings->fileName() == fullAppId) {
405+ return i;
406+ }
407+ }
408+ return -1;
409+}
410
411=== modified file 'plugins/Unity/Launcher/backend/launcherbackend.h'
412--- plugins/Unity/Launcher/backend/launcherbackend.h 2013-07-09 20:33:13 +0000
413+++ plugins/Unity/Launcher/backend/launcherbackend.h 2013-08-29 13:56:41 +0000
414@@ -22,8 +22,10 @@
415 #include "common/quicklistentry.h"
416
417 #include <QObject>
418+#include <QSettings>
419 #include <QStringList>
420-#include <QHash>
421+
422+class AccountsService;
423
424 /**
425 * @brief An interface that provides all the data needed by the launcher.
426@@ -35,7 +37,7 @@
427
428
429 public:
430- LauncherBackend(QObject *parent = 0);
431+ LauncherBackend(bool useStorage = true, QObject *parent = 0);
432 virtual ~LauncherBackend();
433
434 /**
435@@ -57,12 +59,18 @@
436
437 /**
438 * @brief Get the full path to the .desktop file.
439+ *
440+ * The application does not need to be in the list of stored applications.
441+ *
442 * @returns The full path to the .dekstop file.
443 */
444 QString desktopFile(const QString &appId) const;
445
446 /**
447 * @brief Get the user friendly name of an application.
448+ *
449+ * The application does not need to be in the list of stored applications.
450+ *
451 * @param appId The ID of the application.
452 * @returns The user friendly name of the application.
453 */
454@@ -70,6 +78,9 @@
455
456 /**
457 * @brief Get the icon of an application.
458+ *
459+ * The application does not need to be in the list of stored applications.
460+ *
461 * @param appId The ID of the application.
462 * @returns The full path to the icon for the application.
463 */
464@@ -118,15 +129,37 @@
465 */
466 int count(const QString &appId) const;
467
468+ /**
469+ * @brief Sets the username for which to look up launcher items.
470+ * @param username The username to use.
471+ */
472+ void setUser(const QString &username);
473+
474 Q_SIGNALS:
475 void quickListChanged(const QString &appId, const QList<QuickListEntry> &quickList);
476 void progressChanged(const QString &appId, int progress);
477 void countChanged(const QString &appId, int count);
478
479 private:
480- QStringList m_storedApps;
481- QHash<QString, QString> m_displayNameMap;
482- QHash<QString, QString> m_iconMap;
483+ QSettings *parseDesktopFile(const QString &appId) const;
484+ QVariantMap makeAppDetails(const QString &appId, bool pinned) const;
485+ void loadApp(const QVariantMap &details);
486+ int findItem(const QString &appId) const;
487+ bool isDefaultsItem(const QList<QVariantMap> &apps) const;
488+ void syncFromAccounts();
489+ void syncToAccounts();
490+ void clearItems();
491+
492+ class LauncherBackendItem
493+ {
494+ public:
495+ QSettings *settings;
496+ bool pinned;
497+ };
498+
499+ QList<LauncherBackendItem> m_storedApps;
500+ AccountsService *m_accounts;
501+ QString m_user;
502 };
503
504 #endif // LAUNCHERBACKEND_H
505
506=== modified file 'plugins/Unity/Launcher/launchermodel.cpp'
507--- plugins/Unity/Launcher/launchermodel.cpp 2013-08-23 10:25:00 +0000
508+++ plugins/Unity/Launcher/launchermodel.cpp 2013-08-29 13:56:41 +0000
509@@ -23,7 +23,7 @@
510
511 LauncherModel::LauncherModel(QObject *parent):
512 LauncherModelInterface(parent),
513- m_backend(new LauncherBackend(this))
514+ m_backend(new LauncherBackend(true, this))
515 {
516 connect(m_backend, SIGNAL(countChanged(QString,int)), SLOT(countChanged(QString,int)));
517 connect(m_backend, SIGNAL(progressChanged(QString,int)), SLOT(progressChanged(QString,int)));
518@@ -123,6 +123,7 @@
519 if (currentIndex >= 0) {
520 if (index == -1 || index == currentIndex) {
521 m_list.at(currentIndex)->setPinned(true);
522+ m_backend->setPinned(appId, true);
523 QModelIndex modelIndex = this->index(currentIndex);
524 Q_EMIT dataChanged(modelIndex, modelIndex);
525 } else {
526@@ -138,6 +139,7 @@
527 m_backend->displayName(appId),
528 m_backend->icon(appId));
529 item->setPinned(true);
530+ m_backend->setPinned(appId, true);
531 m_list.insert(index, item);
532 endInsertRows();
533 }
534@@ -186,8 +188,7 @@
535
536 void LauncherModel::setUser(const QString &username)
537 {
538- Q_UNUSED(username)
539- // TODO: Implement this...
540+ m_backend->setUser(username);
541 }
542
543 void LauncherModel::storeAppList()
544
545=== modified file 'plugins/Unity/Launcher/launchermodel.h'
546--- plugins/Unity/Launcher/launchermodel.h 2013-08-23 10:25:00 +0000
547+++ plugins/Unity/Launcher/launchermodel.h 2013-08-29 13:56:41 +0000
548@@ -17,7 +17,7 @@
549 * Michael Zanetti <michael.zanetti@canonical.com>
550 */
551
552-#ifndef LAUNCHERMODELH
553+#ifndef LAUNCHERMODEL_H
554 #define LAUNCHERMODEL_H
555
556 // unity-api
557
558=== modified file 'tests/mocks/Unity/Launcher/CMakeLists.txt'
559--- tests/mocks/Unity/Launcher/CMakeLists.txt 2013-07-10 17:13:28 +0000
560+++ tests/mocks/Unity/Launcher/CMakeLists.txt 2013-08-29 13:56:41 +0000
561@@ -1,6 +1,8 @@
562 include(FindPkgConfig)
563 pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=2)
564
565+add_definitions(-DTOP_SRCDIR="${CMAKE_SOURCE_DIR}")
566+
567 include_directories(
568 ${CMAKE_CURRENT_SOURCE_DIR}
569 )
570
571=== modified file 'tests/mocks/Unity/Launcher/MockLauncherItem.cpp'
572--- tests/mocks/Unity/Launcher/MockLauncherItem.cpp 2013-08-20 17:39:58 +0000
573+++ tests/mocks/Unity/Launcher/MockLauncherItem.cpp 2013-08-29 13:56:41 +0000
574@@ -27,7 +27,7 @@
575 m_appId(appId),
576 m_desktopFile(desktopFile),
577 m_name(name),
578- m_icon(icon),
579+ m_icon(TOP_SRCDIR "/graphics/applicationIcons/" + icon + ".png"),
580 m_pinned(false),
581 m_running(false),
582 m_recent(false),
583
584=== modified file 'tests/plugins/Unity/CMakeLists.txt'
585--- tests/plugins/Unity/CMakeLists.txt 2013-07-12 08:37:48 +0000
586+++ tests/plugins/Unity/CMakeLists.txt 2013-08-29 13:56:41 +0000
587@@ -1,4 +1,5 @@
588 add_subdirectory(Indicators)
589+add_subdirectory(Launcher)
590
591 pkg_check_modules(UNITYCORE REQUIRED unity-core-6.0)
592 pkg_check_modules(LIBUNITYPROTO REQUIRED unity-protocol-private)
593
594=== added directory 'tests/plugins/Unity/Launcher'
595=== added file 'tests/plugins/Unity/Launcher/CMakeLists.txt'
596--- tests/plugins/Unity/Launcher/CMakeLists.txt 1970-01-01 00:00:00 +0000
597+++ tests/plugins/Unity/Launcher/CMakeLists.txt 2013-08-29 13:56:41 +0000
598@@ -0,0 +1,22 @@
599+include_directories(
600+ ${CMAKE_CURRENT_BINARY_DIR}
601+ ${CMAKE_SOURCE_DIR}/plugins/AccountsService
602+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher
603+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/backend
604+ )
605+
606+add_definitions(-DSM_BUSNAME=sessionBus)
607+add_definitions(-DSRCDIR="${CMAKE_CURRENT_SOURCE_DIR}")
608+
609+set(testCommand ${CMAKE_CURRENT_BINARY_DIR}/launcherbackendtestExec
610+ -o ${CMAKE_BINARY_DIR}/launcherbackendtest.xml,xunitxml
611+ -o -,txt)
612+add_test(NAME launcherbackendtest COMMAND ${testCommand})
613+add_custom_target(launcherbackendtest ${testCommand})
614+add_executable(launcherbackendtestExec
615+ launcherbackendtest.cpp
616+ ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsService.cpp
617+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/backend/launcherbackend.cpp
618+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/common/quicklistentry.cpp
619+ )
620+qt5_use_modules(launcherbackendtestExec Test Core DBus)
621
622=== added file 'tests/plugins/Unity/Launcher/abs-icon.desktop'
623--- tests/plugins/Unity/Launcher/abs-icon.desktop 1970-01-01 00:00:00 +0000
624+++ tests/plugins/Unity/Launcher/abs-icon.desktop 2013-08-29 13:56:41 +0000
625@@ -0,0 +1,3 @@
626+[Desktop Entry]
627+Name=Absolute Icon
628+Icon=/path/to/icon.png
629
630=== added file 'tests/plugins/Unity/Launcher/launcherbackendtest.cpp'
631--- tests/plugins/Unity/Launcher/launcherbackendtest.cpp 1970-01-01 00:00:00 +0000
632+++ tests/plugins/Unity/Launcher/launcherbackendtest.cpp 2013-08-29 13:56:41 +0000
633@@ -0,0 +1,94 @@
634+/*
635+ * Copyright 2013 Canonical Ltd.
636+ *
637+ * This program is free software; you can redistribute it and/or modify
638+ * it under the terms of the GNU Lesser General Public License as published by
639+ * the Free Software Foundation; version 3.
640+ *
641+ * This program is distributed in the hope that it will be useful,
642+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
643+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
644+ * GNU Lesser General Public License for more details.
645+ *
646+ * You should have received a copy of the GNU Lesser General Public License
647+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
648+ *
649+ * Authors:
650+ * Michael Terry <michael.terry@canonical.com>
651+ */
652+
653+#include "launcherbackend.h"
654+
655+#include <QtTest>
656+#include <QDebug>
657+
658+class LauncherBackendTest : public QObject
659+{
660+ Q_OBJECT
661+
662+private Q_SLOTS:
663+ void testFileNames()
664+ {
665+ LauncherBackend backend(false);
666+
667+ backend.setStoredApplications(QStringList() << "relative.path" << "/full/path");
668+ QCOMPARE(backend.storedApplications(), QStringList() << "/usr/share/applications/relative.path" << "/full/path");
669+
670+ QCOMPARE(backend.desktopFile("one.desktop"), QString("/usr/share/applications/one.desktop"));
671+ QCOMPARE(backend.desktopFile("two"), QString("/usr/share/applications/two"));
672+ QCOMPARE(backend.desktopFile("/full"), QString("/full"));
673+
674+ QCOMPARE(backend.desktopFile("/usr/share/applications/one.desktop"), QString("/usr/share/applications/one.desktop"));
675+ }
676+
677+ void testDesktopReading()
678+ {
679+ LauncherBackend backend(false);
680+
681+ QCOMPARE(backend.displayName(SRCDIR "/rel-icon.desktop"), QString("Relative Icon"));
682+ QCOMPARE(backend.icon(SRCDIR "/rel-icon.desktop"), QString("image://gicon/rel-icon"));
683+
684+ QCOMPARE(backend.displayName(SRCDIR "/abs-icon.desktop"), QString("Absolute Icon"));
685+ QCOMPARE(backend.icon(SRCDIR "/abs-icon.desktop"), QString("/path/to/icon.png"));
686+
687+ QCOMPARE(backend.displayName(SRCDIR "/no-icon.desktop"), QString("No Icon"));
688+ QCOMPARE(backend.icon(SRCDIR "/no-icon.desktop"), QString(""));
689+
690+ QCOMPARE(backend.displayName(SRCDIR "/no-name.desktop"), QString(""));
691+ QCOMPARE(backend.icon(SRCDIR "/no-name.desktop"), QString("image://gicon/no-name"));
692+
693+ QCOMPARE(backend.displayName(SRCDIR "/no-exist.desktop"), QString(""));
694+ QCOMPARE(backend.icon(SRCDIR "/no-exist.desktop"), QString(""));
695+ }
696+
697+ void testPinning()
698+ {
699+ LauncherBackend backend(false);
700+
701+ // Confirm that default entries are all pinned
702+ auto defaultApps = backend.storedApplications();
703+ QVERIFY(defaultApps.size() > 0);
704+ for (auto app: defaultApps) {
705+ QCOMPARE(backend.isPinned(app), true);
706+ }
707+
708+ backend.setStoredApplications(QStringList() << "one" << "two");
709+ QCOMPARE(backend.isPinned("one"), false);
710+ QCOMPARE(backend.isPinned("two"), false);
711+
712+ backend.setPinned("two", true);
713+ QCOMPARE(backend.isPinned("two"), true);
714+
715+ backend.setStoredApplications(QStringList() << "one" << "two" << "three");
716+ QCOMPARE(backend.isPinned("two"), true);
717+ QCOMPARE(backend.isPinned("three"), false);
718+
719+ backend.setPinned("three", true);
720+ backend.setStoredApplications(QStringList() << "one" << "two");
721+ QCOMPARE(backend.isPinned("two"), true);
722+ QCOMPARE(backend.isPinned("three"), false); // doesn't exist anymore!
723+ }
724+};
725+
726+QTEST_GUILESS_MAIN(LauncherBackendTest)
727+#include "launcherbackendtest.moc"
728
729=== added file 'tests/plugins/Unity/Launcher/no-icon.desktop'
730--- tests/plugins/Unity/Launcher/no-icon.desktop 1970-01-01 00:00:00 +0000
731+++ tests/plugins/Unity/Launcher/no-icon.desktop 2013-08-29 13:56:41 +0000
732@@ -0,0 +1,2 @@
733+[Desktop Entry]
734+Name=No Icon
735
736=== added file 'tests/plugins/Unity/Launcher/no-name.desktop'
737--- tests/plugins/Unity/Launcher/no-name.desktop 1970-01-01 00:00:00 +0000
738+++ tests/plugins/Unity/Launcher/no-name.desktop 2013-08-29 13:56:41 +0000
739@@ -0,0 +1,2 @@
740+[Desktop Entry]
741+Icon=no-name
742
743=== added file 'tests/plugins/Unity/Launcher/rel-icon.desktop'
744--- tests/plugins/Unity/Launcher/rel-icon.desktop 1970-01-01 00:00:00 +0000
745+++ tests/plugins/Unity/Launcher/rel-icon.desktop 2013-08-29 13:56:41 +0000
746@@ -0,0 +1,3 @@
747+[Desktop Entry]
748+Name=Relative Icon
749+Icon=rel-icon

Subscribers

People subscribed via source and target branches