feat: 启动器支持玲珑应用卸载功能实现
1. 使用命令行方式实现玲珑应用卸载 2. 当linglong-bin版本大于3.3.14, ll-cli uninstall appId,才是同步执行,根据卸载后返回的字段信息确认卸载状态;否则为异步方式进行调用卸载的,这种情况下,AM 是无法得到卸载应用的状态的。 Log: Influence: 启动器支持玲珑应用卸载 Task: https://pms.uniontech.com/task-view-207481.html Change-Id: Ia809babd2d6f5508e1ea06a60315c40397d3d944
This commit is contained in:
parent
6c06ed86b7
commit
ecc94f01c8
@ -69,6 +69,7 @@ void DBusAdaptorLauncher::setFullscreen(bool value)
|
|||||||
|
|
||||||
LauncherItemInfoList DBusAdaptorLauncher::GetAllItemInfos()
|
LauncherItemInfoList DBusAdaptorLauncher::GetAllItemInfos()
|
||||||
{
|
{
|
||||||
|
parent()->initItems();
|
||||||
return parent()->getAllItemInfos();
|
return parent()->getAllItemInfos();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,11 +108,11 @@ bool DBusAdaptorLauncher::RequestSendToDesktop(const QString &id)
|
|||||||
return parent()->requestSendToDesktop(id);
|
return parent()->requestSendToDesktop(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DBusAdaptorLauncher::RequestUninstall(const QString &id, bool unused)
|
void DBusAdaptorLauncher::RequestUninstall(const QString &desktop, bool unused)
|
||||||
{
|
{
|
||||||
Q_UNUSED(unused);
|
Q_UNUSED(unused);
|
||||||
|
|
||||||
parent()->requestUninstall(id);
|
parent()->requestUninstall(desktop);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DBusAdaptorLauncher::SetDisableScaling(const QString &id, bool value)
|
void DBusAdaptorLauncher::SetDisableScaling(const QString &id, bool value)
|
||||||
|
@ -78,7 +78,7 @@ class DBusAdaptorLauncher: public QDBusAbstractAdaptor
|
|||||||
" <arg direction=\"out\" type=\"b\" name=\"ok\"/>\n"
|
" <arg direction=\"out\" type=\"b\" name=\"ok\"/>\n"
|
||||||
" </method>\n"
|
" </method>\n"
|
||||||
" <method name=\"RequestUninstall\">\n"
|
" <method name=\"RequestUninstall\">\n"
|
||||||
" <arg direction=\"in\" type=\"s\" name=\"id\"/>\n"
|
" <arg direction=\"in\" type=\"s\" name=\"desktop\"/>\n"
|
||||||
" <arg direction=\"in\" type=\"b\" name=\"unused\"/>\n"
|
" <arg direction=\"in\" type=\"b\" name=\"unused\"/>\n"
|
||||||
" </method>\n"
|
" </method>\n"
|
||||||
" <method name=\"SetDisableScaling\">\n"
|
" <method name=\"SetDisableScaling\">\n"
|
||||||
@ -134,7 +134,7 @@ public Q_SLOTS: // METHODS
|
|||||||
bool IsItemOnDesktop(const QString &id);
|
bool IsItemOnDesktop(const QString &id);
|
||||||
bool RequestRemoveFromDesktop(const QString &id);
|
bool RequestRemoveFromDesktop(const QString &id);
|
||||||
bool RequestSendToDesktop(const QString &id);
|
bool RequestSendToDesktop(const QString &id);
|
||||||
void RequestUninstall(const QString &id, bool unused);
|
void RequestUninstall(const QString &desktop, bool unused);
|
||||||
void SetDisableScaling(const QString &id, bool value);
|
void SetDisableScaling(const QString &id, bool value);
|
||||||
void SetUseProxy(const QString &id, bool value);
|
void SetUseProxy(const QString &id, bool value);
|
||||||
|
|
||||||
|
@ -53,7 +53,6 @@ const QString LASTORE_INTERFACE = "org.deepin.lastore1.Manager";
|
|||||||
|
|
||||||
Launcher::Launcher(QObject *parent)
|
Launcher::Launcher(QObject *parent)
|
||||||
: SynModule(parent)
|
: SynModule(parent)
|
||||||
, m_removeState(false)
|
|
||||||
, m_appInfo(DesktopInfo(""))
|
, m_appInfo(DesktopInfo(""))
|
||||||
{
|
{
|
||||||
registeModule("launcher");
|
registeModule("launcher");
|
||||||
@ -130,7 +129,7 @@ void Launcher::setFullscreen(bool isFull)
|
|||||||
LauncherItemInfoList Launcher::getAllItemInfos()
|
LauncherItemInfoList Launcher::getAllItemInfos()
|
||||||
{
|
{
|
||||||
LauncherItemInfoList allItemList;
|
LauncherItemInfoList allItemList;
|
||||||
for (auto item : m_desktopAndItemMap)
|
for (auto &item : m_desktopAndItemMap)
|
||||||
allItemList.push_back(item.info);
|
allItemList.push_back(item.info);
|
||||||
|
|
||||||
return allItemList;
|
return allItemList;
|
||||||
@ -279,10 +278,18 @@ bool Launcher::requestSendToDesktop(QString appId)
|
|||||||
* @brief Launcher::requestUninstall 卸载应用
|
* @brief Launcher::requestUninstall 卸载应用
|
||||||
* @param appId
|
* @param appId
|
||||||
*/
|
*/
|
||||||
void Launcher::requestUninstall(QString appId)
|
void Launcher::requestUninstall(const QString &desktop)
|
||||||
{
|
{
|
||||||
if (itemsMap.find(appId) == itemsMap.end()) {
|
bool exist = false;
|
||||||
qDebug() << QString(" %1 uninstall faill ...").arg(appId);
|
for (const Item &item : m_desktopAndItemMap.values()) {
|
||||||
|
if (item.info.path == desktop) {
|
||||||
|
exist = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!exist) {
|
||||||
|
qWarning() << QString(" %1 uninstall faill ...").arg(desktop);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,14 +303,19 @@ void Launcher::requestUninstall(QString appId)
|
|||||||
QString result = QString::fromUtf8(process.readAllStandardOutput());
|
QString result = QString::fromUtf8(process.readAllStandardOutput());
|
||||||
process.close();
|
process.close();
|
||||||
if (result != launcherExe) {
|
if (result != launcherExe) {
|
||||||
qDebug() << result << " has no right to uninstall " << appId;
|
qWarning() << result << " has no right to uninstall " << desktop;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Item &item = itemsMap[appId];
|
if (!m_desktopAndItemMap.keys().contains(desktop)) {
|
||||||
|
qWarning() << QString("can't find desktopPath: %1").arg(desktop);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Item &item = m_desktopAndItemMap[desktop];
|
||||||
DesktopInfo info(item.info.path.toStdString());
|
DesktopInfo info(item.info.path.toStdString());
|
||||||
if (!info.isValidDesktop()) {
|
if (!info.isValidDesktop()) {
|
||||||
qDebug() << QString("%1 desktop file is invalid...").arg(item.info.name);
|
qWarning() << QString("%1 desktop file is invalid...").arg(item.info.name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -436,29 +448,13 @@ void Launcher::onHandleUninstall(const QDBusMessage &message)
|
|||||||
#ifdef QT_DEBUG
|
#ifdef QT_DEBUG
|
||||||
qInfo() << "changedProps: " << changedProps << ", status: " << status;
|
qInfo() << "changedProps: " << changedProps << ", status: " << status;
|
||||||
#endif
|
#endif
|
||||||
const QString &appId = QString::fromStdString(m_appInfo.getId());
|
const QString &desktop = QString::fromStdString(m_appInfo.getFileName());
|
||||||
if (status == "succeed" || status == "end") {
|
if (status == "succeed" || status == "end") {
|
||||||
// 移除desktop文件
|
removeDesktop(desktop);
|
||||||
Item appItem = Item();
|
removeAutoStart(desktop);
|
||||||
for (const Item &item : m_desktopAndItemMap) {
|
Q_EMIT uninstallSuccess(desktop);
|
||||||
if (item.info.path == QString::fromStdString(m_appInfo.getFileName())) {
|
|
||||||
appItem = item;
|
|
||||||
qDebug() << QString("app-%1 removed successfully").arg(item.info.name);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
removeDesktop(appItem);
|
|
||||||
|
|
||||||
// 从自动启动目录中移除
|
|
||||||
removeAutoStart();
|
|
||||||
m_removeState = true;
|
|
||||||
|
|
||||||
Q_EMIT uninstallSuccess(appId);
|
|
||||||
} else if (status == "failed") {
|
} else if (status == "failed") {
|
||||||
m_removeState = false;
|
Q_EMIT uninstallFailed(desktop, QString());
|
||||||
QString msg = QString("uninstall %1 result %2").arg(m_appInfo.getName().c_str()).arg(false);
|
|
||||||
Q_EMIT uninstallFailed(appId, msg);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -693,6 +689,8 @@ void Launcher::loadNameMap()
|
|||||||
*/
|
*/
|
||||||
void Launcher::initItems()
|
void Launcher::initItems()
|
||||||
{
|
{
|
||||||
|
itemsMap.clear();
|
||||||
|
m_desktopAndItemMap.clear();
|
||||||
std::vector<DesktopInfo> infos = AppsDir::getAllDesktopInfos();
|
std::vector<DesktopInfo> infos = AppsDir::getAllDesktopInfos();
|
||||||
for (auto &app : infos) {
|
for (auto &app : infos) {
|
||||||
if (!app.isExecutableOk()
|
if (!app.isExecutableOk()
|
||||||
@ -965,6 +963,11 @@ end:
|
|||||||
return ty;
|
return ty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Launcher::isLingLongApp(const QString &filePath)
|
||||||
|
{
|
||||||
|
return filePath.contains("/persistent/linglong");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Launcher::doUninstall 执行卸载操作
|
* @brief Launcher::doUninstall 执行卸载操作
|
||||||
* @param info
|
* @param info
|
||||||
@ -973,46 +976,48 @@ end:
|
|||||||
*/
|
*/
|
||||||
void Launcher::doUninstall(DesktopInfo &info, const Item &item)
|
void Launcher::doUninstall(DesktopInfo &info, const Item &item)
|
||||||
{
|
{
|
||||||
// 查询包名
|
if (isLingLongApp(QString::fromStdString(info.getFileName()))) {
|
||||||
QString pkg = queryPkgName(item.info.id, item.info.path);
|
QProcess process;
|
||||||
if (pkg.isEmpty())
|
// 获取 appId
|
||||||
pkg = queryPkgNameWithDpkg(item.info.path);
|
QStringList args;
|
||||||
|
QString appId = item.exec.section(' ', 2, 2);
|
||||||
|
args.append("uninstall");
|
||||||
|
args.append(appId);
|
||||||
|
|
||||||
if (pkg.isEmpty()) {
|
int retCode = QProcess::execute("ll-cli", args);
|
||||||
qDebug() << "uninstall failed, becase package name is Empty";
|
if (retCode != 0) {
|
||||||
return;
|
Q_EMIT uninstallFailed(item.info.path, QString());
|
||||||
|
notifyUninstallDone(item, false);
|
||||||
|
qWarning() << QString("uninstall %1 failed...").arg(info.getFileName().c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_EMIT uninstallSuccess(item.info.path);
|
||||||
|
|
||||||
|
// 浮窗通知
|
||||||
|
notifyUninstallDone(item, true);
|
||||||
|
} else {
|
||||||
|
// 查询包名
|
||||||
|
QString pkg = queryPkgName(item.info.id, item.info.path);
|
||||||
|
if (pkg.isEmpty())
|
||||||
|
pkg = queryPkgNameWithDpkg(item.info.path);
|
||||||
|
|
||||||
|
if (pkg.isEmpty()) {
|
||||||
|
qWarning() << "uninstall failed, becase package name is Empty";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测包是否安装
|
||||||
|
QDBusInterface lastoreDbus = QDBusInterface(LASTORE_SERVICE, LASTORE_PATH, LASTORE_INTERFACE, QDBusConnection::systemBus());
|
||||||
|
QDBusReply<bool> reply = lastoreDbus.call("PackageExists", pkg);
|
||||||
|
|
||||||
|
// 包未安装时
|
||||||
|
if (!(reply.isValid() && reply.value()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// 卸载系统应用
|
||||||
|
uninstallApp(item.info.name, pkg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检测包是否安装
|
|
||||||
QDBusInterface lastoreDbus = QDBusInterface(LASTORE_SERVICE, LASTORE_PATH, LASTORE_INTERFACE, QDBusConnection::systemBus());
|
|
||||||
QDBusReply<bool> reply = lastoreDbus.call("PackageExists", pkg);
|
|
||||||
|
|
||||||
// 包未安装时
|
|
||||||
if (!(reply.isValid() && reply.value()))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// 卸载系统应用
|
|
||||||
m_desktopAndItemMap.remove(item.info.path);
|
|
||||||
uninstallApp(item.info.name, pkg);
|
|
||||||
|
|
||||||
// TODO: 不同类型的应用分别进行处理
|
|
||||||
// switch (getAppType(info, item)) {
|
|
||||||
// case (AppType::Flatpak):
|
|
||||||
// uninstallFlatpak(info, item); // 待测
|
|
||||||
// break;
|
|
||||||
// case (AppType::ChromeShortcut):
|
|
||||||
// removeDesktop(item);
|
|
||||||
// break;
|
|
||||||
// case (AppType::CrossOver):
|
|
||||||
// uninstallApp(item.info.name, "crossvoer"); // 待测
|
|
||||||
// break;
|
|
||||||
// case (AppType::WineApp):
|
|
||||||
// uninstallWineApp(item); // 待测
|
|
||||||
// break;
|
|
||||||
// case (AppType::Default):
|
|
||||||
// removeDesktop(item);
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1094,7 +1099,7 @@ void Launcher::uninstallApp(const QString &name, const QString &pkg)
|
|||||||
QDBusReply<QDBusObjectPath> reply = lastoreDbus.call(QDBus::Block, "RemovePackage", name, pkg);
|
QDBusReply<QDBusObjectPath> reply = lastoreDbus.call(QDBus::Block, "RemovePackage", name, pkg);
|
||||||
|
|
||||||
if (!reply.isValid() || reply.value().path().isEmpty()) {
|
if (!reply.isValid() || reply.value().path().isEmpty()) {
|
||||||
qDebug() << "RemovePackage failed: " << reply.error();
|
qWarning() << "RemovePackage failed: " << reply.error();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1106,27 +1111,20 @@ void Launcher::uninstallApp(const QString &name, const QString &pkg)
|
|||||||
"PropertiesChanged","sa{sv}as", this, SLOT(onHandleUninstall(const QDBusMessage &)));
|
"PropertiesChanged","sa{sv}as", this, SLOT(onHandleUninstall(const QDBusMessage &)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 移除desktop文件
|
||||||
* @brief Launcher::removeDesktop 移除desktop文件
|
* @brief Launcher::removeDesktop
|
||||||
* @param item
|
* @param desktop
|
||||||
* @return
|
|
||||||
*/
|
*/
|
||||||
bool Launcher::removeDesktop(const Item &item)
|
void Launcher::removeDesktop(const QString &desktop)
|
||||||
{
|
{
|
||||||
// 移除desktop文件
|
QFile file(desktop);
|
||||||
QFile file(item.info.path);
|
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
qDebug() << "file not exist...item info: " << item.info;
|
qDebug() << "file not exist...item info: " << desktop;
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ret = file.remove();
|
m_desktopAndItemMap.remove(desktop);
|
||||||
std::thread thread([ this, item, ret ] {
|
file.remove();
|
||||||
notifyUninstallDone(item, ret);
|
|
||||||
});
|
|
||||||
thread.detach();
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1138,21 +1136,33 @@ void Launcher::notifyUninstallDone(const Item &item, bool result)
|
|||||||
{
|
{
|
||||||
QString msg;
|
QString msg;
|
||||||
if (result)
|
if (result)
|
||||||
msg = QString("%1 removed successfully").arg(item.info.name);
|
msg = QString(tr("Removed successfully"));
|
||||||
else
|
else
|
||||||
msg = QString("Failed to uninstall %1").arg(item.info.name);
|
msg = QString(tr("Failed to remove the app"));
|
||||||
|
|
||||||
|
QList<QVariant> argList;
|
||||||
|
argList << QString("deepin-app-store")
|
||||||
|
<< quint32(0)
|
||||||
|
<< QString("deepin-app-store")
|
||||||
|
<< msg
|
||||||
|
<< QString("")
|
||||||
|
<< QStringList("")
|
||||||
|
<< QVariantMap()
|
||||||
|
<< qint32(-1);
|
||||||
|
|
||||||
QDBusInterface interface = QDBusInterface("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications");
|
QDBusInterface interface = QDBusInterface("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications");
|
||||||
interface.call("Notify", "deepin-app-store", 0, "deepin-appstore", msg, "", QVariant(), QVariant(), -1);
|
interface.callWithArgumentList(QDBus::Block, "Notify", argList);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Launcher::removeAutoStart()
|
void Launcher::removeAutoStart(const QString &desktop)
|
||||||
{
|
{
|
||||||
QString filePath(QDir::homePath() + "/.config/autostart/" + m_appInfo.getName().c_str() + ".desktop");
|
QFile file(desktop);
|
||||||
QFile file(filePath);
|
if (!file.exists()) {
|
||||||
|
qDebug() << QString("desktop file %1 doesn't exist...").arg(desktop);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (file.exists())
|
file.remove();
|
||||||
file.remove();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -86,6 +86,8 @@ public:
|
|||||||
// 设置配置
|
// 设置配置
|
||||||
void setSynConfig(QByteArray ba);
|
void setSynConfig(QByteArray ba);
|
||||||
QByteArray getSyncConfig();
|
QByteArray getSyncConfig();
|
||||||
|
|
||||||
|
void initItems();
|
||||||
const QMap<QString, Item> *getItems();
|
const QMap<QString, Item> *getItems();
|
||||||
|
|
||||||
int getDisplayMode();
|
int getDisplayMode();
|
||||||
@ -101,15 +103,15 @@ public:
|
|||||||
bool isItemOnDesktop(QString appId);
|
bool isItemOnDesktop(QString appId);
|
||||||
bool requestRemoveFromDesktop(QString appId);
|
bool requestRemoveFromDesktop(QString appId);
|
||||||
bool requestSendToDesktop(QString appId);
|
bool requestSendToDesktop(QString appId);
|
||||||
void requestUninstall(QString appId);
|
void requestUninstall(const QString &desktop);
|
||||||
void setDisableScaling(QString appId, bool value);
|
void setDisableScaling(QString appId, bool value);
|
||||||
void setUseProxy(QString appId, bool value);
|
void setUseProxy(QString appId, bool value);
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void itemChanged(QString status, LauncherItemInfo itemInfo, qint64 ty);
|
void itemChanged(QString status, LauncherItemInfo itemInfo, qint64 ty);
|
||||||
void newAppLaunched(QString appId);
|
void newAppLaunched(QString appId);
|
||||||
void uninstallSuccess(QString appId);
|
void uninstallSuccess(const QString &desktop);
|
||||||
void uninstallFailed(QString appId, QString errMsg);
|
void uninstallFailed(const QString &desktop, QString errMsg);
|
||||||
|
|
||||||
void displayModeChanged(int mode);
|
void displayModeChanged(int mode);
|
||||||
void fullScreenChanged(bool isFull);
|
void fullScreenChanged(bool isFull);
|
||||||
@ -129,7 +131,6 @@ private:
|
|||||||
void loadPkgCategoryMap();
|
void loadPkgCategoryMap();
|
||||||
void handleAppHiddenChanged();
|
void handleAppHiddenChanged();
|
||||||
void loadNameMap();
|
void loadNameMap();
|
||||||
void initItems();
|
|
||||||
QString getAppIdByFilePath(QString filePath, QStringList dirs);
|
QString getAppIdByFilePath(QString filePath, QStringList dirs);
|
||||||
bool isDeepinCustomDesktopFile(QString fileName);
|
bool isDeepinCustomDesktopFile(QString fileName);
|
||||||
Item NewItemWithDesktopInfo(DesktopInfo &info);
|
Item NewItemWithDesktopInfo(DesktopInfo &info);
|
||||||
@ -141,13 +142,14 @@ private:
|
|||||||
Item getItemByPath(QString itemPath);
|
Item getItemByPath(QString itemPath);
|
||||||
void emitItemChanged(const Item *item, QString status);
|
void emitItemChanged(const Item *item, QString status);
|
||||||
AppType getAppType(DesktopInfo &info, const Item &item);
|
AppType getAppType(DesktopInfo &info, const Item &item);
|
||||||
|
bool isLingLongApp(const QString &filePath);
|
||||||
void doUninstall(DesktopInfo &info, const Item &item);
|
void doUninstall(DesktopInfo &info, const Item &item);
|
||||||
void uninstallFlatpak(DesktopInfo &info, const Item &item);
|
void uninstallFlatpak(DesktopInfo &info, const Item &item);
|
||||||
bool uninstallWineApp(const Item &item);
|
bool uninstallWineApp(const Item &item);
|
||||||
void uninstallApp(const QString &name, const QString &pkg);
|
void uninstallApp(const QString &name, const QString &pkg);
|
||||||
bool removeDesktop(const Item &item);
|
void removeDesktop(const QString &desktop);
|
||||||
void notifyUninstallDone(const Item &item, bool result);
|
void notifyUninstallDone(const Item &item, bool result);
|
||||||
void removeAutoStart();
|
void removeAutoStart(const QString &desktop);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QMap<QString, Item> itemsMap; // appId, Item
|
QMap<QString, Item> itemsMap; // appId, Item
|
||||||
@ -160,7 +162,6 @@ private:
|
|||||||
QStringList appDirs;
|
QStringList appDirs;
|
||||||
|
|
||||||
QMap<QString, Item> m_desktopAndItemMap; // desktoppath,Item
|
QMap<QString, Item> m_desktopAndItemMap; // desktoppath,Item
|
||||||
bool m_removeState; // 卸载状态
|
|
||||||
DesktopInfo m_appInfo; // 卸载应用
|
DesktopInfo m_appInfo; // 卸载应用
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user