diff --git a/libdiscover/backends/PackageKitBackend/PKTransaction.cpp b/libdiscover/backends/PackageKitBackend/PKTransaction.cpp index 1431b039..b8b67387 100644 --- a/libdiscover/backends/PackageKitBackend/PKTransaction.cpp +++ b/libdiscover/backends/PackageKitBackend/PKTransaction.cpp @@ -1,279 +1,287 @@ /*************************************************************************** * Copyright © 2013 Aleix Pol Gonzalez * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #include "PKTransaction.h" #include "PackageKitBackend.h" #include "PackageKitResource.h" #include "PackageKitMessages.h" #include "utils.h" #include "LocalFilePKResource.h" #include #include #include #include #include #include #include PKTransaction::PKTransaction(const QVector& apps, Transaction::Role role) : Transaction(apps.first(), apps.first(), role) , m_apps(apps) { Q_ASSERT(!apps.contains(nullptr)); foreach(auto r, apps) { PackageKitResource* res = qobject_cast(r); m_pkgnames.unite(res->allPackageNames().toSet()); } QTimer::singleShot(0, this, &PKTransaction::start); } static QStringList packageIds(const QVector& res, std::function func) { QStringList ret; foreach(auto r, res) { ret += func(qobject_cast(r)); } ret.removeDuplicates(); return ret; } void PKTransaction::start() { trigger(PackageKit::Transaction::TransactionFlagSimulate); } void PKTransaction::trigger(PackageKit::Transaction::TransactionFlags flags) { if (m_trans) m_trans->deleteLater(); m_newPackageStates.clear(); if (m_apps.size() == 1 && qobject_cast(m_apps.at(0))) { auto app = qobject_cast(m_apps.at(0)); m_trans = PackageKit::Daemon::installFile(QUrl(app->packageName()).toLocalFile(), flags); connect(m_trans.data(), &PackageKit::Transaction::finished, this, [app](PackageKit::Transaction::Exit status) { if (status == PackageKit::Transaction::ExitSuccess) { app->markInstalled(); } }); } else switch (role()) { case Transaction::ChangeAddonsRole: case Transaction::InstallRole: m_trans = PackageKit::Daemon::installPackages(packageIds(m_apps, [](PackageKitResource* r){return r->availablePackageId(); }), flags); break; case Transaction::RemoveRole: //see bug #315063 m_trans = PackageKit::Daemon::removePackages(packageIds(m_apps, [](PackageKitResource* r){return r->installedPackageId(); }), true /*allowDeps*/, false, flags); break; }; Q_ASSERT(m_trans); // connect(m_trans.data(), &PackageKit::Transaction::statusChanged, this, [this]() { qDebug() << "state..." << m_trans->status(); }); connect(m_trans.data(), &PackageKit::Transaction::package, this, &PKTransaction::packageResolved); connect(m_trans.data(), &PackageKit::Transaction::finished, this, &PKTransaction::cleanup); connect(m_trans.data(), &PackageKit::Transaction::errorCode, this, &PKTransaction::errorFound); connect(m_trans.data(), &PackageKit::Transaction::mediaChangeRequired, this, &PKTransaction::mediaChange); connect(m_trans.data(), &PackageKit::Transaction::requireRestart, this, &PKTransaction::requireRestart); connect(m_trans.data(), &PackageKit::Transaction::repoSignatureRequired, this, &PKTransaction::repoSignatureRequired); - connect(m_trans.data(), &PackageKit::Transaction::itemProgress, this, &PKTransaction::progressChanged); + connect(m_trans.data(), &PackageKit::Transaction::percentageChanged, this, &PKTransaction::progressChanged); + connect(m_trans.data(), &PackageKit::Transaction::statusChanged, this, &PKTransaction::statusChanged); connect(m_trans.data(), &PackageKit::Transaction::eulaRequired, this, &PKTransaction::eulaRequired); connect(m_trans.data(), &PackageKit::Transaction::allowCancelChanged, this, &PKTransaction::cancellableChanged); connect(m_trans.data(), &PackageKit::Transaction::speedChanged, this, [this]() { setDownloadSpeed(m_trans->speed()); }); setCancellable(m_trans->allowCancel()); } -void PKTransaction::progressChanged(const QString &id, PackageKit::Transaction::Status status, uint percentage) +void PKTransaction::statusChanged() { - PackageKitResource * res = qobject_cast(resource()); - if (!res->allPackageNames().contains(PackageKit::Daemon::packageName(id))) - return; + setStatus(m_trans->status() == PackageKit::Transaction::StatusDownload ? Transaction::DownloadingStatus : Transaction::CommittingStatus); + progressChanged(); +} - setProgress(percentage); +int percentageWithStatus(PackageKit::Transaction::Status status, uint percentage); - if (status == PackageKit::Transaction::StatusDownload) - setStatus(Transaction::DownloadingStatus); - else - setStatus(Transaction::CommittingStatus); +void PKTransaction::progressChanged() +{ + auto percent = m_trans->percentage(); + if (percent == 101) { + qWarning() << "percentage cannot be calculated"; + percent = 50; + } + + const auto processedPercentage = percentageWithStatus(m_trans->status(), qBound(0, percent, 100)); + if (processedPercentage >= 0) + setProgress(processedPercentage); } void PKTransaction::cancellableChanged() { setCancellable(m_trans->allowCancel()); } void PKTransaction::cancel() { if (!m_trans) { setStatus(CancelledStatus); } else if (m_trans->allowCancel()) { m_trans->cancel(); } else { qWarning() << "trying to cancel a non-cancellable transaction: " << resource()->name(); } } void PKTransaction::cleanup(PackageKit::Transaction::Exit exit, uint runtime) { Q_UNUSED(runtime) const bool cancel = !m_proceedFunctions.isEmpty() || exit == PackageKit::Transaction::ExitCancelled; const bool failed = exit == PackageKit::Transaction::ExitFailed; const bool simulate = m_trans->transactionFlags() & PackageKit::Transaction::TransactionFlagSimulate; disconnect(m_trans, nullptr, this, nullptr); m_trans = nullptr; const auto backend = qobject_cast(resource()->backend()); if (!cancel && !failed && simulate) { auto packagesToRemove = m_newPackageStates.value(PackageKit::Transaction::InfoRemoving); QMutableListIterator i(packagesToRemove); QSet removedResources; while (i.hasNext()) { const auto pkgname = PackageKit::Daemon::packageName(i.next()); removedResources.unite(backend->resourcesByPackageName(pkgname)); if (m_pkgnames.contains(pkgname)) { i.remove(); } } removedResources.subtract(m_apps.toList().toSet()); if (!packagesToRemove.isEmpty() || !removedResources.isEmpty()) { QString msg = QStringLiteral("
  • ") + PackageKitResource::joinPackages(packagesToRemove, QStringLiteral("
  • ")); if (!removedResources.isEmpty()) { const QStringList removedResourcesStr = kTransform(removedResources, [](AbstractResource* a) { return a->name(); }); msg += QLatin1Char('\n'); msg += removedResourcesStr.join(QStringLiteral("
  • ")); } msg += QStringLiteral("
"); Q_EMIT proceedRequest(i18n("Confirm package removal"), i18np("This action will also remove the following package:\n%2", "This action will also remove the following packages:\n%2", packagesToRemove.count(), msg)); } else { proceed(); } return; } this->submitResolve(); if (failed) setStatus(Transaction::DoneWithErrorStatus); else setStatus(Transaction::CancelledStatus); } void PKTransaction::processProceedFunction() { auto t = m_proceedFunctions.takeFirst()(); connect(t, &PackageKit::Transaction::finished, this, [this](PackageKit::Transaction::Exit status) { if (status != PackageKit::Transaction::Exit::ExitSuccess) { qWarning() << "transaction failed" << sender() << status; cancel(); return; } if (!m_proceedFunctions.isEmpty()) { processProceedFunction(); } else { start(); } }); } void PKTransaction::proceed() { if (!m_proceedFunctions.isEmpty()) { processProceedFunction(); } else { trigger(PackageKit::Transaction::TransactionFlagOnlyTrusted); } } void PKTransaction::packageResolved(PackageKit::Transaction::Info info, const QString& packageId) { m_newPackageStates[info].append(packageId); } void PKTransaction::submitResolve() { QStringList needResolving; foreach(const auto &pkgids, m_newPackageStates) { foreach(const auto &pkgid, pkgids) { needResolving += PackageKit::Daemon::packageName(pkgid); } } if (!needResolving.isEmpty()) { needResolving.removeDuplicates(); const auto backend = qobject_cast(resource()->backend()); backend->clearPackages(needResolving); backend->resolvePackages(needResolving); } } PackageKit::Transaction* PKTransaction::transaction() { return m_trans; } void PKTransaction::eulaRequired(const QString& eulaID, const QString& packageID, const QString& vendor, const QString& licenseAgreement) { m_proceedFunctions << [eulaID](){ return PackageKit::Daemon::acceptEula(eulaID); }; Q_EMIT proceedRequest(i18n("Accept EULA"), i18n("The package %1 and its vendor %2 require that you accept their license:\n %3", PackageKit::Daemon::packageName(packageID), vendor, licenseAgreement)); } void PKTransaction::errorFound(PackageKit::Transaction::Error err, const QString& error) { if (err == PackageKit::Transaction::ErrorNoLicenseAgreement) return; qWarning() << "PackageKit error:" << err << PackageKitMessages::errorMessage(err) << error; Q_EMIT passiveMessage(PackageKitMessages::errorMessage(err)); } void PKTransaction::mediaChange(PackageKit::Transaction::MediaType media, const QString& type, const QString& text) { Q_UNUSED(media) Q_EMIT passiveMessage(i18n("Media Change of type '%1' is requested.\n%2", type, text)); } void PKTransaction::requireRestart(PackageKit::Transaction::Restart restart, const QString& pkgid) { Q_EMIT passiveMessage(PackageKitMessages::restartMessage(restart, pkgid)); } void PKTransaction::repoSignatureRequired(const QString& packageID, const QString& repoName, const QString& keyUrl, const QString& keyUserid, const QString& keyId, const QString& keyFingerprint, const QString& keyTimestamp, PackageKit::Transaction::SigType type) { Q_EMIT proceedRequest(i18n("Missing signature for %1 in %2", packageID, repoName), i18n("Do you trust the following key?\n\nUrl: %1\nUser: %2\nKey: %3\nFingerprint: %4\nTimestamp: %4\n", keyUrl, keyUserid, keyFingerprint, keyTimestamp)); m_proceedFunctions << [type, keyId, packageID](){ return PackageKit::Daemon::installSignature(type, keyId, packageID); }; } diff --git a/libdiscover/backends/PackageKitBackend/PKTransaction.h b/libdiscover/backends/PackageKitBackend/PKTransaction.h index f2f44e0f..e3b41898 100644 --- a/libdiscover/backends/PackageKitBackend/PKTransaction.h +++ b/libdiscover/backends/PackageKitBackend/PKTransaction.h @@ -1,72 +1,73 @@ /*************************************************************************** * Copyright © 2013 Aleix Pol Gonzalez * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #ifndef PKTRANSACTION_H #define PKTRANSACTION_H #include #include #include #include class PKTransaction : public Transaction { Q_OBJECT public: explicit PKTransaction(const QVector& app, Transaction::Role role); PackageKit::Transaction* transaction(); void cancel() override; void proceed() override; public Q_SLOTS: void start(); private: void processProceedFunction(); + void statusChanged(); void cleanup(PackageKit::Transaction::Exit, uint); void errorFound(PackageKit::Transaction::Error err, const QString& error); void mediaChange(PackageKit::Transaction::MediaType media, const QString& type, const QString& text); void requireRestart(PackageKit::Transaction::Restart restart, const QString& p); - void progressChanged(const QString&, PackageKit::Transaction::Status, uint); + void progressChanged(); void eulaRequired(const QString &eulaID, const QString &packageID, const QString &vendor, const QString &licenseAgreement); void cancellableChanged(); void packageResolved(PackageKit::Transaction::Info info, const QString& packageId); void submitResolve(); void repoSignatureRequired(const QString &packageID, const QString &repoName, const QString &keyUrl, const QString &keyUserid, const QString &keyId, const QString &keyFingerprint, const QString &keyTimestamp, PackageKit::Transaction::SigType type); void trigger(PackageKit::Transaction::TransactionFlags flags); QPointer m_trans; const QVector m_apps; QSet m_pkgnames; QVector> m_proceedFunctions; QMap m_newPackageStates; }; #endif // PKTRANSACTION_H diff --git a/libdiscover/backends/PackageKitBackend/PackageKitResource.cpp b/libdiscover/backends/PackageKitBackend/PackageKitResource.cpp index be4a0d6f..4b7e807c 100644 --- a/libdiscover/backends/PackageKitBackend/PackageKitResource.cpp +++ b/libdiscover/backends/PackageKitBackend/PackageKitResource.cpp @@ -1,292 +1,292 @@ /*************************************************************************** * Copyright © 2012 Aleix Pol Gonzalez * * Copyright © 2013 Lukas Appelhans * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #include "PackageKitResource.h" #include "PackageKitBackend.h" #include "PackageKitMessages.h" #include #include #include #include #include PackageKitResource::PackageKitResource(QString packageName, QString summary, PackageKitBackend* parent) : AbstractResource(parent) , m_summary(std::move(summary)) , m_name(std::move(packageName)) , m_objects({ QStringLiteral("qrc:/qml/DependenciesButton.qml") }) { setObjectName(m_name); connect(this, &PackageKitResource::dependenciesFound, this, [this](const QJsonObject& obj) { setDependenciesCount(obj.size()); }); } QString PackageKitResource::name() const { return m_name; } QString PackageKitResource::packageName() const { return m_name; } QStringList PackageKitResource::allPackageNames() const { return { m_name }; } QString PackageKitResource::availablePackageId() const { //First we check if it's upgradeable and use this version to display const QString pkgid = backend()->upgradeablePackageId(this); if (!pkgid.isEmpty()) return pkgid; QMap::const_iterator it = m_packages.constFind(PackageKit::Transaction::InfoAvailable); if (it != m_packages.constEnd()) return it->last(); return installedPackageId(); } QString PackageKitResource::installedPackageId() const { const auto installed = m_packages[PackageKit::Transaction::InfoInstalled]; return installed.isEmpty() ? QString() : installed.last(); } QString PackageKitResource::comment() { return m_summary; } QString PackageKitResource::longDescription() { fetchDetails(); return m_details.description(); } QUrl PackageKitResource::homepage() { fetchDetails(); return QUrl(m_details.url()); } QVariant PackageKitResource::icon() const { return QStringLiteral("applications-other"); } QString PackageKitResource::license() { fetchDetails(); return m_details.license().isEmpty() ? i18n("Unknown") : m_details.license(); } QList PackageKitResource::addonsInformation() { return QList(); } QString PackageKitResource::availableVersion() const { return PackageKit::Daemon::packageVersion(availablePackageId()); } QString PackageKitResource::installedVersion() const { return PackageKit::Daemon::packageVersion(installedPackageId()); } int PackageKitResource::size() { fetchDetails(); return m_details.size(); } QString PackageKitResource::origin() const { auto pkgid = availablePackageId(); return PackageKit::Daemon::packageData(pkgid); } QString PackageKitResource::section() { return QString(); } AbstractResource::State PackageKitResource::state() { if (backend()->isPackageNameUpgradeable(this)) return Upgradeable; else if(m_packages.contains(PackageKit::Transaction::InfoInstalled)) return Installed; else if(m_packages.contains(PackageKit::Transaction::InfoAvailable)) return None; else return Broken; } void PackageKitResource::addPackageId(PackageKit::Transaction::Info info, const QString &packageId, bool arch) { if (arch) m_packages[info].append(packageId); else m_packages[info].prepend(packageId); emit stateChanged(); } QStringList PackageKitResource::categories() { return { QStringLiteral("Unknown") }; } bool PackageKitResource::isTechnical() const { return true; } void PackageKitResource::fetchDetails() { const QString pkgid = availablePackageId(); if (!m_details.isEmpty() || pkgid.isEmpty()) return; m_details.insert(QStringLiteral("fetching"), true);//we add an entry so it's not re-fetched. backend()->fetchDetails(pkgid); } void PackageKitResource::failedFetchingDetails(PackageKit::Transaction::Error, const QString& msg) { qWarning() << "error fetching details" << msg; } void PackageKitResource::setDependenciesCount(int deps) { if (deps != m_dependenciesCount) { m_dependenciesCount = deps; Q_EMIT sizeChanged(); } } void PackageKitResource::setDetails(const PackageKit::Details & details) { const bool ourDetails = details.packageId() == availablePackageId(); if (!ourDetails) return; if (m_details != details) { m_details = details; emit stateChanged(); if (!backend()->isFetching()) backend()->resourcesChanged(this, {"size", "homepage", "license"}); } } void PackageKitResource::fetchChangelog() { PackageKit::Transaction* t = PackageKit::Daemon::getUpdateDetail(availablePackageId()); connect(t, &PackageKit::Transaction::updateDetail, this, &PackageKitResource::updateDetail); connect(t, &PackageKit::Transaction::errorCode, this, [this](PackageKit::Transaction::Error err, const QString & error) { qWarning() << "error fetching updates:" << err << error; emit changelogFetched(QString()); }); } static void addIfNotEmpty(const QString& title, const QString& content, QString& where) { if (!content.isEmpty()) where += QStringLiteral("

") + title + QStringLiteral(" ") + QString(content).replace(QStringLiteral("\n"), QStringLiteral("
")) + QStringLiteral("

"); } QString PackageKitResource::joinPackages(const QStringList& pkgids, const QString &_sep) { QStringList ret; foreach(const QString& pkgid, pkgids) { ret += PackageKit::Daemon::packageVersion(pkgid); } const QString sep = _sep.isEmpty() ? i18nc("comma separating package names", ", ") : _sep; return ret.join(sep); } static QStringList urlToLinks(const QStringList& urls) { QStringList ret; foreach(const QString& in, urls) ret += QStringLiteral("%1").arg(in); return ret; } void PackageKitResource::updateDetail(const QString& /*packageID*/, const QStringList& updates, const QStringList& obsoletes, const QStringList& vendorUrls, const QStringList& /*bugzillaUrls*/, const QStringList& /*cveUrls*/, PackageKit::Transaction::Restart restart, const QString& updateText, const QString& /*changelog*/, PackageKit::Transaction::UpdateState state, const QDateTime& /*issued*/, const QDateTime& /*updated*/) { QString info; addIfNotEmpty(i18n("Current Version:"), joinPackages(updates), info); addIfNotEmpty(i18n("Obsoletes:"), joinPackages(obsoletes), info); addIfNotEmpty(i18n("New Version:"), updateText, info); addIfNotEmpty(i18n("Update State:"), PackageKitMessages::updateStateMessage(state), info); addIfNotEmpty(i18n("Restart:"), PackageKitMessages::restartMessage(restart), info); if (!vendorUrls.isEmpty()) addIfNotEmpty(i18n("Vendor:"), urlToLinks(vendorUrls).join(QStringLiteral(", ")), info); emit changelogFetched(info); } PackageKitBackend* PackageKitResource::backend() const { return qobject_cast(parent()); } QString PackageKitResource::sizeDescription() { if (m_dependenciesCount < 0) { fetchDetails(); fetchDependencies(); } if (m_dependenciesCount <= 0) return AbstractResource::sizeDescription(); else return i18np("%2 (plus %1 dependency)", "%2 (plus %1 dependencies)", m_dependenciesCount, AbstractResource::sizeDescription()); } QString PackageKitResource::sourceIcon() const { return QStringLiteral("package-available"); } void PackageKitResource::fetchDependencies() { const auto id = availablePackageId(); if (id.isEmpty()) return; m_dependenciesCount = 0; QSharedPointer packageDependencies(new QJsonObject); auto trans = PackageKit::Daemon::installPackage(id, PackageKit::Transaction::TransactionFlagSimulate); - connect(trans, &PackageKit::Transaction::errorCode, backend(), &PackageKitBackend::transactionError); - connect(trans, &PackageKit::Transaction::package, this, [packageDependencies](PackageKit::Transaction::Info info, const QString &packageID, const QString &summary) { + connect(trans, &PackageKit::Transaction::errorCode, this, [this](PackageKit::Transaction::Error, const QString& message) { qWarning() << "Transaction error: " << message << sender(); }); + connect(trans, &PackageKit::Transaction::package, this, [packageDependencies](PackageKit::Transaction::Info /*info*/, const QString &packageID, const QString &summary) { (*packageDependencies)[PackageKit::Daemon::packageName(packageID)] = summary ; }); - connect(trans, &PackageKit::Transaction::finished, this, [this, packageDependencies](PackageKit::Transaction::Exit status) { + connect(trans, &PackageKit::Transaction::finished, this, [this, packageDependencies](PackageKit::Transaction::Exit /*status*/) { Q_EMIT dependenciesFound(*packageDependencies); }); } diff --git a/libdiscover/backends/PackageKitBackend/PackageKitUpdater.cpp b/libdiscover/backends/PackageKitBackend/PackageKitUpdater.cpp index bf2ef360..f57aa128 100644 --- a/libdiscover/backends/PackageKitBackend/PackageKitUpdater.cpp +++ b/libdiscover/backends/PackageKitBackend/PackageKitUpdater.cpp @@ -1,403 +1,405 @@ /*************************************************************************** * Copyright © 2013 Lukas Appelhans * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #include "PackageKitUpdater.h" #include "PackageKitMessages.h" #include #ifdef PKQT_1_0 #include #endif #include #include #include #include -static int percentageWithStatus(PackageKit::Transaction::Status status, uint percentage) +int percentageWithStatus(PackageKit::Transaction::Status status, uint percentage) { + const auto was = percentage; if (status != PackageKit::Transaction::StatusUnknown) { static const QMap statuses = { { PackageKit::Transaction::Status::StatusDownload, 0 }, { PackageKit::Transaction::Status::StatusInstall, 1}, + { PackageKit::Transaction::Status::StatusRemove, 1}, { PackageKit::Transaction::Status::StatusUpdate, 1} }; const auto idx = statuses.value(status, -1); if (idx < 0) { qDebug() << "Status not present" << status << "among" << statuses .keys() << percentage; return -1; } percentage = (idx * 100 + percentage) / 2 /*the maximum in statuses*/; } - qDebug() << "reporing progress with status:" << status << percentage; + qDebug() << "reporting progress with status:" << status << percentage << was; return percentage; } PackageKitUpdater::PackageKitUpdater(PackageKitBackend * parent) : AbstractBackendUpdater(parent), m_transaction(nullptr), m_backend(parent), m_isCancelable(false), m_isProgressing(false), m_percentage(0), m_lastUpdate() { fetchLastUpdateTime(); } PackageKitUpdater::~PackageKitUpdater() { } void PackageKitUpdater::prepare() { Q_ASSERT(!m_transaction); m_toUpgrade = m_backend->upgradeablePackages(); m_allUpgradeable = m_toUpgrade; } void PackageKitUpdater::setupTransaction(PackageKit::Transaction::TransactionFlags flags) { m_packagesModified.clear(); auto pkgs = involvedPackages(m_toUpgrade).toList(); pkgs.sort(); m_transaction = PackageKit::Daemon::updatePackages(pkgs, flags); m_isCancelable = m_transaction->allowCancel(); connect(m_transaction.data(), &PackageKit::Transaction::finished, this, &PackageKitUpdater::finished); connect(m_transaction.data(), &PackageKit::Transaction::package, this, &PackageKitUpdater::packageResolved); connect(m_transaction.data(), &PackageKit::Transaction::errorCode, this, &PackageKitUpdater::errorFound); connect(m_transaction.data(), &PackageKit::Transaction::mediaChangeRequired, this, &PackageKitUpdater::mediaChange); connect(m_transaction.data(), &PackageKit::Transaction::requireRestart, this, &PackageKitUpdater::requireRestart); connect(m_transaction.data(), &PackageKit::Transaction::eulaRequired, this, &PackageKitUpdater::eulaRequired); connect(m_transaction.data(), &PackageKit::Transaction::repoSignatureRequired, this, &PackageKitUpdater::repoSignatureRequired); connect(m_transaction.data(), &PackageKit::Transaction::allowCancelChanged, this, &PackageKitUpdater::cancellableChanged); connect(m_transaction.data(), &PackageKit::Transaction::percentageChanged, this, &PackageKitUpdater::percentageChanged); connect(m_transaction.data(), &PackageKit::Transaction::itemProgress, this, &PackageKitUpdater::itemProgress); connect(m_transaction.data(), &PackageKit::Transaction::speedChanged, this, [this] { Q_EMIT downloadSpeedChanged(downloadSpeed()); }); } QSet PackageKitUpdater::packagesForPackageId(const QSet& pkgids) const { QSet packages; packages.reserve(pkgids.size()); foreach(const QString& pkgid, pkgids) { packages += PackageKit::Daemon::packageName(pkgid); } QSet ret; foreach (AbstractResource * res, m_allUpgradeable) { PackageKitResource* pres = qobject_cast(res); if (packages.contains(pres->allPackageNames().toSet())) { ret.insert(res); } } return ret; } QSet PackageKitUpdater::involvedPackages(const QSet& packages) const { QSet packageIds; packageIds.reserve(packages.size()); foreach (AbstractResource * res, packages) { PackageKitResource * app = qobject_cast(res); QString pkgid = m_backend->upgradeablePackageId(app); packageIds.insert(pkgid); } return packageIds; } void PackageKitUpdater::processProceedFunction() { auto t = m_proceedFunctions.takeFirst()(); connect(t, &PackageKit::Transaction::finished, this, [this](PackageKit::Transaction::Exit status) { if (status != PackageKit::Transaction::Exit::ExitSuccess) { qWarning() << "transaction failed" << sender() << status; cancel(); return; } if (!m_proceedFunctions.isEmpty()) { processProceedFunction(); } else { start(); } }); } void PackageKitUpdater::proceed() { if (!m_proceedFunctions.isEmpty()) processProceedFunction(); #ifdef PKQT_1_0 else if (qEnvironmentVariableIsSet("PK_OFFLINE_UPDATE")) setupTransaction(PackageKit::Transaction::TransactionFlagOnlyTrusted | PackageKit::Transaction::TransactionFlagOnlyDownload); #endif else setupTransaction(PackageKit::Transaction::TransactionFlagOnlyTrusted); } void PackageKitUpdater::start() { Q_ASSERT(!isProgressing()); setupTransaction(PackageKit::Transaction::TransactionFlagSimulate); setProgressing(true); } void PackageKitUpdater::finished(PackageKit::Transaction::Exit exit, uint /*time*/) { // qDebug() << "update finished!" << exit << time; if (!m_proceedFunctions.isEmpty()) return; const bool cancel = exit == PackageKit::Transaction::ExitCancelled; const bool simulate = m_transaction->transactionFlags() & PackageKit::Transaction::TransactionFlagSimulate; disconnect(m_transaction, nullptr, this, nullptr); m_transaction = nullptr; if (!cancel && simulate) { const auto toremove = m_packagesModified.value(PackageKit::Transaction::InfoRemoving); if (!toremove.isEmpty()) { const auto toinstall = QStringList() << m_packagesModified.value(PackageKit::Transaction::InfoInstalling) << m_packagesModified.value(PackageKit::Transaction::InfoUpdating); Q_EMIT proceedRequest(i18n("Packages to remove"), i18n("The following packages will be removed by the update:\n
  • %1
\nin order to install:\n
  • %2
", PackageKitResource::joinPackages(toremove, QStringLiteral("
  • ")), PackageKitResource::joinPackages(toinstall, QStringLiteral("
  • ")) )); } else { proceed(); } return; } setProgressing(false); m_backend->checkForUpdates(); fetchLastUpdateTime(); if (qEnvironmentVariableIsSet("PK_OFFLINE_UPDATE")) #ifdef PKQT_1_0 PackageKit::Daemon::global()->offline()->trigger(PackageKit::Offline::ActionReboot); #else qWarning() << "PK_OFFLINE_UPDATE is set but discover was built against an old version of PackageKitQt that didn't support offline updates"; #endif } void PackageKitUpdater::cancellableChanged() { if (m_isCancelable != m_transaction->allowCancel()) { m_isCancelable = m_transaction->allowCancel(); emit cancelableChanged(m_isCancelable); } } void PackageKitUpdater::percentageChanged() { const auto actualPercentage = percentageWithStatus(m_transaction->status(), m_transaction->percentage()); if (actualPercentage >= 0 && m_percentage != actualPercentage) { m_percentage = actualPercentage; emit progressChanged(m_percentage); } } bool PackageKitUpdater::hasUpdates() const { return m_backend->updatesCount() > 0; } qreal PackageKitUpdater::progress() const { return m_percentage; } void PackageKitUpdater::removeResources(const QList& apps) { QSet pkgs = involvedPackages(apps.toSet()); m_toUpgrade.subtract(packagesForPackageId(pkgs)); } void PackageKitUpdater::addResources(const QList& apps) { QSet pkgs = involvedPackages(apps.toSet()); m_toUpgrade.unite(packagesForPackageId(pkgs)); } QList PackageKitUpdater::toUpdate() const { return m_toUpgrade.toList(); } bool PackageKitUpdater::isMarked(AbstractResource* res) const { return m_toUpgrade.contains(res); } QDateTime PackageKitUpdater::lastUpdate() const { return m_lastUpdate; } bool PackageKitUpdater::isCancelable() const { return m_isCancelable; } bool PackageKitUpdater::isProgressing() const { return m_isProgressing; } void PackageKitUpdater::cancel() { if (m_transaction) m_transaction->cancel(); else setProgressing(false); } void PackageKitUpdater::errorFound(PackageKit::Transaction::Error err, const QString& error) { if (err == PackageKit::Transaction::ErrorNoLicenseAgreement) return; Q_EMIT passiveMessage(QStringLiteral("%1\n%2").arg(PackageKitMessages::errorMessage(err), error)); qWarning() << "Error happened" << err << error; } void PackageKitUpdater::mediaChange(PackageKit::Transaction::MediaType media, const QString& type, const QString& text) { Q_UNUSED(media) Q_EMIT passiveMessage(i18n("Media Change of type '%1' is requested.\n%2", type, text)); } void PackageKitUpdater::requireRestart(PackageKit::Transaction::Restart restart, const QString& pkgid) { Q_EMIT passiveMessage(PackageKitMessages::restartMessage(restart, pkgid)); } void PackageKitUpdater::eulaRequired(const QString& eulaID, const QString& packageID, const QString& vendor, const QString& licenseAgreement) { m_proceedFunctions << [eulaID](){ return PackageKit::Daemon::acceptEula(eulaID); }; Q_EMIT proceedRequest(i18n("Accept EULA"), i18n("The package %1 and its vendor %2 require that you accept their license:\n %3", PackageKit::Daemon::packageName(packageID), vendor, licenseAgreement)); } void PackageKitUpdater::setProgressing(bool progressing) { if (m_isProgressing != progressing) { m_isProgressing = progressing; emit progressingChanged(m_isProgressing); } } void PackageKitUpdater::fetchLastUpdateTime() { QDBusPendingReply transaction = PackageKit::Daemon::global()->getTimeSinceAction(PackageKit::Transaction::RoleGetUpdates); QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(transaction, this); connect(watcher, &QDBusPendingCallWatcher::finished, this, &PackageKitUpdater::lastUpdateTimeReceived); } void PackageKitUpdater::lastUpdateTimeReceived(QDBusPendingCallWatcher* w) { QDBusPendingReply reply = w->reply(); if (reply.isError()) { qWarning() << "Error when fetching the last update time" << reply.error(); } else { m_lastUpdate = QDateTime::currentDateTime().addSecs(-int(reply.value())); } w->deleteLater(); } void PackageKitUpdater::itemProgress(const QString& itemID, PackageKit::Transaction::Status status, uint percentage) { auto res = packagesForPackageId({itemID}); const auto actualPercentage = percentageWithStatus(status, percentage); if (actualPercentage<0) return; foreach(auto r, res) { Q_EMIT resourceProgressed(r, actualPercentage); } } void PackageKitUpdater::fetchChangelog() const { QStringList pkgids; foreach(AbstractResource* res, m_allUpgradeable) { pkgids += static_cast(res)->availablePackageId(); } Q_ASSERT(!pkgids.isEmpty()); PackageKit::Transaction* t = PackageKit::Daemon::getUpdatesDetails(pkgids); connect(t, &PackageKit::Transaction::updateDetail, this, &PackageKitUpdater::updateDetail); connect(t, &PackageKit::Transaction::errorCode, this, &PackageKitUpdater::errorFound); } void PackageKitUpdater::updateDetail(const QString& packageID, const QStringList& updates, const QStringList& obsoletes, const QStringList& vendorUrls, const QStringList& bugzillaUrls, const QStringList& cveUrls, PackageKit::Transaction::Restart restart, const QString& updateText, const QString& changelog, PackageKit::Transaction::UpdateState state, const QDateTime& issued, const QDateTime& updated) { auto res = packagesForPackageId({packageID}); foreach(auto r, res) { static_cast(r)->updateDetail(packageID, updates, obsoletes, vendorUrls, bugzillaUrls, cveUrls, restart, updateText, changelog, state, issued, updated); } } void PackageKitUpdater::packageResolved(PackageKit::Transaction::Info info, const QString& packageId) { m_packagesModified[info] << packageId; } void PackageKitUpdater::repoSignatureRequired(const QString& packageID, const QString& repoName, const QString& keyUrl, const QString& keyUserid, const QString& keyId, const QString& keyFingerprint, const QString& keyTimestamp, PackageKit::Transaction::SigType type) { Q_EMIT proceedRequest(i18n("Missing signature for %1 in %2", packageID, repoName), i18n("Do you trust the following key?\n\nUrl: %1\nUser: %2\nKey: %3\nFingerprint: %4\nTimestamp: %4\n", keyUrl, keyUserid, keyFingerprint, keyTimestamp)); m_proceedFunctions << [type, keyId, packageID](){ return PackageKit::Daemon::installSignature(type, keyId, packageID); }; } double PackageKitUpdater::updateSize() const { double ret = 0.; QSet donePkgs; for (AbstractResource * res : m_toUpgrade) { PackageKitResource * app = qobject_cast(res); QString pkgid = m_backend->upgradeablePackageId(app); if (!donePkgs.contains(pkgid)) { donePkgs.insert(pkgid); ret += app->size(); } } return ret; } quint64 PackageKitUpdater::downloadSpeed() const { return m_transaction ? m_transaction->speed() : 0; }