Merge lp:~mterry/unity8/launcher-items into lp:unity8
- launcher-items
- Merge into trunk
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 |
Related bugs: |
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
PS Jenkins bot (ps-jenkins) wrote : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:216
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Michael Zanetti (mzanetti) wrote : | # |
172 + for (int i = 0; i < m_storedApps.
173 + delete m_storedApps[
174 + }
Just a hint (not really necessary to change)
Q_FOREACH(const LauncherBackendItem &item, m_storedApps) {
delete item.settings;
}
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_storedApplica
===
338 + // Mark all apps as pinned (since we just refreshed the set)
339 + for (int i = 0; i < m_storedApps.
340 + m_storedApps[
341 + }
344 +void LauncherBackend
345 +{
346 + auto pinnedItems = QStringList();
347 + for (int i = 0; i < m_storedApps.
348 + if (m_storedApps[
349 + pinnedItems.
350 + }
351 + }
352 +
353 + if (m_user != "" && m_accounts != nullptr) {
354 + m_accounts-
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.
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)
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;
Michael Zanetti (mzanetti) wrote : | # |
and there is an API change in LauncherModel, which means we need to update lp:unity-api and its tests.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:216
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
SUCCESS: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:221
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Omer Akram (om26er) wrote : | # |
There is a conflict now. please merge trunk.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:222
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
FAILURE: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:222
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
FAILURE: http://
UNSTABLE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:223
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Michael Zanetti (mzanetti) wrote : | # |
file://
This causes some weird effect at startup that the width somehow is broken and everything is displaced on the Nexus 4.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:224
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Michael Terry (mterry) wrote : | # |
Fixed the user warning.
Michael Zanetti (mzanetti) : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Albert Astals Cid (aacid) wrote : | # |
plugins/
Albert Astals Cid (aacid) wrote : | # |
Reading
234 - // TODO: return real path instead of this hardcoded one
235 - return QLatin1String(
236 + QFileInfo fileInfo(
237 + return fileInfo.
maybe we want canonicalFilePath in there instead of absolute?
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.
Albert Astals Cid (aacid) wrote : | # |
Ok, still
236 + QFileInfo fileInfo(
237 + return fileInfo.
looks like a really slow way of doing
return "/usr/share/
no?
- 225. By Michael Terry
-
Merge from trunk
Michael Terry (mterry) wrote : | # |
> return "/usr/share/
That won't gracefully handle a full-path appId, which the current code allows.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:225
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Michael Zanetti (mzanetti) : | # |
Preview Diff
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 |
FAILED: Continuous integration, rev:216 jenkins. qa.ubuntu. com/job/ unity8- ci/693/ jenkins. qa.ubuntu. com/job/ generic- mediumtests- saucy/2414 jenkins. qa.ubuntu. com/job/ unity-phablet- qmluitests- saucy/1184 jenkins. qa.ubuntu. com/job/ unity8- saucy-armhf- ci/694 jenkins. qa.ubuntu. com/job/ unity8- saucy-armhf- ci/694/ artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ unity8- saucy-i386- ci/693 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- saucy/2420 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- saucy/2420/ artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ generic- mediumtests- runner- saucy/2049
http://
Executed test runs:
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins: 8080/job/ unity8- ci/693/ rebuild
http://