diff --git a/libdiscover/backends/FlatpakBackend/FlatpakBackend.cpp b/libdiscover/backends/FlatpakBackend/FlatpakBackend.cpp --- a/libdiscover/backends/FlatpakBackend/FlatpakBackend.cpp +++ b/libdiscover/backends/FlatpakBackend/FlatpakBackend.cpp @@ -966,7 +966,7 @@ FlatpakInstallation *installation = resource->installation(); if (resource->propertyState(FlatpakResource::RequiredRuntime) == FlatpakResource::NotKnownYet && resource->type() == FlatpakResource::DesktopApp) { - transaction = new FlatpakTransaction(installation, resource, Transaction::InstallRole, true); + transaction = new FlatpakTransaction(resource, Transaction::InstallRole, true); connect(resource, &FlatpakResource::propertyStateChanged, [resource, transaction, this] (FlatpakResource::PropertyKind kind, FlatpakResource::PropertyState state) { if (kind != FlatpakResource::RequiredRuntime) { return; @@ -983,9 +983,9 @@ } else { FlatpakResource *runtime = getRuntimeForApp(resource); if (runtime && !runtime->isInstalled()) { - transaction = new FlatpakTransaction(installation, resource, runtime, Transaction::InstallRole); + transaction = new FlatpakTransaction(resource, runtime, Transaction::InstallRole); } else { - transaction = new FlatpakTransaction(installation, resource, Transaction::InstallRole); + transaction = new FlatpakTransaction(resource, Transaction::InstallRole); } } @@ -1015,7 +1015,7 @@ } FlatpakInstallation *installation = resource->installation(); - FlatpakTransaction *transaction = new FlatpakTransaction(installation, resource, Transaction::RemoveRole); + FlatpakTransaction *transaction = new FlatpakTransaction(resource, Transaction::RemoveRole); connect(transaction, &FlatpakTransaction::statusChanged, [this, installation, resource] (Transaction::Status status) { if (status == Transaction::Status::DoneStatus) { diff --git a/libdiscover/backends/FlatpakBackend/FlatpakTransaction.h b/libdiscover/backends/FlatpakBackend/FlatpakTransaction.h --- a/libdiscover/backends/FlatpakBackend/FlatpakTransaction.h +++ b/libdiscover/backends/FlatpakBackend/FlatpakTransaction.h @@ -37,33 +37,28 @@ { Q_OBJECT public: - FlatpakTransaction(FlatpakInstallation *installation, FlatpakResource *app, Role role, bool delayStart = false); - FlatpakTransaction(FlatpakInstallation *installation, FlatpakResource *app, FlatpakResource *runtime, Role role, bool delayStart = false); - // FIXME ignore addons, they are not used in flatpak world (yet) + FlatpakTransaction(FlatpakResource *app, Role role, bool delayStart = false); + FlatpakTransaction(FlatpakResource *app, FlatpakResource *runtime, Role role, bool delayStart = false); ~FlatpakTransaction(); void cancel() override; void setRuntime(FlatpakResource *runtime); public Q_SLOTS: - void onAppJobFinished(); - void onAppJobProgressChanged(int progress); - void onRuntimeJobFinished(); - void onRuntimeJobProgressChanged(int progress); + void onJobFinished(); + void onJobProgressChanged(int progress); void finishTransaction(); void start(); private: + void processRelatedRefs(FlatpakResource *resource); void updateProgress(); - int m_appJobProgress; - int m_runtimeJobProgress; QPointer m_app; QPointer m_runtime; - FlatpakInstallation *m_installation; QPointer m_appJob; - QPointer m_runtimeJob; + QList > m_jobs; }; #endif // FLATPAKTRANSACTION_H diff --git a/libdiscover/backends/FlatpakBackend/FlatpakTransaction.cpp b/libdiscover/backends/FlatpakBackend/FlatpakTransaction.cpp --- a/libdiscover/backends/FlatpakBackend/FlatpakTransaction.cpp +++ b/libdiscover/backends/FlatpakBackend/FlatpakTransaction.cpp @@ -27,18 +27,21 @@ #include #include -FlatpakTransaction::FlatpakTransaction(FlatpakInstallation *installation, FlatpakResource *app, Role role, bool delayStart) - : FlatpakTransaction(installation, app, nullptr, role, delayStart) +extern "C" { +#include +#include +#include +} + +FlatpakTransaction::FlatpakTransaction(FlatpakResource *app, Role role, bool delayStart) + : FlatpakTransaction(app, nullptr, role, delayStart) { } -FlatpakTransaction::FlatpakTransaction(FlatpakInstallation* installation, FlatpakResource *app, FlatpakResource *runtime, Transaction::Role role, bool delayStart) +FlatpakTransaction::FlatpakTransaction(FlatpakResource *app, FlatpakResource *runtime, Transaction::Role role, bool delayStart) : Transaction(app->backend(), app, role, {}) - , m_appJobProgress(0) - , m_runtimeJobProgress(0) , m_app(app) , m_runtime(runtime) - , m_installation(installation) { setCancellable(true); @@ -54,9 +57,8 @@ void FlatpakTransaction::cancel() { Q_ASSERT(m_appJob); - m_appJob->cancel(); - if (m_runtime) { - m_runtimeJob->cancel(); + foreach (const QPointer &job, m_jobs) { + job->cancel(); } setStatus(CancelledStatus); } @@ -69,75 +71,116 @@ void FlatpakTransaction::start() { if (m_runtime) { - m_runtimeJob = new FlatpakTransactionJob(m_installation, m_runtime, role(), this); - connect(m_runtimeJob, &FlatpakTransactionJob::finished, this, &FlatpakTransaction::onRuntimeJobFinished); - connect(m_runtimeJob, &FlatpakTransactionJob::progressChanged, this, &FlatpakTransaction::onRuntimeJobProgressChanged); - m_runtimeJob->start(); + // TODO related refs for runtime as well + QPointer job = new FlatpakTransactionJob(m_runtime, QPair(), role(), this); + connect(job, &FlatpakTransactionJob::finished, this, &FlatpakTransaction::onJobFinished); + connect(job, &FlatpakTransactionJob::progressChanged, this, &FlatpakTransaction::onJobProgressChanged); + m_jobs << job; + + processRelatedRefs(m_runtime); } - // App job will be started everytime - m_appJob = new FlatpakTransactionJob(m_installation, m_app, role(), this); - connect(m_appJob, &FlatpakTransactionJob::finished, this, &FlatpakTransaction::onAppJobFinished); - connect(m_appJob, &FlatpakTransactionJob::progressChanged, this, &FlatpakTransaction::onAppJobProgressChanged); - m_appJob->start(); -} + // App job will be added everytime + m_appJob = new FlatpakTransactionJob(m_app, QPair(), role(), this); + connect(m_appJob, &FlatpakTransactionJob::finished, this, &FlatpakTransaction::onJobFinished); + connect(m_appJob, &FlatpakTransactionJob::progressChanged, this, &FlatpakTransaction::onJobProgressChanged); + m_jobs << m_appJob; -void FlatpakTransaction::onAppJobFinished() -{ - m_appJobProgress = 100; + processRelatedRefs(m_app); - updateProgress(); - if (!m_appJob->result()) { - Q_EMIT passiveMessage(m_appJob->errorMessage()); - } - - if ((m_runtimeJob && m_runtimeJob->isFinished()) || !m_runtimeJob) { - finishTransaction(); + // Now start all the jobs together + foreach (const QPointer &job, m_jobs) { + job->start(); } } -void FlatpakTransaction::onAppJobProgressChanged(int progress) +void FlatpakTransaction::processRelatedRefs(FlatpakResource* resource) { - m_appJobProgress = progress; + g_autoptr(GPtrArray) refs = nullptr; + g_autoptr(GError) error = nullptr; + g_autoptr(GCancellable) cancellable = g_cancellable_new();; + QList additionalResources; + + g_autofree gchar *ref = nullptr; + ref = g_strdup_printf ("%s/%s/%s/%s", + resource->typeAsString().toUtf8().constData(), + resource->flatpakName().toUtf8().constData(), + resource->arch().toUtf8().constData(), + resource->branch().toUtf8().constData()); + + if (role() == Transaction::Role::InstallRole) { + if (resource->state() == AbstractResource::Upgradeable) { + refs = flatpak_installation_list_installed_related_refs_sync(resource->installation(), resource->origin().toUtf8().constData(), ref, cancellable, &error); + if (error) { + qWarning() << "Failed to list installed related refs for update: " << error->message; + } + } else { + refs = flatpak_installation_list_remote_related_refs_sync(resource->installation(), resource->origin().toUtf8().constData(), ref, cancellable, &error); + if (error) { + qWarning() << "Failed to list related refs for installation: " << error->message; + } + } + } else if (role() == Transaction::Role::RemoveRole) { + refs = flatpak_installation_list_installed_related_refs_sync(resource->installation(), resource->origin().toUtf8().constData(), ref, cancellable, &error); + if (error) { + qWarning() << "Failed to list installed related refs for removal: " << error->message; + } + } - updateProgress(); + if (refs) { + for (uint i = 0; i < refs->len; i++) { + FlatpakRef *flatpakRef = FLATPAK_REF(g_ptr_array_index(refs, i)); + if (flatpak_related_ref_should_download(FLATPAK_RELATED_REF(flatpakRef))) { + QPointer job = new FlatpakTransactionJob(resource, QPair(QString::fromUtf8(flatpak_ref_get_name(flatpakRef)), flatpak_ref_get_kind(flatpakRef)), role(), this); + connect(job, &FlatpakTransactionJob::finished, this, &FlatpakTransaction::onJobFinished); + connect(job, &FlatpakTransactionJob::progressChanged, this, &FlatpakTransaction::onJobProgressChanged); + // Add to the list of all jobs + m_jobs << job; + } + } + } } -void FlatpakTransaction::onRuntimeJobFinished() +void FlatpakTransaction::onJobFinished() { - m_runtimeJobProgress = 100; + FlatpakTransactionJob *job = static_cast(sender()); - updateProgress(); + if (job != m_appJob) { + if (!job->result()) { + Q_EMIT passiveMessage(job->errorMessage()); + } - if (!m_runtimeJob->result()) { - Q_EMIT passiveMessage(m_runtimeJob->errorMessage()); - } else { - // This should be the only case when runtime is automatically installed, but better to check - if (role() == InstallRole) { - m_runtime->setState(AbstractResource::Installed); + // Mark runtime as installed + if (m_runtime && job->app()->flatpakName() == m_runtime->flatpakName() && !job->isRelated() && role() != Transaction::Role::RemoveRole) { + if (job->result()) { + m_runtime->setState(AbstractResource::Installed); + } } } - if (m_appJob->isFinished()) { - finishTransaction(); + foreach (const QPointer &job, m_jobs) { + if (job->isRunning()) { + return; + } } + + // No other job is running → finish transaction + finishTransaction(); } -void FlatpakTransaction::onRuntimeJobProgressChanged(int progress) +void FlatpakTransaction::onJobProgressChanged(int progress) { - m_runtimeJobProgress = progress; + Q_UNUSED(progress); - updateProgress(); -} + int total = 0; -void FlatpakTransaction::updateProgress() -{ - if (m_runtime) { - setProgress((m_appJobProgress + m_runtimeJobProgress) / 2); - } else { - setProgress(m_appJobProgress); + // Count progress from all the jobs + foreach (const QPointer &job, m_jobs) { + total += job->progress(); } + + setProgress(total / m_jobs.count()); } void FlatpakTransaction::finishTransaction() diff --git a/libdiscover/backends/FlatpakBackend/FlatpakTransactionJob.h b/libdiscover/backends/FlatpakBackend/FlatpakTransactionJob.h --- a/libdiscover/backends/FlatpakBackend/FlatpakTransactionJob.h +++ b/libdiscover/backends/FlatpakBackend/FlatpakTransactionJob.h @@ -35,24 +35,34 @@ { Q_OBJECT public: - FlatpakTransactionJob(FlatpakInstallation *installation, FlatpakResource *app, Transaction::Role role, QObject *parent = nullptr); + FlatpakTransactionJob(FlatpakResource *app, const QPair &relatedRef, Transaction::Role role, QObject *parent = nullptr); ~FlatpakTransactionJob(); void cancel(); void run() override; + FlatpakResource * app() const; + + bool isRelated() const; + + int progress() const; + void setProgress(int progress); + QString errorMessage() const; bool result() const; Q_SIGNALS: void progressChanged(int progress); private: bool m_result; + int m_progress; QString m_errorMessage; + QString m_relatedRef; + uint m_relatedRefKind; GCancellable *m_cancellable; FlatpakResource *m_app; - FlatpakInstallation *m_installation; +// FlatpakInstallation *m_installation; Transaction::Role m_role; }; diff --git a/libdiscover/backends/FlatpakBackend/FlatpakTransactionJob.cpp b/libdiscover/backends/FlatpakBackend/FlatpakTransactionJob.cpp --- a/libdiscover/backends/FlatpakBackend/FlatpakTransactionJob.cpp +++ b/libdiscover/backends/FlatpakBackend/FlatpakTransactionJob.cpp @@ -33,14 +33,18 @@ return; } + transactionJob->setProgress(progress); + Q_EMIT transactionJob->progressChanged(progress); } -FlatpakTransactionJob::FlatpakTransactionJob(FlatpakInstallation *installation, FlatpakResource *app, Transaction::Role role, QObject *parent) +FlatpakTransactionJob::FlatpakTransactionJob(FlatpakResource *app, const QPair &relatedRef, Transaction::Role role, QObject *parent) : QThread(parent) , m_result(false) + , m_progress(0) + , m_relatedRef(relatedRef.first) + , m_relatedRefKind(relatedRef.second) , m_app(app) - , m_installation(installation) , m_role(role) { m_cancellable = g_cancellable_new(); @@ -61,12 +65,15 @@ g_autoptr(GError) localError = nullptr; g_autoptr(FlatpakInstalledRef) ref = nullptr; + const QString refName = m_relatedRef.isEmpty() ? m_app->flatpakName() : m_relatedRef; + const uint kind = m_relatedRef.isEmpty() ? (uint)m_app->type() : m_relatedRefKind; + if (m_role == Transaction::Role::InstallRole) { if (m_app->state() == AbstractResource::Upgradeable) { - ref = flatpak_installation_update(m_installation, + ref = flatpak_installation_update(m_app->installation(), FLATPAK_UPDATE_FLAGS_NONE, - m_app->type() == FlatpakResource::DesktopApp ? FLATPAK_REF_KIND_APP : FLATPAK_REF_KIND_RUNTIME, - m_app->flatpakName().toUtf8().constData(), + kind == FlatpakResource::DesktopApp ? FLATPAK_REF_KIND_APP : FLATPAK_REF_KIND_RUNTIME, + refName.toUtf8().constData(), m_app->arch().toUtf8().constData(), m_app->branch().toUtf8().constData(), flatpakInstallationProgressCallback, @@ -76,45 +83,73 @@ if (m_app->flatpakFileType() == QStringLiteral("flatpak")) { g_autoptr(GFile) file = g_file_new_for_path(m_app->resourceFile().toLocalFile().toUtf8().constData()); if (!file) { - qWarning() << "Failed to install bundled application" << m_app->name(); + qWarning() << "Failed to install bundled application" << refName; } - ref = flatpak_installation_install_bundle(m_installation, file, flatpakInstallationProgressCallback, this, m_cancellable, &localError); + ref = flatpak_installation_install_bundle(m_app->installation(), file, flatpakInstallationProgressCallback, this, m_cancellable, &localError); } else { - ref = flatpak_installation_install(m_installation, - m_app->origin().toUtf8().constData(), - m_app->type() == FlatpakResource::DesktopApp ? FLATPAK_REF_KIND_APP : FLATPAK_REF_KIND_RUNTIME, - m_app->flatpakName().toUtf8().constData(), - m_app->arch().toUtf8().constData(), - m_app->branch().toUtf8().constData(), - flatpakInstallationProgressCallback, - this, - m_cancellable, &localError); + ref = flatpak_installation_install(m_app->installation(), + m_app->origin().toUtf8().constData(), + kind == FlatpakResource::DesktopApp ? FLATPAK_REF_KIND_APP : FLATPAK_REF_KIND_RUNTIME, + refName.toUtf8().constData(), + m_app->arch().toUtf8().constData(), + m_app->branch().toUtf8().constData(), + flatpakInstallationProgressCallback, + this, + m_cancellable, &localError); } } if (!ref) { m_result = false; m_errorMessage = QString::fromUtf8(localError->message); - qWarning() << "Failed to install" << m_app->name() << ':' << m_errorMessage; + // We are done so we can set the progress to 100 + m_progress = 100; + qWarning() << "Failed to install" << refName << ':' << m_errorMessage; return; } } else if (m_role == Transaction::Role::RemoveRole) { - if (!flatpak_installation_uninstall(m_installation, - m_app->type() == FlatpakResource::DesktopApp ? FLATPAK_REF_KIND_APP : FLATPAK_REF_KIND_RUNTIME, - m_app->flatpakName().toUtf8().constData(), + if (!flatpak_installation_uninstall(m_app->installation(), + kind == FlatpakResource::DesktopApp ? FLATPAK_REF_KIND_APP : FLATPAK_REF_KIND_RUNTIME, + refName.toUtf8().constData(), m_app->arch().toUtf8().constData(), m_app->branch().toUtf8().constData(), flatpakInstallationProgressCallback, this, m_cancellable, &localError)) { m_result = false; m_errorMessage = QString::fromUtf8(localError->message); - qWarning() << "Failed to uninstall" << m_app->name() << ':' << m_errorMessage; + // We are done so we can set the progress to 100 + m_progress = 100; + qWarning() << "Failed to uninstall" << refName << ':' << m_errorMessage; return; } } + // We are done so we can set the progress to 100 + m_progress = 100; m_result = true; + + Q_EMIT progressChanged(m_progress); +} + +FlatpakResource * FlatpakTransactionJob::app() const +{ + return m_app; +} + +bool FlatpakTransactionJob::isRelated() const +{ + return !m_relatedRef.isEmpty(); +} + +int FlatpakTransactionJob::progress() const +{ + return m_progress; +} + +void FlatpakTransactionJob::setProgress(int progress) +{ + m_progress = progress; } QString FlatpakTransactionJob::errorMessage() const