File indexing completed on 2024-05-05 17:33:22
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 : qAsConst(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<AbstractResource *> &resources) { 0181 for (auto res : resources) 0182 if (res->state() == AbstractResource::Upgradeable) 0183 m_upgradeable.insert(res); 0184 }); 0185 connect(r, &ResultsStream::destroyed, this, [this]() { 0186 m_settingUp = false; 0187 Q_EMIT updatesCountChanged(updatesCount()); 0188 Q_EMIT progressingChanged(false); 0189 }); 0190 } 0191 0192 qreal StandardBackendUpdater::progress() const 0193 { 0194 return m_progress; 0195 } 0196 0197 void StandardBackendUpdater::setProgress(qreal p) 0198 { 0199 if (p > m_progress || p < 0) { 0200 m_progress = p; 0201 Q_EMIT progressChanged(p); 0202 } 0203 } 0204 0205 void StandardBackendUpdater::prepare() 0206 { 0207 m_lastUpdate = QDateTime::currentDateTime(); 0208 m_toUpgrade = m_upgradeable; 0209 } 0210 0211 int StandardBackendUpdater::updatesCount() const 0212 { 0213 return m_upgradeable.count(); 0214 } 0215 0216 void StandardBackendUpdater::addResources(const QList<AbstractResource *> &apps) 0217 { 0218 const QSet<AbstractResource *> upgradeableApps = kToSet(apps); 0219 Q_ASSERT(m_upgradeable.contains(upgradeableApps)); 0220 m_toUpgrade += upgradeableApps; 0221 } 0222 0223 void StandardBackendUpdater::removeResources(const QList<AbstractResource *> &apps) 0224 { 0225 const QSet<AbstractResource *> upgradeableApps = kToSet(apps); 0226 Q_ASSERT(m_upgradeable.contains(upgradeableApps)); 0227 Q_ASSERT(m_toUpgrade.contains(upgradeableApps)); 0228 m_toUpgrade -= upgradeableApps; 0229 } 0230 0231 void StandardBackendUpdater::cleanup() 0232 { 0233 m_lastUpdate = QDateTime::currentDateTime(); 0234 m_toUpgrade.clear(); 0235 0236 refreshUpdateable(); 0237 Q_EMIT progressingChanged(false); 0238 } 0239 0240 QList<AbstractResource *> StandardBackendUpdater::toUpdate() const 0241 { 0242 return m_toUpgrade.values(); 0243 } 0244 0245 bool StandardBackendUpdater::isMarked(AbstractResource *res) const 0246 { 0247 return m_toUpgrade.contains(res); 0248 } 0249 0250 QDateTime StandardBackendUpdater::lastUpdate() const 0251 { 0252 return m_lastUpdate; 0253 } 0254 0255 bool StandardBackendUpdater::isCancelable() const 0256 { 0257 return m_canCancel; 0258 } 0259 0260 bool StandardBackendUpdater::isProgressing() const 0261 { 0262 return m_settingUp || !m_pendingResources.isEmpty(); 0263 } 0264 0265 double StandardBackendUpdater::updateSize() const 0266 { 0267 double ret = 0.; 0268 for (AbstractResource *res : m_toUpgrade) { 0269 ret += res->size(); 0270 } 0271 return ret; 0272 } 0273 0274 QVector<Transaction *> StandardBackendUpdater::transactions() const 0275 { 0276 const auto trans = TransactionModel::global()->transactions(); 0277 return kFilter<QVector<Transaction *>>(trans, [this](Transaction *t) { 0278 return t->property("updater").value<QObject *>() == this; 0279 }); 0280 } 0281 0282 quint64 StandardBackendUpdater::downloadSpeed() const 0283 { 0284 quint64 ret = 0; 0285 const auto trans = transactions(); 0286 for (Transaction *t : trans) { 0287 ret += t->downloadSpeed(); 0288 } 0289 return ret; 0290 }