ukui-search/ukui-search-app-data-service/app-db-manager.cpp

1507 lines
56 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
*
* Copyright (C) 2023, KylinSoft Co., Ltd.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "app-db-manager.h"
#include "file-utils.h"
#include "convert-winid-to-desktop.h"
#include "application-property-helper.h"
#include <qt5xdg/XdgDesktopFile>
#include <QMutexLocker>
#include <QCryptographicHash>
#include <QFile>
#define GENERAL_APP_DESKTOP_PATH "/usr/share/applications"
#define ANDROID_APP_DESKTOP_PATH QDir::homePath() + "/.local/share/applications"
#define SNAPD_APP_DESKTOP_PATH "/var/lib/snapd/desktop/applications"
#define AUTOSTART_APP_DESKTOP_PATH "/etc/xdg/autostart"
#define LAST_LOCALE_NAME QDir::homePath() + "/.config/org.ukui/ukui-search/appdata/last-locale-name.conf"
#define LOCALE_NAME_VALUE "CurrentLocaleName"
#define APP_DATABASE_VERSION_CONFIG QDir::homePath() + "/.config/org.ukui/ukui-search/appdata/app-database-version.conf"
#define APP_DATABASE_VERSION_VALUE "AppDatabaseVersion"
using namespace UkuiSearch;
static AppDBManager *global_instance;
QMutex AppDBManager::s_mutex;
AppDBManager *AppDBManager::getInstance()
{
if (!global_instance) {
global_instance = new AppDBManager();
}
return global_instance;
}
AppDBManager::AppDBManager(QObject *parent) : QThread(parent), m_database(QSqlDatabase())
{
//链接数据库
if (openDataBase()) {
//建数据库
buildAppInfoDB();
//监听系统语言变化
m_lastLocaleNameQsettings = new QSettings(LAST_LOCALE_NAME, QSettings::IniFormat);
m_localeChanged = false;
m_lastLocaleNameQsettings->beginGroup(LOCALE_NAME_VALUE);
QString lastLocale = m_lastLocaleNameQsettings->value(LOCALE_NAME_VALUE).toString();
if (QLocale::system().name().compare(lastLocale)) {
qDebug() << "I'm going to update the locale name in conf file.";
if (!lastLocale.isEmpty()) {
m_localeChanged = true;
}
m_lastLocaleNameQsettings->setValue(LOCALE_NAME_VALUE, QLocale::system().name());
}
m_lastLocaleNameQsettings->endGroup();
//检查数据库版本
m_dbVersionQsettings = new QSettings(APP_DATABASE_VERSION_CONFIG, QSettings::IniFormat);
m_dbVersionQsettings->beginGroup(APP_DATABASE_VERSION_VALUE);
QString dbVersion = m_dbVersionQsettings->value(APP_DATABASE_VERSION_VALUE).toString();
if (dbVersion.isEmpty()) {
m_dbVersionNeedUpdate = true;
} else if (dbVersion != APP_DATABASE_VERSION) {
if (dbVersion.toDouble() < APP_DATABASE_VERSION.toDouble()) {
m_dbVersionNeedUpdate = true;
} else {
qDebug() << "app db version old version:" << dbVersion.toDouble() << "new version:" << APP_DATABASE_VERSION.toDouble();
}
}
m_dbVersionQsettings->endGroup();
if (m_dbVersionNeedUpdate) {
qDebug() << "app db version need update! old version:" << dbVersion.toDouble() << "new version:" << APP_DATABASE_VERSION.toDouble();
}
//版本号改变更新数据库字段
if (m_dbVersionNeedUpdate) {
for (auto iter = m_namesOfAppinfoTable.constBegin(); iter!= m_namesOfAppinfoTable.constEnd(); iter++) {
this->addItem2BackIfNotExist(iter.key(), iter.value());
}
}
//刷新应用数据
QStringList appPaths;
appPaths << GENERAL_APP_DESKTOP_PATH << ANDROID_APP_DESKTOP_PATH
<< SNAPD_APP_DESKTOP_PATH << AUTOSTART_APP_DESKTOP_PATH;
refreshAllData2DB(appPaths, m_dbVersionNeedUpdate);
if (m_dbVersionNeedUpdate) {
m_dbVersionQsettings->beginGroup(APP_DATABASE_VERSION_VALUE);
m_dbVersionQsettings->setValue(APP_DATABASE_VERSION_VALUE, APP_DATABASE_VERSION);
m_dbVersionQsettings->endGroup();
m_dbVersionNeedUpdate = false;
}
//初始化FileSystemWatcher
initFileSystemWatcher();
/*
//初始化FileSystemWatcher
m_watchAppDir = new QFileSystemWatcher(this);
m_watchAppDir->addPath(GENERAL_APP_DESKTOP_PATH);
QDir androidPath(ANDROID_APP_DESKTOP_PATH);
if(!androidPath.exists()) {
androidPath.mkpath(ANDROID_APP_DESKTOP_PATH);
}
m_watchAppDir->addPath(ANDROID_APP_DESKTOP_PATH);
QDir snapdPath(SNAPD_APP_DESKTOP_PATH);
if(!snapdPath.exists()) {
snapdPath.mkpath(SNAPD_APP_DESKTOP_PATH);
}
m_watchAppDir->addPath(SNAPD_APP_DESKTOP_PATH);
//初始化timer
this->start();
m_timer = new QTimer();
m_maxProcessTimer = new QTimer();
m_timer->setInterval(2*1000);
m_maxProcessTimer->setInterval(5*1000);
m_timer->setSingleShot(true);
m_maxProcessTimer->setSingleShot(true);
m_timer->moveToThread(this);
m_maxProcessTimer->moveToThread(this);
connect(this, SIGNAL(startTimer()), m_timer, SLOT(start()));
connect(this, SIGNAL(maxProcessTimerStart()), m_maxProcessTimer, SLOT(start()));
connect(this, &AppDBManager::stopTimer, m_timer, &QTimer::stop);
connect(this, &AppDBManager::stopTimer, m_maxProcessTimer, &QTimer::stop);
//监听desktop文件所在目录由于directoryChange会发多次信号使用计时器阻塞
connect(m_watchAppDir, &QFileSystemWatcher::directoryChanged, this, [ = ](const QString & path) {
qDebug() << "m_watchAppDir directoryChanged:" << path;
Q_EMIT this->startTimer();
if (!m_maxProcessTimer->isActive()) {
Q_EMIT this->maxProcessTimerStart();
}
});
//计时器超时统一进行更新操作
connect(m_timer, &QTimer::timeout, this, [ & ] {
qDebug() << "Time out! Now I can update the database!";
Q_EMIT this->stopTimer();
this->refreshAllData2DB();
}, Qt::DirectConnection);
connect(m_maxProcessTimer, &QTimer::timeout, this, [ & ] {
qDebug() << "I've waited too lang, I have to update the database now!";
Q_EMIT this->stopTimer();
this->refreshAllData2DB();
}, Qt::DirectConnection);
*/
//监控应用进程开启
connect(KWindowSystem::self(), &KWindowSystem::windowAdded, [ = ](WId id) {
KWindowInfo info = KWindowSystem::windowInfo(id, 0, NET::WM2AllProperties);
if (info.valid()) {
QString desktopfp;
desktopfp = this->tranPidToDesktopFp(info.pid());
if (!desktopfp.isEmpty()) {
this->updateLaunchTimes(desktopfp);
}
}
});
} else {
qDebug() << "App-db-manager does nothing.";
}
}
AppDBManager::~AppDBManager()
{
if (m_watcher) {
delete m_watcher;
}
m_watcher = NULL;
if (m_snapdWatcher) {
delete m_snapdWatcher;
}
m_snapdWatcher = NULL;
// if(m_watchAppDir) {
// delete m_watchAppDir;
// }
// m_watchAppDir = NULL;
closeDataBase();
}
void AppDBManager::buildAppInfoDB()
{
qDebug() << "I'm going to build app info database.";
QSqlQuery sql(m_database);
QString cmd = QString("CREATE TABLE IF NOT EXISTS appInfo(%1, %2, %3, %4, %5, %6, %7, %8,%9, %10, %11, %12, %13, %14, %15, %16, %17, %18, %19, %20, %21)")
// .arg("ID INT")//自增id
.arg("DESKTOP_FILE_PATH TEXT PRIMARY KEY NOT NULL")//desktop文件路径
.arg("MODIFYED_TIME TEXT")//YYYYMMDDHHmmSS 修改日期
.arg("INSERT_TIME TEXT")//YYYYMMDDHHmmSS 插入日期
.arg("LOCAL_NAME TEXT")//本地名称,跟随系统语言
.arg("NAME_EN TEXT")//应用英文名称
.arg("NAME_ZH TEXT")//应用中文名称
.arg("PINYIN_NAME TEXT")//中文拼音
.arg("FIRST_LETTER_OF_PINYIN TEXT")//中文拼音首字母
.arg("FIRST_LETTER_ALL TEXT")//拼音和英文全拼
.arg("ICON TEXT")//图标名称(或路径)
.arg("TYPE TEXT")//应用类型
.arg("CATEGORY TEXT")//应用分类
.arg("EXEC TEXT")//应用命令
.arg("COMMENT TEXT")//应用注释
.arg("MD5 TEXT")//desktop文件内容md5值
.arg("LAUNCH_TIMES INT")//应用打开次数, 等比例缩减
.arg("FAVORITES INT")//收藏顺序0:为收藏,>0的数字表示收藏顺序
.arg("LAUNCHED INT")//应用安装后是否打开过0:未打开过;1:打开过
.arg("TOP INT")//置顶顺序 0:未置顶;>0的数字表示置顶顺序
.arg("LOCK INT")//应用是否锁定管控0未锁定1锁定
.arg("DONT_DISPLAY INT");//应用隐藏(NoDisplay, NotShowIn或位于autostart)
if (!sql.exec(cmd)) {
qWarning() << m_database.lastError() << cmd;
return;
}
}
void AppDBManager::initFileSystemWatcher()
{
m_watcher = new FileSystemWatcher;
m_watcher->addWatch(GENERAL_APP_DESKTOP_PATH);
QDir androidDir(ANDROID_APP_DESKTOP_PATH);
if(!androidDir.exists()) {
androidDir.mkpath(ANDROID_APP_DESKTOP_PATH);
}
m_watcher->addWatch(ANDROID_APP_DESKTOP_PATH);
m_snapdDir = new QDir(SNAPD_APP_DESKTOP_PATH);
if(!m_snapdDir->exists()) {
m_snapdWatcher = new FileSystemWatcher(false);
QDir dir("/var/lib/snapd");
if (!dir.exists()) {
dir.setPath("/var/lib");
}
m_snapdPath = dir.absolutePath();
m_snapdWatcher->addWatch(m_snapdPath);
} else {
m_watcher->addWatch(SNAPD_APP_DESKTOP_PATH);
}
m_watcher->addWatch(AUTOSTART_APP_DESKTOP_PATH);
connect(m_snapdWatcher, &FileSystemWatcher::created, this, [ = ] (const QString &path, bool isDir) {
if (isDir) {
//监测新增目录为/var/lib/snapd时将其替换为snapdWatcher的watchpath
if (path == "/var/lib/snapd") {
m_snapdWatcher->removeWatch(m_snapdPath);
m_snapdWatcher->addWatch(path);
qDebug() << "~~~~~~~add watch" << path << "~~~~~remove watch" << m_snapdPath;
m_snapdPath = path;
//snapd下的desktop目录可能在还没替换监听目录为/var/lib/snapd时就已经被创建因此需要特别判断
QDir dir("/var/lib/snapd/desktop");
if (dir.exists()) {
if (m_snapdDir->exists()) {
m_watcher->addWatch(SNAPD_APP_DESKTOP_PATH);
m_snapdWatcher->removeWatch(m_snapdPath);
qDebug() << "======add watch" << SNAPD_APP_DESKTOP_PATH << "======remove watch" << m_snapdPath;
}
}
}
//检测到/var/lib/snapd/desktop被创建则将监听目录替换为/var/lib/snapd/desktop/applications
if (path == "/var/lib/snapd/desktop" and m_snapdDir->exists()) {
m_watcher->addWatch(SNAPD_APP_DESKTOP_PATH);
m_snapdWatcher->removeWatch(m_snapdPath);
qDebug() << "======add watch" << SNAPD_APP_DESKTOP_PATH << "======remove watch" << m_snapdPath;
}
}
});
connect(m_watcher, &FileSystemWatcher::created, this, [ = ] (const QString &desktopfp, bool isDir) {
//event is IN_CREATE
if (!isDir and desktopfp.endsWith(".desktop")) {
this->insertDBItem(desktopfp);
}
});
connect(m_watcher, &FileSystemWatcher::moveTo, this, [ = ] (const QString &desktopfp, bool isDir) {
//event is IN_MOVED_TO
if (!isDir and desktopfp.endsWith(".desktop")) {
QStringList appPaths(desktopfp.left(desktopfp.lastIndexOf("/")));
this->refreshAllData2DB(appPaths);
}
});
connect(m_watcher, &FileSystemWatcher::modified, this, [ = ] (const QString &desktopfp) {
//event is IN_MODIFY
if (desktopfp.endsWith(".desktop")) {
this->updateDBItem(desktopfp);
}
});
connect(m_watcher, &FileSystemWatcher::moved, this, [ = ] (const QString &desktopfp, bool isDir) {
//event is IN_MOVED_FROM
if (!isDir) {
if (desktopfp.endsWith(".desktop")) {
this->deleteDBItem(desktopfp);
}
} else {
//event is IN_MOVE_SELF
qWarning() << "Dir:" << desktopfp << "has been moved to other place! Stop the watching of the desktop files in it!";
}
});
connect(m_watcher, &FileSystemWatcher::deleted, this, [ = ] (const QString &desktopfp, bool isDir) {
//event is IN_DELETE
if (!isDir) {
if (desktopfp.endsWith(".desktop")) {
this->deleteDBItem(desktopfp);
}
} else {
//event is IN_DELETE_SELF
qWarning() << "Dir:" << desktopfp << "has been deleted! Stop the watching of the desktop files in it!";
}
});
}
void AppDBManager::loadDesktopFilePaths(QString path, QFileInfoList &infolist)
{
QDir dir(path);
dir.setSorting(QDir::DirsFirst);
dir.setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
infolist.append(dir.entryInfoList());
}
bool AppDBManager::addItem2BackIfNotExist(QString itemName, QString itemDataType, QVariant defult)
{
bool res(true);
QSqlQuery sql(m_database);
sql.setForwardOnly(true);
QString cmd = QString("SELECT * FROM sqlite_master WHERE name = 'appInfo' AND sql like '%%1%' ").arg(itemName);
if (!sql.exec(cmd)) {
qWarning() << m_database.lastError() << cmd;
res = false;
return res;
}
if (sql.next()) {
qDebug() << itemName << "is exist!";
return res;
}
cmd = QString("ALTER TABLE appInfo ADD '%0' %1 ").arg(itemName)
.arg(itemDataType);
if (defult != QVariant()) {
//TODO 根据数据类型将初始值转化为对应格式数据
if (itemDataType == "INT(4)") {
cmd += QString("DEFAULT %1").arg(defult.toInt());
} else if (itemDataType == "TEXT") {
cmd += QString("DEFAULT %1").arg(QString(defult.toByteArray()));
}
}
if (!sql.exec(cmd)) {
qWarning() << m_database.lastError() << cmd;
res = false;
} else {
qDebug() << "Add item" << itemName << "successful.";
}
if (!res) {
qDebug() << "Fail to addItem2Back :" << itemName;
}
return res;
}
void AppDBManager::refreshAllData2DB(const QStringList &appPaths, bool dbVersionNeedUpdate)
{
PendingAppInfo items;
items.setHandleType(PendingAppInfo::RefreshDataBase);
items.setPathsNeedRefreshData(appPaths);
items.setDBUpdate(dbVersionNeedUpdate);
PendingAppInfoQueue::getAppInfoQueue().enqueue(items);
}
bool AppDBManager::handleLocaleDataUpdate(const QString &desktopFilePath)
{
bool res(true);
XdgDesktopFile desktopFile;
desktopFile.load(desktopFilePath);
QString localName = desktopFile.localizedValue("Name", "NULL").toString();
QString firstLetter2All = localName;
if (localName.contains(QRegExp("[\\x4e00-\\x9fa5]+"))) {
firstLetter2All = FileUtils::findMultiToneWords(localName).at(0);
}
QSqlQuery query(m_database);
query.prepare("UPDATE appInfo SET LOCAL_NAME=:localName, FIRST_LETTER_ALL=:firstOfLetter2All WHERE DESKTOP_FILE_PATH=:desktopFilePath");
query.bindValue(":localName", localName);
query.bindValue(":firstOfLetter2All", firstLetter2All);
query.bindValue(":desktopFilePath", desktopFilePath);
if (!this->startTransaction()) {
return false;
}
if (query.exec()) {
if (this->startCommit()) {
ApplicationInfoMap appInfo;
appInfo[desktopFilePath].insert(ApplicationProperty::LocalName, QVariant(localName));
appInfo[desktopFilePath].insert(ApplicationProperty::FirstLetterAll, QVariant(firstLetter2All));
Q_EMIT this->appDBItemUpdate(appInfo);
qDebug() << "Update the locale data of " << desktopFilePath;
} else {
res = false;
}
} else {
qDebug() << "Fail to update the locale data of " << desktopFilePath;
qWarning() << query.lastError() << query.lastQuery();
m_database.rollback();
res = false;
}
return res;
}
void AppDBManager::run()
{
exec();
}
void AppDBManager::refreshDataBase()
{
// if (m_database.transaction()) {
// this->updateAllData2DB();
// if (!m_database.commit()) {
// qWarning() << "Failed to commit !";
// m_database.rollback();
// } else if (!m_dbChanged) {
// qDebug() << "app DataBase has no changes!";
// } else {
// Q_EMIT this->finishHandleAppDB();
// }
// } else {
// qWarning() << "Failed to start transaction mode!!!";
// }
}
bool AppDBManager::openDataBase()
{
bool res(true);
QDir dir;
if (!dir.exists(APP_DATABASE_PATH)) {
dir.mkpath(APP_DATABASE_PATH);
}
if (QSqlDatabase::contains(CONNECTION_NAME)) {
m_database = QSqlDatabase::database(CONNECTION_NAME);
} else {
m_database = QSqlDatabase::addDatabase("QSQLITE", CONNECTION_NAME);
m_database.setDatabaseName(APP_DATABASE_PATH + APP_DATABASE_NAME);
}
if(!m_database.open()) {
qWarning() << "Fail to open AppDataBase, because" << m_database.lastError();
res = false;
}
return res;
}
void AppDBManager::closeDataBase()
{
m_database.close();
// delete m_database;
QSqlDatabase::removeDatabase(CONNECTION_NAME);
}
QString AppDBManager::getAppDesktopMd5(const QString &desktopfd)
{
QString res;
QFile file(desktopfd);
file.open(QIODevice::ReadOnly);
res = QString::fromStdString(QCryptographicHash::hash(file.readAll(), QCryptographicHash::Md5).toHex().toStdString());
file.close();
return res;
}
bool AppDBManager::startTransaction()
{
if (m_database.transaction()) {
return true;
} else {
qWarning() << "Failed to start transaction mode!!!" << m_database.lastError();
return false;
}
}
bool AppDBManager::startCommit()
{
if (!m_database.commit()) {
qWarning() << "Failed to commit !" << m_database.lastError();
m_database.rollback();
return false;
} else {
return true;
}
}
bool AppDBManager::handleDBItemInsert(const QString &desktopFilePath)
{
bool res(true);
QSqlQuery sql(m_database);
XdgDesktopFile desktopfile;
desktopfile.load(desktopFilePath);
QString hanzi, pinyin, firstLetterOfPinyin;
QString localName = desktopfile.localizedValue("Name", "NULL").toString();
QString firstLetter2All = localName;
bool isHanzi = true;
if (localName.contains(QRegExp("[\\x4e00-\\x9fa5]+"))) {
firstLetter2All = FileUtils::findMultiToneWords(localName).at(0);
}
if (desktopfile.contains("Name[zh_CN]")) {
hanzi = desktopfile.value("Name[zh_CN]").toString();
} else {
hanzi = desktopfile.value("Name").toString();
if (!hanzi.contains(QRegExp("[\\x4e00-\\x9fa5]+"))) {
isHanzi = false;
}
}
if (isHanzi) {
QStringList pinyinList = FileUtils::findMultiToneWords(hanzi);
for (int i = 0; i<pinyinList.size(); ++i) {
if (i % 2) {
firstLetterOfPinyin += pinyinList.at(i);
} else {
pinyin += pinyinList.at(i);
}
}
}
int dontDisplay = 0;
if (desktopfile.value("NoDisplay").toString().contains("true") ||
desktopfile.value("NotShowIn").toString().contains("UKUI") ||
desktopFilePath.startsWith(AUTOSTART_APP_DESKTOP_PATH)) {
dontDisplay = 1;
}
sql.prepare(QString("INSERT INTO appInfo "
"(DESKTOP_FILE_PATH, MODIFYED_TIME, INSERT_TIME, "
"LOCAL_NAME, NAME_EN, NAME_ZH, PINYIN_NAME, "
"FIRST_LETTER_OF_PINYIN, FIRST_LETTER_ALL, "
"ICON, TYPE, CATEGORY, EXEC, COMMENT, MD5, "
"LAUNCH_TIMES, FAVORITES, LAUNCHED, TOP, LOCK, DONT_DISPLAY) "
"VALUES(:desktopFilePath, '%0', '%1', :localName, :enName, :zhName, :pinyinName, :firstLetterOfPinyin, :firstLetter2All, "
":icon, :type, :categories, :exec, :comment,'%2',%3,%4,%5,%6,%7,%8)")
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"))
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"))
.arg(getAppDesktopMd5(desktopFilePath))
.arg(0)
.arg(0)
.arg(0)
.arg(0)
.arg(0)
.arg(dontDisplay));
sql.bindValue(":desktopFilePath", desktopFilePath);
sql.bindValue(":localName", localName);
sql.bindValue(":enName", desktopfile.value("Name").toString());
sql.bindValue(":zhName", hanzi);
sql.bindValue(":pinyinName", pinyin);
sql.bindValue(":firstLetterOfPinyin", firstLetterOfPinyin);
sql.bindValue(":firstLetter2All", firstLetter2All);
sql.bindValue(":icon", desktopfile.value("Icon").toString());
sql.bindValue(":type", desktopfile.value("Type").toString());
sql.bindValue(":categories", desktopfile.value("Categories").toString());
sql.bindValue(":exec", desktopfile.value("Exec").toString());
sql.bindValue(":comment", desktopfile.value("Comment").toString());
if (!this->startTransaction()) {
return false;
}
if (sql.exec()) {
if (this->startCommit()) {
Q_EMIT this->appDBItemAdd(desktopFilePath);
qDebug() << "app database add " << desktopFilePath << "success!";
} else {
res = false;
}
} else {
qDebug() << "app database add " << desktopFilePath << "failed!";
qWarning() << m_database.lastError() << sql.lastQuery();
m_database.rollback();
res = false;
}
return res;
}
bool AppDBManager::handleDBItemDelete(const QString &desktopFilePath)
{
if (!this->startTransaction()) {
return false;
}
bool res(true);
QSqlQuery query(m_database);
query.setForwardOnly(true);
QString cmd = "SELECT FAVORITES, TOP FROM APPINFO WHERE DESKTOP_FILE_PATH=:desktopFilePath";
query.prepare(cmd);
query.bindValue(":desktopFilePath", desktopFilePath);
//查询要删除信息的应用是否被收藏或顶置过
if (!query.exec()) {
res = false;
qWarning() << m_database.lastError() << query.lastQuery();
} else if (query.next()) {
int favorites = query.value("FAVORITES").toInt();
int top = query.value("TOP").toInt();
if (favorites) {
query.prepare(QString("UPDATE appInfo SET FAVORITES = FAVORITES -1 WHERE FAVORITES > %1").arg(favorites));
if (query.exec()) {
cmd = QString("SELECT DESKTOP_FILE_PATH,FAVORITES FROM APPINFO WHERE FAVORITES >= %1").arg(favorites);
if (query.exec(cmd)) {
ApplicationInfoMap infos;
while (query.next()) {
if (query.value("DESKTOP_FILE_PATH").toString() == desktopFilePath) {
continue;
}
infos[query.value("DESKTOP_FILE_PATH").toString()].insert(ApplicationProperty::Favorites, query.value("FAVORITES"));
}
if (!infos.isEmpty()) {
Q_EMIT this->appDBItemUpdate(infos);
}
} else {
qWarning() << query.lastError() << query.lastQuery();
}
} else {
res = false;
qWarning() << "I'm going to delete item in db, fail to update the FAVORITES because:" << query.lastError() << cmd;
}
}
if (top) {
query.prepare(QString("UPDATE appInfo SET TOP = TOP -1 WHERE TOP > %1").arg(top));
if (query.exec()) {
cmd = QString("SELECT DESKTOP_FILE_PATH,TOP FROM APPINFO WHERE TOP >= %1").arg(top);
if (query.exec(cmd)) {
ApplicationInfoMap infos;
while (query.next()) {
if (query.value("DESKTOP_FILE_PATH").toString() == desktopFilePath) {
continue;
}
infos[query.value("DESKTOP_FILE_PATH").toString()].insert(ApplicationProperty::Top, query.value("TOP"));
}
if (!infos.isEmpty()) {
Q_EMIT this->appDBItemUpdate(infos);
}
} else {
qWarning() << query.lastError() << query.lastQuery();
}
} else {
res = false;
qWarning() << "I'm going to delete item in db, fail to update the TOP because:" << query.lastError() << cmd;
}
}
} else {
qWarning() << "Fail to exec next, because" << query.lastError() << "while executing " << query.lastQuery();
}
if (!res) {
m_database.rollback();
return res;
}
//执行删除操作
cmd = "DELETE FROM APPINFO WHERE DESKTOP_FILE_PATH=:desktopFilePath";
query.prepare(cmd);
query.bindValue(":desktopFilePath", desktopFilePath);
if (query.exec()) {
if (this->startCommit()) {
Q_EMIT this->appDBItemDelete(desktopFilePath);
qDebug() << "app database delete " << desktopFilePath << "success!";
} else {
res = false;
}
} else {
qDebug() << "app database delete " << desktopFilePath << "failed!";
qWarning() << query.lastError() << cmd;
m_database.rollback();
res = false;
}
return res;
}
bool AppDBManager::handleDBItemUpdate(const QString &desktopFilePath)
{
bool res(true);
int dontDisplay = 0;
XdgDesktopFile desktopfile;
desktopfile.load(desktopFilePath);
if (desktopfile.value("NoDisplay").toString().contains("true") ||
desktopfile.value("NotShowIn").toString().contains("UKUI") ||
desktopFilePath.startsWith(AUTOSTART_APP_DESKTOP_PATH)) {
dontDisplay = 1;
}
QString hanzi, pinyin, firstLetterOfPinyin;
QString localName = desktopfile.localizedValue("Name", "NULL").toString();
QString firstLetter2All = localName;
bool isHanzi = true;
if (localName.contains(QRegExp("[\\x4e00-\\x9fa5]+"))) {
firstLetter2All = FileUtils::findMultiToneWords(localName).at(0);
}
if (desktopfile.contains("Name[zh_CN]")) {
hanzi = desktopfile.value("Name[zh_CN]").toString();
} else {
hanzi = desktopfile.value("Name").toString();
if (!hanzi.contains(QRegExp("[\\x4e00-\\x9fa5]+"))) {
isHanzi = false;
}
}
if (isHanzi) {
QStringList pinyinList = FileUtils::findMultiToneWords(hanzi);
firstLetter2All = pinyinList.at(0);
for (int i = 0; i<pinyinList.size(); ++i) {
if (i % 2) {
firstLetterOfPinyin += pinyinList.at(i);
} else {
pinyin += pinyinList.at(i);
}
}
}
QSqlQuery sql(m_database);
sql.prepare(QString("UPDATE appInfo SET "
"MODIFYED_TIME='%0',"
"LOCAL_NAME=:localName,"
"NAME_EN=:enName,"
"NAME_ZH=:zhName,"
"PINYIN_NAME=:pinyinName,"
"FIRST_LETTER_OF_PINYIN=:firstLetterOfPinyin,"
"FIRST_LETTER_ALL=:firstLetter2All,"
"ICON=:icon,"
"TYPE=:type,"
"CATEGORY=:categories,"
"EXEC=:exec,"
"COMMENT=:comment,"
"MD5='%1',"
"DONT_DISPLAY=%2 "
"WHERE DESKTOP_FILE_PATH=:desktopFilePath")
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"))
.arg(getAppDesktopMd5(desktopFilePath))
.arg(dontDisplay));
sql.bindValue(":desktopFilePath", desktopFilePath);
sql.bindValue(":localName", localName);
sql.bindValue(":enName", desktopfile.value("Name").toString());
sql.bindValue(":zhName", hanzi);
sql.bindValue(":pinyinName", pinyin);
sql.bindValue(":firstLetterOfPinyin", firstLetterOfPinyin);
sql.bindValue(":firstLetter2All", firstLetter2All);
sql.bindValue(":icon", desktopfile.value("Icon").toString());
sql.bindValue(":type", desktopfile.value("Type").toString());
sql.bindValue(":categories", desktopfile.value("Categories").toString());
sql.bindValue(":exec", desktopfile.value("Exec").toString());
sql.bindValue(":comment", desktopfile.value("Comment").toString());
if (!this->startTransaction()) {
return false;
}
if (sql.exec()) {
if (this->startCommit()) {
Q_EMIT this->appDBItemUpdateAll(desktopFilePath);
qDebug() << "app database update all data of" << desktopFilePath << "success!";
} else {
res = false;
}
} else {
qDebug() << "app database update " << desktopFilePath << "failed!";
qWarning() << m_database.lastError() << sql.lastQuery();
m_database.rollback();
res = false;
}
return res;
}
bool AppDBManager::handleLaunchTimesUpdate(const QString &desktopFilePath, int num)
{
qDebug() << "launch times will add:" << num;
bool res(true);
QSqlQuery query(m_database);
query.setForwardOnly(true);
query.prepare("SELECT LAUNCH_TIMES FROM APPINFO WHERE DESKTOP_FILE_PATH=:desktopFilePath");
query.bindValue(":desktopFilePath", desktopFilePath);
if (query.exec()) {
if (query.next()) {
int launchTimes = query.value(0).toInt() + num;
if (!this->startTransaction()) {
res = false;
return res;
}
query.prepare(QString("UPDATE appInfo SET MODIFYED_TIME='%0', LAUNCH_TIMES=%1, LAUNCHED=%2 WHERE DESKTOP_FILE_PATH=:desktopFilePath")
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"))
.arg(launchTimes)
.arg(1));
query.bindValue(":desktopFilePath", desktopFilePath);
if (!query.exec()) {
qWarning() << "Set app launch times failed!" << m_database.lastError();
res = false;
m_database.rollback();
} else {
if (this->startCommit()) {
ApplicationInfoMap appInfo;
appInfo[desktopFilePath].insert(ApplicationProperty::LaunchTimes, QVariant(launchTimes));
appInfo[desktopFilePath].insert(ApplicationProperty::Launched, QVariant(1));
Q_EMIT this->appDBItemUpdate(appInfo);
qDebug() << "app database update " << desktopFilePath << "launch times: " << launchTimes << "success!";
} else {
res = false;
}
}
} else {
qWarning() << "Failed to exec next!" << query.lastQuery() << query.lastError();
res = false;
}
} else {
qWarning() << "Failed to exec:" << query.lastQuery();
res = false;
}
return res;
}
bool AppDBManager::handleFavoritesStateUpdate(const QString &desktopFilePath, const uint num)
{
bool res(true);
QSqlQuery query(m_database);
query.setForwardOnly(true);
QString cmd;
ApplicationInfoMap infos;
//获取应用在数据库中的favorites标志位
int previousPos = -1;
cmd = "SELECT FAVORITES FROM APPINFO WHERE DESKTOP_FILE_PATH =:desktopFilePath";
query.prepare(cmd);
query.bindValue(":desktopFilePath", desktopFilePath);
if (query.exec()) {
if (query.next()) {
previousPos = query.value("FAVORITES").toInt();
} else {
qWarning() << query.lastQuery() << query.lastError();
}
} else {
qWarning() << query.lastQuery() << query.lastError();
}
//收藏位未改变
if (previousPos == num) {
res = false;
qWarning() << "favorites state has no changes, I quit!";
return res;
}
//查询目前favorites最大值
int maxFavorite = -1;
query.prepare("SELECT max(FAVORITES) as max FROM APPINFO");
if (query.exec()) {
if (query.next()) {
maxFavorite = query.value("max").toInt();
if (maxFavorite + 1 < num) {
qWarning() << QString("Max favorites pos is %0.To be moved to a invalid pos, I quit!!").arg(query.value("max").toInt());
res = false;
}
}
} else {
qWarning() << query.lastError() << query.lastQuery();
res = false;
}
if (!res) {
return res;
}
//取消收藏时,将对应应用前移
if (!num) {
if (previousPos < 1) {
res = false;
}
if (res) {
if (!this->startTransaction()) {
res = false;
return res;
}
cmd = QString("UPDATE APPINFO SET MODIFYED_TIME='%0', FAVORITES=FAVORITES-1 WHERE FAVORITES > %1;")
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"))
.arg(previousPos);
if (query.exec(cmd)) {
cmd = QString("SELECT DESKTOP_FILE_PATH,FAVORITES FROM APPINFO WHERE FAVORITES >= %0;").arg(previousPos);
if (query.exec(cmd)) {
qDebug() << "Prepare to cancel the favorite state of" << desktopFilePath;
while (query.next()) {
infos[query.value("DESKTOP_FILE_PATH").toString()].insert(ApplicationProperty::Favorites, query.value("FAVORITES"));
}
} else {
qWarning() << query.lastQuery() << query.lastError();
}
} else {
res = false;
qWarning() << query.lastQuery() << query.lastError();
m_database.rollback();
}
}
} else {
//直接设置时(要设置的应用未被收藏查询要设置的favorites标志位是否被占用占用则将该应用及其之后的应用的favorites标志位后移
if (!previousPos) {
cmd = QString("SELECT DESKTOP_FILE_PATH FROM APPINFO WHERE FAVORITES = %1").arg(num);
if (!query.exec(cmd)) {
qWarning() << "Fail to exec:" << cmd << "because:" << query.lastError();
res = false;
} else {
if (!this->startTransaction()) {
return false;
}
if (query.next()) {
//默认收藏后顶到第一个,其余后移,若想直接收藏到对应位置则将该位置后的收藏应用后移
query.prepare(QString("UPDATE APPINFO SET MODIFYED_TIME='%0', FAVORITES=FAVORITES+1 WHERE FAVORITES >= %1;")
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"))
.arg(num));
if (!query.exec()) {
res = false;
qWarning() << query.lastError() << query.lastQuery();
m_database.rollback();
} else {
cmd = QString("SELECT DESKTOP_FILE_PATH, FAVORITES FROM APPINFO WHERE FAVORITES >= %1").arg(num);
if (!query.exec(cmd)) {
qWarning() << query.lastError() << query.lastQuery();
res = false;
} else {
while (query.next()) {
infos[query.value("DESKTOP_FILE_PATH").toString()].insert(ApplicationProperty::Favorites, query.value("FAVORITES"));
}
}
}
}
}
} else {
//触发修改位置逻辑
res = maxFavorite < num? false : this->handleChangeFavoritesPos(desktopFilePath, num, previousPos, infos);
qDebug() << "change favorites pos:" << res;
}
}
//移动位置执行不成功则不去更新对应应用的favorites标志位
if (!res) {
return res;
}
//更新favorites状态
cmd = QString("UPDATE APPINFO SET MODIFYED_TIME='%0', FAVORITES=%1 WHERE DESKTOP_FILE_PATH=:desktopFilePath")
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"))
.arg(num);
query.prepare(cmd);
query.bindValue(":desktopFilePath", desktopFilePath);
if (!query.exec()) {
qWarning() << "Set app favorites state failed!" << m_database.lastError();
res = false;
m_database.rollback();
} else {
if (this->startCommit()) {
infos[desktopFilePath].insert(ApplicationProperty::Favorites, QVariant(num));
Q_EMIT this->appDBItemUpdate(infos);
qDebug() << "app database update " << desktopFilePath << "favorites state: " << num << "success!";
} else {
res = false;
}
}
return res;
}
bool AppDBManager::handleChangeFavoritesPos(const QString &desktopFilePath, const uint pos, const int previousPos, ApplicationInfoMap &updatedInfo)
{
if (pos < 1) {
qWarning() << "To be moved to a invalid favorites pos , I quit!!";
return false;
}
if (previousPos < 1) {
qWarning() << QString("app: %1 is not a favorites app, I quit!!").arg(desktopFilePath);
return false;
}
bool res(true);
QSqlQuery query(m_database);
query.setForwardOnly(true);
QString condition;
if (previousPos > pos) {
condition = "FAVORITES=FAVORITES+1";
} else {
condition = "FAVORITES=FAVORITES-1";
}
query.prepare(QString("UPDATE APPINFO SET %0 WHERE FAVORITES BETWEEN MIN(%1, %2) AND MAX(%1, %2)")
.arg(condition)
.arg(previousPos)
.arg(pos));
if (!this->startTransaction()) {
return false;
}
//更新原位置和新位置之间的应用的位置
if (query.exec()) {
query.prepare(QString("SELECT DESKTOP_FILE_PATH,FAVORITES FROM APPINFO WHERE FAVORITES BETWEEN MIN(%1, %2) AND MAX(%1, %2)")
.arg(previousPos)
.arg(pos));
if (query.exec()) {
while (query.next()) {
updatedInfo[query.value("DESKTOP_FILE_PATH").toString()].insert(ApplicationProperty::Favorites, query.value("FAVORITES"));
}
}
} else {
res = false;
qWarning() << "Fail to change favorite-app pos, because: " << query.lastError() << " when exec :" << query.lastQuery();
m_database.rollback();
}
return res;
}
bool AppDBManager::handleTopStateUpdate(const QString &desktopFilePath, const uint num)
{
bool res(true);
QSqlQuery query(m_database);
query.setForwardOnly(true);
QString cmd;
ApplicationInfoMap infos;
//获取应用在数据库中的top标志位
int previousPos = -1;
cmd = "SELECT TOP FROM APPINFO WHERE DESKTOP_FILE_PATH =:desktopFilePath";
query.prepare(cmd);
query.bindValue(":desktopFilePath", desktopFilePath);
if (query.exec()) {
if (query.next()) {
previousPos = query.value("TOP").toInt();
} else {
qWarning() << query.lastQuery() << query.lastError();
}
} else {
qWarning() << query.lastQuery() << query.lastError();
}
//top位未改变
if (previousPos == num) {
res = false;
qWarning() << "Top state has no changes, I quit!";
return res;
}
//查询目前top最大值
int maxTop = -1;
query.prepare("SELECT max(TOP) as max FROM APPINFO");
if (query.exec()) {
if (query.next()) {
maxTop = query.value("max").toInt();
if (maxTop + 1 < num) {
qWarning() << QString("Max top pos is %0.To be moved to a invalid pos, I quit!!").arg(query.value("max").toInt());
res = false;
}
} else {
res = false;
}
} else {
qWarning() << query.lastError() << query.lastQuery();
res = false;
}
if (!res) {
return res;
}
//取消置顶时,将对应应用前移
if (!num) {
if (previousPos < 1) {
res = false;
}
if (res) {
if (!this->startTransaction()) {
res = false;
return res;
}
cmd = QString("UPDATE APPINFO SET MODIFYED_TIME='%0', TOP=TOP-1 WHERE TOP > %1;")
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"))
.arg(previousPos);
if (query.exec(cmd)) {
cmd = QString("SELECT DESKTOP_FILE_PATH,TOP FROM APPINFO WHERE TOP >= %0;").arg(previousPos);
if (query.exec(cmd)) {
qDebug() << "Prepare to cancel the top state of" << desktopFilePath;
while (query.next()) {
infos[query.value("DESKTOP_FILE_PATH").toString()].insert(ApplicationProperty::Top, query.value("TOP"));
}
} else {
qWarning() << query.lastQuery() << query.lastError();
}
} else {
res = false;
qWarning() << query.lastQuery() << query.lastError();
m_database.rollback();
}
}
} else {
//直接设置时查询要设置的top标志位是否被占用占用则将该应用及其之后的应用的top标志位后移
if (!previousPos) {
cmd = QString("SELECT DESKTOP_FILE_PATH FROM APPINFO WHERE TOP = %1").arg(num);
if (!query.exec(cmd)) {
qWarning() << "Fail to exec:" << cmd << "because:" << query.lastError();
} else {
if (!this->startTransaction()) {
return false;
}
if (query.next()) {
//默认置顶后顶到第一个,其余后移,若想直接置顶到对应位置则将该位置后的置顶应用后移
query.prepare(QString("UPDATE APPINFO SET MODIFYED_TIME='%0', TOP=TOP+1 WHERE TOP >= %1;")
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"))
.arg(num));
if (!query.exec()) {
qWarning() << query.lastError() << query.lastQuery();
res = false;
m_database.rollback();
} else {
cmd = QString("SELECT DESKTOP_FILE_PATH, TOP FROM APPINFO WHERE TOP >= %1").arg(num);
if (!query.exec(cmd)) {
qWarning() << query.lastError() << query.lastQuery();
res = false;
} else {
while (query.next()) {
infos[query.value("DESKTOP_FILE_PATH").toString()].insert(ApplicationProperty::Top, query.value("TOP"));
}
}
}
}
}
} else {
//触发修改位置逻辑
res = maxTop < num ? false : this->handleChangeTopPos(desktopFilePath, num, previousPos, infos);
qDebug() << "Change top pos:" << res;
}
}
//移动位置执行不成功则不去更新对应应用的top标志位
if (!res) {
return res;
}
//更新top状态
cmd = QString("UPDATE APPINFO SET MODIFYED_TIME='%0', TOP=%1 WHERE DESKTOP_FILE_PATH=:desktopFilePath")
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"))
.arg(num);
query.prepare(cmd);
query.bindValue(":desktopFilePath", desktopFilePath);
if (!query.exec()) {
qWarning() << "Set app top state failed!" << query.lastError();
res = false;
m_database.rollback();
} else {
if (this->startCommit()) {
infos[desktopFilePath].insert(ApplicationProperty::Top, QVariant(num));
Q_EMIT this->appDBItemUpdate(infos);
qDebug() << "app database update " << desktopFilePath << "top state: " << num << "success!";
} else {
res = false;
}
}
return res;
}
bool AppDBManager::handleChangeTopPos(const QString &desktopFilePath, uint pos, const int previousPos, ApplicationInfoMap &updatedInfo)
{
if (pos < 1) {
qWarning() << "To be moved to a invalid top pos , I quit!!";
return false;
}
if (previousPos < 1) {
qWarning() << QString("app: %1 is not a top app, I quit!!").arg(desktopFilePath);
return false;
}
bool res(true);
QSqlQuery query(m_database);
query.setForwardOnly(true);
QString condition;
if (previousPos > pos) {
condition = "TOP=TOP+1";
} else {
condition = "TOP=TOP-1";
}
query.prepare(QString("UPDATE APPINFO SET %0 WHERE TOP BETWEEN MIN(%1, %2) AND MAX(%1, %2)")
.arg(condition)
.arg(previousPos)
.arg(pos));
if (!this->startTransaction()) {
res = false;
return res;
}
//更新原位置和新位置之间的应用的位置
if (query.exec()) {
query.prepare(QString("SELECT DESKTOP_FILE_PATH,TOP FROM APPINFO WHERE TOP BETWEEN MIN(%1, %2) AND MAX(%1, %2)")
.arg(previousPos)
.arg(pos));
if (query.exec()) {
while (query.next()) {
updatedInfo[query.value("DESKTOP_FILE_PATH").toString()].insert(ApplicationProperty::Top, query.value("TOP"));
}
}
} else {
qWarning() << "Fail to change favorite-app pos, because: " << query.lastError() << " when exec :" << query.lastQuery();
res = false;
m_database.rollback();
}
return res;
}
bool AppDBManager::handleLockStateUpdate(const QString &desktopFilePath, int num)
{
if (!this->startTransaction()) {
return false;
}
bool res(true);
QSqlQuery sql(m_database);
sql.prepare(QString("UPDATE appInfo SET MODIFYED_TIME='%0', LOCK=%1 WHERE DESKTOP_FILE_PATH=:desktopFilePath")
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"))
.arg(num));
sql.bindValue(":desktopFilePath", desktopFilePath);
if (!sql.exec()) {
qWarning() << "Set app lock state failed!" << m_database.lastError();
res = false;
m_database.rollback();
} else {
if (this->startCommit()) {
ApplicationInfoMap appInfo;
appInfo[desktopFilePath].insert(ApplicationProperty::Lock, QVariant(num));
Q_EMIT this->appDBItemUpdate(appInfo);
qDebug() << "app database update " << desktopFilePath << "lock state: " << num << "success!";
} else {
res = false;
}
}
return res;
}
void AppDBManager::handleDataBaseRefresh(const QStringList &appPaths, bool dbVersionNeedUpdate)
{
QMap<QString, QString> dataMap;
QSqlQuery query(m_database);
query.setForwardOnly(true);
QString condition;
for (int i = 0; i < appPaths.size(); i++) {
condition.append("DESKTOP_FILE_PATH LIKE ? OR ");
}
condition = condition.left(condition.lastIndexOf(" OR "));
query.prepare(QString("SELECT DESKTOP_FILE_PATH,MD5 FROM APPINFO WHERE %0").arg(condition));
for (int t = 0; t < appPaths.size(); t++) {
query.bindValue(t, appPaths.at(t) + "/%");
}
if (!query.exec()) {
qWarning() << m_database.lastError() << query.lastError();
} else {
query.exec();
while (query.next()) {
dataMap.insert(query.value("DESKTOP_FILE_PATH").toString(), query.value("MD5").toString());
}
}
//遍历desktop文件
QFileInfoList infos;
for (const QString &path : appPaths) {
this->loadDesktopFilePaths(path, infos);
}
if(infos.size() < 1) {
return;
}
XdgDesktopFile desktopfile;
for (int i = 0; i < infos.length(); i++) {
QFileInfo fileInfo = infos.at(i);
QString path = fileInfo.filePath();
//对目录递归
if (fileInfo.isDir()) {
loadDesktopFilePaths(path, infos);
continue;
}
//排除非法路径非desktop文件
if (!path.endsWith(".desktop")) {
continue;
}
desktopfile.load(path);
//排除loaclized名字为空
if (desktopfile.localizedValue("Name").toString().isEmpty()) {
continue;
}
if (!dataMap.isEmpty()) {
//数据库有记录
if (dataMap.contains(path)) {
if (!QString::compare(dataMap.value(path), getAppDesktopMd5(path)) && !dbVersionNeedUpdate) {
//判断系统语言是否改变
if (m_localeChanged) {
this->handleLocaleDataUpdate(path);
}
dataMap.remove(path);
continue;
} else {
//数据库有记录但md5值改变或数据库版本需要更新则update
this->handleDBItemUpdate(path);
dataMap.remove(path);
continue;
}
} else {
//数据库中没有记录则insert
this->handleDBItemInsert(path);
dataMap.remove(path);
continue;
}
}
//数据库为空则全部insert
this->handleDBItemInsert(path);
dataMap.remove(path);
}
//遍历完成后重置标志位
m_localeChanged = false;
//数据库冗余项直接delete
if (!dataMap.isEmpty()) {
for (auto i = dataMap.constBegin(); i != dataMap.constEnd(); i++) {
this->handleDBItemDelete(i.key());
}
}
}
bool AppDBManager::handleValueSet(const ApplicationInfoMap appInfoMap)
{
bool res(true);
QSqlQuery query(m_database);
for (auto iter = appInfoMap.constBegin(); iter != appInfoMap.constEnd(); iter++) {
QString desktopFilePath = iter.key();
ApplicationPropertyMap propMap = iter.value();
for (auto propIter = propMap.constBegin(); propIter != propMap.constEnd(); ++propIter) {
QString field = ApplicationPropertyHelper(propIter.key()).dataBaseField();
query.prepare(QString("UPDATE appInfo SET MODIFYED_TIME='%0', %1=:value WHERE DESKTOP_FILE_PATH=:desktopFilePath")
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"))
.arg(field));
query.bindValue(":value", propIter.value());
query.bindValue(":desktopFilePath", desktopFilePath);
if (!this->startTransaction()) {
return false;
}
if (!query.exec()) {
qWarning() << "Set state failed!" << query.lastError();
res = false;
m_database.rollback();
} else {
if (this->startCommit()) {
ApplicationInfoMap infos2BSend;
infos2BSend[desktopFilePath].insert(propIter.key(), propIter.value());
Q_EMIT this->appDBItemUpdate(infos2BSend);
qDebug() << "app database update " << desktopFilePath << field << propIter.value() << "success!";
} else {
res = false;
}
}
}
}
return res;
}
QString AppDBManager::tranPidToDesktopFp(int pid)
{
QString exePath = QFile::symLinkTarget("/proc/" + QString::number(pid) + "/exe");
QString desktopfp;
QSqlQuery sql(m_database);
sql.setForwardOnly(true);
sql.prepare("SELECT DESKTOP_FILE_PATH, EXEC FROM APPINFO WHERE EXEC LIKE :exePath AND DONT_DISPLAY = 0");
sql.bindValue(":exePath", "%" + exePath.section('/', -1) + "%");
if (sql.exec()) {
QMap<QString, QString> execInfos;
while (sql.next()) {
execInfos[sql.value("DESKTOP_FILE_PATH").toString()] = sql.value("EXEC").toString();
desktopfp = sql.value("DESKTOP_FILE_PATH").toString();
}
//筛选后有多个结果时进一步过滤
if (execInfos.size() > 1) {
desktopfp.clear();
for (const QString &path : execInfos.values()) {
QStringList execlist = path.split(" ", QString::SkipEmptyParts);
for (QString &partOfExec : execlist) {
//remove the cmd option
if (partOfExec.contains("%")) {
continue;
}
//remove the " in cmd
if (partOfExec.contains("\"")) {
partOfExec.remove("\"");
}
//compare the binary path
if ((partOfExec.contains("/") && partOfExec == exePath) ||
(partOfExec == exePath.section("/", -1))) {
desktopfp = execInfos.key(path);
break;
}
}
if (!desktopfp.isEmpty()) {
break;
}
}
}
if (!desktopfp.isEmpty()) {
qDebug() << "PID: " << pid << "Desktop file path: " << desktopfp;
} else {
qWarning() << "Can not find the desktop file of" << exePath << "by pid:" << pid;
}
} else {
qWarning() << "Fail to exec cmd" << sql.lastQuery() << m_database.lastError();
}
return desktopfp;
}
void AppDBManager::insertDBItem(const QString &desktopfd)
{
PendingAppInfo item(desktopfd, PendingAppInfo::HandleType::Insert);
PendingAppInfoQueue::getAppInfoQueue().enqueue(item);
}
void AppDBManager::updateDBItem(const QString &desktopfd)
{
PendingAppInfo item(desktopfd, PendingAppInfo::HandleType::UpdateAll);
PendingAppInfoQueue::getAppInfoQueue().enqueue(item);
}
void AppDBManager::deleteDBItem(const QString &desktopfd)
{
PendingAppInfo item(desktopfd, PendingAppInfo::HandleType::Delete);
PendingAppInfoQueue::getAppInfoQueue().enqueue(item);
}
void AppDBManager::updateLocaleData(const QString &desktopFilePath)
{
PendingAppInfo item(desktopFilePath, PendingAppInfo::HandleType::UpdateLocaleData);
PendingAppInfoQueue::getAppInfoQueue().enqueue(item);
}
void AppDBManager::updateLaunchTimes(const QString &desktopFilePath)
{
PendingAppInfo item(desktopFilePath, PendingAppInfo::HandleType::UpdateLaunchTimes);
item.setLaunchWillAdd(true);
item.setLaunchTimes(1);
PendingAppInfoQueue::getAppInfoQueue().enqueue(item);
}
void AppDBManager::updateFavoritesState(const QString &desktopFilePath, uint num)
{
PendingAppInfo item(desktopFilePath, PendingAppInfo::HandleType::UpdateFavorites);
item.setFavorites(num);
PendingAppInfoQueue::getAppInfoQueue().enqueue(item);
}
void AppDBManager::updateTopState(const QString &desktopFilePath, uint num)
{
PendingAppInfo item(desktopFilePath, PendingAppInfo::HandleType::UpdateTop);
item.setTop(num);
PendingAppInfoQueue::getAppInfoQueue().enqueue(item);
}
void AppDBManager::setValue(const ApplicationInfoMap &infos2BSet)
{
for (const QString &desktopFilePath : infos2BSet.keys()) {
PendingAppInfo item(desktopFilePath, PendingAppInfo::SetValue);
item.setValue(infos2BSet);
PendingAppInfoQueue::getAppInfoQueue().enqueue(item);
}
}