feat: add mimeManager Service

Signed-off-by: ComixHe <heyuming@deepin.org>
This commit is contained in:
ComixHe
2023-09-20 18:29:42 +08:00
committed by black-desk
parent 8970298ad0
commit f63741b023
25 changed files with 1282 additions and 296 deletions

View File

@ -13,6 +13,7 @@ qt_add_dbus_adaptor(dde_am_dbus_SOURCE ${PROJECT_SOURCE_DIR}/api/dbus/org.deskto
qt_add_dbus_adaptor(dde_am_dbus_SOURCE ${PROJECT_SOURCE_DIR}/api/dbus/org.desktopspec.JobManager1.Job.xml dbus/jobservice.h JobService)
qt_add_dbus_adaptor(dde_am_dbus_SOURCE ${PROJECT_SOURCE_DIR}/api/dbus/org.desktopspec.ObjectManager1.xml dbus/applicationmanager1service.h ApplicationManager1Service AMobjectmanager1adaptor AMObjectManagerAdaptor)
qt_add_dbus_adaptor(dde_am_dbus_SOURCE ${PROJECT_SOURCE_DIR}/api/dbus/org.desktopspec.ObjectManager1.xml dbus/applicationservice.h ApplicationService APPobjectmanager1adaptor APPObjectManagerAdaptor)
qt_add_dbus_adaptor(dde_am_dbus_SOURCE ${PROJECT_SOURCE_DIR}/api/dbus/org.desktopspec.MimeManager1.xml dbus/mimemanager1service.h MimeManager1Service)
target_sources(dde_am_dbus PRIVATE
${dde_am_dbus_SOURCE}

View File

@ -37,17 +37,20 @@ void ApplicationManager1Service::initService(QDBusConnection &connection) noexce
std::terminate();
}
if (!registerObjectToDBus(this, DDEApplicationManager1ObjectPath, ApplicationManagerInterface)) {
if (!registerObjectToDBus(this, DDEApplicationManager1ObjectPath, ApplicationManager1Interface)) {
std::terminate();
}
m_jobManager.reset(new (std::nothrow) JobManager1Service(this));
if (!m_jobManager) {
if (m_jobManager.reset(new (std::nothrow) JobManager1Service(this)); !m_jobManager) {
qCritical() << "new JobManager failed.";
std::terminate();
}
if (m_mimeManager.reset(new (std::nothrow) MimeManager1Service(this)); !m_mimeManager) {
qCritical() << "new MimeManager failed.";
std::terminate();
}
auto &dispatcher = SystemdSignalDispatcher::instance();
connect(&dispatcher, &SystemdSignalDispatcher::SystemdUnitNew, this, &ApplicationManager1Service::addInstanceToApplication);
@ -67,6 +70,8 @@ void ApplicationManager1Service::initService(QDBusConnection &connection) noexce
qFatal("connect to ApplicationUpdated failed.");
}
scanMimeInfos();
scanApplications();
scanInstances();
@ -208,6 +213,20 @@ void ApplicationManager1Service::removeInstanceFromApplication(const QString &un
});
}
void ApplicationManager1Service::scanMimeInfos() noexcept
{
QStringList dirs;
dirs.append(getXDGConfigDirs());
dirs.append(getDesktopFileDirs());
for (const auto &dir : dirs) {
auto info = MimeInfo::createMimeInfo(dir);
if (info) {
m_mimeManager->appendMimeInfo(std::move(info).value());
}
}
}
void ApplicationManager1Service::scanApplications() noexcept
{
const auto &desktopFileDirs = getDesktopFileDirs();
@ -215,7 +234,7 @@ void ApplicationManager1Service::scanApplications() noexcept
applyIteratively(
QList<QDir>(desktopFileDirs.cbegin(), desktopFileDirs.cend()),
[this](const QFileInfo &info) -> bool {
DesktopErrorCode err{DesktopErrorCode::NoError};
ParserError err{ParserError::NoError};
auto ret = DesktopFile::searchDesktopFileByPath(info.absoluteFilePath(), err);
if (!ret.has_value()) {
qWarning() << "failed to search File:" << err;
@ -449,7 +468,7 @@ void ApplicationManager1Service::updateApplication(const QSharedPointer<Applicat
}
auto err = newEntry->parse(desktopFile);
if (err != DesktopErrorCode::NoError) {
if (err != ParserError::NoError) {
qWarning() << "update desktop file failed:" << err << ", content wouldn't change.";
return;
}
@ -475,7 +494,7 @@ void ApplicationManager1Service::ReloadApplications()
applyIteratively(
QList<QDir>(desktopFileDirs.cbegin(), desktopFileDirs.cend()),
[this, &apps](const QFileInfo &info) -> bool {
DesktopErrorCode err{DesktopErrorCode::NoError};
ParserError err{ParserError::NoError};
auto ret = DesktopFile::searchDesktopFileByPath(info.absoluteFilePath(), err);
if (!ret.has_value()) {
return false;
@ -488,7 +507,7 @@ void ApplicationManager1Service::ReloadApplications()
m_applicationList.cend(),
[&file](const QSharedPointer<ApplicationService> &app) { return file.desktopId() == app->id(); });
if (err != DesktopErrorCode::NoError) {
if (err != ParserError::NoError) {
qWarning() << "error occurred:" << err << " skip this application.";
return false;
}
@ -515,3 +534,17 @@ ObjectMap ApplicationManager1Service::GetManagedObjects() const
{
return dumpDBusObject(m_applicationList);
}
QMap<QDBusObjectPath, QSharedPointer<ApplicationService>>
ApplicationManager1Service::findApplicationsByIds(const QStringList &appIds) const noexcept
{
QMap<QDBusObjectPath, QSharedPointer<ApplicationService>> ret;
for (auto it = m_applicationList.constKeyValueBegin(); it != m_applicationList.constKeyValueEnd(); ++it) {
const auto &ptr = it->second;
if (appIds.contains(ptr->id())) {
ret.insert(it->first, it->second);
}
}
return ret;
}

View File

@ -14,6 +14,7 @@
#include <QMap>
#include "applicationmanagerstorage.h"
#include "dbus/jobmanager1service.h"
#include "dbus/mimemanager1service.h"
#include "desktopentry.h"
#include "identifier.h"
@ -38,12 +39,15 @@ public:
bool addApplication(DesktopFile desktopFileSource) noexcept;
void removeOneApplication(const QDBusObjectPath &application) noexcept;
void removeAllApplication() noexcept;
[[nodiscard]] QMap<QDBusObjectPath, QSharedPointer<ApplicationService>>
findApplicationsByIds(const QStringList &appIds) const noexcept;
void updateApplication(const QSharedPointer<ApplicationService> &destApp, DesktopFile desktopFile) noexcept;
[[nodiscard]] JobManager1Service &jobManager() noexcept { return *m_jobManager; }
[[nodiscard]] const JobManager1Service &jobManager() const noexcept { return *m_jobManager; }
[[nodiscard]] const QStringList &applicationHooks() const noexcept { return m_hookElements; }
[[nodiscard]] MimeManager1Service &mimeManager() noexcept { return *m_mimeManager; }
[[nodiscard]] const MimeManager1Service &mimeManager() const noexcept { return *m_mimeManager; }
public Q_SLOTS:
QString Identify(const QDBusUnixFileDescriptor &pidfd,
@ -60,10 +64,12 @@ Q_SIGNALS:
private:
std::unique_ptr<Identifier> m_identifier;
std::weak_ptr<ApplicationManager1Storage> m_storage;
QScopedPointer<MimeManager1Service> m_mimeManager{nullptr};
QScopedPointer<JobManager1Service> m_jobManager{nullptr};
QStringList m_hookElements;
QMap<QDBusObjectPath, QSharedPointer<ApplicationService>> m_applicationList;
void scanMimeInfos() noexcept;
void scanApplications() noexcept;
void scanInstances() noexcept;
void scanAutoStart() noexcept;

View File

@ -5,7 +5,6 @@
#include "dbus/applicationservice.h"
#include "APPobjectmanager1adaptor.h"
#include "applicationchecker.h"
#include "applicationmanager1service.h"
#include "applicationmanagerstorage.h"
#include "propertiesForwarder.h"
#include "dbus/instanceadaptor.h"
@ -83,7 +82,7 @@ QSharedPointer<ApplicationService> ApplicationService::createApplicationService(
std::unique_ptr<DesktopEntry> entry{std::make_unique<DesktopEntry>()};
auto error = entry->parse(sourceStream);
if (error != DesktopErrorCode::NoError) {
if (error != ParserError::NoError) {
qWarning() << "parse failed:" << error << app->desktopFileSource().sourcePath();
return nullptr;
}
@ -486,6 +485,79 @@ void ApplicationService::setAutoStart(bool autostart) noexcept
emit autostartChanged();
}
QStringList ApplicationService::mimeTypes() const noexcept
{
QStringList ret;
const auto &desktopFilePath = m_desktopSource.sourcePath();
const auto &cacheList = parent()->mimeManager().infos();
auto cache = std::find_if(cacheList.cbegin(), cacheList.cend(), [&desktopFilePath](const MimeInfo &info) {
return desktopFilePath.startsWith(info.directory());
});
const auto &info = cache->cacheInfo();
if (info) {
ret.append(info->queryTypes(id()));
}
AppList tmp;
for (auto it = cacheList.crbegin(); it != cacheList.crend(); ++it) {
const auto &list = it->appsList();
std::for_each(list.crbegin(), list.crend(), [&tmp, this](const MimeApps &app) {
auto [added, removed] = app.queryTypes(id());
tmp.added.append(std::move(added));
tmp.removed.append(std::move(removed));
});
};
tmp.added.removeDuplicates();
tmp.removed.removeDuplicates();
for (const auto &it : tmp.removed) {
tmp.added.removeOne(it);
}
ret.append(std::move(tmp.added));
ret.removeDuplicates();
return ret;
}
void ApplicationService::setMimeTypes(const QStringList &value) noexcept
{
auto oldMimes = mimeTypes();
auto newMimes = value;
std::sort(oldMimes.begin(), oldMimes.end());
std::sort(newMimes.begin(), newMimes.end());
QStringList newAdds;
QStringList newRemoved;
std::set_difference(oldMimes.begin(), oldMimes.end(), newMimes.begin(), newMimes.end(), std::back_inserter(newRemoved));
std::set_difference(newMimes.begin(), newMimes.end(), oldMimes.begin(), oldMimes.end(), std::back_inserter(newAdds));
static QString userDir = getXDGConfigHome();
auto &infos = parent()->mimeManager().infos();
auto userInfo = std::find_if(infos.begin(), infos.end(), [](const MimeInfo &info) { return info.directory() == userDir; });
if (userInfo == infos.cend()) {
sendErrorReply(QDBusError::Failed, "user-specific config file doesn't exists.");
return;
}
const auto &list = userInfo->appsList().rbegin();
const auto &appId = id();
for (const auto &add : newAdds) {
list->addAssociation(add, appId);
}
for (const auto &remove : newRemoved) {
list->removeAssociation(remove, appId);
}
if (!list->writeToFile()) {
qWarning() << "error occurred when write mime association to file";
}
emit MimeTypesChanged();
}
QList<QDBusObjectPath> ApplicationService::instances() const noexcept
{
return m_Instances.keys();
@ -567,6 +639,7 @@ void ApplicationService::resetEntry(DesktopEntry *newEntry) noexcept
emit actionNameChanged();
emit actionsChanged();
emit categoriesChanged();
emit MimeTypesChanged();
}
LaunchTask ApplicationService::unescapeExec(const QString &str, const QStringList &fields)

View File

@ -23,6 +23,7 @@
#include "global.h"
#include "desktopentry.h"
#include "dbus/jobmanager1service.h"
#include "applicationmanager1service.h"
QString getDeepinWineScaleFactor(const QString &appId) noexcept;
@ -67,6 +68,10 @@ public:
[[nodiscard]] bool isAutoStart() const noexcept;
void setAutoStart(bool autostart) noexcept;
Q_PROPERTY(QStringList MimeTypes READ mimeTypes WRITE setMimeTypes)
[[nodiscard]] QStringList mimeTypes() const noexcept;
void setMimeTypes(const QStringList &value) noexcept;
Q_PROPERTY(QList<QDBusObjectPath> Instances READ instances NOTIFY instanceChanged)
[[nodiscard]] QList<QDBusObjectPath> instances() const noexcept;
@ -129,6 +134,7 @@ Q_SIGNALS:
void actionNameChanged();
void actionsChanged();
void categoriesChanged();
void MimeTypesChanged();
private:
friend class ApplicationManager1Service;

View File

@ -9,7 +9,7 @@ JobManager1Service::JobManager1Service(ApplicationManager1Service *parent)
: m_parent(parent)
{
new JobManager1Adaptor{this};
if (!registerObjectToDBus(this, DDEApplicationManager1JobManagerObjectPath, JobManagerInterface)) {
if (!registerObjectToDBus(this, DDEApplicationManager1JobManager1ObjectPath, JobManager1Interface)) {
std::terminate();
}
qRegisterMetaType<LaunchTask>();

View File

@ -51,7 +51,7 @@ public:
static_assert(std::is_invocable_v<F, QVariant>, "param type must be QVariant.");
QString objectPath =
QString{"%1/%2"}.arg(DDEApplicationManager1JobManagerObjectPath).arg(QUuid::createUuid().toString(QUuid::Id128));
QString{"%1/%2"}.arg(DDEApplicationManager1JobManager1ObjectPath).arg(QUuid::createUuid().toString(QUuid::Id128));
QFuture<QVariantList> future = QtConcurrent::mappedReduced(std::move(args),
func,
qOverload<QVariantList::parameter_type>(&QVariantList::append),

View File

@ -0,0 +1,114 @@
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "dbus/mimemanager1adaptor.h"
#include "applicationmanager1service.h"
#include "applicationservice.h"
#include "constant.h"
MimeManager1Service::MimeManager1Service(ApplicationManager1Service *parent)
: QObject(parent)
{
new MimeManager1Adaptor{this};
if (!registerObjectToDBus(this, DDEApplicationManager1MimeManager1ObjectPath, MimeManager1Interface)) {
std::terminate();
}
}
MimeManager1Service::~MimeManager1Service() = default;
ObjectMap MimeManager1Service::listApplications(const QString &mimeType) const noexcept
{
auto mime = m_database.mimeTypeForName(mimeType);
if (!mime.isValid()) {
qWarning() << "can't find" << mimeType;
return {};
}
QStringList appIds;
for (auto it = m_infos.rbegin(); it != m_infos.rend(); ++it) {
const auto &info = it->cacheInfo();
if (!info) {
continue;
}
auto apps = info->queryApps(mime);
appIds.append(std::move(apps));
}
appIds.removeDuplicates();
qInfo() << "query" << mimeType << "find:" << appIds;
const auto &apps = static_cast<ApplicationManager1Service *>(parent())->findApplicationsByIds(appIds);
return dumpDBusObject(apps);
}
QString MimeManager1Service::queryFileTypeAndDefaultApplication(const QString &filePath,
QDBusObjectPath &application) const noexcept
{
QString mimeType;
application = QDBusObjectPath{"/"};
auto mime = m_database.mimeTypeForFile(filePath);
if (mime.isValid()) {
mimeType = mime.name();
}
QString defaultAppId;
for (auto it1 = m_infos.rbegin(); it1 != m_infos.rend(); ++it1) {
const auto &list = it1->appsList();
for (auto it2 = list.rbegin(); it2 != list.rend(); ++it2) {
if (auto app = it2->queryDefaultApp(mime); !app.isEmpty()) {
defaultAppId = app;
}
}
}
if (defaultAppId.isEmpty()) {
qInfo() << "file's mimeType:" << mime.name() << "but can't find a default application for this type.";
return mimeType;
}
const auto &apps = static_cast<ApplicationManager1Service *>(parent())->findApplicationsByIds({defaultAppId});
if (apps.isEmpty()) {
qWarning() << "default application has been found:" << defaultAppId
<< " but we can't find corresponding application in ApplicationManagerService.";
} else {
application = apps.firstKey();
}
return mimeType;
}
void MimeManager1Service::setDefaultApplication(const KVPairs &defaultApps) noexcept
{
auto &app = m_infos.front().appsList();
auto userConfig = std::find_if(
app.begin(), app.end(), [](const MimeApps &config) { return !config.isDesktopSpecific(); }); // always find this
for (auto it = defaultApps.constKeyValueBegin(); it != defaultApps.constKeyValueEnd(); ++it) {
userConfig->setDefaultApplication(it->first, it->second);
}
if (!userConfig->writeToFile()) {
sendErrorReply(QDBusError::Failed, "set default app failed, these config will be reset after re-login.");
}
}
void MimeManager1Service::unsetDefaultApplication(const QStringList &mimeTypes) noexcept
{
auto &app = m_infos.front().appsList();
auto userConfig = std::find_if(app.begin(), app.end(), [](const MimeApps &config) { return !config.isDesktopSpecific(); });
for (const auto &mime : mimeTypes) {
userConfig->unsetDefaultApplication(mime);
}
if (!userConfig->writeToFile()) {
sendErrorReply(QDBusError::Failed, "unset default app failed, these config will be reset after re-login.");
}
}
void MimeManager1Service::appendMimeInfo(MimeInfo &&info)
{
m_infos.emplace_back(std::move(info));
}

View File

@ -0,0 +1,39 @@
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#ifndef MIMEMANAGERSERVICE_H
#define MIMEMANAGERSERVICE_H
#include <QObject>
#include <QDBusContext>
#include <QDBusObjectPath>
#include "global.h"
#include "applicationmimeinfo.h"
class ApplicationManager1Service;
class MimeManager1Service : public QObject, public QDBusContext
{
Q_OBJECT
public:
explicit MimeManager1Service(ApplicationManager1Service *parent);
~MimeManager1Service() override;
void appendMimeInfo(MimeInfo &&info);
[[nodiscard]] const auto &infos() const noexcept { return m_infos; }
[[nodiscard]] auto &infos() noexcept { return m_infos; }
public Q_SLOTS:
[[nodiscard]] ObjectMap listApplications(const QString &mimeType) const noexcept;
[[nodiscard]] QString queryFileTypeAndDefaultApplication(const QString &filePath,
QDBusObjectPath &application) const noexcept;
void setDefaultApplication(const KVPairs &defaultApps) noexcept;
void unsetDefaultApplication(const QStringList &mimeTypes) noexcept;
private:
QMimeDatabase m_database;
std::vector<MimeInfo> m_infos;
};
#endif