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 }