feat: support persistent stroage and lastLaunchedTime

Signed-off-by: ComixHe <heyuming@deepin.org>
This commit is contained in:
ComixHe 2023-09-13 11:47:04 +08:00 committed by Comix
parent 7b527c609b
commit 23dcd13f10
10 changed files with 335 additions and 22 deletions

View File

@ -8,6 +8,7 @@
#include <QDir>
#include "dbus/applicationmanager1service.h"
#include "cgroupsidentifier.h"
#include "applicationmanagerstorage.h"
#include <chrono>
#include <iostream>
@ -42,7 +43,10 @@ int main(int argc, char *argv[])
auto &AMBus = bus.globalServerBus();
registerComplexDbusType();
ApplicationManager1Service AMService{std::make_unique<CGroupsIdentifier>(), AMBus};
auto storageDir = getXDGDataHome() + QDir::separator() + "deepin" + QDir::separator() + "ApplicationManager";
auto storage = ApplicationManager1Storage::createApplicationManager1Storage(storageDir);
ApplicationManager1Service AMService{std::make_unique<CGroupsIdentifier>(), AMBus, storage};
#ifdef PROFILING_MODE
auto end = std::chrono::high_resolution_clock::now();

View File

@ -0,0 +1,205 @@
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "applicationmanagerstorage.h"
#include "constant.h"
#include <QFileInfo>
#include <QJsonDocument>
#include <QDir>
#include <memory>
std::shared_ptr<ApplicationManager1Storage>
ApplicationManager1Storage::createApplicationManager1Storage(const QString &storageDir) noexcept
{
QDir dir;
auto dirPath = QDir::cleanPath(storageDir);
if (!dir.mkpath(dirPath)) {
qCritical() << "can't create directory";
return nullptr;
}
dir.setPath(dirPath);
auto storagePath = dir.filePath("storage.json");
auto obj = std::shared_ptr<ApplicationManager1Storage>(new (std::nothrow) ApplicationManager1Storage{storagePath});
if (!obj) {
qCritical() << "new ApplicationManager1Storage failed.";
return nullptr;
}
if (!obj->m_file->open(QFile::ReadWrite)) {
qCritical() << "can't open file:" << storagePath;
return nullptr;
}
auto content = obj->m_file->readAll();
if (content.isEmpty()) { // new file
obj->setVersion(STORAGE_VERSION);
return obj;
}
// TODO: support migrate from lower storage version.
QJsonParseError err;
auto json = QJsonDocument::fromJson(content, &err);
if (err.error != QJsonParseError::NoError) {
qDebug() << "parse json failed:" << err.errorString() << "clear this file content.";
obj->m_file->resize(0);
} else {
obj->m_data = json.object();
}
return obj;
}
ApplicationManager1Storage::ApplicationManager1Storage(const QString &storagePath)
: m_file(std::make_unique<QFile>(storagePath))
{
}
void ApplicationManager1Storage::writeToFile() const noexcept
{
if (!m_file) {
qCritical() << "file is nullptr";
return;
}
if (!m_file->resize(0)) {
qCritical() << "failed to clear file:" << m_file->errorString();
return;
}
auto content = QJsonDocument{m_data}.toJson(QJsonDocument::Compact);
auto bytes = m_file->write(content, content.size());
if (bytes != content.size()) {
qWarning() << "Incomplete file writes:" << m_file->errorString();
}
if (!m_file->flush()) {
qCritical() << "io error.";
}
}
void ApplicationManager1Storage::setVersion(uint8_t version) noexcept
{
m_data["version"] = version;
writeToFile();
}
uint8_t ApplicationManager1Storage::version() const noexcept
{
return m_data["version"].toInt(0);
}
void ApplicationManager1Storage::createApplicationValue(const QString &appId,
const QString &groupName,
const QString &valueKey,
const QVariant &value) noexcept
{
if (appId.isEmpty() or groupName.isEmpty() or valueKey.isEmpty()) {
return;
}
QJsonObject appObj;
if (m_data.contains(appId)) {
appObj = m_data[appId].toObject();
}
QJsonObject groupObj;
if (appObj.contains(groupName)) {
groupObj = appObj[groupName].toObject();
}
if (groupObj.contains(valueKey)) {
return;
}
groupObj.insert(valueKey, value.toJsonValue());
appObj.insert(groupName, groupObj);
m_data.insert(appId, appObj);
writeToFile();
}
void ApplicationManager1Storage::updateApplicationValue(const QString &appId,
const QString &groupName,
const QString &valueKey,
const QVariant &value) noexcept
{
if (appId.isEmpty() or groupName.isEmpty() or valueKey.isEmpty()) {
return;
}
if (!m_data.contains(appId)) {
return;
}
auto appObj = m_data[appId].toObject();
if (!appObj.contains(groupName)) {
return;
}
auto groupObj = appObj[groupName].toObject();
if (!groupObj.contains(valueKey)) {
return;
}
groupObj.insert(valueKey, value.toJsonValue());
appObj.insert(groupName, groupObj);
m_data.insert(appId, appObj);
writeToFile();
}
QVariant ApplicationManager1Storage::readApplicationValue(const QString &appId,
const QString &groupName,
const QString &valueKey) const noexcept
{
return m_data[appId][groupName][valueKey].toVariant();
}
void ApplicationManager1Storage::deleteApplicationValue(const QString &appId,
const QString &groupName,
const QString &valueKey) noexcept
{
if (appId.isEmpty()) {
auto empty = QJsonObject{};
m_data.swap(empty);
return;
}
auto app = m_data.find(appId).value();
if (app.isNull()) {
return;
}
auto appObj = app.toObject();
if (groupName.isEmpty()) {
m_data.remove(appId);
return;
}
auto group = appObj.find(groupName).value();
if (group.isNull()) {
return;
}
auto groupObj = group.toObject();
if (valueKey.isEmpty()) {
appObj.remove(groupName);
m_data.insert(appId, appObj);
return;
}
auto val = groupObj.find(valueKey).value();
if (val.isNull()) {
return;
}
groupObj.remove(valueKey);
appObj.insert(groupName, groupObj);
m_data.insert(appId, appObj);
writeToFile();
}

View File

@ -0,0 +1,47 @@
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#ifndef APPLICATIONMANAGERSTORAGE_H
#define APPLICATIONMANAGERSTORAGE_H
#include <QString>
#include <QJsonObject>
#include <QFile>
enum class ModifyMode { Create, Update };
class ApplicationManager1Storage
{
public:
ApplicationManager1Storage(const ApplicationManager1Storage &) = delete;
ApplicationManager1Storage(ApplicationManager1Storage &&) = default;
ApplicationManager1Storage &operator=(const ApplicationManager1Storage &) = delete;
ApplicationManager1Storage &operator=(ApplicationManager1Storage &&) = default;
~ApplicationManager1Storage() = default;
void createApplicationValue(const QString &appId,
const QString &groupName,
const QString &valueKey,
const QVariant &value) noexcept;
void updateApplicationValue(const QString &appId,
const QString &groupName,
const QString &valueKey,
const QVariant &value) noexcept;
[[nodiscard]] QVariant
readApplicationValue(const QString &appId, const QString &groupName, const QString &valueKey) const noexcept;
void deleteApplicationValue(const QString &appId = "", const QString &groupName = "", const QString &valueKey = "") noexcept;
void setVersion(uint8_t version) noexcept;
[[nodiscard]] uint8_t version() const noexcept;
static std::shared_ptr<ApplicationManager1Storage> createApplicationManager1Storage(const QString &storageDir) noexcept;
private:
void writeToFile() const noexcept;
explicit ApplicationManager1Storage(const QString &storagePath);
std::unique_ptr<QFile> m_file;
QJsonObject m_data;
};
#endif

View File

@ -47,4 +47,8 @@ constexpr auto systemdOption = u8"systemd";
constexpr auto splitOption = u8"split";
constexpr auto AppExecOption = u8"appExec";
constexpr auto STORAGE_VERSION = 1;
constexpr auto ApplicationPropertiesGroup = u8"Application Properties";
constexpr auto LastLaunchedTime = u8"LastLaunchedTime";
#endif

View File

@ -13,8 +13,11 @@
ApplicationManager1Service::~ApplicationManager1Service() = default;
ApplicationManager1Service::ApplicationManager1Service(std::unique_ptr<Identifier> ptr, QDBusConnection &connection) noexcept
ApplicationManager1Service::ApplicationManager1Service(std::unique_ptr<Identifier> ptr,
QDBusConnection &connection,
std::weak_ptr<ApplicationManager1Storage> storage) noexcept
: m_identifier(std::move(ptr))
, m_storage(std::move(storage))
{
if (!connection.registerService(DDEApplicationManager1ServiceName)) {
qFatal("%s", connection.lastError().message().toLocal8Bit().data());
@ -116,6 +119,17 @@ void ApplicationManager1Service::addInstanceToApplication(const QString &unitNam
return;
}
if (sender() != nullptr) { // activate by signal
auto timestamp = QDateTime::currentMSecsSinceEpoch();
if (auto ptr = m_storage.lock(); ptr) {
ptr->updateApplicationValue((*appIt)->m_desktopSource.desktopId(),
ApplicationPropertiesGroup,
LastLaunchedTime,
QVariant::fromValue(timestamp));
}
}
const auto &applicationPath = (*appIt)->applicationPath().path();
if (!(*appIt)->addOneInstance(instanceId, applicationPath, systemdUnitPath.path())) [[likely]] {
@ -229,7 +243,7 @@ QList<QDBusObjectPath> ApplicationManager1Service::list() const
bool ApplicationManager1Service::addApplication(DesktopFile desktopFileSource) noexcept
{
QSharedPointer<ApplicationService> application =
ApplicationService::createApplicationService(std::move(desktopFileSource), this);
ApplicationService::createApplicationService(std::move(desktopFileSource), this, m_storage);
if (!application) {
return false;
}
@ -251,6 +265,18 @@ bool ApplicationManager1Service::addApplication(DesktopFile desktopFileSource) n
return false;
}
m_applicationList.insert(application->applicationPath(), application);
if (auto storagePtr = m_storage.lock(); storagePtr) {
auto appId = ptr->id();
auto value = storagePtr->readApplicationValue(appId, ApplicationPropertiesGroup, LastLaunchedTime);
if (value.isNull()) {
storagePtr->createApplicationValue(
appId, ApplicationPropertiesGroup, LastLaunchedTime, QVariant::fromValue<qint64>(0));
} else {
ptr->m_lastLaunch = value.toInt();
}
}
emit listChanged();
emit InterfacesAdded(application->applicationPath(), getChildInterfacesAndPropertiesFromObject(ptr));
@ -261,8 +287,12 @@ void ApplicationManager1Service::removeOneApplication(const QDBusObjectPath &app
{
if (auto it = m_applicationList.find(application); it != m_applicationList.cend()) {
emit InterfacesRemoved(application, getChildInterfacesFromObject(it->data()));
if (auto ptr = m_storage.lock(); ptr) {
ptr->deleteApplicationValue((*it)->id());
}
unregisterObjectFromDBus(application.path());
m_applicationList.remove(application);
emit listChanged();
}
}

View File

@ -12,6 +12,7 @@
#include <QScopedPointer>
#include <memory>
#include <QMap>
#include "applicationmanagerstorage.h"
#include "dbus/jobmanager1service.h"
#include "desktopentry.h"
#include "identifier.h"
@ -22,7 +23,9 @@ class ApplicationManager1Service final : public QObject
{
Q_OBJECT
public:
explicit ApplicationManager1Service(std::unique_ptr<Identifier> ptr, QDBusConnection &connection) noexcept;
explicit ApplicationManager1Service(std::unique_ptr<Identifier> ptr,
QDBusConnection &connection,
std::weak_ptr<ApplicationManager1Storage> storage) noexcept;
~ApplicationManager1Service() override;
ApplicationManager1Service(const ApplicationManager1Service &) = delete;
ApplicationManager1Service(ApplicationManager1Service &&) = delete;
@ -52,6 +55,7 @@ Q_SIGNALS:
private:
std::unique_ptr<Identifier> m_identifier;
std::weak_ptr<ApplicationManager1Storage> m_storage;
QScopedPointer<JobManager1Service> m_jobManager{nullptr};
QMap<QDBusObjectPath, QSharedPointer<ApplicationService>> m_applicationList;

View File

@ -6,6 +6,7 @@
#include "APPobjectmanager1adaptor.h"
#include "applicationchecker.h"
#include "applicationmanager1service.h"
#include "applicationmanagerstorage.h"
#include "propertiesForwarder.h"
#include "dbus/instanceadaptor.h"
#include "launchoptions.h"
@ -18,10 +19,14 @@
#include <QStandardPaths>
#include <algorithm>
#include <new>
#include <utility>
#include <wordexp.h>
ApplicationService::ApplicationService(DesktopFile source, ApplicationManager1Service *parent)
ApplicationService::ApplicationService(DesktopFile source,
ApplicationManager1Service *parent,
std::weak_ptr<ApplicationManager1Storage> storage)
: QObject(parent)
, m_storage(std::move(storage))
, m_desktopSource(std::move(source))
{
}
@ -36,10 +41,10 @@ ApplicationService::~ApplicationService()
}
}
QSharedPointer<ApplicationService> ApplicationService::createApplicationService(DesktopFile source,
ApplicationManager1Service *parent) noexcept
QSharedPointer<ApplicationService> ApplicationService::createApplicationService(
DesktopFile source, ApplicationManager1Service *parent, std::weak_ptr<ApplicationManager1Storage> storage) noexcept
{
QSharedPointer<ApplicationService> app{new (std::nothrow) ApplicationService{std::move(source), parent}};
QSharedPointer<ApplicationService> app{new (std::nothrow) ApplicationService{std::move(source), parent, std::move(storage)}};
if (!app) {
qCritical() << "new application service failed.";
return nullptr;
@ -90,6 +95,12 @@ QSharedPointer<ApplicationService> ApplicationService::createApplicationService(
return nullptr;
}
auto ptr = app->m_storage.lock();
if (!ptr) {
qWarning() << "runtime storage doesn't exists.";
return app;
}
return app;
}
@ -176,7 +187,7 @@ QDBusObjectPath ApplicationService::Launch(const QString &action, const QStringL
auto &jobManager = static_cast<ApplicationManager1Service *>(parent())->jobManager();
return jobManager.addJob(
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)](const QVariant &variantValue) mutable -> QVariant {
auto resourceFile = variantValue.toString();
auto instanceRandomUUID = QUuid::createUuid().toString(QUuid::Id128);
auto objectPath = m_applicationPath.path() + "/" + instanceRandomUUID;

View File

@ -17,6 +17,7 @@
#include <QFile>
#include <memory>
#include <utility>
#include "applicationmanagerstorage.h"
#include "dbus/instanceservice.h"
#include "global.h"
#include "desktopentry.h"
@ -110,10 +111,13 @@ Q_SIGNALS:
private:
friend class ApplicationManager1Service;
explicit ApplicationService(DesktopFile source, ApplicationManager1Service *parent);
static QSharedPointer<ApplicationService> createApplicationService(DesktopFile source,
ApplicationManager1Service *parent) noexcept;
qlonglong m_lastLaunch{0};
explicit ApplicationService(DesktopFile source,
ApplicationManager1Service *parent,
std::weak_ptr<ApplicationManager1Storage> storage);
static QSharedPointer<ApplicationService> createApplicationService(
DesktopFile source, ApplicationManager1Service *parent, std::weak_ptr<ApplicationManager1Storage> storage) noexcept;
qint64 m_lastLaunch{0};
std::weak_ptr<ApplicationManager1Storage> m_storage;
QDBusObjectPath m_applicationPath;
QString m_launcher{getApplicationLauncherBinary()};
DesktopFile m_desktopSource;

View File

@ -363,6 +363,15 @@ inline QString getRelativePathFromAppId(const QString &id)
return path;
}
inline QString getXDGDataHome()
{
auto XDGDataHome = QString::fromLocal8Bit(qgetenv("XDG_DATA_HOME"));
if (XDGDataHome.isEmpty()) {
XDGDataHome = QString::fromLocal8Bit(qgetenv("HOME")) + QDir::separator() + ".local" + QDir::separator() + "share";
}
return XDGDataHome;
}
inline QStringList getDesktopFileDirs()
{
auto XDGDataDirs = QString::fromLocal8Bit(qgetenv("XDG_DATA_DIRS")).split(':', Qt::SkipEmptyParts);
@ -372,12 +381,7 @@ inline QStringList getDesktopFileDirs()
XDGDataDirs.append("/usr/share");
}
auto XDGDataHome = QString::fromLocal8Bit(qgetenv("XDG_DATA_HOME"));
if (XDGDataHome.isEmpty()) {
XDGDataHome = QString::fromLocal8Bit(qgetenv("HOME")) + QDir::separator() + ".local" + QDir::separator() + "share";
}
XDGDataDirs.push_front(std::move(XDGDataHome));
XDGDataDirs.push_front(getXDGDataHome());
std::for_each(XDGDataDirs.begin(), XDGDataDirs.end(), [](QString &str) {
if (!str.endsWith(QDir::separator())) {

View File

@ -33,11 +33,11 @@ public:
auto &bus = ApplicationManager1DBus::instance();
bus.initGlobalServerBus(DBusType::Session);
bus.setDestBus();
m_am = new ApplicationManager1Service{std::make_unique<CGroupsIdentifier>(), bus.globalServerBus()};
std::shared_ptr<ApplicationManager1Storage> tmp{nullptr};
m_am = new ApplicationManager1Service{std::make_unique<CGroupsIdentifier>(), bus.globalServerBus(), tmp};
auto ptr = std::make_unique<QFile>(QString{"/usr/share/applications/test-Application.desktop"});
DesktopFile file{std::move(ptr), "test-Application", 0, 0};
QSharedPointer<ApplicationService> app = QSharedPointer<ApplicationService>::create(std::move(file), nullptr);
QSharedPointer<ApplicationService> app = QSharedPointer<ApplicationService>::create(std::move(file), nullptr, tmp);
QSharedPointer<InstanceService> instance =
QSharedPointer<InstanceService>::create(InstancePath.path().split('/').last(), ApplicationPath.path(), QString{"/"});
app->m_Instances.insert(InstancePath, instance);