diff --git a/api/dbus/org.desktopspec.ApplicationManager1.Application.xml b/api/dbus/org.desktopspec.ApplicationManager1.Application.xml index 9298ca7..476cf87 100644 --- a/api/dbus/org.desktopspec.ApplicationManager1.Application.xml +++ b/api/dbus/org.desktopspec.ApplicationManager1.Application.xml @@ -17,6 +17,10 @@ + + + + - - - - - @@ -36,22 +31,5 @@ /> - - - - - - - - - diff --git a/src/constant.h b/src/constant.h index bffef65..ccf5161 100644 --- a/src/constant.h +++ b/src/constant.h @@ -16,10 +16,7 @@ constexpr auto DDEApplicationManager1ServiceName = #endif constexpr auto DDEApplicationManager1ObjectPath = u8"/org/deepin/dde/ApplicationManager1"; -constexpr auto DDEApplicationManager1ApplicationObjectPath = u8"/org/deepin/dde/ApplicationManager1/Application/"; -constexpr auto DDEApplicationManager1InstanceObjectPath = u8"/org/deepin/dde/ApplicationManager1/Instance/"; constexpr auto DDEApplicationManager1JobManagerObjectPath = u8"/org/deepin/dde/ApplicationManager1/JobManager1"; -constexpr auto DDEApplicationManager1JobObjectPath = u8"/org/deepin/dde/ApplicationManager1/JobManager1/Job/"; constexpr auto DesktopFileEntryKey = u8"Desktop Entry"; constexpr auto DesktopFileActionKey = u8"Desktop Action "; diff --git a/src/dbus/applicationmanager1service.cpp b/src/dbus/applicationmanager1service.cpp index dc28b02..19ef5d3 100644 --- a/src/dbus/applicationmanager1service.cpp +++ b/src/dbus/applicationmanager1service.cpp @@ -110,18 +110,6 @@ void ApplicationManager1Service::removeAllApplication() } } -QDBusObjectPath ApplicationManager1Service::Application(const QString &id) const -{ - auto ret = std::find_if(m_applicationList.cbegin(), m_applicationList.cend(), [&id](const auto &app) { - return static_cast(app->id() == id); - }); - - if (ret != m_applicationList.cend()) { - return ret.key(); - } - return {}; -} - QString ApplicationManager1Service::Identify(const QDBusUnixFileDescriptor &pidfd, QDBusObjectPath &application, QDBusObjectPath &application_instance) @@ -174,20 +162,6 @@ QString ApplicationManager1Service::Identify(const QDBusUnixFileDescriptor &pidf return ret.ApplicationId; } -QDBusObjectPath ApplicationManager1Service::Launch(const QString &id, - const QString &actions, - const QStringList &fields, - const QVariantMap &options) -{ - auto app = Application(id); - if (app.path().isEmpty()) { - qWarning() << "No such application."; - return {}; - } - const auto &value = m_applicationList.value(app); - return value->Launch(actions, fields, options); -} - void ApplicationManager1Service::updateApplication(const QSharedPointer &destApp, const DesktopFile &desktopFile) noexcept { diff --git a/src/dbus/applicationmanager1service.h b/src/dbus/applicationmanager1service.h index 52bdbc2..d798d85 100644 --- a/src/dbus/applicationmanager1service.h +++ b/src/dbus/applicationmanager1service.h @@ -30,15 +30,18 @@ public: Q_PROPERTY(QList List READ list) [[nodiscard]] QList list() const; + template bool addApplication(T &&desktopFileSource) { - QSharedPointer application{new ApplicationService{std::forward(desktopFileSource), this}}; + QSharedPointer application = makeApplication(std::forward(desktopFileSource), this); if (!application) { return false; } + auto *ptr = application.data(); new ApplicationAdaptor{ptr}; + if (!registerObjectToDBus(ptr, application->m_applicationPath.path(), getDBusInterface())) { return false; } @@ -53,9 +56,7 @@ public: JobManager1Service &jobManager() noexcept { return *m_jobManager; } public Q_SLOTS: - [[nodiscard]] QDBusObjectPath Application(const QString &id) const; QString Identify(const QDBusUnixFileDescriptor &pidfd, QDBusObjectPath &application, QDBusObjectPath &application_instance); - QDBusObjectPath Launch(const QString &id, const QString &action, const QStringList &fields, const QVariantMap &options); void UpdateApplicationInfo(const QStringList &appIdList); private: diff --git a/src/dbus/applicationservice.cpp b/src/dbus/applicationservice.cpp index 69ed66a..52f61e1 100644 --- a/src/dbus/applicationservice.cpp +++ b/src/dbus/applicationservice.cpp @@ -19,7 +19,9 @@ ApplicationService::~ApplicationService() m_desktopSource.destruct(m_isPersistence); for (auto &instance : m_Instances.values()) { instance->m_Application = QDBusObjectPath{"/"}; - instance->setParent(qApp); // detach all instances to qApp + auto *ptr = instance.get(); + instance.reset(nullptr); + ptr->setParent(qApp); // detach all instances to qApp } } @@ -132,8 +134,10 @@ QDBusObjectPath ApplicationService::Launch(QString action, QStringList fields, Q m_applicationPath.path(), [this, binary = std::move(bin), commands = std::move(cmds)](QVariant variantValue) mutable -> QVariant { auto resourceFile = variantValue.toString(); + auto instanceRandomUUID = QUuid::createUuid().toString(QUuid::Id128); + auto objectPath = m_applicationPath.path() + "/" + instanceRandomUUID; + if (resourceFile.isEmpty()) { - auto instanceRandomUUID = QUuid::createUuid().toString(QUuid::Id128); commands.push_front(QString{R"(--unitName=app-DDE-%1@%2.service)"}.arg( escapeApplicationId(this->id()), instanceRandomUUID)); // launcher should use this instanceId QProcess process; @@ -143,7 +147,7 @@ QDBusObjectPath ApplicationService::Launch(QString action, QStringList fields, Q qWarning() << "Launch Application Failed. exitCode:" << code; return QString{""}; } - return DDEApplicationManager1InstanceObjectPath + instanceRandomUUID; + return objectPath; } int location{0}; @@ -163,7 +167,6 @@ QDBusObjectPath ApplicationService::Launch(QString action, QStringList fields, Q // resourceFile must be available in the following contexts auto tmp = commands; tmp.insert(location, resourceFile); - auto instanceRandomUUID = QUuid::createUuid().toString(QUuid::Id128); tmp.push_front(QString{R"(--unitName=DDE-%1@%2.service)"}.arg(this->id(), instanceRandomUUID)); QProcess process; process.start(getApplicationLauncherBinary(), tmp); @@ -173,7 +176,7 @@ QDBusObjectPath ApplicationService::Launch(QString action, QStringList fields, Q qWarning() << "Launch Application Failed:" << binary << tmp; return QString{""}; } - return DDEApplicationManager1InstanceObjectPath + instanceRandomUUID; + return objectPath; }, std::move(res)); } @@ -239,11 +242,55 @@ QList ApplicationService::instances() const noexcept return m_Instances.keys(); } +QString ApplicationService::iconName() const noexcept +{ + if (m_entry.isNull()) { + qWarning() << "desktop entry is empty, isPersistence:" << m_isPersistence; + return {}; + } + + const auto &actions = m_entry->value(DesktopFileEntryKey, "Icon"); + if (!actions) { + return {}; + } + + bool ok{false}; + const auto &icon = actions->toIconString(ok); + if (!ok) { + qWarning() << "Icon convert to String failed."; + return {}; + } + + return icon; +} + +QString ApplicationService::displayName() const noexcept +{ + if (m_entry.isNull()) { + qWarning() << "desktop entry is empty, isPersistence:" << m_isPersistence; + return {}; + } + + const auto &actions = m_entry->value(DesktopFileEntryKey, "Name"); + if (!actions) { + return {}; + } + + bool ok{false}; + const auto &name = actions->toString(ok); + if (!ok) { + qWarning() << "Icon convert to String failed."; + return {}; + } + + return name; +} + bool ApplicationService::addOneInstance(const QString &instanceId, const QString &application, const QString &systemdUnitPath) { auto service = new InstanceService{instanceId, application, systemdUnitPath}; auto adaptor = new InstanceAdaptor(service); - QString objectPath{DDEApplicationManager1InstanceObjectPath + instanceId}; + QString objectPath{m_applicationPath.path() + "/" + instanceId}; if (registerObjectToDBus(service, objectPath, getDBusInterface())) { m_Instances.insert(QDBusObjectPath{objectPath}, QSharedPointer{service}); diff --git a/src/dbus/applicationservice.h b/src/dbus/applicationservice.h index de98e5f..1cccf57 100644 --- a/src/dbus/applicationservice.h +++ b/src/dbus/applicationservice.h @@ -14,6 +14,7 @@ #include #include #include +#include #include "dbus/instanceservice.h" #include "global.h" #include "desktopentry.h" @@ -47,6 +48,12 @@ public: Q_PROPERTY(QList Instances READ instances) [[nodiscard]] QList instances() const noexcept; + Q_PROPERTY(QString IconName READ iconName CONSTANT) + [[nodiscard]] QString iconName() const noexcept; + + Q_PROPERTY(QString DisplayName READ displayName CONSTANT) + [[nodiscard]] QString displayName() const noexcept; + [[nodiscard]] QDBusObjectPath findInstance(const QString &instanceId) const; [[nodiscard]] const QString &getLauncher() const noexcept { return m_launcher; } @@ -64,45 +71,13 @@ public Q_SLOTS: private: friend class ApplicationManager1Service; template - explicit ApplicationService(T &&source, ApplicationManager1Service *parent = nullptr) - : m_parent(parent) + friend QSharedPointer makeApplication(T &&source, ApplicationManager1Service *parent); + + template + explicit ApplicationService(T &&source) + : m_isPersistence(static_cast(std::is_same_v)) , m_desktopSource(std::forward(source)) { - static_assert(std::is_same_v or std::is_same_v, "param type must be QString or DesktopFile."); - QString objectPath{DDEApplicationManager1ApplicationObjectPath}; - QTextStream sourceStream; - QFile sourceFile; - auto dbusAppid = m_desktopSource.m_file.desktopId(); - - if constexpr (std::is_same_v) { - m_applicationPath = -#ifdef DEBUG_MODE - QDBusObjectPath{objectPath + escapeToObjectPath(dbusAppid)}; -#else - QDBusObjectPath{objectPath + QUuid::createUuid().toString(QUuid::Id128)}; -#endif - m_isPersistence = true; - sourceFile.setFileName(m_desktopSource.m_file.filePath()); - if (!sourceFile.open(QFile::ExistingOnly | QFile::ReadOnly | QFile::Text)) { -#ifndef DEBUG_MODE - qCritical() << "desktop file can't open:" << m_desktopSource.m_file.filePath() << sourceFile.errorString(); -#endif - return; - } - sourceStream.setDevice(&sourceFile); - } else if (std::is_same_v) { - m_applicationPath = QDBusObjectPath{objectPath + QUuid::createUuid().toString(QUuid::Id128)}; - m_isPersistence = false; - sourceStream.setString(&m_desktopSource.m_temp, QTextStream::ReadOnly | QTextStream::Text); - } - m_entry.reset(new DesktopEntry()); - if (auto error = m_entry->parse(sourceStream); error != DesktopErrorCode::NoError) { - if (error != DesktopErrorCode::EntryKeyInvalid) { - m_entry.reset(nullptr); - return; - } - } - // TODO: icon lookup } bool m_AutoStart{false}; @@ -143,4 +118,58 @@ private: [[nodiscard]] LaunchTask unescapeExec(const QString &str, const QStringList &fields); }; +template +QSharedPointer makeApplication(T &&source, ApplicationManager1Service *parent) +{ + static_assert(std::is_same_v or std::is_same_v, "param type must be QString or DesktopFile."); + QString objectPath; + QTextStream sourceStream; + QFile sourceFile; + QSharedPointer app{nullptr}; + + if constexpr (std::is_same_v) { + DesktopFile in{std::forward(source)}; + objectPath = QString{DDEApplicationManager1ObjectPath} + "/" + escapeToObjectPath(in.desktopId()); + sourceFile.setFileName(in.filePath()); + + if (!sourceFile.open(QFile::ExistingOnly | QFile::ReadOnly | QFile::Text)) { + qCritical() << "desktop file can't open:" << in.filePath() << sourceFile.errorString(); + return nullptr; + } + + app.reset(new ApplicationService{std::move(in)}); + sourceStream.setDevice(&sourceFile); + } else if (std::is_same_v) { + QString in{std::forward(source)}; + objectPath = QString{DDEApplicationManager1ObjectPath} + "/" + QUuid::createUuid().toString(QUuid::Id128); + + app.reset(new ApplicationService{std::move(in)}); + sourceStream.setString(&in, QTextStream::ReadOnly | QTextStream::Text); + } + + std::unique_ptr entry{std::make_unique()}; + auto error = entry->parse(sourceStream); + + if (error != DesktopErrorCode::NoError) { + if (error != DesktopErrorCode::EntryKeyInvalid) { + return nullptr; + } + } + + if (auto val = entry->value(DesktopFileEntryKey, "Hidden"); val.has_value()) { + bool ok{false}; + if (auto hidden = val.value().toBoolean(ok); ok and hidden) { + return nullptr; + } + } + + app->m_parent = parent; + app->m_entry.reset(entry.release()); + app->m_applicationPath = QDBusObjectPath{std::move(objectPath)}; + + // TODO: icon lookup + + return app; +} + #endif diff --git a/src/dbus/jobmanager1service.h b/src/dbus/jobmanager1service.h index b64291e..23801aa 100644 --- a/src/dbus/jobmanager1service.h +++ b/src/dbus/jobmanager1service.h @@ -50,7 +50,8 @@ public: { static_assert(std::is_invocable_v, "param type must be QVariant."); - QString objectPath{DDEApplicationManager1JobObjectPath + QUuid::createUuid().toString(QUuid::Id128)}; + QString objectPath = + QString{"%1/%2"}.arg(DDEApplicationManager1JobManagerObjectPath).arg(QUuid::createUuid().toString(QUuid::Id128)); QFuture future = QtConcurrent::mappedReduced(std::move(args), func, qOverload(&QVariantList::append), diff --git a/src/dbus/jobservice.cpp b/src/dbus/jobservice.cpp index e80368f..4549195 100644 --- a/src/dbus/jobservice.cpp +++ b/src/dbus/jobservice.cpp @@ -11,7 +11,7 @@ JobService::JobService(const QFuture &job) JobService::~JobService() = default; -QString JobService::status() const +QString JobService::status() const // FIXME: job status aren't mutually exclusive { if (m_job.isFinished()) { return "finished"; diff --git a/src/desktopentry.cpp b/src/desktopentry.cpp index 881d135..52c3141 100644 --- a/src/desktopentry.cpp +++ b/src/desktopentry.cpp @@ -56,7 +56,9 @@ DesktopErrorCode DesktopEntry::parseEntry(const QString &str, decltype(m_entryMa re.optimize(); auto matcher = re.match(keyStr); if (!matcher.hasMatch()) { +#ifdef DEBUG_MODE qWarning() << "invalid key: " << keyStr; +#endif return DesktopErrorCode::EntryKeyInvalid; } @@ -205,7 +207,9 @@ DesktopErrorCode DesktopEntry::parse(QTextStream &stream) noexcept if (auto error = parseEntry(line, currentGroup); error != DesktopErrorCode::NoError) { err = error; +#ifdef DEBUG_MODE qWarning() << "an error occurred,this line will be skipped:" << line; +#endif } } return err; @@ -223,13 +227,17 @@ std::optional DesktopEntry::value(const QString &groupKey, { const auto &destGroup = group(groupKey); if (!destGroup) { +#ifdef DEBUG_MODE qWarning() << "group " << groupKey << " can't be found."; +#endif return std::nullopt; } auto it = destGroup->find(valueKey); if (it == destGroup->cend()) { +#ifdef DEBUG_MODE qWarning() << "value " << valueKey << " can't be found."; +#endif return std::nullopt; } return *it; diff --git a/tests/ut_applicationmanager.cpp b/tests/ut_applicationmanager.cpp index d44caf8..adec108 100644 --- a/tests/ut_applicationmanager.cpp +++ b/tests/ut_applicationmanager.cpp @@ -44,23 +44,11 @@ public: static void TearDownTestCase() { m_am->deleteLater(); } static inline ApplicationManager1Service *m_am{nullptr}; - const static inline QDBusObjectPath ApplicationPath{DDEApplicationManager1ApplicationObjectPath + + const static inline QDBusObjectPath ApplicationPath{QString{DDEApplicationManager1ObjectPath} + "/" + QUuid::createUuid().toString(QUuid::Id128)}; - const static inline QDBusObjectPath InstancePath{DDEApplicationManager1InstanceObjectPath + - QUuid::createUuid().toString(QUuid::Id128)}; + const static inline QDBusObjectPath InstancePath{ApplicationPath.path() + "/" + QUuid::createUuid().toString(QUuid::Id128)}; }; -TEST_F(TestApplicationManager, list) -{ - auto lists = m_am->list(); - EXPECT_EQ(lists.first(), ApplicationPath); -} - -TEST_F(TestApplicationManager, application) -{ - EXPECT_EQ(m_am->Application("test-Application"), ApplicationPath); -} - TEST_F(TestApplicationManager, identifyService) { using namespace std::chrono_literals;