File indexing completed on 2024-05-12 05:29:04
0001 /* 0002 * SPDX-FileCopyrightText: 2012 Aleix Pol Gonzalez <aleixpol@blue-systems.com> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "ResourcesModel.h" 0008 #include "libdiscover_debug.h" 0009 #include "utils.h" 0010 #include <KLocalizedString> 0011 #include <QIcon> 0012 #include <Transaction/Transaction.h> 0013 #include <Transaction/TransactionModel.h> 0014 #include <resources/AbstractResource.h> 0015 #include <resources/AbstractResourcesBackend.h> 0016 #include <resources/StandardBackendUpdater.h> 0017 0018 StandardBackendUpdater::StandardBackendUpdater(AbstractResourcesBackend *parent) 0019 : AbstractBackendUpdater(parent) 0020 , m_backend(parent) 0021 , m_settingUp(false) 0022 , m_progress(0) 0023 , m_lastUpdate(QDateTime()) 0024 { 0025 connect(m_backend, &AbstractResourcesBackend::fetchingChanged, this, &StandardBackendUpdater::refreshUpdateable); 0026 connect(m_backend, &AbstractResourcesBackend::resourcesChanged, this, &StandardBackendUpdater::resourcesChanged); 0027 connect(m_backend, &AbstractResourcesBackend::resourceRemoved, this, [this](AbstractResource *resource) { 0028 if (m_upgradeable.remove(resource)) { 0029 Q_EMIT updatesCountChanged(updatesCount()); 0030 } 0031 m_toUpgrade.remove(resource); 0032 }); 0033 connect(TransactionModel::global(), &TransactionModel::transactionRemoved, this, &StandardBackendUpdater::transactionRemoved); 0034 connect(TransactionModel::global(), &TransactionModel::transactionAdded, this, &StandardBackendUpdater::transactionAdded); 0035 0036 m_timer.setSingleShot(true); 0037 m_timer.setInterval(10); 0038 connect(&m_timer, &QTimer::timeout, this, &StandardBackendUpdater::refreshUpdateable); 0039 } 0040 0041 void StandardBackendUpdater::resourcesChanged(AbstractResource *res, const QVector<QByteArray> &props) 0042 { 0043 if (props.contains("state") && (res->state() == AbstractResource::Upgradeable || m_upgradeable.contains(res))) 0044 m_timer.start(); 0045 } 0046 0047 bool StandardBackendUpdater::hasUpdates() const 0048 { 0049 return !m_upgradeable.isEmpty(); 0050 } 0051 0052 void StandardBackendUpdater::start() 0053 { 0054 m_settingUp = true; 0055 Q_EMIT progressingChanged(true); 0056 setProgress(0); 0057 auto upgradeList = m_toUpgrade.values(); 0058 std::sort(upgradeList.begin(), upgradeList.end(), [](const AbstractResource *a, const AbstractResource *b) { 0059 return a->name() < b->name(); 0060 }); 0061 0062 const bool couldCancel = m_canCancel; 0063 for (AbstractResource *res : std::as_const(upgradeList)) { 0064 m_pendingResources += res; 0065 auto t = m_backend->installApplication(res); 0066 t->setVisible(false); 0067 t->setProperty("updater", QVariant::fromValue<QObject *>(this)); 0068 connect(t, &Transaction::downloadSpeedChanged, this, [this]() { 0069 Q_EMIT downloadSpeedChanged(downloadSpeed()); 0070 }); 0071 connect(this, &StandardBackendUpdater::cancelTransaction, t, &Transaction::cancel); 0072 TransactionModel::global()->addTransaction(t); 0073 m_canCancel |= t->isCancellable(); 0074 } 0075 if (m_canCancel != couldCancel) { 0076 Q_EMIT cancelableChanged(m_canCancel); 0077 } 0078 m_settingUp = false; 0079 0080 if (m_pendingResources.isEmpty()) { 0081 cleanup(); 0082 } else { 0083 setProgress(1); 0084 } 0085 } 0086 0087 void StandardBackendUpdater::cancel() 0088 { 0089 Q_EMIT cancelTransaction(); 0090 } 0091 0092 void StandardBackendUpdater::transactionAdded(Transaction *newTransaction) 0093 { 0094 if (!m_pendingResources.contains(newTransaction->resource())) 0095 return; 0096 0097 connect(newTransaction, &Transaction::progressChanged, this, &StandardBackendUpdater::transactionProgressChanged); 0098 connect(newTransaction, &Transaction::statusChanged, this, &StandardBackendUpdater::transactionProgressChanged); 0099 } 0100 0101 AbstractBackendUpdater::State toUpdateState(Transaction *t) 0102 { 0103 switch (t->status()) { 0104 case Transaction::SetupStatus: 0105 case Transaction::QueuedStatus: 0106 return AbstractBackendUpdater::None; 0107 case Transaction::DownloadingStatus: 0108 return AbstractBackendUpdater::Downloading; 0109 case Transaction::CommittingStatus: 0110 return AbstractBackendUpdater::Installing; 0111 case Transaction::DoneStatus: 0112 case Transaction::DoneWithErrorStatus: 0113 case Transaction::CancelledStatus: 0114 return AbstractBackendUpdater::Done; 0115 } 0116 Q_UNREACHABLE(); 0117 } 0118 0119 void StandardBackendUpdater::transactionProgressChanged() 0120 { 0121 Transaction *t = qobject_cast<Transaction *>(sender()); 0122 Q_EMIT resourceProgressed(t->resource(), t->progress(), toUpdateState(t)); 0123 0124 refreshProgress(); 0125 } 0126 0127 void StandardBackendUpdater::transactionRemoved(Transaction *t) 0128 { 0129 const bool fromOurBackend = t->resource() && t->resource()->backend() == m_backend; 0130 if (!fromOurBackend) { 0131 return; 0132 } 0133 0134 const bool found = fromOurBackend && m_pendingResources.remove(t->resource()); 0135 m_anyTransactionFailed |= t->status() != Transaction::DoneStatus; 0136 0137 if (found && !m_settingUp) { 0138 refreshProgress(); 0139 if (m_pendingResources.isEmpty()) { 0140 cleanup(); 0141 if (needsReboot() && !m_anyTransactionFailed) { 0142 enableReadyToReboot(); 0143 } 0144 } 0145 } 0146 refreshUpdateable(); 0147 } 0148 0149 void StandardBackendUpdater::refreshProgress() 0150 { 0151 if (m_toUpgrade.isEmpty()) { 0152 return; 0153 } 0154 0155 int allProgresses = (m_toUpgrade.size() - m_pendingResources.size()) * 100; 0156 const auto allTransactions = transactions(); 0157 for (auto t : allTransactions) { 0158 allProgresses += t->progress(); 0159 } 0160 setProgress(allProgresses / m_toUpgrade.size()); 0161 } 0162 0163 void StandardBackendUpdater::refreshUpdateable() 0164 { 0165 if (m_backend->isFetching() || !m_backend->isValid()) { 0166 return; 0167 } 0168 0169 if (isProgressing()) { 0170 m_timer.start(1000); 0171 return; 0172 } 0173 0174 m_settingUp = true; 0175 Q_EMIT progressingChanged(true); 0176 AbstractResourcesBackend::Filters f; 0177 f.state = AbstractResource::Upgradeable; 0178 m_upgradeable.clear(); 0179 auto r = m_backend->search(f); 0180 connect(r, &ResultsStream::resourcesFound, this, [this](const QVector<StreamResult> &resources) { 0181 const auto predicate = [](const StreamResult &result) -> bool { 0182 return result.resource->state() == AbstractResource::Upgradeable; 0183 }; 0184 const auto count = std::count_if(resources.constBegin(), resources.constEnd(), predicate); 0185 m_upgradeable.reserve(m_upgradeable.count() + count); 0186 for (auto result : resources) { 0187 if (predicate(result)) { 0188 m_upgradeable.insert(result.resource); 0189 } 0190 } 0191 }); 0192 connect(r, &ResultsStream::destroyed, this, [this]() { 0193 m_settingUp = false; 0194 Q_EMIT updatesCountChanged(updatesCount()); 0195 Q_EMIT progressingChanged(false); 0196 }); 0197 } 0198 0199 qreal StandardBackendUpdater::progress() const 0200 { 0201 return m_progress; 0202 } 0203 0204 void StandardBackendUpdater::setProgress(qreal p) 0205 { 0206 if (p > m_progress || p < 0) { 0207 m_progress = p; 0208 Q_EMIT progressChanged(p); 0209 } 0210 } 0211 0212 void StandardBackendUpdater::prepare() 0213 { 0214 m_lastUpdate = QDateTime::currentDateTime(); 0215 m_toUpgrade = m_upgradeable; 0216 } 0217 0218 int StandardBackendUpdater::updatesCount() const 0219 { 0220 return m_upgradeable.count(); 0221 } 0222 0223 void StandardBackendUpdater::addResources(const QList<AbstractResource *> &apps) 0224 { 0225 const QSet<AbstractResource *> upgradeableApps = kToSet(apps); 0226 Q_ASSERT(m_upgradeable.contains(upgradeableApps)); 0227 m_toUpgrade += upgradeableApps; 0228 } 0229 0230 void StandardBackendUpdater::removeResources(const QList<AbstractResource *> &apps) 0231 { 0232 const QSet<AbstractResource *> upgradeableApps = kToSet(apps); 0233 Q_ASSERT(m_upgradeable.contains(upgradeableApps)); 0234 Q_ASSERT(m_toUpgrade.contains(upgradeableApps)); 0235 m_toUpgrade -= upgradeableApps; 0236 } 0237 0238 void StandardBackendUpdater::cleanup() 0239 { 0240 m_lastUpdate = QDateTime::currentDateTime(); 0241 m_toUpgrade.clear(); 0242 0243 refreshUpdateable(); 0244 Q_EMIT progressingChanged(false); 0245 } 0246 0247 QList<AbstractResource *> StandardBackendUpdater::toUpdate() const 0248 { 0249 return m_toUpgrade.values(); 0250 } 0251 0252 bool StandardBackendUpdater::isMarked(AbstractResource *res) const 0253 { 0254 return m_toUpgrade.contains(res); 0255 } 0256 0257 QDateTime StandardBackendUpdater::lastUpdate() const 0258 { 0259 return m_lastUpdate; 0260 } 0261 0262 bool StandardBackendUpdater::isCancelable() const 0263 { 0264 return m_canCancel; 0265 } 0266 0267 bool StandardBackendUpdater::isProgressing() const 0268 { 0269 return m_settingUp || !m_pendingResources.isEmpty(); 0270 } 0271 0272 double StandardBackendUpdater::updateSize() const 0273 { 0274 double ret = 0.; 0275 for (AbstractResource *res : m_toUpgrade) { 0276 ret += res->size(); 0277 } 0278 return ret; 0279 } 0280 0281 QVector<Transaction *> StandardBackendUpdater::transactions() const 0282 { 0283 const auto trans = TransactionModel::global()->transactions(); 0284 return kFilter<QVector<Transaction *>>(trans, [this](Transaction *t) { 0285 return t->property("updater").value<QObject *>() == this; 0286 }); 0287 } 0288 0289 quint64 StandardBackendUpdater::downloadSpeed() const 0290 { 0291 quint64 ret = 0; 0292 const auto trans = transactions(); 0293 for (Transaction *t : trans) { 0294 ret += t->downloadSpeed(); 0295 } 0296 return ret; 0297 }