feat: change dbus interface api

1. remove method: Application, Launch. (ApplicationManager1)
2. add property: IconName, DisplayName. (Application1)
3. refact the way of construct ApplicationService.
4. if Desktop Entry Key `Hidden` is true, this application wouldn't
   export to DBus.

Signed-off-by: ComixHe <heyuming@deepin.org>
This commit is contained in:
ComixHe 2023-08-16 17:44:56 +08:00 committed by Comix
parent a3dd315e33
commit bb83716d27
11 changed files with 140 additions and 113 deletions

View File

@ -17,6 +17,10 @@
<property name="AutoStart" type="b" access="readwrite"/> <property name="AutoStart" type="b" access="readwrite"/>
<property name="IconName" type="s" access="read"/>
<property name="DisplayName" type="s" access="read"/>
<property name="Instances" type="ao" access="read"> <property name="Instances" type="ao" access="read">
<annotation <annotation
name="org.freedesktop.DBus.Description" name="org.freedesktop.DBus.Description"

View File

@ -12,11 +12,6 @@
/> />
</method> </method>
<method name="Application">
<arg type="s" name="id" direction="in" />
<arg type="o" name="application" direction="out" />
</method>
<method name="Identify"> <method name="Identify">
<arg type="h" name="pidfd" direction="in" /> <arg type="h" name="pidfd" direction="in" />
@ -36,22 +31,5 @@
/> />
</method> </method>
<method name="Launch">
<arg type="s" name="id" direction="in" />
<arg type="s" name="action" direction="in" />
<arg type="as" name="fields" direction="in" />
<arg type="a{sv}" name="options" direction="in" />
<arg type="o" name="job" direction="out" />
<annotation name="org.qtproject.QtDBus.QtTypeName.In3" value="QVariantMap"/>
<annotation
name="org.freedesktop.DBus.Description"
value="Given a desktop file id,
this method will launch a desktop application.
Check description of the `Launch` method in
`org.desktopspec.ApplicationManager1.Application`
interface for futher infomation."
/>
</method>
</interface> </interface>
</node> </node>

View File

@ -16,10 +16,7 @@ constexpr auto DDEApplicationManager1ServiceName =
#endif #endif
constexpr auto DDEApplicationManager1ObjectPath = u8"/org/deepin/dde/ApplicationManager1"; 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 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 DesktopFileEntryKey = u8"Desktop Entry";
constexpr auto DesktopFileActionKey = u8"Desktop Action "; constexpr auto DesktopFileActionKey = u8"Desktop Action ";

View File

@ -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<bool>(app->id() == id);
});
if (ret != m_applicationList.cend()) {
return ret.key();
}
return {};
}
QString ApplicationManager1Service::Identify(const QDBusUnixFileDescriptor &pidfd, QString ApplicationManager1Service::Identify(const QDBusUnixFileDescriptor &pidfd,
QDBusObjectPath &application, QDBusObjectPath &application,
QDBusObjectPath &application_instance) QDBusObjectPath &application_instance)
@ -174,20 +162,6 @@ QString ApplicationManager1Service::Identify(const QDBusUnixFileDescriptor &pidf
return ret.ApplicationId; 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<ApplicationService> &destApp, void ApplicationManager1Service::updateApplication(const QSharedPointer<ApplicationService> &destApp,
const DesktopFile &desktopFile) noexcept const DesktopFile &desktopFile) noexcept
{ {

View File

@ -30,15 +30,18 @@ public:
Q_PROPERTY(QList<QDBusObjectPath> List READ list) Q_PROPERTY(QList<QDBusObjectPath> List READ list)
[[nodiscard]] QList<QDBusObjectPath> list() const; [[nodiscard]] QList<QDBusObjectPath> list() const;
template <typename T> template <typename T>
bool addApplication(T &&desktopFileSource) bool addApplication(T &&desktopFileSource)
{ {
QSharedPointer<ApplicationService> application{new ApplicationService{std::forward<T>(desktopFileSource), this}}; QSharedPointer<ApplicationService> application = makeApplication(std::forward<T>(desktopFileSource), this);
if (!application) { if (!application) {
return false; return false;
} }
auto *ptr = application.data(); auto *ptr = application.data();
new ApplicationAdaptor{ptr}; new ApplicationAdaptor{ptr};
if (!registerObjectToDBus(ptr, application->m_applicationPath.path(), getDBusInterface<ApplicationAdaptor>())) { if (!registerObjectToDBus(ptr, application->m_applicationPath.path(), getDBusInterface<ApplicationAdaptor>())) {
return false; return false;
} }
@ -53,9 +56,7 @@ public:
JobManager1Service &jobManager() noexcept { return *m_jobManager; } JobManager1Service &jobManager() noexcept { return *m_jobManager; }
public Q_SLOTS: public Q_SLOTS:
[[nodiscard]] QDBusObjectPath Application(const QString &id) const;
QString Identify(const QDBusUnixFileDescriptor &pidfd, QDBusObjectPath &application, QDBusObjectPath &application_instance); 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); void UpdateApplicationInfo(const QStringList &appIdList);
private: private:

View File

@ -19,7 +19,9 @@ ApplicationService::~ApplicationService()
m_desktopSource.destruct(m_isPersistence); m_desktopSource.destruct(m_isPersistence);
for (auto &instance : m_Instances.values()) { for (auto &instance : m_Instances.values()) {
instance->m_Application = QDBusObjectPath{"/"}; 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(), m_applicationPath.path(),
[this, binary = std::move(bin), commands = std::move(cmds)](QVariant variantValue) mutable -> QVariant { [this, binary = std::move(bin), commands = std::move(cmds)](QVariant variantValue) mutable -> QVariant {
auto resourceFile = variantValue.toString(); auto resourceFile = variantValue.toString();
auto instanceRandomUUID = QUuid::createUuid().toString(QUuid::Id128);
auto objectPath = m_applicationPath.path() + "/" + instanceRandomUUID;
if (resourceFile.isEmpty()) { if (resourceFile.isEmpty()) {
auto instanceRandomUUID = QUuid::createUuid().toString(QUuid::Id128);
commands.push_front(QString{R"(--unitName=app-DDE-%1@%2.service)"}.arg( commands.push_front(QString{R"(--unitName=app-DDE-%1@%2.service)"}.arg(
escapeApplicationId(this->id()), instanceRandomUUID)); // launcher should use this instanceId escapeApplicationId(this->id()), instanceRandomUUID)); // launcher should use this instanceId
QProcess process; QProcess process;
@ -143,7 +147,7 @@ QDBusObjectPath ApplicationService::Launch(QString action, QStringList fields, Q
qWarning() << "Launch Application Failed. exitCode:" << code; qWarning() << "Launch Application Failed. exitCode:" << code;
return QString{""}; return QString{""};
} }
return DDEApplicationManager1InstanceObjectPath + instanceRandomUUID; return objectPath;
} }
int location{0}; int location{0};
@ -163,7 +167,6 @@ QDBusObjectPath ApplicationService::Launch(QString action, QStringList fields, Q
// resourceFile must be available in the following contexts // resourceFile must be available in the following contexts
auto tmp = commands; auto tmp = commands;
tmp.insert(location, resourceFile); 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)); tmp.push_front(QString{R"(--unitName=DDE-%1@%2.service)"}.arg(this->id(), instanceRandomUUID));
QProcess process; QProcess process;
process.start(getApplicationLauncherBinary(), tmp); process.start(getApplicationLauncherBinary(), tmp);
@ -173,7 +176,7 @@ QDBusObjectPath ApplicationService::Launch(QString action, QStringList fields, Q
qWarning() << "Launch Application Failed:" << binary << tmp; qWarning() << "Launch Application Failed:" << binary << tmp;
return QString{""}; return QString{""};
} }
return DDEApplicationManager1InstanceObjectPath + instanceRandomUUID; return objectPath;
}, },
std::move(res)); std::move(res));
} }
@ -239,11 +242,55 @@ QList<QDBusObjectPath> ApplicationService::instances() const noexcept
return m_Instances.keys(); 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) bool ApplicationService::addOneInstance(const QString &instanceId, const QString &application, const QString &systemdUnitPath)
{ {
auto service = new InstanceService{instanceId, application, systemdUnitPath}; auto service = new InstanceService{instanceId, application, systemdUnitPath};
auto adaptor = new InstanceAdaptor(service); auto adaptor = new InstanceAdaptor(service);
QString objectPath{DDEApplicationManager1InstanceObjectPath + instanceId}; QString objectPath{m_applicationPath.path() + "/" + instanceId};
if (registerObjectToDBus(service, objectPath, getDBusInterface<InstanceAdaptor>())) { if (registerObjectToDBus(service, objectPath, getDBusInterface<InstanceAdaptor>())) {
m_Instances.insert(QDBusObjectPath{objectPath}, QSharedPointer<InstanceService>{service}); m_Instances.insert(QDBusObjectPath{objectPath}, QSharedPointer<InstanceService>{service});

View File

@ -14,6 +14,7 @@
#include <QUuid> #include <QUuid>
#include <QTextStream> #include <QTextStream>
#include <QFile> #include <QFile>
#include <memory>
#include "dbus/instanceservice.h" #include "dbus/instanceservice.h"
#include "global.h" #include "global.h"
#include "desktopentry.h" #include "desktopentry.h"
@ -47,6 +48,12 @@ public:
Q_PROPERTY(QList<QDBusObjectPath> Instances READ instances) Q_PROPERTY(QList<QDBusObjectPath> Instances READ instances)
[[nodiscard]] QList<QDBusObjectPath> instances() const noexcept; [[nodiscard]] QList<QDBusObjectPath> 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]] QDBusObjectPath findInstance(const QString &instanceId) const;
[[nodiscard]] const QString &getLauncher() const noexcept { return m_launcher; } [[nodiscard]] const QString &getLauncher() const noexcept { return m_launcher; }
@ -64,45 +71,13 @@ public Q_SLOTS:
private: private:
friend class ApplicationManager1Service; friend class ApplicationManager1Service;
template <typename T> template <typename T>
explicit ApplicationService(T &&source, ApplicationManager1Service *parent = nullptr) friend QSharedPointer<ApplicationService> makeApplication(T &&source, ApplicationManager1Service *parent);
: m_parent(parent)
template <typename T>
explicit ApplicationService(T &&source)
: m_isPersistence(static_cast<bool>(std::is_same_v<T, DesktopFile>))
, m_desktopSource(std::forward<T>(source)) , m_desktopSource(std::forward<T>(source))
{ {
static_assert(std::is_same_v<T, DesktopFile> or std::is_same_v<T, QString>, "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<T, DesktopFile>) {
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<T, QString>) {
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}; bool m_AutoStart{false};
@ -143,4 +118,58 @@ private:
[[nodiscard]] LaunchTask unescapeExec(const QString &str, const QStringList &fields); [[nodiscard]] LaunchTask unescapeExec(const QString &str, const QStringList &fields);
}; };
template <typename T>
QSharedPointer<ApplicationService> makeApplication(T &&source, ApplicationManager1Service *parent)
{
static_assert(std::is_same_v<T, DesktopFile> or std::is_same_v<T, QString>, "param type must be QString or DesktopFile.");
QString objectPath;
QTextStream sourceStream;
QFile sourceFile;
QSharedPointer<ApplicationService> app{nullptr};
if constexpr (std::is_same_v<T, DesktopFile>) {
DesktopFile in{std::forward<T>(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<T, QString>) {
QString in{std::forward<T>(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<DesktopEntry> entry{std::make_unique<DesktopEntry>()};
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 #endif

View File

@ -50,7 +50,8 @@ public:
{ {
static_assert(std::is_invocable_v<F, QVariant>, "param type must be QVariant."); static_assert(std::is_invocable_v<F, QVariant>, "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<QVariantList> future = QtConcurrent::mappedReduced(std::move(args), QFuture<QVariantList> future = QtConcurrent::mappedReduced(std::move(args),
func, func,
qOverload<QVariantList::parameter_type>(&QVariantList::append), qOverload<QVariantList::parameter_type>(&QVariantList::append),

View File

@ -11,7 +11,7 @@ JobService::JobService(const QFuture<QVariantList> &job)
JobService::~JobService() = default; JobService::~JobService() = default;
QString JobService::status() const QString JobService::status() const // FIXME: job status aren't mutually exclusive
{ {
if (m_job.isFinished()) { if (m_job.isFinished()) {
return "finished"; return "finished";

View File

@ -56,7 +56,9 @@ DesktopErrorCode DesktopEntry::parseEntry(const QString &str, decltype(m_entryMa
re.optimize(); re.optimize();
auto matcher = re.match(keyStr); auto matcher = re.match(keyStr);
if (!matcher.hasMatch()) { if (!matcher.hasMatch()) {
#ifdef DEBUG_MODE
qWarning() << "invalid key: " << keyStr; qWarning() << "invalid key: " << keyStr;
#endif
return DesktopErrorCode::EntryKeyInvalid; return DesktopErrorCode::EntryKeyInvalid;
} }
@ -205,7 +207,9 @@ DesktopErrorCode DesktopEntry::parse(QTextStream &stream) noexcept
if (auto error = parseEntry(line, currentGroup); error != DesktopErrorCode::NoError) { if (auto error = parseEntry(line, currentGroup); error != DesktopErrorCode::NoError) {
err = error; err = error;
#ifdef DEBUG_MODE
qWarning() << "an error occurred,this line will be skipped:" << line; qWarning() << "an error occurred,this line will be skipped:" << line;
#endif
} }
} }
return err; return err;
@ -223,13 +227,17 @@ std::optional<DesktopEntry::Value> DesktopEntry::value(const QString &groupKey,
{ {
const auto &destGroup = group(groupKey); const auto &destGroup = group(groupKey);
if (!destGroup) { if (!destGroup) {
#ifdef DEBUG_MODE
qWarning() << "group " << groupKey << " can't be found."; qWarning() << "group " << groupKey << " can't be found.";
#endif
return std::nullopt; return std::nullopt;
} }
auto it = destGroup->find(valueKey); auto it = destGroup->find(valueKey);
if (it == destGroup->cend()) { if (it == destGroup->cend()) {
#ifdef DEBUG_MODE
qWarning() << "value " << valueKey << " can't be found."; qWarning() << "value " << valueKey << " can't be found.";
#endif
return std::nullopt; return std::nullopt;
} }
return *it; return *it;

View File

@ -44,23 +44,11 @@ public:
static void TearDownTestCase() { m_am->deleteLater(); } static void TearDownTestCase() { m_am->deleteLater(); }
static inline ApplicationManager1Service *m_am{nullptr}; static inline ApplicationManager1Service *m_am{nullptr};
const static inline QDBusObjectPath ApplicationPath{DDEApplicationManager1ApplicationObjectPath + const static inline QDBusObjectPath ApplicationPath{QString{DDEApplicationManager1ObjectPath} + "/" +
QUuid::createUuid().toString(QUuid::Id128)}; QUuid::createUuid().toString(QUuid::Id128)};
const static inline QDBusObjectPath InstancePath{DDEApplicationManager1InstanceObjectPath + const static inline QDBusObjectPath InstancePath{ApplicationPath.path() + "/" + QUuid::createUuid().toString(QUuid::Id128)};
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) TEST_F(TestApplicationManager, identifyService)
{ {
using namespace std::chrono_literals; using namespace std::chrono_literals;