21 #include "AccountsServiceDBusAdaptor.h"
22 #include "launcherbackend.h"
25 #include <QDBusArgument>
29 #include <QStandardPaths>
31 class LauncherBackendItem
41 LauncherBackend::LauncherBackend(QObject *parent):
42 QDBusVirtualObject(parent),
45 #ifndef LAUNCHER_TESTING
46 m_accounts =
new AccountsServiceDBusAdaptor(
this);
48 m_user = qgetenv(
"USER");
52 QDBusConnection con = QDBusConnection::sessionBus();
53 if (!con.registerService(
"com.canonical.Unity.Launcher")) {
54 qDebug() <<
"Unable to register launcher name";
56 if (!con.registerVirtualObject(
"/com/canonical/Unity/Launcher",
this, QDBusConnection::VirtualObjectRegisterOption::SubPath)) {
57 qDebug() <<
"Unable to register launcher object";
61 LauncherBackend::~LauncherBackend()
64 QDBusConnection con = QDBusConnection::sessionBus();
65 con.unregisterService(
"com.canonical.Unity.Launcher");
66 con.unregisterObject(
"/com/canonical/Unity/Launcher");
71 Q_FOREACH(LauncherBackendItem *item, m_itemCache) {
77 QStringList LauncherBackend::storedApplications()
const
82 void LauncherBackend::setStoredApplications(
const QStringList &appIds)
84 if (appIds.count() < m_storedApps.count()) {
85 Q_FOREACH(
const QString &appId, m_storedApps) {
86 if (!appIds.contains(appId)) {
87 delete m_itemCache.take(appId);
91 m_storedApps = appIds;
92 Q_FOREACH(
const QString &appId, appIds) {
93 if (!m_itemCache.contains(appId)) {
94 QString df = findDesktopFile(appId);
96 LauncherBackendItem *item = parseDesktopFile(df);
97 m_itemCache.insert(appId, item);
100 qWarning() <<
"cannot find desktop file for" << appId <<
". discarding app.";
101 m_storedApps.removeAll(appId);
108 QString LauncherBackend::desktopFile(
const QString &appId)
const
110 LauncherBackendItem *item = m_itemCache.value(appId,
nullptr);
112 return item->desktopFile;
115 return findDesktopFile(appId);
118 QString LauncherBackend::displayName(
const QString &appId)
const
120 LauncherBackendItem *item = m_itemCache.value(appId,
nullptr);
122 return item->displayName;
125 QString df = findDesktopFile(appId);
127 LauncherBackendItem *item = parseDesktopFile(df);
128 m_itemCache.insert(appId, item);
129 return item->displayName;
135 QString LauncherBackend::icon(
const QString &appId)
const
138 LauncherBackendItem *item = getItem(appId);
140 iconName = item->icon;
146 QList<QuickListEntry> LauncherBackend::quickList(
const QString &appId)
const
153 return QList<QuickListEntry>();
156 int LauncherBackend::progress(const QString &appId)
const
164 int LauncherBackend::count(const QString &appId)
const
167 LauncherBackendItem *item = getItem(appId);
170 if (item->countVisible) {
178 void LauncherBackend::setCount(
const QString &appId,
int count)
const
180 LauncherBackendItem *item = getItem(appId);
182 bool emitchange =
false;
184 emitchange = (item->count != count);
192 Q_EMIT countChanged(appId, this->count(appId));
193 QVariant vcount(item->count);
194 emitPropChangedDbus(appId,
"count", vcount);
198 bool LauncherBackend::countVisible(
const QString &appId)
const
200 bool visible =
false;
201 LauncherBackendItem *item = getItem(appId);
204 visible = item->countVisible;
210 void LauncherBackend::setCountVisible(
const QString &appId,
bool visible)
const
212 LauncherBackendItem *item = getItem(appId);
214 bool emitchange =
false;
216 emitchange = (item->countVisible != visible);
217 item->countVisible = visible;
219 qDebug() <<
"Unable to find:" << appId;
225 Q_EMIT countChanged(appId, this->count(appId));
226 Q_EMIT countVisibleChanged(appId, item->countVisible);
227 QVariant vCountVisible(item->countVisible);
228 emitPropChangedDbus(appId,
"countVisible", vCountVisible);
232 void LauncherBackend::setUser(
const QString &username)
234 if (qgetenv(
"USER") ==
"lightdm" && m_user != username) {
240 void LauncherBackend::triggerQuickListAction(
const QString &appId,
const QString &quickListId)
244 Q_UNUSED(quickListId)
247 void LauncherBackend::syncFromAccounts()
249 QList<QVariantMap> apps;
250 bool defaults =
true;
252 m_storedApps.clear();
254 if (m_accounts && !m_user.isEmpty()) {
255 QVariant variant = m_accounts->getUserProperty(m_user,
"com.canonical.unity.AccountsService",
"launcher-items");
256 if (variant.isValid() && variant.canConvert<QDBusArgument>()) {
257 apps = qdbus_cast<QList<QVariantMap>>(variant.value<QDBusArgument>());
258 defaults = isDefaultsItem(apps);
262 if (m_accounts && defaults) {
263 QGSettings gSettings(
"com.canonical.Unity.Launcher",
"/com/canonical/unity/launcher/");
264 Q_FOREACH(
const QString &entry, gSettings.get(
"favorites").toStringList()) {
265 if (entry.startsWith(
"application://")) {
266 QString appId = entry;
268 appId.remove(
"application://");
269 if (appId.endsWith(
".desktop")) {
272 QString df = findDesktopFile(appId);
275 m_storedApps << appId;
277 if (!m_itemCache.contains(appId)) {
278 m_itemCache.insert(appId, parseDesktopFile(df));
284 for (
const QVariant &app: apps) {
285 loadFromVariant(app.toMap());
290 void LauncherBackend::syncToAccounts()
292 if (m_accounts && !m_user.isEmpty()) {
293 QList<QVariantMap> items;
295 Q_FOREACH(
const QString &appId, m_storedApps) {
296 items << itemToVariant(appId);
299 m_accounts->setUserProperty(m_user,
"com.canonical.unity.AccountsService",
"launcher-items", QVariant::fromValue(items));
303 QString LauncherBackend::findDesktopFile(
const QString &appId)
const
306 QString helper = appId;
308 QStringList searchDirs = QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation);
309 #ifdef LAUNCHER_TESTING
315 helper = helper.replace(dashPos, 1,
'/');
318 Q_FOREACH(
const QString &searchDir, searchDirs) {
319 QFileInfo fileInfo(QDir(searchDir), helper +
".desktop");
320 if (fileInfo.exists()) {
321 return fileInfo.absoluteFilePath();
325 dashPos = helper.indexOf(
"-");
326 }
while (dashPos != -1);
331 LauncherBackendItem* LauncherBackend::parseDesktopFile(
const QString &desktopFile)
const
333 QSettings settings(desktopFile, QSettings::IniFormat);
335 LauncherBackendItem* item =
new LauncherBackendItem();
336 item->desktopFile = desktopFile;
337 item->displayName = settings.value(
"Desktop Entry/Name").toString();
339 QString iconString = settings.value(
"Desktop Entry/Icon").toString();
340 QString pathString = settings.value(
"Desktop Entry/Path").toString();
341 if (QFileInfo(iconString).exists()) {
342 item->icon = QFileInfo(iconString).absoluteFilePath();
343 }
else if (QFileInfo(pathString +
'/' + iconString).exists()) {
344 item->icon = pathString +
'/' + iconString;
346 item->icon =
"image://theme/" + iconString;
351 item->countVisible =
false;
357 LauncherBackendItem* LauncherBackend::getItem(
const QString &appId)
const
359 LauncherBackendItem *item = m_itemCache.value(appId,
nullptr);
361 QString df = findDesktopFile(appId);
363 item = parseDesktopFile(df);
365 m_itemCache[appId] = item;
367 qWarning() <<
"Unable to parse desktop file for" << appId <<
"path" << df;
370 qDebug() <<
"Unable to find desktop file for:" << appId;
375 qWarning() <<
"Unable to find item for: " << appId;
380 void LauncherBackend::loadFromVariant(
const QVariantMap &details)
382 if (!details.contains(
"id")) {
385 QString appId = details.value(
"id").toString();
387 LauncherBackendItem *item = m_itemCache.value(appId,
nullptr);
392 item =
new LauncherBackendItem();
394 item->desktopFile = details.value(
"desktopFile").toString();
395 item->displayName = details.value(
"name").toString();
396 item->icon = details.value(
"icon").toString();
397 item->count = details.value(
"count").toInt();
398 item->countVisible = details.value(
"countVisible").toBool();
400 m_itemCache.insert(appId, item);
401 m_storedApps.append(appId);
404 QVariantMap LauncherBackend::itemToVariant(
const QString &appId)
const
406 LauncherBackendItem *item = m_itemCache.value(appId);
408 details.insert(
"id", appId);
409 details.insert(
"name", item->displayName);
410 details.insert(
"icon", item->icon);
411 details.insert(
"desktopFile", item->desktopFile);
412 details.insert(
"count", item->count);
413 details.insert(
"countVisible", item->countVisible);
417 bool LauncherBackend::isDefaultsItem(
const QList<QVariantMap> &apps)
const
422 return (apps.size() == 1 && apps[0].value(
"defaults").toBool());
425 bool LauncherBackend::handleMessage(
const QDBusMessage& message,
const QDBusConnection& connection)
428 if (message.type() != QDBusMessage::MessageType::MethodCallMessage) {
431 if (message.interface() !=
"org.freedesktop.DBus.Properties") {
434 if (message.arguments()[0].toString() !=
"com.canonical.Unity.Launcher.Item") {
439 QString pathtemp = message.path();
440 if (!pathtemp.startsWith(
"/com/canonical/Unity/Launcher/")) {
443 pathtemp.remove(
"/com/canonical/Unity/Launcher/");
444 if (pathtemp.indexOf(
'/') >= 0) {
449 QString appid = decodeAppId(pathtemp);
452 if (message.member() ==
"Get") {
453 if (message.arguments()[1].toString() ==
"count") {
454 retval.append(QVariant::fromValue(QDBusVariant(this->count(appid))));
455 }
else if (message.arguments()[1].toString() ==
"countVisible") {
456 retval.append(QVariant::fromValue(QDBusVariant(this->countVisible(appid))));
458 }
else if (message.member() ==
"Set") {
459 if (message.arguments()[1].toString() ==
"count") {
460 this->setCount(appid, message.arguments()[2].value<QDBusVariant>().variant().toInt());
461 }
else if (message.arguments()[1].toString() ==
"countVisible") {
462 this->setCountVisible(appid, message.arguments()[2].value<QDBusVariant>().variant().toBool());
464 }
else if (message.member() ==
"GetAll") {
465 retval.append(this->itemToVariant(appid));
470 QDBusMessage reply = message.createReply(retval);
471 return connection.send(reply);
474 QString LauncherBackend::introspect(
const QString &path)
const
477 if (path ==
"/com/canonical/Unity/Launcher/" || path ==
"/com/canonical/Unity/Launcher") {
480 Q_FOREACH(
const QString &appId, m_itemCache.keys()) {
481 nodes.append(
"<node name=\"");
482 nodes.append(encodeAppId(appId));
483 nodes.append(
"\"/>\n");
490 if (!path.startsWith(
"/com/canonical/Unity/Launcher")) {
496 "<interface name=\"com.canonical.Unity.Launcher.Item\">"
497 "<property name=\"count\" type=\"i\" access=\"readwrite\" />"
498 "<property name=\"countVisible\" type=\"b\" access=\"readwrite\" />"
503 QString LauncherBackend::decodeAppId(
const QString& path)
505 QByteArray bytes = path.toUtf8();
508 for (
int i = 0; i < bytes.size(); ++i) {
509 char chr = bytes.at(i);
513 number.append(bytes.at(i+1));
514 number.append(bytes.at(i+2));
517 char newchar = number.toUInt(&okay, 16);
519 decoded.append(newchar);
527 return QString::fromUtf8(decoded);
530 QString LauncherBackend::encodeAppId(
const QString& appId)
532 QByteArray bytes = appId.toUtf8();
535 for (
int i = 0; i < bytes.size(); ++i) {
536 uchar chr = bytes.at(i);
538 if ((chr >=
'a' && chr <=
'z') ||
539 (chr >=
'A' && chr <=
'Z') ||
540 (chr >=
'0' && chr <=
'9'&& i != 0)) {
543 QString hexval = QString(
"_%1").arg(chr, 2, 16, QChar(
'0'));
544 encoded.append(hexval.toUpper());
551 void LauncherBackend::emitPropChangedDbus(
const QString& appId,
const QString& property, QVariant &value)
const
553 QString path(
"/com/canonical/Unity/Launcher/");
554 path.append(encodeAppId(appId));
556 QDBusMessage message = QDBusMessage::createSignal(path,
"org.freedesktop.DBus.Properties",
"PropertiesChanged");
558 QList<QVariant> arguments;
559 QVariantHash changedprops;
560 changedprops[property] = QVariant::fromValue(QDBusVariant(value));
561 QVariantList deletedprops;
563 arguments.append(changedprops);
564 arguments.append(deletedprops);
566 message.setArguments(arguments);
568 QDBusConnection con = QDBusConnection::sessionBus();