@@ -1,5 +1,6 @@
2 * Copyright (C) 2016 Canonical, Ltd.
2 * Copyright (C) 2016 Canonical, Ltd.
3+ * Copyright (C) 2020 UBports Foundation.
4 * This program is free software; you can redistribute it and/or modify
5 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * it under the terms of the GNU General Public License as published by
@@ -20,28 +21,31 @@
21#include <QDebug>
22#include <QDebug>
22#include <QDateTime>
23#include <QDateTime>
24+#include <QtConcurrentRun>
26+static std::shared_ptr<LauncherItem> makeSharedLauncherItem(
27+ const QString &appId, const QString &name, const QString &icon, QObject * parent)
29+ return std::shared_ptr<LauncherItem>(
30+ new LauncherItem(appId, name, icon, parent),
31+ [] (LauncherItem *item) { item->deleteLater(); });
24AppDrawerModel::AppDrawerModel(QObject *parent):
34AppDrawerModel::AppDrawerModel(QObject *parent):
25 AppDrawerModelInterface(parent),
35 AppDrawerModelInterface(parent),
26 m_ual(new UalWrapper(this)),
36 m_ual(new UalWrapper(this)),
27− m_xdgWatcher(new XdgWatcher(this))
37+ m_xdgWatcher(new XdgWatcher(this)),
29− Q_FOREACH (const QString &appId, UalWrapper::installedApps()) {
40+ connect(&m_refreshFutureWatcher, &QFutureWatcher<ItemList>::finished,
30− UalWrapper::AppInfo info = UalWrapper::getApplicationInfo(appId);
41+ this, &AppDrawerModel::onRefreshFinished);
32− qWarning() << "Failed to get app info for app" << appId;
35− LauncherItem* item = new LauncherItem(appId, info.name, info.icon, this);
36− item->setKeywords(info.keywords);
37− item->setPopularity(info.popularity);
41 // keep this a queued connection as it's coming from another thread.
43 // keep this a queued connection as it's coming from another thread.
42 connect(m_xdgWatcher, &XdgWatcher::appAdded, this, &AppDrawerModel::appAdded, Qt::QueuedConnection);
44 connect(m_xdgWatcher, &XdgWatcher::appAdded, this, &AppDrawerModel::appAdded, Qt::QueuedConnection);
43 connect(m_xdgWatcher, &XdgWatcher::appRemoved, this, &AppDrawerModel::appRemoved, Qt::QueuedConnection);
45 connect(m_xdgWatcher, &XdgWatcher::appRemoved, this, &AppDrawerModel::appRemoved, Qt::QueuedConnection);
44 connect(m_xdgWatcher, &XdgWatcher::appInfoChanged, this, &AppDrawerModel::appInfoChanged, Qt::QueuedConnection);
46 connect(m_xdgWatcher, &XdgWatcher::appInfoChanged, this, &AppDrawerModel::appInfoChanged, Qt::QueuedConnection);
47int AppDrawerModel::rowCount(const QModelIndex &parent) const
51int AppDrawerModel::rowCount(const QModelIndex &parent) const
@@ -70,6 +74,10 @@ QVariant AppDrawerModel::data(const QModelIndex &index, int role) const
71void AppDrawerModel::appAdded(const QString &appId)
75void AppDrawerModel::appAdded(const QString &appId)
78+ // Will be replaced by the refresh result anyway.
73 UalWrapper::AppInfo info = UalWrapper::getApplicationInfo(appId);
81 UalWrapper::AppInfo info = UalWrapper::getApplicationInfo(appId);
74 if (!info.valid) {
82 if (!info.valid) {
75 qWarning() << "App added signal received but failed to get app info for app" << appId;
83 qWarning() << "App added signal received but failed to get app info for app" << appId;
@@ -77,15 +85,19 @@ void AppDrawerModel::appAdded(const QString &appId)
79 beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
87 beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
80− LauncherItem* item = new LauncherItem(appId, info.name, info.icon, this);
88+ auto item = makeSharedLauncherItem(appId, info.name, info.icon, /* parent */ nullptr);
81 item->setKeywords(info.keywords);
89 item->setKeywords(info.keywords);
82 item->setPopularity(info.popularity);
90 item->setPopularity(info.popularity);
83− m_list.append(item);
91+ m_list.append(std::move(item));
84 endInsertRows();
92 endInsertRows();
87void AppDrawerModel::appRemoved(const QString &appId)
95void AppDrawerModel::appRemoved(const QString &appId)
98+ // Will be replaced by the refresh result anyway.
89 int idx = -1;
101 int idx = -1;
90 for (int i = 0; i < m_list.count(); i++) {
102 for (int i = 0; i < m_list.count(); i++) {
91 if (m_list.at(i)->appId() == appId) {
103 if (m_list.at(i)->appId() == appId) {
@@ -98,13 +110,17 @@ void AppDrawerModel::appRemoved(const QString &appId)
100 beginRemoveRows(QModelIndex(), idx, idx);
112 beginRemoveRows(QModelIndex(), idx, idx);
101− m_list.takeAt(idx)->deleteLater();
113+ m_list.removeAt(idx);
102 endRemoveRows();
114 endRemoveRows();
105void AppDrawerModel::appInfoChanged(const QString &appId)
117void AppDrawerModel::appInfoChanged(const QString &appId)
120+ // Will be replaced by the refresh result anyway.
107− LauncherItem *item = nullptr;
123+ std::shared_ptr<LauncherItem> item;
108 int idx = -1;
124 int idx = -1;
110 for(int i = 0; i < m_list.count(); i++) {
126 for(int i = 0; i < m_list.count(); i++) {
@@ -123,3 +139,54 @@ void AppDrawerModel::appInfoChanged(const QString &appId)
123 item->setPopularity(info.popularity);
139 item->setPopularity(info.popularity);
124 Q_EMIT dataChanged(index(idx), index(idx), {AppDrawerModelInterface::RoleUsage});
140 Q_EMIT dataChanged(index(idx), index(idx), {AppDrawerModelInterface::RoleUsage});
143+bool AppDrawerModel::refreshing() {
144+ return m_refreshing;
147+void AppDrawerModel::refresh() {
151+ m_refreshFutureWatcher.setFuture(QtConcurrent::run([](QThread *thread) {
154+ Q_FOREACH (const QString &appId, UalWrapper::installedApps()) {
155+ UalWrapper::AppInfo info = UalWrapper::getApplicationInfo(appId);
157+ qWarning() << "Failed to get app info for app" << appId;
160+ // We don't pass parent in because this may run after the model is destroyed.
161+ // (And, in fact, we can't, because the model is in a diferent thread.)
162+ auto item = makeSharedLauncherItem(appId, info.name, info.icon, /* parent */ nullptr);
163+ item->setKeywords(info.keywords);
164+ item->setPopularity(info.popularity);
165+ item->moveToThread(thread);
166+ list.append(std::move(item));
170+ }, this->thread()));
172+ m_refreshing = true;
173+ Q_EMIT refreshingChanged();
176+void AppDrawerModel::onRefreshFinished() {
177+ if (m_refreshFutureWatcher.isCanceled())
178+ // This is the result of setting canceled future below.
183+ m_list = m_refreshFutureWatcher.result();
184+ // Remove the future & its result, so that future modifications won't
186+ m_refreshFutureWatcher.setFuture(QFuture<ItemList>());
190+ m_refreshing = false;
191+ Q_EMIT refreshingChanged();