diff --git a/src/appimageservices-interface/org.appimage.Services.Inspector.xml b/src/appimageservices-interface/org.appimage.Services.Inspector.xml
index ee9bf4a2c4ef12c97a980443fbc88bf4b69596f9..93b4f937c01672fe1a8a33dd5a921bd605ca232f 100644
--- a/src/appimageservices-interface/org.appimage.Services.Inspector.xml
+++ b/src/appimageservices-interface/org.appimage.Services.Inspector.xml
@@ -5,6 +5,11 @@
+
+
+
+
+
diff --git a/src/bin/CMakeLists.txt b/src/bin/CMakeLists.txt
index e2680510be15ff152af27abe7aee8daca5e3d47a..305f0cc9a018602f7f1247d9528ca97ac83882b6 100644
--- a/src/bin/CMakeLists.txt
+++ b/src/bin/CMakeLists.txt
@@ -2,9 +2,11 @@ add_executable(
plasma-appimage-integration
main.cpp
UpdateJob.cpp
+ RegisterJob.cpp
RemoveJob.cpp
InstallJob.cpp
UninstallJob.cpp
+ TargetDataLoader.cpp
)
target_link_libraries(plasma-appimage-integration appimageservices-interfaces KF5::KIOWidgets KF5::I18n KF5::Notifications)
diff --git a/src/bin/InstallJob.cpp b/src/bin/InstallJob.cpp
index 171c73c6ca4bfb5656d148cf5c2bb516c061e5d3..6bee3e7e59d866e125d31163dff03ab254bb51e3 100644
--- a/src/bin/InstallJob.cpp
+++ b/src/bin/InstallJob.cpp
@@ -15,8 +15,8 @@ void InstallJob::start() {
qDebug() << "calling pkexec appimage-services install " << target;
connect(&process, SIGNAL(finished(int)), this, SLOT(onProcessFinished(int)));
- description(this, i18n("Installing Application"),
- qMakePair(i18nc("The AppImage being installed", "Source"), target));
+ description(this, i18n("Installing application"),
+ qMakePair(i18nc("The AppImage being installed", "Application"), target));
process.start();
}
diff --git a/src/bin/RegisterJob.cpp b/src/bin/RegisterJob.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3e05fe35a47115ae205a6c467607d343bd28bf6d
--- /dev/null
+++ b/src/bin/RegisterJob.cpp
@@ -0,0 +1,37 @@
+// libraries
+#include
+
+// local
+#include "RegisterJob.h"
+#include "LauncherInterface.h"
+
+RegisterJob::RegisterJob(const QString& target, QObject* parent)
+ : KJob(parent), target(target),
+ launcherInterface(new OrgAppimageServices1LauncherInterface("org.appimage.Services1.Launcher",
+ "/org/appimage/Services1/Launcher",
+ QDBusConnection::sessionBus(), this)) {}
+
+void RegisterJob::start() {
+ description(this, i18n("Creating launcher entry"),
+ qMakePair(i18nc("Target AppImage", "Application"), target));
+
+ auto reply = launcherInterface->registerApp(target);
+ if (reply.isError()) {
+ setError(-1);
+ setErrorText(reply.error().message());
+ }
+
+ auto* watcher = new QDBusPendingCallWatcher(reply, this);
+
+ QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, &RegisterJob::callFinishedSlot);
+}
+
+void RegisterJob::callFinishedSlot(QDBusPendingCallWatcher* watcher) {
+ if (watcher->isError()) {
+ setError(-1);
+ setErrorText(watcher->error().message());
+ }
+
+ // notify result delayed
+ QTimer::singleShot(1000, this, &RegisterJob::emitResult);
+}
diff --git a/src/bin/RegisterJob.h b/src/bin/RegisterJob.h
new file mode 100644
index 0000000000000000000000000000000000000000..6de4b60993180c55825d68238bc39cbeb56f0763
--- /dev/null
+++ b/src/bin/RegisterJob.h
@@ -0,0 +1,24 @@
+#pragma once
+
+// libraries
+#include
+#include
+
+class QDBusPendingCallWatcher;
+
+class OrgAppimageServices1LauncherInterface;
+
+class RegisterJob : public KJob {
+public:
+ RegisterJob(const QString& target, QObject* parent = nullptr);
+
+ void start() override;
+
+protected slots:
+
+ void callFinishedSlot(QDBusPendingCallWatcher* watcher);
+
+private:
+ QString target;
+ OrgAppimageServices1LauncherInterface* launcherInterface;
+};
diff --git a/src/bin/RemoveJob.cpp b/src/bin/RemoveJob.cpp
index 04268f046960ac88eb7002d86d0856f9a9cc610a..383fbcf92a7856f537afb782577247235e08a9f8 100644
--- a/src/bin/RemoveJob.cpp
+++ b/src/bin/RemoveJob.cpp
@@ -6,16 +6,19 @@
#include "LauncherInterface.h"
RemoveJob::RemoveJob(const QString& target, QObject* parent)
- : KJob(parent), target(target),
- launcherInterface(new OrgAppimageServices1LauncherInterface("org.appimage.Services1.Launcher",
- "/org/appimage/Services1/Launcher",
- QDBusConnection::sessionBus(), this)) {}
+ : KJob(parent), target(target),
+ launcherInterface(new OrgAppimageServices1LauncherInterface("org.appimage.Services1.Launcher",
+ "/org/appimage/Services1/Launcher",
+ QDBusConnection::sessionBus(), this)) {}
void RemoveJob::start() {
+ description(this, i18n("Removing launcher entry"),
+ qMakePair(i18nc("Target AppImage", "Application"), target));
+
auto reply = launcherInterface->unregisterApp(target);
if (reply.isError()) {
setError(-1);
- setErrorText(i18n("Remove failed: %0").arg(reply.error().message()));
+ setErrorText(reply.error().message());
}
QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this);
@@ -26,9 +29,9 @@ void RemoveJob::start() {
void RemoveJob::callFinishedSlot(QDBusPendingCallWatcher* watcher) {
if (watcher->isError()) {
setError(-1);
- setErrorText(i18n("Remove failed: %0").arg(watcher->error().message()));
+ setErrorText(watcher->error().message());
} else
- description(this, i18n("Application successfully removed"));
+ infoMessage(this, i18n("Entry removed"));
// notify result delayed
QTimer::singleShot(1000, this, &RemoveJob::emitResult);
diff --git a/src/bin/TargetDataLoader.cpp b/src/bin/TargetDataLoader.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0dbcad8cdf0de21fd937917a04f37513f5730744
--- /dev/null
+++ b/src/bin/TargetDataLoader.cpp
@@ -0,0 +1,81 @@
+// libraries
+#include
+#include
+
+// local
+#include "InspectorInterface.h"
+#include "TargetDataLoader.h"
+
+TargetDataLoader::TargetDataLoader(QString target, QObject* parent) : QObject(parent), target(std::move(target)) {}
+
+void TargetDataLoader::loadTargetDataIntoApplication() {
+ auto inspectorInterface = new OrgAppimageServices1InspectorInterface("org.appimage.Services1.Inspector",
+ "/org/appimage/Services1/Inspector",
+ QDBusConnection::sessionBus(), this);
+ loadApplicationIcon(inspectorInterface);
+ loadApplicationName(inspectorInterface);
+}
+
+void TargetDataLoader::loadApplicationName(OrgAppimageServices1InspectorInterface* inspectorInterface) const {
+ auto reply = inspectorInterface->getApplicationInfo(target);
+ if (reply.isError())
+ qWarning() << "Unable to fetch AppImage information " << reply.error().message();
+
+ else {
+ QString response = reply.value();
+ QJsonDocument document = QJsonDocument::fromJson(response.toLocal8Bit());
+ QJsonObject root = document.object();
+
+ QString nameValue = root.value("name").toString();
+ if (!nameValue.isEmpty())
+ QApplication::setApplicationName(nameValue);
+ }
+}
+
+void TargetDataLoader::loadApplicationIcon(OrgAppimageServices1InspectorInterface* inspectorInterface) {
+ QTemporaryFile temporaryIconFile("XXXXXX");
+ if (temporaryIconFile.open()) {
+ auto reply = inspectorInterface->extractApplicationIcon(target, temporaryIconFile.fileName());
+ if (reply.isError())
+ qWarning() << "Unable to fetch AppImage icon " << reply.error().message();
+
+ QString themeIconName = "application-vnd.appimage";
+ QImage applicationIcon(temporaryIconFile.fileName());
+ if (!applicationIcon.isNull()) {
+ // scale the icon to a valid icon size
+ applicationIcon = applicationIcon.scaled(512, 512, Qt::KeepAspectRatio);
+
+ // prepare target dir
+ QString dirPath = QDir::homePath() + "/.local/share/icons/hicolor/%1x%1/apps/";
+ dirPath = dirPath.arg(applicationIcon.height());
+ QDir::home().mkpath(dirPath);
+
+ // prepare file name
+ QFileInfo temporaryIconFileInfo(temporaryIconFile);
+ QString fileName = "plasma-appimage-integration_%2.png";
+ fileName = fileName.arg(temporaryIconFileInfo.baseName());
+
+ // save the icon to the right place on the hicolor theme to make it accessible from the kjob dialog
+ temporaryAppIconPath = dirPath + fileName;
+ if (applicationIcon.save(temporaryAppIconPath, "PNG")) {
+ // use the temporary created icon
+ themeIconName = "plasma-appimage-integration_" + temporaryIconFileInfo.baseName();
+ } else {
+ qWarning() << "unable so save icon to " << temporaryAppIconPath;
+ }
+ }
+
+ // double check that the icon was properly registered in the theme before using it
+ if (QIcon::hasThemeIcon(themeIconName)) {
+ QApplication::setWindowIcon(QIcon::fromTheme(themeIconName));
+ } else {
+ qDebug() << "Icon not found in theme " << themeIconName;
+ QApplication::setWindowIcon(QIcon::fromTheme("application-vnd.appimage"));
+ }
+ }
+}
+
+TargetDataLoader::~TargetDataLoader() {
+ if (QFile::exists(temporaryAppIconPath))
+ QFile::remove(temporaryAppIconPath);
+}
diff --git a/src/bin/TargetDataLoader.h b/src/bin/TargetDataLoader.h
new file mode 100644
index 0000000000000000000000000000000000000000..795370321b241ee23cfe1240622149d6d34fdd5f
--- /dev/null
+++ b/src/bin/TargetDataLoader.h
@@ -0,0 +1,25 @@
+#pragma once
+
+// libraries
+#include
+#include
+
+class OrgAppimageServices1InspectorInterface;
+
+class TargetDataLoader : public QObject {
+Q_OBJECT
+public:
+ explicit TargetDataLoader(QString target, QObject* parent = nullptr);
+
+ ~TargetDataLoader() override;
+
+ void loadTargetDataIntoApplication();
+
+private:
+ QString target;
+ QString temporaryAppIconPath;
+
+ void loadApplicationIcon(OrgAppimageServices1InspectorInterface* inspectorInterface);
+
+ void loadApplicationName(OrgAppimageServices1InspectorInterface* inspectorInterface) const;
+};
\ No newline at end of file
diff --git a/src/bin/UninstallJob.cpp b/src/bin/UninstallJob.cpp
index 4b9ffd6d8273cc3023b7729622282578ff666f72..fc81325c1aec041165bec99ffe13a4d2b355967c 100644
--- a/src/bin/UninstallJob.cpp
+++ b/src/bin/UninstallJob.cpp
@@ -15,8 +15,8 @@ void UninstallJob::start() {
qDebug() << "calling pkexec appimage-services install " << target;
connect(&process, SIGNAL(finished(int)), this, SLOT(onProcessFinished(int)));
- description(this, i18n("Uninstalling Application"),
- qMakePair(i18nc("The AppImage being uninstalled", "Source"), target));
+ description(this, i18n("Uninstalling application"),
+ qMakePair(i18nc("The AppImage being uninstalled", "Application"), target));
process.start();
}
@@ -27,6 +27,8 @@ void UninstallJob::onProcessFinished(int exitCode) {
if (exitCode != 0) {
setError(exitCode);
setErrorText(process.readAllStandardError());
+ } else {
+ infoMessage(this, i18n("Application uninstalled"));
}
// notify result delayed
diff --git a/src/bin/UpdateJob.cpp b/src/bin/UpdateJob.cpp
index c3d26f24b8b0577cf857b59ee377a664ba536aa3..cdbfb0561f082088521fdf58029116186f132f32 100644
--- a/src/bin/UpdateJob.cpp
+++ b/src/bin/UpdateJob.cpp
@@ -1,6 +1,4 @@
// libraries
-#include
-#include
#include
#include
#include
@@ -25,16 +23,13 @@ void UpdateJob::onBytesReceivedChanged(int value) {
void UpdateJob::onStateChanged(int state) {
switch (state) {
case 10:
- description(this, i18nc("Job heading, like 'Copying'", "Reading update data"),
- qMakePair(i18nc("The AppImage being updated", "Source"), target));
+ infoMessage(this, i18nc("Job heading, like 'Copying'", "Reading update data"));
break;
case 20:
- description(this, i18nc("Job heading, like 'Copying'", "Looking for updates"),
- qMakePair(i18nc("The AppImage being updated", "Source"), target));
+ infoMessage(this, i18nc("Job heading, like 'Copying'", "Looking for updates"));
break;
case 30:
- description(this, i18nc("Job heading, like 'Copying'", "Downloading"),
- qMakePair(i18nc("The AppImage being updated", "Source"), target));
+ infoMessage(this, i18nc("Job heading, like 'Copying'", "Downloading update"));
break;
// final states
case 21:
@@ -58,6 +53,9 @@ bool UpdateJob::doKill() {
}
void UpdateJob::start() {
+ description(this, i18nc("Job heading, like 'Copying'", "Updating application"),
+ qMakePair(i18nc("The AppImage being updated", "Application"), target));
+
if (error() == 0)
connectUpdaterInterface();
@@ -146,15 +144,6 @@ void UpdateJob::onError(int errorCode) {
setErrorText(errorTitle);
}
-void UpdateJob::notifyError(const QString& title, const QString& message, QWidget* parentWidget) {
- KNotification* notify = new KNotification(QStringLiteral("notification"), parentWidget,
- KNotification::CloseOnTimeout | KNotification::DefaultEvent);
- notify->setTitle(title);
- notify->setText(message);
- notify->setIconName("dialog-warning");
- notify->sendEvent();
-}
-
void UpdateJob::emitResultDelayed() {
QTimer::singleShot(400, [this]() {
emitResult();
diff --git a/src/bin/UpdateJob.h b/src/bin/UpdateJob.h
index 79f09c4a91a720f0886f8442f6814982b4e263a3..a9a7a727ae99a442009a061dcbb3f0636e844703 100644
--- a/src/bin/UpdateJob.h
+++ b/src/bin/UpdateJob.h
@@ -20,8 +20,6 @@ public:
void start() override;
- static void notifyError(const QString& title, const QString& message, QWidget* parentWidget = nullptr);
-
protected slots:
void onBytesTotalChanged(int total);
diff --git a/src/bin/main.cpp b/src/bin/main.cpp
index 22b809c0be0370ed84a768463c13b97841064b57..9744618b65b9e7bca1b90370404478fe5f94b440 100644
--- a/src/bin/main.cpp
+++ b/src/bin/main.cpp
@@ -10,9 +10,11 @@
// local
#include "UpdateJob.h"
+#include "RegisterJob.h"
#include "RemoveJob.h"
#include "InstallJob.h"
#include "UninstallJob.h"
+#include "TargetDataLoader.h"
QString parseTarget(QCommandLineParser& parser) {
@@ -32,41 +34,6 @@ QString parseTarget(QCommandLineParser& parser) {
}
}
-void executeUpdateCommand(const QString& target) {
- KJob* job = new UpdateJob(target);
-
- KIO::getJobTracker()->registerJob(job);
- job->start();
-
- if (job->error() != 0)
- UpdateJob::notifyError(i18n("Update failed").arg(target), job->errorString());
-}
-
-void executeRemoveCommand(const QString& target) {
- KJob* job = new RemoveJob(target);
-
- KIO::getJobTracker()->registerJob(job);
- job->start();
-
- if (job->error() != 0)
- UpdateJob::notifyError(i18n("Remove failed").arg(target), job->errorString());
-
-}
-
-void executeInstallCommand(const QString& target) {
- KJob* job = new InstallJob(target);
-
- KIO::getJobTracker()->registerJob(job);
- job->start();
-}
-
-void executeUninstallCommand(const QString& target) {
- KJob* job = new UninstallJob(target);
-
- KIO::getJobTracker()->registerJob(job);
- job->start();
-}
-
int main(int argc, char** argv) {
QApplication app(argc, argv);
QApplication::setApplicationName("plasma-appimage-integration");
@@ -88,25 +55,43 @@ int main(int argc, char** argv) {
const QString& command = positionalArguments.at(0);
+ QString target;
+ KJob* job = nullptr;
if (command == "update") {
- QString target = parseTarget(parser);
- executeUpdateCommand(target);
+ target = parseTarget(parser);
+ job = new UpdateJob(target);
}
if (command == "remove") {
- QString target = parseTarget(parser);
- executeRemoveCommand(target);
+ target = parseTarget(parser);
+ job = new RemoveJob(target);
+ }
+
+ if (command == "register") {
+ target = parseTarget(parser);
+ job = new RegisterJob(target);
}
if (command == "install") {
- QString target = parseTarget(parser);
- executeInstallCommand(target);
+ target = parseTarget(parser);
+ job = new InstallJob(target);
}
if (command == "uninstall") {
- QString target = parseTarget(parser);
- executeUninstallCommand(target);
+ target = parseTarget(parser);
+ job = new UninstallJob(target);
}
+
+ if (!target.isEmpty()) {
+ auto targetDataLoader = new TargetDataLoader(target, &app);
+ targetDataLoader->loadTargetDataIntoApplication();
+ }
+
+ if (job != nullptr) {
+ KIO::getJobTracker()->registerJob(job);
+ QMetaObject::invokeMethod(job, "start");
+ }
+
return QApplication::exec();
}
diff --git a/src/fileitem-actions/AppImageFileItemActions.cpp b/src/fileitem-actions/AppImageFileItemActions.cpp
index 8552cffc442e0fab712685968713261b9d4b8c38..60094c50589e1276b5337c9f4fe85c4187ccdbf0 100644
--- a/src/fileitem-actions/AppImageFileItemActions.cpp
+++ b/src/fileitem-actions/AppImageFileItemActions.cpp
@@ -94,32 +94,26 @@ QAction *AppImageFileItemActions::createInstallAction(const KFileItemListPropert
void AppImageFileItemActions::addToMenu() {
const QList urls = sender()->property("urls").value>();
- QWidget* parentWidget = sender()->property("parentWidget").value();
-
- QList> replies;
- for (const QUrl& url : urls)
- replies += launcherInterface->registerApp(url.toString());
-
- QString errorTitle = i18n("Add to launcher failed");
- for (QDBusPendingReply& reply: replies) {
- reply.waitForFinished();
-
- if (reply.isError())
- showErrorMessage(errorTitle, reply.error().message(), parentWidget);
- else {
- // notify failed operation
- if (!reply.value()) {
- QString url = urls.at(replies.indexOf(reply)).toString();
- showErrorMessage(errorTitle, i18n("\"%0\"\nthe file seems broken").arg(url), parentWidget);
- }
- }
+ QString program = "plasma-appimage-integration";
+
+ for (const QUrl& url : urls) {
+ QStringList arguments;
+ arguments << "register" << url.toLocalFile();
+
+ QProcess::startDetached(program, arguments);
}
}
void AppImageFileItemActions::removeFromMenu() {
const QList urls = sender()->property("urls").value>();
- for (const QUrl& url : urls)
- launcherInterface->unregisterApp(url.toString());
+ QString program = "plasma-appimage-integration";
+
+ for (const QUrl& url : urls) {
+ QStringList arguments;
+ arguments << "remove" << url.toLocalFile();
+
+ QProcess::startDetached(program, arguments);
+ }
}
void AppImageFileItemActions::update() {