From 80a07bcf489f4659fc827be7d644b34c9ea428e5 Mon Sep 17 00:00:00 2001 From: xiongbo Date: Fri, 20 Mar 2026 16:51:29 +0800 Subject: [PATCH 1/2] feat: [Update Client] Merge backend logic for control center update with private update module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Integrate the private update logic from v20 into the v25 control center update module. 1. Functionality is enabled based on the `intranet-update` configuration item under `lastore-daemon`. When disabled, the public update interface of the control center is displayed; when enabled, the interface switches to the private update interface. 2. The private update interface is largely similar to the public interface, with the following key differences: a. The update settings entry is hidden — enterprise-level update users are not allowed to modify update-related configurations. b. The process from checking for updates to downloading is atomic. Enterprise update users cannot refuse downloads, and downloads cannot be canceled during the process. c. After download completion, a re-check button is added. d. For non-mandatory updates, users can choose the update method after download completion: update immediately or update after shutdown. e. A dedicated private update tray is added to provide more detailed download/update status information. The tray is loaded only when the private update switch is enabled. Log: Integrate v20 private update logic into v25 Story: https://pms.uniontech.com/story-view-40191.html Influence: Added private update logic to the control center update module feat: 【更新客户端】更新客户端后端合并-控制中心更新客户端合入私有化更新模块逻辑 v20的私有化更新逻辑合入v25控制中心更新模块 1.通过lastore-daemon下面的intranet-update配置项决定功能私有化功能是否开启,未开启时仍为控制中心公网更新模块界面,开启后切换为私有化更新界面 2.私有化更新界面与公网界面大致相同,主要有以下区别点: a.屏蔽更新设置入口,企业级更新的用户不允许私自修改更新相关配置 b.私有化更新的检查更新到下载流程为原子化操作,企业及更新用户无拒绝下载的权利,下载过程中不允许取消 c.下载完成后添加重复检查按钮 d.若为非强制更新类型的任务,下载完成后允许用户自行决定更新方式,立即更新or关机后更新 e.添加私有化专属托盘,提供更详细的下载更新状态,仅在私有化开关开启时加载托盘 Log: v20私有化更新合入v25 Story: https://pms.uniontech.com/story-view-40191.html Influence: 控制中心更新模块新增私有化更新逻辑 --- CMakeLists.txt | 3 +- src/common/dbus/updatedbusproxy.cpp | 10 +- src/common/dbus/updatedbusproxy.h | 4 +- .../operation/updatemodel.cpp | 14 +- src/dcc-update-plugin/operation/updatemodel.h | 8 +- .../operation/updatework.cpp | 14 + src/dcc-update-plugin/operation/updatework.h | 2 + src/dcc-update-plugin/qml/UpdateControl.qml | 115 ++++++- src/dcc-update-plugin/qml/UpdateMain.qml | 25 +- .../translations/update_zh_CN.ts | 16 + .../translations/update_zh_HK.ts | 16 + .../translations/update_zh_TW.ts | 16 + src/private-lastore-tray/CMakeLists.txt | 57 +++ src/private-lastore-tray/commoniconbutton.cpp | 220 ++++++++++++ src/private-lastore-tray/commoniconbutton.h | 78 +++++ src/private-lastore-tray/constants.h | 255 ++++++++++++++ .../pluginproxyinterface.h | 81 +++++ .../plugins-logging-category.h | 30 ++ .../pluginsiteminterface.h | 247 +++++++++++++ .../privatelastoreitem.cpp | 97 ++++++ src/private-lastore-tray/privatelastoreitem.h | 44 +++ .../privatelastoremode.json | 3 + .../privatelastoreplugin.cpp | 69 ++++ .../privatelastoreplugin.h | 41 +++ .../resources/private-lastore-active_16px.svg | 7 + .../resources/private-lastore-sleep_16px.svg | 7 + .../resources/private-lastore-tray.qrc | 6 + src/private-lastore-tray/tipswidget.cpp | 324 ++++++++++++++++++ src/private-lastore-tray/tipswidget.h | 81 +++++ .../translations/private-lastore-tray.ts | 83 +++++ .../private-lastore-tray_zh_CN.ts | 83 +++++ 31 files changed, 2037 insertions(+), 19 deletions(-) create mode 100644 src/private-lastore-tray/CMakeLists.txt create mode 100644 src/private-lastore-tray/commoniconbutton.cpp create mode 100644 src/private-lastore-tray/commoniconbutton.h create mode 100644 src/private-lastore-tray/constants.h create mode 100644 src/private-lastore-tray/pluginproxyinterface.h create mode 100644 src/private-lastore-tray/plugins-logging-category.h create mode 100644 src/private-lastore-tray/pluginsiteminterface.h create mode 100644 src/private-lastore-tray/privatelastoreitem.cpp create mode 100644 src/private-lastore-tray/privatelastoreitem.h create mode 100644 src/private-lastore-tray/privatelastoremode.json create mode 100644 src/private-lastore-tray/privatelastoreplugin.cpp create mode 100644 src/private-lastore-tray/privatelastoreplugin.h create mode 100644 src/private-lastore-tray/resources/private-lastore-active_16px.svg create mode 100644 src/private-lastore-tray/resources/private-lastore-sleep_16px.svg create mode 100644 src/private-lastore-tray/resources/private-lastore-tray.qrc create mode 100644 src/private-lastore-tray/tipswidget.cpp create mode 100644 src/private-lastore-tray/tipswidget.h create mode 100644 src/private-lastore-tray/translations/private-lastore-tray.ts create mode 100644 src/private-lastore-tray/translations/private-lastore-tray_zh_CN.ts diff --git a/CMakeLists.txt b/CMakeLists.txt index 35f2fe3db..0d3e74800 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd. +# SPDX-FileCopyrightText: 2025-2026 UnionTech Software Technology Co., Ltd. # # SPDX-License-Identifier: CC0-1.0 @@ -40,4 +40,5 @@ add_subdirectory("src/dde-update") add_subdirectory("src/dde-abrecovery") add_subdirectory("src/dcc-update-plugin") add_subdirectory("src/dock-update-plugin") +add_subdirectory("src/private-lastore-tray") diff --git a/src/common/dbus/updatedbusproxy.cpp b/src/common/dbus/updatedbusproxy.cpp index e582a2271..c6cd74f6a 100644 --- a/src/common/dbus/updatedbusproxy.cpp +++ b/src/common/dbus/updatedbusproxy.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2018 - 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2018 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "updatedbusproxy.h" @@ -428,6 +428,14 @@ QDBusPendingReply UpdateDBusProxy::GetUpdateDetails(int fd, bool realtime) return m_managerInter->asyncCallWithArgumentList(QStringLiteral("GetUpdateDetails"), argumentList); } +QDBusPendingReply UpdateDBusProxy::SetShutdownForceUpdate(bool isShutdownUpdate) +{ + qCDebug(logCommon) << "Setting shutdown force update , isShutdownUpdate:" << isShutdownUpdate; + QList argumentList; + argumentList << QVariant::fromValue(isShutdownUpdate); + return m_managerInter->asyncCallWithArgumentList(QStringLiteral("SetShutdownForceUpdate"), argumentList); +} + bool UpdateDBusProxy::onBattery() { return qvariant_cast(m_powerInter->property("OnBattery")); diff --git a/src/common/dbus/updatedbusproxy.h b/src/common/dbus/updatedbusproxy.h index cce6ae314..8c15215da 100644 --- a/src/common/dbus/updatedbusproxy.h +++ b/src/common/dbus/updatedbusproxy.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2018 - 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2018 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef UPDATEDBUSPROXY_H @@ -108,7 +108,7 @@ class UpdateDBusProxy : public QObject QDBusPendingReply fixError(const QString &errorType); QDBusPendingCall CheckUpgrade(int checkMode, int checkOrder); QDBusPendingReply GetUpdateDetails(int fd, bool realtime); - + QDBusPendingReply SetShutdownForceUpdate(bool isShutdownUpdate); // Power bool onBattery(); diff --git a/src/dcc-update-plugin/operation/updatemodel.cpp b/src/dcc-update-plugin/operation/updatemodel.cpp index b7a7dcb5e..861e12dea 100644 --- a/src/dcc-update-plugin/operation/updatemodel.cpp +++ b/src/dcc-update-plugin/operation/updatemodel.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2011 - 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2011 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: LGPL-3.0-or-later @@ -72,6 +72,7 @@ UpdateModel::UpdateModel(QObject* parent) , m_backupFailedTips("") , m_installLog("") , m_isUpdatable(false) + , m_isPrivateUpdate(false) , m_securityUpdateEnabled(false) , m_thirdPartyUpdateEnabled(false) , m_updateMode(UpdateType::Invalid) @@ -969,6 +970,17 @@ void UpdateModel::setIsUpdatable(bool isUpdatable) Q_EMIT isUpdatableChanged(isUpdatable); } +void UpdateModel::setIsPrivateUpdate(bool isPrivateUpdate) +{ + qCDebug(logDccUpdatePlugin) << "Setting is private update: " << isPrivateUpdate; + if (m_isPrivateUpdate == isPrivateUpdate) { + return; + } + + m_isPrivateUpdate = isPrivateUpdate; + Q_EMIT isPrivateUpdateChanged(isPrivateUpdate); +} + UpdatesStatus UpdateModel::updateStatus(ControlPanelType type) const { qCDebug(logDccUpdatePlugin) << "Getting update status for control panel type:" << type; diff --git a/src/dcc-update-plugin/operation/updatemodel.h b/src/dcc-update-plugin/operation/updatemodel.h index 6e8bede2c..6a5d8f4a7 100644 --- a/src/dcc-update-plugin/operation/updatemodel.h +++ b/src/dcc-update-plugin/operation/updatemodel.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2011 - 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2011 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: LGPL-3.0-or-later @@ -29,6 +29,7 @@ class UpdateModel : public QObject Q_PROPERTY(bool batterIsOK READ batterIsOK NOTIFY batterIsOKChanged FINAL) Q_PROPERTY(int lastStatus READ lastStatus NOTIFY lastStatusChanged FINAL) Q_PROPERTY(bool isUpdatable READ isUpdatable NOTIFY isUpdatableChanged FINAL) + Q_PROPERTY(bool isPrivateUpdate READ isPrivateUpdate NOTIFY isPrivateUpdateChanged FINAL) // ---------------检查更新页面数据--------------- Q_PROPERTY(bool showCheckUpdate READ showCheckUpdate NOTIFY showCheckUpdateChanged FINAL) @@ -234,6 +235,9 @@ class UpdateModel : public QObject bool isUpdatable() const { return m_isUpdatable; } void setIsUpdatable(bool isUpdatable); + bool isPrivateUpdate() const { return m_isPrivateUpdate; } + void setIsPrivateUpdate(bool isPrivateUpdate); + UpdatesStatus updateStatus(ControlPanelType type) const; UpdatesStatus updateStatus(UpdateType type) const; QList updateTypesList(ControlPanelType type) const; @@ -369,6 +373,7 @@ public slots: void updateInfoChanged(UpdateType); void isUpdatableChanged(const bool isUpdatablePackages); + void isPrivateUpdateChanged(const bool isPrivateUpdate); void updateStatusChanged(ControlPanelType, UpdatesStatus); void controlTypeChanged(); void lastErrorChanged(UpdatesStatus, UpdateErrorType); @@ -443,6 +448,7 @@ public slots: QByteArray m_updateStatus; // lastore daemon发上来的原始json数据 bool m_isUpdatable; // 是否有包可更新 + bool m_isPrivateUpdate; //当前是否接入私有化更新 QMap>> m_controlStatusMap; QMap m_waitingStatusMap; diff --git a/src/dcc-update-plugin/operation/updatework.cpp b/src/dcc-update-plugin/operation/updatework.cpp index 2a8c04feb..9b350cf79 100644 --- a/src/dcc-update-plugin/operation/updatework.cpp +++ b/src/dcc-update-plugin/operation/updatework.cpp @@ -6,6 +6,7 @@ #include "common/common/logwatcherhelper.h" #include "utils.h" #include "dconfigwatcher.h" +#include "common/common/dconfig_helper.h" #include "updateloghelper.h" #include @@ -221,6 +222,7 @@ void UpdateWorker::activate() refreshLastTimeAndCheckCircle(); initTestingChannel(); + m_model->setIsPrivateUpdate(DConfigHelper::instance()->getConfig("org.deepin.dde.lastore", "org.deepin.dde.lastore", "","intranet-update", false).toString() == "true"); m_model->setUpdateMode(m_updateInter->updateMode()); m_model->setCheckUpdateMode(m_updateInter->checkUpdateMode()); m_model->setSecurityUpdateEnabled(DConfigWatcher::instance()->getValue(DConfigWatcher::update, "updateSafety").toString() != "Hidden"); @@ -494,6 +496,12 @@ void UpdateWorker::doCheckUpdates() }); } +void UpdateWorker::reCheckWithUi() +{ + m_model->setShowCheckUpdate(true); + doCheckUpdates(); +} + void UpdateWorker::setCheckUpdatesJob(const QString& jobPath) { qCInfo(logDccUpdatePlugin) << "Set check updates job"; @@ -787,6 +795,12 @@ void UpdateWorker::modalUpgrade(bool rebootAfterUpgrade) } } +void UpdateWorker::setShutdownAndUpgrade(bool isShutdownUpdate) +{ + qCInfo(logDccUpdatePlugin) << "request shutdown upgrade, upgrade after shutdown:" << isShutdownUpdate; + m_updateInter->SetShutdownForceUpdate(isShutdownUpdate); +} + void UpdateWorker::setBackupJob(const QString& jobPath) { qCInfo(logDccUpdatePlugin) << "Create backup upgrade job, path:" << jobPath; diff --git a/src/dcc-update-plugin/operation/updatework.h b/src/dcc-update-plugin/operation/updatework.h index 571de400e..376e4cc5a 100644 --- a/src/dcc-update-plugin/operation/updatework.h +++ b/src/dcc-update-plugin/operation/updatework.h @@ -37,6 +37,7 @@ class UpdateWorker : public QObject // 检查更新 Q_INVOKABLE void checkNeedDoUpdates(); Q_INVOKABLE void doCheckUpdates(); + Q_INVOKABLE void reCheckWithUi(); void setCheckUpdatesJob(const QString& jobPath); void createCheckUpdateJob(const QString& jobPath); void refreshLastTimeAndCheckCircle(); @@ -54,6 +55,7 @@ class UpdateWorker : public QObject Q_INVOKABLE void doUpgrade(int updateTypes, bool doBackup); Q_INVOKABLE void reStart(); Q_INVOKABLE void modalUpgrade(bool rebootAfterUpgrade = true); + Q_INVOKABLE void setShutdownAndUpgrade(bool isShutdownUpdate = false); void setBackupJob(const QString& jobPath); void setDistUpgradeJob(const QString& jobPath); void updateSystemVersion(); diff --git a/src/dcc-update-plugin/qml/UpdateControl.qml b/src/dcc-update-plugin/qml/UpdateControl.qml index da873075c..c2c4ee6d1 100644 --- a/src/dcc-update-plugin/qml/UpdateControl.qml +++ b/src/dcc-update-plugin/qml/UpdateControl.qml @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024-2026 UnionTech Software Technology Co., Ltd. // SPDX-License-Identifier: GPL-3.0-or-later import QtQuick import QtQuick.Controls @@ -118,14 +118,115 @@ ColumnLayout { Repeater { model: btnActions - delegate: D.Button { + delegate: Loader { + id: buttonLoader Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - text: modelData - font: D.DTK.fontManager.t6 visible: modelData.length !== 0 && !initAnimation.visible enabled: updatelistModel.model.isUpdateEnable - onClicked: { - rootLayout.btnClicked(index, updateListModels.getAllUpdateType()) + + Connections { + target: dccData.model() + function onIsPrivateUpdateChanged() { + updateButtonType() + } + function onIsUpdateDisabledChanged() { + updateButtonType() + } + function onShowCheckUpdateChanged() { + updateButtonType() + } + } + + Component.onCompleted: { + updateButtonType() + } + + function updateButtonType() { + var model = dccData.model() + if (!model) { + console.log("Model not ready yet") + return + } + if (index === 1) { + buttonLoader.sourceComponent = normalButtonComponent + } else if (model.isPrivateUpdate && + !model.isUpdateDisabled && + !model.showCheckUpdate && + model.preInstallListModel.anyVisible) { + buttonLoader.sourceComponent = dropdownButtonComponent + } else { + // 默认使用普通按钮 + buttonLoader.sourceComponent = normalButtonComponent + } + } + + Component { + id: normalButtonComponent + D.Button { + text: modelData + font: D.DTK.fontManager.t6 + onClicked: { + rootLayout.btnClicked(index, updateListModels.getAllUpdateType()) + } + } + } + + Component { + id: dropdownButtonComponent + Item { + implicitWidth: button.implicitWidth + implicitHeight: button.implicitHeight + + enum InstallType { + Now, + Shutdown + } + + property int curType: DropdownButton.InstallType.Now + + signal requestUpdateNow() + signal requestUpdateShutdown() + + D.Button { + id: button + text: qsTr("Install") + + Menu { + id: contextMenu + + MenuItem { + text: qsTr("Install Now") + onClicked: { + dccData.work().setShutdownAndUpgrade(false) + dccData.work().doUpgrade(updateListModels.getAllUpdateType(), true) + } + } + + MenuItem { + text: qsTr("Install Shutdown") + onClicked: { + dccData.work().setShutdownAndUpgrade(true) + } + } + } + + // 点击按钮显示菜单 + onClicked: { + contextMenu.open() + } + } + + // 连接内部信号到外部 + Connections { + target: parent + function onRequestUpdateNow() { + rootLayout.btnClicked(index, 0) + } + function onRequestUpdateShutdown() { + rootLayout.btnClicked(index, 1) + } + } + } } } } @@ -180,7 +281,7 @@ ColumnLayout { icon.height: 24 implicitWidth: 24 implicitHeight: 24 - visible: isDownloading + visible: isDownloading && !dccData.model().isPrivateUpdate onClicked: { rootLayout.closeDownload() diff --git a/src/dcc-update-plugin/qml/UpdateMain.qml b/src/dcc-update-plugin/qml/UpdateMain.qml index ef79567a7..b1b975f1a 100644 --- a/src/dcc-update-plugin/qml/UpdateMain.qml +++ b/src/dcc-update-plugin/qml/UpdateMain.qml @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 - 2027 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024-2026 UnionTech Software Technology Co., Ltd. // SPDX-License-Identifier: GPL-3.0-or-later import QtQuick 2.15 @@ -291,7 +291,10 @@ DccObject { page: UpdateControl { updateListModels: dccData.model().preInstallListModel updateTitle: qsTr("Update download completed") - btnActions: [ qsTr("Install updates") ] + btnActions: dccData.model().isPrivateUpdate ? [ + qsTr("Install updates"), + qsTr("Check Again") + ] : [qsTr("Install updates")] updateTips: { if (!dccData.model().batterIsOK) { return qsTr("The battery capacity is lower than 60%. To get successful updates, please plug in.") @@ -302,8 +305,12 @@ DccObject { updateListEnable: !dccData.model().upgradeWaiting onBtnClicked: function(index, updateType) { - updateSelectDialog.updateType = updateType - updateSelectDialog.show() + if (index === 0) { + updateSelectDialog.updateType = updateType + updateSelectDialog.show() + } else if (index === 1) { + dccData.work().reCheckWithUi(); + } } UpdateSelectDialog { @@ -367,7 +374,7 @@ DccObject { page: UpdateControl { updateListModels: dccData.model().preUpdatelistModel - updateTitle: !dccData.model().downloadWaiting ? qsTr("Updates Available") : qsTr("Downloading updates...") + updateTitle: !dccData.model().downloadWaiting || dccData.model().isPrivateUpdate ? qsTr("Updates Available") : qsTr("Downloading updates...") btnActions: [ qsTr("Download") ] updateTips: qsTr("Update size: ") + dccData.model().preUpdatelistModel.downloadSize busyState: dccData.model().downloadWaiting @@ -376,6 +383,12 @@ DccObject { onBtnClicked: function(index, updateType) { dccData.work().startDownload(updateType) } + + Component.onCompleted: { + if (dccData.model().isPrivateUpdate) { + dccData.work().startDownload(1) + } + } } } @@ -387,7 +400,7 @@ DccObject { description: qsTr("Configure Update settings、Security Updates、Auto Download Updates and Updates Notification") icon: "update_set" weight: 120 - visible: dccData.model().systemActivation + visible: dccData.model().systemActivation && !dccData.model().isPrivateUpdate UpdateSetting {} } diff --git a/src/dcc-update-plugin/translations/update_zh_CN.ts b/src/dcc-update-plugin/translations/update_zh_CN.ts index 451293140..7caeb2780 100644 --- a/src/dcc-update-plugin/translations/update_zh_CN.ts +++ b/src/dcc-update-plugin/translations/update_zh_CN.ts @@ -47,6 +47,18 @@ View Update Log 查看更新日志 + + Install + 更新 + + + Install Now + 立即更新 + + + Install Shutdown + 关机后更新 + UpdateHistoryDialog @@ -249,6 +261,10 @@ Install updates 安装更新 + + Check Again + 重新检查更新 + Update download failed 更新下载失败 diff --git a/src/dcc-update-plugin/translations/update_zh_HK.ts b/src/dcc-update-plugin/translations/update_zh_HK.ts index 2cadcf187..7d9e3fba1 100644 --- a/src/dcc-update-plugin/translations/update_zh_HK.ts +++ b/src/dcc-update-plugin/translations/update_zh_HK.ts @@ -47,6 +47,18 @@ View Update Log 查看更新日誌 + + Install + 更新 + + + Install Now + 立即更新 + + + Install Shutdown + 關機後更新 + UpdateHistoryDialog @@ -249,6 +261,10 @@ Install updates 安裝更新 + + Check Again + 重新檢查更新 + Update download failed 更新下載失敗 diff --git a/src/dcc-update-plugin/translations/update_zh_TW.ts b/src/dcc-update-plugin/translations/update_zh_TW.ts index d23421439..140666917 100644 --- a/src/dcc-update-plugin/translations/update_zh_TW.ts +++ b/src/dcc-update-plugin/translations/update_zh_TW.ts @@ -47,6 +47,18 @@ View Update Log 檢視更新日誌 + + Install + 更新 + + + Install Now + 立即更新 + + + Install Shutdown + 關機後更新 + UpdateHistoryDialog @@ -249,6 +261,10 @@ Install updates 安裝更新 + + Check Again + 重新檢查更新 + Update download failed 更新下載失敗 diff --git a/src/private-lastore-tray/CMakeLists.txt b/src/private-lastore-tray/CMakeLists.txt new file mode 100644 index 000000000..2441277fa --- /dev/null +++ b/src/private-lastore-tray/CMakeLists.txt @@ -0,0 +1,57 @@ +# SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +# +# SPDX-License-Identifier: CC0-1.0 + +set(PLUGIN_NAME "private-lastore-tray") + +project(${PLUGIN_NAME}) +include(GNUInstallDirs) +# 启用 qt moc 的支持 +set(CMAKE_AUTOMOC ON) +# 启用 qrc 资源文件的支持 +set(CMAKE_AUTORCC ON) +# Sources files +file(GLOB_RECURSE SRCS + "*.h" + "*.cpp" + "../common/*.h" + "../common/*.cpp" + #"../common/global_util/*.h" + #"../common/global_util/*.cpp" + "resources/private-lastore-tray.qrc" +) + +set(QT_VERSION_MAJOR 6) +set(DTK_VERSION_MAJOR 6) + +find_package(PkgConfig REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} ${REQUIRED_QT_VERSION} REQUIRED COMPONENTS Widgets Core LinguistTools) +find_package(Dtk${DTK_VERSION_MAJOR} REQUIRED COMPONENTS Widget Core Gui) +find_package(DdeDock REQUIRED) + +add_library(${PLUGIN_NAME} SHARED ${SRCS}) +set_target_properties(${PLUGIN_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ../) + +target_link_libraries(${PLUGIN_NAME} PRIVATE + Dtk${DTK_VERSION_MAJOR}::Widget + Dtk${DTK_VERSION_MAJOR}::Core + Dtk${DTK_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Widgets + Qt${QT_VERSION_MAJOR}::Core +) + +file(GLOB TS_FILES "translations/*.ts") + +qt_add_translations(${PLUGIN_NAME}_language + TS_FILES ${TS_FILES} + SOURCES ${SRCS} + LUPDATE_OPTIONS -no-obsolete -no-ui-lines -locations none + QM_FILES_OUTPUT_VARIABLE QM_FILES +) +add_custom_target(${PLUGIN_NAME}_language ALL DEPENDS ${QM_FILES}) + +install(FILES ${QM_FILES} DESTINATION ${CMAKE_INSTALL_DATADIR}/${PLUGIN_NAME}/translations) + +install(TARGETS ${PLUGIN_NAME} LIBRARY DESTINATION lib/dde-dock/plugins) +install(FILES "resources/private-lastore-sleep_16px.svg" DESTINATION share/dde-dock/icons/dcc-setting) +install(FILES "resources/private-lastore-active_16px.svg" DESTINATION share/dde-dock/icons/dcc-setting) diff --git a/src/private-lastore-tray/commoniconbutton.cpp b/src/private-lastore-tray/commoniconbutton.cpp new file mode 100644 index 000000000..b4044c458 --- /dev/null +++ b/src/private-lastore-tray/commoniconbutton.cpp @@ -0,0 +1,220 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#include "commoniconbutton.h" +#include "constants.h" + +#include +#include +#include + +#include + +DGUI_USE_NAMESPACE + +CommonIconButton::CommonIconButton(QWidget *parent) + : QWidget(parent) + , m_refreshTimer(nullptr) + , m_clickable(false) + , m_hover(false) + , m_state(Default) + , m_lightThemeColor(Qt::black) + , m_darkThemeColor(Qt::white) + , m_activeState(false) + , m_hoverEnable(true) + , m_iconSize(QSize()) + , m_rotation(0) +{ + setAccessibleName("IconButton"); + setFixedSize(Dock::DOCK_PLUGIN_ITEM_FIXED_SIZE); + if (parent) + setForegroundRole(parent->foregroundRole()); + + m_defaultPalette = palette(); + + connect(DGuiApplicationHelper::instance(), &DGuiApplicationHelper::themeTypeChanged, this, &CommonIconButton::refreshIcon); +} + +void CommonIconButton::setStateIconMapping(QMap> mapping) +{ + m_fileMapping = mapping; +} + +void CommonIconButton::setState(State state) +{ + m_state = state; + if (m_fileMapping.contains(state)) { + auto pair = m_fileMapping.value(state); + setIcon(pair.first, pair.second); + } + if (!m_icon.isNull()) { + updatePalette(); + } +} + +void CommonIconButton::startRotate() +{ + if (!m_refreshTimer) { + m_refreshTimer = new QTimer(this); + m_refreshTimer->setInterval(70); + connect(m_refreshTimer, &QTimer::timeout, this, &CommonIconButton::startRotate); + } + m_refreshTimer->start(); + m_rotation += 54; + update(); +} + +void CommonIconButton::stopRotate() +{ + m_refreshTimer->stop(); + m_rotation = 0; + update(); +} + +void CommonIconButton::setIcon(const QIcon &icon, QColor lightThemeColor, QColor darkThemeColor) +{ + m_icon = icon; + if (lightThemeColor.isValid() && darkThemeColor.isValid()) { + m_lightThemeColor = lightThemeColor; + m_darkThemeColor = darkThemeColor; + } + + updatePalette(); +} + +void CommonIconButton::updatePalette() +{ + if (isEnabled()) { + if (m_lightThemeColor.isValid() && m_darkThemeColor.isValid()) { + QColor color = DGuiApplicationHelper::instance()->themeType() == DGuiApplicationHelper::LightType ? m_lightThemeColor : m_darkThemeColor; + if (m_activeState) + color = palette().color(QPalette::Highlight); + auto pa = palette(); + pa.setColor(QPalette::WindowText, color); + setPalette(pa); + } + } else { + setPalette(m_defaultPalette); + } + + update(); +} + +void CommonIconButton::setActiveState(bool state) +{ + m_activeState = state; + if (m_lightThemeColor.isValid() && m_darkThemeColor.isValid()) { + updatePalette(); + } else { + setForegroundRole(state ? QPalette::Highlight : QPalette::NoRole); + } +} + +void CommonIconButton::setHoverEnable(bool enable) +{ + m_hoverEnable = enable; +} + +void CommonIconButton::setIcon(const QString &icon, const QString &fallback, const QString &suffix) +{ + if (!m_fileMapping.contains(Default)) { + m_fileMapping.insert(Default, QPair(icon, fallback)); + } + + QString tmp = icon; + QString tmpFallback = fallback; + + static auto addDarkMark = [suffix] (QString &file) { + if (file.contains(suffix)) { + file.replace(suffix, "-dark" + suffix); + } else { + file.append("-dark"); + } + }; + if (DGuiApplicationHelper::instance()->themeType() == DGuiApplicationHelper::LightType) { + addDarkMark(tmp); + addDarkMark(tmpFallback); + } + m_icon = QIcon::fromTheme(tmp, QIcon::fromTheme(tmpFallback)); + update(); +} + +void CommonIconButton::setHoverIcon(const QIcon &icon) +{ + m_hoverIcon = icon; +} + +void CommonIconButton::setClickable(bool clickable) +{ + m_clickable = clickable; +} + +bool CommonIconButton::event(QEvent *e) +{ + switch (e->type()) { + case QEvent::Leave: + case QEvent::Enter: + m_hover = e->type() == QEvent::Enter; + update(); + break; + default: + break; + } + return QWidget::event(e); +} + +void CommonIconButton::paintEvent(QPaintEvent *e) +{ + QWidget::paintEvent(e); + QPainter painter(this); + painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + + if (m_rotation != 0) { + painter.translate(this->width() / 2, this->height() / 2); + painter.rotate(m_rotation); + painter.translate(-(this->width() / 2), -(this->height() / 2)); + } + + if (m_hoverEnable && m_hover && !m_hoverIcon.isNull()) { + m_hoverIcon.paint(&painter, rect()); + } else if (!m_icon.isNull()) { + if (!m_iconSize.isEmpty()) { + const int left = (width() - m_iconSize.width()) / 2; + const int top = (height() - m_iconSize.height()) / 2; + m_icon.paint(&painter, rect().marginsRemoved(QMargins(left, top, left, top))); + } else { + m_icon.paint(&painter, rect()); + } + } +} + +void CommonIconButton::mousePressEvent(QMouseEvent *event) +{ + m_pressPos = event->pos(); + return QWidget::mousePressEvent(event); +} + +void CommonIconButton::mouseReleaseEvent(QMouseEvent *event) +{ + if (m_clickable && rect().contains(m_pressPos) && rect().contains(event->pos()) && (!m_refreshTimer || !m_refreshTimer->isActive())) { + Q_EMIT clicked(); + return; + } + return QWidget::mouseReleaseEvent(event); +} + +void CommonIconButton::refreshIcon() +{ + setState(m_state); +} + +void CommonIconButton::setIconSize(const QSize &size) +{ + m_iconSize = size; +} + +void CommonIconButton::setAllEnabled(bool enable) +{ + setEnabled(enable); + updatePalette(); +} diff --git a/src/private-lastore-tray/commoniconbutton.h b/src/private-lastore-tray/commoniconbutton.h new file mode 100644 index 000000000..f4aec45ab --- /dev/null +++ b/src/private-lastore-tray/commoniconbutton.h @@ -0,0 +1,78 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#ifndef ICONBUTTON_H +#define ICONBUTTON_H + +#include +#include +#include + +class CommonIconButton : public QWidget +{ +public: + enum State { + Default, + On, + Off + }; + + Q_OBJECT +public: + Q_PROPERTY(qreal rotation READ rotation WRITE setRotation) + + explicit CommonIconButton(QWidget *parent = nullptr); + + void setStateIconMapping(QMap> mapping); + void setState(State state); + void setActiveState(bool state); + bool activeState() const { return m_activeState; } + void setHoverEnable(bool enable); + void setIconSize(const QSize &size); + void setAllEnabled(bool enable); + + void startRotate(); + void stopRotate(); + + inline void setRotation(qreal rotation) { m_rotation = rotation; update(); } + inline qreal rotation() const { return m_rotation;} + +public Q_SLOTS: + void setIcon(const QString &icon, const QString &fallback = "", const QString &suffix = ".svg"); + void setIcon(const QIcon &icon, QColor lightThemeColor = QColor(QColor::Invalid), QColor darkThemeColor = QColor(QColor::Invalid)); + void setHoverIcon(const QIcon &icon); + + void setClickable(bool clickable); + +signals: + void clicked(); + +protected: + bool event(QEvent *e) override; + void paintEvent(QPaintEvent *e) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + +private: + void refreshIcon(); + void updatePalette(); + +private: + QTimer *m_refreshTimer; + QIcon m_icon; + QIcon m_hoverIcon; + QPoint m_pressPos; + bool m_clickable; + bool m_hover; + QMap> m_fileMapping; + State m_state; + QColor m_lightThemeColor; + QColor m_darkThemeColor; + bool m_activeState; + bool m_hoverEnable; + QSize m_iconSize; + qreal m_rotation; + QPalette m_defaultPalette; +}; + +#endif // DOCKICONBUTTON_H diff --git a/src/private-lastore-tray/constants.h b/src/private-lastore-tray/constants.h new file mode 100644 index 000000000..dfe8e503f --- /dev/null +++ b/src/private-lastore-tray/constants.h @@ -0,0 +1,255 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef CONSTANTS_H +#define CONSTANTS_H + +#include + +#define DOCK_API_VERSION_MAJOR 2 +#define DOCK_API_VERSION_MINOR 0 +#define DOCK_API_VERSION_PATCH 0 + +// 以下为插件适配会用到的常量 +namespace Dock { + +#define DOCK_PLUGIN_MIME "dock/plugin" +#define DOCK_PLUGIN_API_VERSION "2.0.0" // Deprecated, use DOCK_API_VERSION_CHECK + +#define PROP_DISPLAY_MODE "DisplayMode" + +#define PLUGIN_BACKGROUND_MAX_SIZE 40 // Deprecated, use DOCK_PLUGIN_ITEM_FIXED_SIZE +#define PLUGIN_BACKGROUND_MIN_SIZE 16 // Deprecated, use DOCK_PLUGIN_ITEM_FIXED_SIZE + +#define PLUGIN_ITEM_WIDTH 300 +#define PLUGIN_ICON_MAX_SIZE 16 // Deprecated, use DOCK_PLUGIN_ITEM_FIXED_SIZE +#define PLUGIN_ICON_MIN_SIZE 16 // Deprecated, use DOCK_PLUGIN_ITEM_FIXED_SIZE + +// 插件最小尺寸,图标采用深色 +#define PLUGIN_MIN_ICON_NAME "-dark" + +// dock最大尺寸 +#define DOCK_MAX_SIZE 100 +/// +/// \brief The DisplayMode enum +/// spec dock display mode +/// +enum DisplayMode { + Fashion = 0, + Efficient = 1, +}; + +#define PROP_HIDE_MODE "HideMode" +/// +/// \brief The HideMode enum +/// spec dock hide behavior +/// +enum HideMode { + KeepShowing = 0, + KeepHidden = 1, + SmartHide = 3, +}; + +#define PROP_POSITION "Position" +/// +/// \brief The Position enum +/// spec dock position, dock always placed at primary screen, +/// so all position is the primary screen edge. +/// +enum Position { + Top = 0, + Right = 1, + Bottom = 2, + Left = 3, +}; + +#define PROP_HIDE_STATE "HideState" +/// +/// \brief The HideState enum +/// spec current dock should hide or shown. +/// this argument works only HideMode is SmartHide +/// +enum HideState { + Unknown = 0, + Show = 1, + Hide = 2, +}; + +#define IS_TOUCH_STATE "isTouchState" + + +/** + * @brief 任务栏 API 版本号,插件在编译期可以通过比对版本号判断接口是否兼容 + * DOCK_API_VERSION_VERSION is (major << 16) + (minor << 8) + patch. + * @since 2.0.0 + */ +#define DOCK_API_VERSION DOCK_API_VERSION_CHECK(DOCK_API_VERSION_MAJOR, DOCK_API_VERSION_MINOR, DOCK_API_VERSION_PATCH) + +/** + * @brief 用来和 DOCK_API_VERSION 进行比对 + * can be used like #if (DOCK_API_VERSION >= DOCK_API_VERSION_CHECK(2, 0, 0)) + * @since 2.0.0 + */ +#define DOCK_API_VERSION_CHECK(major, minor, patch) ((major<<16)|(minor<<8)|(patch)) + +/** + * @brief 插件的标志位 + * @since 2.0.0 + */ +enum PluginFlag { + Type_None = 0x00, // 默认值,插件无需关注 + Type_Unadapted = 0x01, // 插件类型-未适配,插件适配时勿使用这个flag + Type_Quick = 0x02, // 插件类型-快捷插件区 + Type_Tool = 0x04, // 插件类型-工具插件,例如回收站 + Type_System = 0x08, // 插件类型-系统插件,例如关机插件 + Type_Tray = 0x10, // 插件类型-托盘区,例如U盘插件 + Type_Fixed = 0x20, // 插件类型-固定区域,例如多任务视图和显示桌面 + + Quick_Panel_Single = 0x40, // 当插件类型为Common时,快捷插件区域只有一列的那种插件 + Quick_Panel_Multi = 0x80, // 当插件类型为Common时,快捷插件区占两列的那种插件 + Quick_Panel_Full = 0x100, // 当插件类型为Common时,快捷插件区占用4列的那种插件,例如声音、亮度设置和音乐等 + + Attribute_CanDrag = 0x200, // 插件属性-是否支持拖动 + Attribute_CanInsert = 0x400, // 插件属性-是否支持在其前面插入其他的插件,普通的快捷插件是支持的 + Attribute_CanSetting = 0x800, // 插件属性-是否可以在控制中心设置显示或隐藏,如果设置了这个属性,请实现PluginsItemInterfaceV2::icon 接口,并返回在`控制中心-个性化-任务栏-插件区域`中显示的图标 + Attribute_ForceDock = 0x1000, // 插件属性-强制显示在任务栏上 + + Attribute_Normal = Attribute_CanDrag | Attribute_CanInsert | Attribute_CanSetting, // 普通插件 + + FlagMask = 0xffffffff // 掩码 +}; +Q_DECLARE_FLAGS(PluginFlags, PluginFlag) +Q_DECLARE_OPERATORS_FOR_FLAGS(PluginFlags) + +/** + * @brief 图标的类型 + * @since 2.0.0 + */ +enum IconType +{ + IconType_None = 0, // 默认,无实际意义 +}; + +/** + * @brief 主题类型,和 dtk 的标志位对应 + * @since 2.0.0 + */ +enum ThemeType { + ThemeType_None, // 不涉及 + ThemeType_Light, // 亮色 + ThemeType_Dark // 暗色 +}; + +/** + * @brief 2.0.0 新增常量 + * @since 2.0.0 + */ +const QString QUICK_TOP_ACTION = QStringLiteral("quick_top_action"); // 快捷面板子页面右上角控件 +const QString QUICK_ITEM_KEY = QStringLiteral("quick_item_key"); // 快捷面板详情页面的itemWidget对应的itemKey +// 在 QApplication 设置的属性,插件可以在运行时获取API版本号,可以通过 qApp->property(DOCK_API_VERSION_PROPERTY) 获取版本号 +// 然后是同DOCK_API_VERSION_CHECK的方法去比较版本大小 +const QByteArray DOCK_API_VERSION_PROPERTY = "dock_api_version"; +const int DOCK_PLUGIN_ITEM_FIXED_WIDTH = 16; // 插件在任务栏上面的固定宽度 +const int DOCK_PLUGIN_ITEM_FIXED_HEIGHT = 16; // 插件在任务栏上面的固定高度 +const QSize DOCK_PLUGIN_ITEM_FIXED_SIZE(DOCK_PLUGIN_ITEM_FIXED_WIDTH, DOCK_PLUGIN_ITEM_FIXED_HEIGHT); // 插件在任务栏上面的固定大小 +const int TRAY_PLUGIN_ITEM_FIXED_WIDTH = 16; // 托盘插件的固定宽度 +const int TRAY_PLUGIN_ITEM_FIXED_HEIGHT = 16; // 托盘插件的固定高度 +const QSize TRAY_PLUGIN_ITEM_FIXED_SIZE(TRAY_PLUGIN_ITEM_FIXED_WIDTH, TRAY_PLUGIN_ITEM_FIXED_HEIGHT); // 托盘插件的固定大小 +const int DOCK_POPUP_WIDGET_WIDTH = 330; // 任务栏弹窗的宽度 +const int DOCK_POPUP_WIDGET_MAX_HEIGHT = 600; // 任务栏弹窗的最大高度 +const int QUICK_PANEL_ICON_WIDTH = 24; // 快捷面板中插件图标的宽度 +const int QUICK_PANEL_ICON_HEIGHT = 24; // 快捷面板中插件图标的高度 +const QSize QUICK_PANEL_ICON_SIZE(QUICK_PANEL_ICON_WIDTH, QUICK_PANEL_ICON_HEIGHT); // 快捷面板中插件图标的大小 +const int QUICK_ITEM_HEIGHT = 60; // 快捷面板插件高度 +const int QUICK_ITEM_SINGLE_WIDTH = 70; // 单格快捷面板插件宽度 +const int QUICK_ITEM_MULTI_WIDTH = 150; // 双格快捷面板插件宽度 +const int QUICK_ITEM_FULL_WIDTH = 310; // 整行快捷面板插件宽度 + +/** + * @brief 用于在插件的 message 和 MessageCallbackFunc 方法中解析 json格式 数据。 + * 详细的说明见 plugins-developer-guide.md 文档 + * @since 2.0.0 + */ +const QString MSG_TYPE = QStringLiteral("msgType"); // 固定 key 值,表明当前消息类型是什么 +const QString MSG_DATA = QStringLiteral("data"); // 固定 key 值,从该字段中获取具体数据 + +/** + * @brief 插件功能是否可用 + * eg:蓝牙被拔掉后,蓝牙插件的 support 的状态应该是 false,任务栏会把蓝牙的图标从控制中心-个性化-任务栏-插件区域中移除 + */ +const QString MSG_GET_SUPPORT_FLAG = QStringLiteral("getSupportFlag"); +const QString MSG_SUPPORT_FLAG = QStringLiteral("supportFlag"); +const QString MSG_SUPPORT_FLAG_CHANGED = QStringLiteral("supportFlagChanged"); + +/** + * @brief 任务栏应用溢出状态 + */ +const QString MSG_UPDATE_OVERFLOW_STATE = QStringLiteral("updateOverflowState"); +const int OVERFLOW_STATE_NOT_EXIST = 0; // 没有溢出区 +const int OVERFLOW_STATE_EXIST = 1; // 有溢出区 +const int OVERFLOW_STATE_ALL = 2; // 所有应用都在溢出区 + +/** + * @brief 最小弹窗高度,根据快捷面板的高度动态变化 + * 任务栏主动发送给插件,只会给快捷插件发送 + */ +const QString MSG_SET_APPLET_MIN_HEIGHT = QStringLiteral("setAppletMinHeight"); + +/** + * @brief 插件自行决定是否要被任务栏加载,不发送此消息默认被加载 + * ture: 希望被加载,false: 不希望被加载 + */ +const QString MSG_WHETHER_WANT_TO_BE_LOADED = QStringLiteral("whetherWantToBeLoaded"); + +/** + * @brief 弹窗是在任务栏上直接显示还是快捷面板的二级页面显示 + * 插件可以根据显示的位置做一些样式调整 + */ +const QString MSG_APPLET_CONTAINER = QStringLiteral("appletContainer"); +const int APPLET_CONTAINER_DOCK = 0; // 任务栏 +const int APPLET_CONTAINER_QUICK_PANEL = 1; // 快捷面板 + +/** + * @brief 插件图标的激活状态;当状态发生变化时,插件需要主动把状态发送给任务栏 + * true: 插件处于激活状态,false:插件出于失活状态 + */ +const QString MSG_ITEM_ACTIVE_STATE = QStringLiteral("itemActiveState"); +/** + * @brief 插件请求任务栏更新插件的 tooltips + * 任务栏收到请求后会主动调用 itemTips() 方法。 + * 一般用于一个插件里面含有多个图标,鼠标 hover 到不同图标上时显示不同 tooltips 的场景。 + */ +const QString MSG_UPDATE_TOOLTIPS_VISIBLE = QStringLiteral("updateTooltipsVisible"); + +/** + * @brief 任务栏面板Size;当任务栏size发生改变时,通知插件 + * 插件根据任务栏size做出大小调整,例如时间日期插件 + */ +const QString MSG_DOCK_PANEL_SIZE_CHANGED = QStringLiteral("dockPanelSizeChanged"); + +/** + * @brief 插件属性,MSG_TYPE + * 任务栏通过获取插件属性来确定插件状态,例如是否需要变色龙效果 + * 返回的MSG_DATA类型为QMap,其中QString为属性名称,QVariant为对应值 + */ +const QString MSG_PLUGIN_PROPERTY = QStringLiteral("pluginProperty"); + +/** + * @brief 属性 - 需要变色龙,即插件需要hover、press样式 + * true: 需要;false: 不需要; + */ +const QString PLUGIN_PROP_NEED_CHAMELEON = QStringLiteral("needChameleon"); + +/** + * @brief 属性 - 变色龙边距 + * 返回需要设置的QMargin() + */ +const QString PLUGIN_PROP_CHAMELEON_MARGIN = QStringLiteral("chameleonMargin"); +} + +Q_DECLARE_METATYPE(Dock::DisplayMode) +Q_DECLARE_METATYPE(Dock::Position) + + +#endif // CONSTANTS_H diff --git a/src/private-lastore-tray/pluginproxyinterface.h b/src/private-lastore-tray/pluginproxyinterface.h new file mode 100644 index 000000000..c88817cdd --- /dev/null +++ b/src/private-lastore-tray/pluginproxyinterface.h @@ -0,0 +1,81 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef PLUGINPROXYINTERFACE_H +#define PLUGINPROXYINTERFACE_H + +#include "constants.h" + +#include + +class PluginsItemInterface; +class PluginProxyInterface +{ +public: + /// + /// \brief itemAdded + /// add a new dock item + /// if itemkey of this plugin inter already exist, the new item + /// will be ignored, so if you need to add multiple item, you need + /// to ensure all itemKey is different. + /// \param itemInter + /// your plugin interface + /// \param itemKey + /// your item unique key + /// + virtual void itemAdded(PluginsItemInterface * const itemInter, const QString &itemKey) = 0; + /// + /// \brief itemUpdate + /// update(repaint) spec item + /// \param itemInter + /// \param itemKey + /// + virtual void itemUpdate(PluginsItemInterface * const itemInter, const QString &itemKey) = 0; + /// + /// \brief itemRemoved + /// remove spec item, if spec item is not exist, dock will to nothing. + /// dock will NOT delete your object, you should manage memory by your self. + /// \param itemInter + /// \param itemKey + /// + virtual void itemRemoved(PluginsItemInterface * const itemInter, const QString &itemKey) = 0; + /// + /// \brief requestContextMenu + /// request show context menu + /// + //virtual void requestContextMenu(PluginsItemInterface * const itemInter, const QString &itemKey) = 0; + + virtual void requestWindowAutoHide(PluginsItemInterface * const itemInter, const QString &itemKey, const bool autoHide) = 0; + virtual void requestRefreshWindowVisible(PluginsItemInterface * const itemInter, const QString &itemKey) = 0; + + virtual void requestSetAppletVisible(PluginsItemInterface * const itemInter, const QString &itemKey, const bool visible) = 0; + + /// + /// \brief saveValue + /// save module config to .config/deepin/dde-dock.conf + /// all key-values of all plugins will be save to that file + /// and grouped by the returned value of pluginName() function which is defined in PluginsItemInterface + /// \param itemInter the plugin object + /// \param key the key of data + /// \param value the data + /// + virtual void saveValue(PluginsItemInterface * const itemInter, const QString &key, const QVariant &value) = 0; + + /// + /// \brief getValue + /// SeeAlse: saveValue + /// return value from .config/deepin/dde-dock.conf + /// + virtual const QVariant getValue(PluginsItemInterface *const itemInter, const QString &key, const QVariant& fallback = QVariant()) = 0; + + /// + /// \brief removeValue + /// remove the values specified by keyList + /// remove all values of itemInter if keyList is empty + /// SeeAlse: saveValue + /// + virtual void removeValue(PluginsItemInterface *const itemInter, const QStringList &keyList) = 0; +}; + +#endif // PLUGINPROXYINTERFACE_H diff --git a/src/private-lastore-tray/plugins-logging-category.h b/src/private-lastore-tray/plugins-logging-category.h new file mode 100644 index 000000000..43b226fa9 --- /dev/null +++ b/src/private-lastore-tray/plugins-logging-category.h @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include + +Q_DECLARE_LOGGING_CATEGORY(APP) +Q_DECLARE_LOGGING_CATEGORY(DOCK_DATETIME) +Q_DECLARE_LOGGING_CATEGORY(SHUTDOWN) +Q_DECLARE_LOGGING_CATEGORY(DOCK_POWER) +Q_DECLARE_LOGGING_CATEGORY(DOCK_SOUND) +Q_DECLARE_LOGGING_CATEGORY(TRAY) +Q_DECLARE_LOGGING_CATEGORY(TRASH) +Q_DECLARE_LOGGING_CATEGORY(KEYBOARD_LAYOUT) +Q_DECLARE_LOGGING_CATEGORY(OVERLAY_WARNING) +Q_DECLARE_LOGGING_CATEGORY(SHOW_DESKTOP) +Q_DECLARE_LOGGING_CATEGORY(MULTI_TASK) +Q_DECLARE_LOGGING_CATEGORY(BLUETOOTH) +Q_DECLARE_LOGGING_CATEGORY(AIRPLANE) +Q_DECLARE_LOGGING_CATEGORY(QUICK_PANEL) +Q_DECLARE_LOGGING_CATEGORY(DND) +Q_DECLARE_LOGGING_CATEGORY(EYE_COMFORT) +Q_DECLARE_LOGGING_CATEGORY(MEDIA) +Q_DECLARE_LOGGING_CATEGORY(BRIGHTNESS) +Q_DECLARE_LOGGING_CATEGORY(PERFORMANCE) + +Q_DECLARE_LOGGING_CATEGORY(DCC_DOCK_SETTING) + diff --git a/src/private-lastore-tray/pluginsiteminterface.h b/src/private-lastore-tray/pluginsiteminterface.h new file mode 100644 index 000000000..604a0a107 --- /dev/null +++ b/src/private-lastore-tray/pluginsiteminterface.h @@ -0,0 +1,247 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef PLUGINSITEMINTERFACE_H +#define PLUGINSITEMINTERFACE_H + +#include "pluginproxyinterface.h" + +#include +#include + +/// +/// \brief The PluginsItemInterface class +/// the dock plugins item interface, all dock plugins should +/// inheirt this class and override all pure virtual function. +/// +class PluginsItemInterface +{ +public: + enum PluginType { + Normal, + Fixed + }; + + /** + * @brief Plugin size policy + */ + enum PluginSizePolicy { + System = 1 << 0, // Follow the system + Custom = 1 << 1 // The custom + }; + + /// + /// \brief ~PluginsItemInterface + /// DON'T try to delete m_proxyInter. + /// + virtual ~PluginsItemInterface() {} + + /// + /// \brief pluginName + /// tell dock the unique plugin id + /// \return + /// + virtual const QString pluginName() const = 0; + virtual const QString pluginDisplayName() const { return QString(); } + + /// + /// \brief init + /// init your plugins, you need to save proxyInter to m_proxyInter + /// member variable. but you shouldn't free this pointer. + /// \param proxyInter + /// DON'T try to delete this pointer. + /// + virtual void init(PluginProxyInterface *proxyInter) = 0; + /// + /// \brief itemWidget + /// your plugin item widget, each item should have a unique key. + /// \param itemKey + /// your widget' unique key. + /// \return + /// + virtual QWidget *itemWidget(const QString &itemKey) = 0; + + /// + /// \brief itemTipsWidget + /// override this function if your item want to have a tips. + /// the tips will shown when user hover your item. + /// nullptr will be ignored. + /// \param itemKey + /// \return + /// + virtual QWidget *itemTipsWidget(const QString &itemKey) {Q_UNUSED(itemKey); return nullptr;} + /// + /// \brief itemPopupApplet + /// override this function if your item wants to have an popup applet. + /// the popup applet will shown when user click your item. + /// + /// Tips: + /// dock should receive mouse press/release event to check user mouse operate, + /// if your item filter mouse event, this function will not be called. + /// so if you override mouse event and want to use popup applet, you + /// should pass event to your parent use QWidget::someEvent(e); + /// \param itemKey + /// \return + /// + virtual QWidget *itemPopupApplet(const QString &itemKey) {Q_UNUSED(itemKey); return nullptr;} + /// + /// \brief itemCommand + /// execute spec command when user clicked your item. + /// ensure your command do not get user input. + /// + /// empty string will be ignored. + /// \param itemKey + /// \return + /// + virtual const QString itemCommand(const QString &itemKey) {Q_UNUSED(itemKey); return QString();} + + /// + /// \brief itemContextMenu + /// context menu is shown when RequestPopupMenu called. + /// \param itemKey + /// \return + /// + virtual const QString itemContextMenu(const QString &itemKey) {Q_UNUSED(itemKey); return QString();} + /// + /// \brief invokedMenuItem + /// call if context menu item is clicked + /// \param itemKey + /// \param itemId + /// menu item id + /// \param checked + /// + virtual void invokedMenuItem(const QString &itemKey, const QString &menuId, const bool checked) {Q_UNUSED(itemKey); Q_UNUSED(menuId); Q_UNUSED(checked);} + + /// + /// \brief itemSortKey + /// tell dock where your item wants to put on. + /// + /// this index is start from 1 and + /// 0 for left side + /// -1 for right side + /// \param itemKey + /// \return + /// + virtual int itemSortKey(const QString &itemKey) {Q_UNUSED(itemKey); return 1;} + /// + /// \brief setSortKey + /// save your item new position + /// sort key will be changed when plugins order + /// changed(by user drag-drop) + /// \param itemKey + /// \param order + /// + virtual void setSortKey(const QString &itemKey, const int order) {Q_UNUSED(itemKey); Q_UNUSED(order);} + + /// + /// \brief itemAllowContainer + /// tell dock is your item allow to move into container + /// + /// if your item placed into container, popup tips and popup + /// applet will be disabled. + /// \param itemKey + /// \return + /// + virtual bool itemAllowContainer(const QString &itemKey) {Q_UNUSED(itemKey); return false;} + /// + /// \brief itemIsInContainer + /// tell dock your item is in container, this function + /// called at item init and if your item enable container. + /// \param itemKey + /// \return + /// + virtual bool itemIsInContainer(const QString &itemKey) {Q_UNUSED(itemKey); return false;} + /// + /// \brief setItemIsInContainer + /// save your item new state. + /// this function called when user drag out your item from + /// container or user drop item into container(if your item + /// allow drop into container). + /// \param itemKey + /// \param container + /// + virtual void setItemIsInContainer(const QString &itemKey, const bool container) {Q_UNUSED(itemKey); Q_UNUSED(container);} + + virtual bool pluginIsAllowDisable() { return false; } + virtual bool pluginIsDisable() { return false; } + virtual void pluginStateSwitched() {} + + /// + /// \brief displayModeChanged + /// override this function to receive display mode changed signal + /// \param displayMode + /// + virtual void displayModeChanged(const Dock::DisplayMode displayMode) {Q_UNUSED(displayMode);} + /// + /// \brief positionChanged + /// override this function to receive dock position changed signal + /// \param position + /// + virtual void positionChanged(const Dock::Position position) {Q_UNUSED(position);} + + /// + /// \brief refreshIcon + /// refresh item icon, its triggered when system icon theme changed. + /// \param itemKey + /// item key + /// + virtual void refreshIcon(const QString &itemKey) { Q_UNUSED(itemKey); } + + /// + /// \brief displayMode + /// get current dock display mode + /// \return + /// + inline Dock::DisplayMode displayMode() const + { + return qApp->property(PROP_DISPLAY_MODE).value(); + } + + /// + /// \brief position + /// get current dock position + /// \return + /// + inline Dock::Position position() const + { + return qApp->property(PROP_POSITION).value(); + } + + /// + /// \brief settingsChanged + /// override this function to receive plugin settings changed signal(DeepinSync) + /// + virtual void pluginSettingsChanged() {} + + /// + /// \brief type + /// Default plugin add dock right, fixed plugin add to dock fixed area + /// + /// This function is deprecated, please use `flags()` instead of it + /// + QT_DEPRECATED_X("Use flags()") + virtual PluginType type() { return Normal; } + + /// + /// \brief plugin size policy + /// default plugin size policy + /// + virtual PluginSizePolicy pluginSizePolicy() const { return System; } + +protected: + /// + /// \brief m_proxyInter + /// NEVER delete this object. + /// + PluginProxyInterface *m_proxyInter = nullptr; +}; + +QT_BEGIN_NAMESPACE + +#define ModuleInterface_iid "com.deepin.dock.PluginsItemInterface" + +Q_DECLARE_INTERFACE(PluginsItemInterface, ModuleInterface_iid) +QT_END_NAMESPACE + +#endif // PLUGINSITEMINTERFACE_H diff --git a/src/private-lastore-tray/privatelastoreitem.cpp b/src/private-lastore-tray/privatelastoreitem.cpp new file mode 100644 index 000000000..45166c7e9 --- /dev/null +++ b/src/private-lastore-tray/privatelastoreitem.cpp @@ -0,0 +1,97 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "privatelastoreitem.h" +#include "constants.h" +#include "tipswidget.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +DGUI_USE_NAMESPACE + +PrivateLastoreItem::PrivateLastoreItem(QWidget* parent) + : QWidget(parent) + , m_tipsLabel(new TipsWidget(this)) + , m_icon(new CommonIconButton(this)) + , m_managerInter(new UpdateDBusProxy(this)) + , m_controlCenterInterface(new QDBusInterface("com.deepin.dde.ControlCenter", "/com/deepin/dde/ControlCenter", "com.deepin.dde.ControlCenter", QDBusConnection::sessionBus())) +{ + m_tipsLabel->setVisible(false); + auto vLayout = new QVBoxLayout(this); + vLayout->setSpacing(0); + vLayout->setContentsMargins(0, 0, 0, 0); + vLayout->addWidget(m_icon, 0, Qt::AlignCenter); + m_icon->setFixedSize(Dock::DOCK_PLUGIN_ITEM_FIXED_SIZE); + m_icon->setIcon(QIcon(":resources/private-lastore-sleep_16px.svg")); + m_icon->setContentsMargins(0, 0, 0, 0); + connect(m_managerInter, &UpdateDBusProxy::JobListChanged, this, &PrivateLastoreItem::onRefreshIcon); +} + +QWidget* PrivateLastoreItem::tipsWidget() +{ + m_tipsLabel->refreshContent(); + return m_tipsLabel; +} + +void PrivateLastoreItem::onRefreshIcon(const QList &jobs) +{ + qInfo() << "Job list changed"; + + for (const auto &job : jobs) { + const QString &jobPath = job.path(); + qInfo() << "Path : " << jobPath; + UpdateJobDBusProxy jobInter(jobPath, this); + if (!jobInter.isValid()) { + qWarning() << "Job is invalid"; + continue; + } + + const QString &id = jobInter.id(); + qInfo() << "Job id : " << id; + if (id == "dist_upgrade" || id == "prepare_dist_upgrade") { + m_icon->setIcon(QIcon(":resources/private-lastore-active_16px.svg")); + return; + } + } + m_icon->setIcon(QIcon(":resources/private-lastore-sleep_16px.svg")); +} + +void PrivateLastoreItem::resizeEvent(QResizeEvent* e) +{ + QWidget::resizeEvent(e); + + const Dock::Position position = qApp->property(PROP_POSITION).value(); + if (position == Dock::Bottom || position == Dock::Top) { + setMaximumWidth(height()); + setMaximumHeight(QWIDGETSIZE_MAX); + } else { + setMaximumHeight(width()); + setMaximumWidth(QWIDGETSIZE_MAX); + } +} + +void PrivateLastoreItem::mouseReleaseEvent(QMouseEvent *event) { + if (event->button() == Qt::LeftButton && m_controlCenterInterface) { + DDBusSender() + .service("org.deepin.dde.ControlCenter1") + .interface("org.deepin.dde.ControlCenter1") + .path("/org/deepin/dde/ControlCenter1") + .method("ShowModule") + .arg(QString("update")) + .call(); + } + QWidget::mouseReleaseEvent(event); +} + diff --git a/src/private-lastore-tray/privatelastoreitem.h b/src/private-lastore-tray/privatelastoreitem.h new file mode 100644 index 000000000..258748d50 --- /dev/null +++ b/src/private-lastore-tray/privatelastoreitem.h @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef PRIVATELASTOREITEM_H +#define PRIVATELASTOREITEM_H + +#include "commoniconbutton.h" +#include "common/dbus/updatedbusproxy.h" +#include "common/dbus/updatejobdbusproxy.h" +#include "tipswidget.h" + +#include +#include + +namespace Dock { +class TipsWidget; +} + +class AirplaneModeApplet; +class PrivateLastoreItem : public QWidget +{ + Q_OBJECT + +public: + explicit PrivateLastoreItem(QWidget *parent = nullptr); + + QWidget *tipsWidget(); + +protected: + void resizeEvent(QResizeEvent *e); + void mouseReleaseEvent(QMouseEvent *event) override; + +private slots: + void onRefreshIcon(const QList &jobs); + +private: + TipsWidget *m_tipsLabel; + CommonIconButton *m_icon; + UpdateDBusProxy *m_managerInter = nullptr; + QDBusInterface *m_controlCenterInterface = nullptr; +}; + +#endif // PRIVATELASTOREITEM_H diff --git a/src/private-lastore-tray/privatelastoremode.json b/src/private-lastore-tray/privatelastoremode.json new file mode 100644 index 000000000..bec81f0da --- /dev/null +++ b/src/private-lastore-tray/privatelastoremode.json @@ -0,0 +1,3 @@ +{ + "api": "2.0.0" +} diff --git a/src/private-lastore-tray/privatelastoreplugin.cpp b/src/private-lastore-tray/privatelastoreplugin.cpp new file mode 100644 index 000000000..18052e47c --- /dev/null +++ b/src/private-lastore-tray/privatelastoreplugin.cpp @@ -0,0 +1,69 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "privatelastoreplugin.h" +#include "privatelastoreitem.h" +#include "qdbusconnection.h" +#include "common/global_util/public_func.h" + +#include +#include + +#define PRIVATE_LASTORE_KEY "private-lastore-key" +#define STATE_KEY "enabled" + +DCORE_USE_NAMESPACE +DGUI_USE_NAMESPACE +Q_LOGGING_CATEGORY(dockPrivateUpdatePlugin, "org.deepin.dde.dock.update") + +PrivateLastorePlugin::PrivateLastorePlugin(QObject *parent) + : QObject(parent) + , m_item(new PrivateLastoreItem) +{ + QTranslator *translator = new QTranslator(this); + translator->load(QLocale(), "private-lastore-tray", "_", "/usr/share/private-lastore-tray/translations"); + QCoreApplication::installTranslator(translator); +} + +PrivateLastorePlugin::~PrivateLastorePlugin() +{ + if (m_item) { + delete m_item; + m_item = nullptr; + } +} + +void PrivateLastorePlugin::loadPlugin() +{ + m_proxyInter->itemAdded(this, PRIVATE_LASTORE_KEY); + displayModeChanged(displayMode()); +} + +const QString PrivateLastorePlugin::pluginName() const +{ + return "private-lastore"; +} + +const QString PrivateLastorePlugin::pluginDisplayName() const +{ + return tr("Private Lastore"); +} + +void PrivateLastorePlugin::init(PluginProxyInterface *proxyInter) +{ + m_proxyInter = proxyInter; + + m_proxyInter->itemAdded(this, pluginName()); + m_proxyInter->saveValue(this, STATE_KEY, true); +} + +QWidget *PrivateLastorePlugin::itemWidget(const QString &itemKey) +{ + return m_item; +} + +QWidget *PrivateLastorePlugin::itemTipsWidget(const QString &itemKey) +{ + return m_item->tipsWidget(); +} diff --git a/src/private-lastore-tray/privatelastoreplugin.h b/src/private-lastore-tray/privatelastoreplugin.h new file mode 100644 index 000000000..2359264c7 --- /dev/null +++ b/src/private-lastore-tray/privatelastoreplugin.h @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef PRIVATELASTOREPLUGIN_H +#define PRIVATELASTOREPLUGIN_H + +#include "pluginsiteminterface_v2.h" +#include "plugins-logging-category.h" + +#include + +#include "dtkcore_global.h" + +class PrivateLastoreItem; +class PrivateLastorePlugin : public QObject, public PluginsItemInterfaceV2 +{ + Q_OBJECT + Q_INTERFACES(PluginsItemInterfaceV2) + Q_PLUGIN_METADATA(IID "com.deepin.dock.PluginsItemInterface" FILE "privatelastoremode.json") + +public: + explicit PrivateLastorePlugin(QObject *parent = nullptr); + ~PrivateLastorePlugin(); + + const QString pluginName() const Q_DECL_OVERRIDE; + const QString pluginDisplayName() const Q_DECL_OVERRIDE; + void init(PluginProxyInterface *proxyInter) override; + + bool pluginIsAllowDisable() override { return true; } + QWidget *itemWidget(const QString &itemKey) Q_DECL_OVERRIDE; + QWidget *itemTipsWidget(const QString &itemKey) Q_DECL_OVERRIDE; + +private: + void loadPlugin(); + +private: + PrivateLastoreItem *m_item; +}; + +#endif // PRIVATELASTOREPLUGIN_H diff --git a/src/private-lastore-tray/resources/private-lastore-active_16px.svg b/src/private-lastore-tray/resources/private-lastore-active_16px.svg new file mode 100644 index 000000000..42699c403 --- /dev/null +++ b/src/private-lastore-tray/resources/private-lastore-active_16px.svg @@ -0,0 +1,7 @@ + + + status-intranet-update-platform + + + + \ No newline at end of file diff --git a/src/private-lastore-tray/resources/private-lastore-sleep_16px.svg b/src/private-lastore-tray/resources/private-lastore-sleep_16px.svg new file mode 100644 index 000000000..881ee6e39 --- /dev/null +++ b/src/private-lastore-tray/resources/private-lastore-sleep_16px.svg @@ -0,0 +1,7 @@ + + + status-intranet-update-platform-dark + + + + \ No newline at end of file diff --git a/src/private-lastore-tray/resources/private-lastore-tray.qrc b/src/private-lastore-tray/resources/private-lastore-tray.qrc new file mode 100644 index 000000000..9e5b0841f --- /dev/null +++ b/src/private-lastore-tray/resources/private-lastore-tray.qrc @@ -0,0 +1,6 @@ + + + private-lastore-active_16px.svg + private-lastore-sleep_16px.svg + + diff --git a/src/private-lastore-tray/tipswidget.cpp b/src/private-lastore-tray/tipswidget.cpp new file mode 100644 index 000000000..dee99773f --- /dev/null +++ b/src/private-lastore-tray/tipswidget.cpp @@ -0,0 +1,324 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "tipswidget.h" +#include "privatelastoreplugin.h" +#include "common/common/dconfig_helper.h" + +#include +#include +#include +#include + +Q_LOGGING_CATEGORY(dockUpdatePlugin, "org.deepin.dde.dock.update") +#define PADDING 4 +#define SHUTDOWNUPDATESTATUS 5 +static const int BACKUP_START_PROGRESS = 20; +static const int BACKUP_SUCCESS_PROGRESS = 50; + +TipsWidget::TipsWidget(QWidget *parent) + : QFrame(parent) + , m_managerInter(new UpdateDBusProxy(this)) +{ + m_type = TipsWidget::MultiLine; + if (m_managerInter) { + connect(m_managerInter, &UpdateDBusProxy::JobListChanged, this, &TipsWidget::onRefreshJobList); + } +} + +void TipsWidget::setText(const QString &text) +{ + m_text = text; + setFixedSize(fontMetrics().boundingRect(m_text).width() + 20, fontMetrics().boundingRect(m_text).height() + PADDING); + update(); + +#ifndef QT_NO_ACCESSIBILITY + if (accessibleName().isEmpty()) { + QAccessibleEvent event(this, QAccessible::NameChanged); + QAccessible::updateAccessibility(&event); + } +#endif +} + +void TipsWidget::setTextList(const QStringList &textList) +{ + m_type = TipsWidget::MultiLine; + m_textList = textList; + + int width = 0; + int height = 0; + for (const QString& text : m_textList) { + width = qMax(width, fontMetrics().boundingRect(text).width()); + height += fontMetrics().boundingRect(text).height(); + } + + setFixedSize(width + 20, height + PADDING); + + update(); +} + +bool TipsWidget::checkShutdownUpdate() +{ + int lastoreStatus = DConfigHelper::instance()->getConfig("org.deepin.dde.lastore", "org.deepin.dde.lastore", "","lastore-daemon-status", 0).toInt(); + if (lastoreStatus == SHUTDOWNUPDATESTATUS) { + m_textList.append(tr("Download complete")); + m_textList.append(tr("shutdown update")); + return true; + } else { + return false; + } +} + +bool TipsWidget::checkRegularlyUpdate() +{ + QString updateTime = DConfigHelper::instance()->getConfig("org.deepin.dde.lastore", "org.deepin.dde.lastore", "","update-time", "").toString(); + + if (!updateTime.isEmpty()) { + QDateTime dateTime = QDateTime::fromString(updateTime, Qt::ISODate); + if (dateTime.isValid()) { + QString formattedDateTime = dateTime.toString("HH:mm:ss"); + m_textList.append(tr("Download complete")); + QString info = tr("will upgrade at %1"); + m_textList.append(info.arg(formattedDateTime)); + return true; + } + return false; + } + return false; +} + +void TipsWidget::onUpdatePropertiesChanged(const QString& interfaceName, const QVariantMap& changedProperties, const QStringList& invalidatedProperties) +{ + Q_UNUSED(invalidatedProperties) + qCInfo(dockUpdatePlugin) << "xiongbo111 onUpdatePropertiesChanged " << interfaceName << " changedProperties " << changedProperties; + + if (interfaceName == "org.deepin.dde.Lastore1.Job") { + if (changedProperties.contains("Speed")) { + m_speed = changedProperties.value("Speed").toULongLong(); + } + + if (changedProperties.contains("Proto")) { + m_proto = changedProperties.value("Proto").toString(); + } + } + refreshContent(); + update(); +} + +void TipsWidget::onSetUpdateProgress(double progress) +{ + m_updateProgress = progress; + refreshContent(); + update(); +} + +void TipsWidget::onSetBackUpProgress(double progress) +{ + m_backupProgress = progress; + refreshContent(); + update(); +} + +void TipsWidget::refreshContent() +{ + m_textList.clear(); + if (m_downloadLimitOnChanging) { + m_textList.append(tr("Is changing download speed limit. Please wait")); + return; + } + if (m_managerInter) { + QList jobList = m_managerInter->jobList(); + if (jobList.isEmpty()) { + //当前无任务,需检测是否为任务执行完成状态 + //检查当前是否为关机更新状态 + if (checkShutdownUpdate()) return; + //检查当前是否为定时更新状态 + if (checkRegularlyUpdate()) return; + //检查是否有可更新内容 + QString systemUpgradeStatus = checkHasSystemUpdate(m_managerInter->updateStatus()); + if (systemUpgradeStatus == "downloaded") { + m_textList.append(tr("Download complete.Please go to control-center to check.")); + } else if (systemUpgradeStatus == "notDownload" || systemUpgradeStatus == "isDownloading"|| + systemUpgradeStatus == "downloadPause" || systemUpgradeStatus == "upgradeReady" + || systemUpgradeStatus == "upgrading") { + m_textList.append(tr("Has new version.Please go to check.")); + return; + } + } else { + for (const auto &job : jobList) { + const QString &jobPath = job.path(); + qInfo() << "Path : " << jobPath; + UpdateJobDBusProxy jobInter(jobPath, this); + if (!jobInter.isValid()) { + qWarning() << "Job is invalid"; + continue; + } + QString curJobStatus = jobInter.status(); + QString curJobId = jobInter.id(); + QString curStatus; + if (curJobStatus == "running" || curJobStatus == "ready") { + //当前有任务在进行 + if (curJobId == "backup" && m_backupJobInter) { + QString curProgress = tr("current upgrade process"); + curStatus = tr("backing up"); + m_textList.append(curStatus); + m_textList.append(QString("%1: %2%") + .arg(curProgress) + .arg(QString::number(qRound(m_backupProgress / 0.01)))); + } else if (curJobId == "prepare_dist_upgrade" && m_updateJobInter) { + //任务类型为下载任务,展示下载信息 + curStatus = tr("download"); + QString curProgress = tr("current download process"); + QString curSpeed = tr("current speed"); + m_textList.append(QString("%1, %2: %3%") + .arg(curStatus) + .arg(curProgress) + .arg(QString::number(qRound(m_updateProgress / 0.01)))); + m_textList.append(QString("%1: %2(%3)") + .arg(curSpeed) + .arg(regulateSpeed()) + .arg(m_proto)); + } else if (curJobId == "dist_upgrade" && m_updateJobInter) { + //任务类型为更新任务,展示更新信息 + QString curProgress = tr("current upgrade process"); + curStatus = tr("install"); + m_textList.append(curStatus); + m_textList.append(QString("%1: %2%") + .arg(curProgress) + .arg(QString::number(qRound(m_updateProgress / 0.01)))); + } else if (curJobId == "update_source" && m_updateJobInter) { + QString systemUpgradeStatus = checkHasSystemUpdate(m_managerInter->updateStatus()); + if (systemUpgradeStatus == "notDownload" || systemUpgradeStatus == "isDownloading"|| + systemUpgradeStatus == "downloadPause" || systemUpgradeStatus == "upgradeReady" + || systemUpgradeStatus == "upgrading" || systemUpgradeStatus == "downloaded") { + // 由于每次打开控制中心都会触发检查更新,此时有可能已经是downloaded状态且也有job + m_textList.append(tr("Has new version.Please go to check.")); + } + } + } else { + //无running任务状态下没有监控任务状态的必要,展示标题文案,防止出现空白气泡 + m_speed = 0.0; + if (m_textList.length() != 0) + m_textList.clear(); + continue; + } + } + } + } +} + +QString TipsWidget::regulateSpeed() +{ + QString unitName; + double regulatedProcess; + bool needDecimal = false; //是否需要保留一位小数 + + if (m_speed >= 1024 * 1024) { + regulatedProcess = static_cast(m_speed) / (1024 * 1024); + regulatedProcess = qRound(regulatedProcess * 10) / 10.0; + needDecimal = true; + unitName = "MB/s"; + } else if (m_speed >= 1024) { + regulatedProcess = static_cast(m_speed) / 1024; + regulatedProcess = qRound(regulatedProcess); + unitName = "KB/s"; + } else { + regulatedProcess = static_cast(m_speed); + unitName = "B/s"; + } + return QString("%1%2").arg(regulatedProcess, 0, 'f', needDecimal ? 1 : 0).arg(unitName); +} + +void TipsWidget::onDownloadLimitChanged(bool value) { + m_downloadLimitOnChanging = value; +} + +void TipsWidget::onRefreshJobList(const QList &jobs) +{ + qInfo() << "Job list changed"; + + for (const auto &job : jobs) { + const QString &jobPath = job.path(); + UpdateJobDBusProxy jobInter(jobPath, this); + + //检测到有更新任务 + const QString &id = jobInter.id(); + if (id == "dist_upgrade" || id == "prepare_dist_upgrade") { + m_updateJobInter = new UpdateJobDBusProxy(jobPath, this); + connect(m_updateJobInter, &UpdateJobDBusProxy::ProgressChanged, this, &TipsWidget::onSetUpdateProgress); + } else if (id == "backup") { + m_backupJobInter = new UpdateJobDBusProxy(jobPath, this); + connect(m_backupJobInter, &UpdateJobDBusProxy::ProgressChanged, this, &TipsWidget::onSetBackUpProgress); + } + } +} + + +/** + * @brief TipsWidget::paintEvent 任务栏插件提示信息绘制 + * @param event + */ +void TipsWidget::paintEvent(QPaintEvent *event) +{ + QFrame::paintEvent(event); + + QPainter painter(this); + painter.setPen(QPen(palette().brightText(), 1)); + + QTextOption option; + option.setAlignment(Qt::AlignCenter); + + switch (m_type) { + case SingleLine: { + painter.drawText(rect(), m_text, option); + } + break; + case MultiLine: { + if (m_textList.size() == 0) { + m_textList.append(tr("Enterprise-level Upgrade Management System")); + } + + int x = rect().x(); + int y = rect().y(); + if (m_textList.size() != 1) { + x += 10; + option.setAlignment(Qt::AlignLeft | Qt::AlignVCenter); + } + + int width = 0; + int height = 0; + for (const QString& text : m_textList) { + int lineHeight = fontMetrics().boundingRect(text).height(); + painter.drawText(QRect(x, y, rect().width(), lineHeight), text, option); + y += lineHeight; + + width = qMax(width, fontMetrics().boundingRect(text).width()); + height += fontMetrics().boundingRect(text).height(); + } + setFixedSize(width + 20, height + PADDING); + } break; + } +} + +bool TipsWidget::event(QEvent *event) +{ + if (event->type() == QEvent::FontChange) { + switch (m_type) { + case SingleLine: + { + setText(m_text); + break; + } + case MultiLine: + { + setTextList(m_textList); + break; + } + } + } else if (event->type() == QEvent::MouseButtonRelease + && static_cast(event)->button() == Qt::RightButton) { + return true; + } + return QFrame::event(event); +} diff --git a/src/private-lastore-tray/tipswidget.h b/src/private-lastore-tray/tipswidget.h new file mode 100644 index 000000000..6854a493d --- /dev/null +++ b/src/private-lastore-tray/tipswidget.h @@ -0,0 +1,81 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef TIPSWIDGET_H +#define TIPSWIDGET_H + +#include +#include +#include +#include +#include +#include "common/dbus/updatedbusproxy.h" +#include "common/dbus/updatejobdbusproxy.h" + +inline QString checkHasSystemUpdate(const QString& updateStatus) +{ + QJsonParseError parseError; + QJsonDocument doc = QJsonDocument::fromJson(updateStatus.toUtf8(), &parseError); + if (parseError.error != QJsonParseError::NoError) { + qWarning() << "JSON parse error:" << parseError.errorString(); + return ""; + } + if (!doc.isObject()) { + qWarning() << "JSON is not an object"; + return ""; + } + QJsonObject rootObj = doc.object(); + QJsonObject updateStatusObj = rootObj.value("UpdateStatus").toObject(); + QString systemUpgradeStatus = updateStatusObj.value("system_upgrade").toString(); + return systemUpgradeStatus; +} + +class TipsWidget : public QFrame +{ + Q_OBJECT + enum ShowType + { + SingleLine, + MultiLine + }; +public: + explicit TipsWidget(QWidget *parent = nullptr); + + void setText(const QString &text); + void setTextList(const QStringList &textList); + void refreshContent(); + +protected: + void paintEvent(QPaintEvent *event) override; + bool event(QEvent *event) override; + +private: + bool checkShutdownUpdate(); + bool checkRegularlyUpdate(); + QString regulateSpeed(); + +private slots: + void onDownloadLimitChanged(bool value); + void onRefreshJobList(const QList &jobs); + void onUpdatePropertiesChanged(const QString& interfaceName, + const QVariantMap& changedProperties, + const QStringList& invalidatedProperties); + void onSetUpdateProgress(double progress); + void onSetBackUpProgress(double progress); + +private: + UpdateDBusProxy *m_managerInter = nullptr; + UpdateJobDBusProxy *m_updateJobInter = nullptr; + UpdateJobDBusProxy *m_backupJobInter = nullptr; + QString m_text; + QStringList m_textList; + ShowType m_type; + qulonglong m_speed = 0; + QString m_proto = ""; + bool m_downloadLimitOnChanging = false; + double m_updateProgress = 0.0; + double m_backupProgress = 0.0; +}; + +#endif // TIPSWIDGET_H diff --git a/src/private-lastore-tray/translations/private-lastore-tray.ts b/src/private-lastore-tray/translations/private-lastore-tray.ts new file mode 100644 index 000000000..0e07b6063 --- /dev/null +++ b/src/private-lastore-tray/translations/private-lastore-tray.ts @@ -0,0 +1,83 @@ + + + + + PrivateLastorePlugin + + + Private Lastore + 更新独立客户端 + + + + TipsWidget + + + + Download complete + 更新包下载完成 + + + + shutdown update + 将在下次关机/重启时开始更新 + + + + will upgrade at %1 + 将于%1开始系统更新 + + + + Download complete.Please go to control-center to check. + 更新包下载完成,点击图标进入更新界面进行查看与安装更新 + + + + Is changing download speed limit. Please wait + 下载限速配置切换中,切换完成后将会继续下载更新资源 + + + + + Has new version.Please go to check. + 有新版本,点击图标查看 + + + + + current upgrade process + 更新进度 + + + + backing up + 备份中 + + + + download + 更新包下载中 + + + + current download process + 已下载 + + + + current speed + 下载速度 + + + + install + 系统更新中,请勿关闭计算机 + + + + Enterprise-level Upgrade Management System + 企业级更新管理系统 + + + diff --git a/src/private-lastore-tray/translations/private-lastore-tray_zh_CN.ts b/src/private-lastore-tray/translations/private-lastore-tray_zh_CN.ts new file mode 100644 index 000000000..0e07b6063 --- /dev/null +++ b/src/private-lastore-tray/translations/private-lastore-tray_zh_CN.ts @@ -0,0 +1,83 @@ + + + + + PrivateLastorePlugin + + + Private Lastore + 更新独立客户端 + + + + TipsWidget + + + + Download complete + 更新包下载完成 + + + + shutdown update + 将在下次关机/重启时开始更新 + + + + will upgrade at %1 + 将于%1开始系统更新 + + + + Download complete.Please go to control-center to check. + 更新包下载完成,点击图标进入更新界面进行查看与安装更新 + + + + Is changing download speed limit. Please wait + 下载限速配置切换中,切换完成后将会继续下载更新资源 + + + + + Has new version.Please go to check. + 有新版本,点击图标查看 + + + + + current upgrade process + 更新进度 + + + + backing up + 备份中 + + + + download + 更新包下载中 + + + + current download process + 已下载 + + + + current speed + 下载速度 + + + + install + 系统更新中,请勿关闭计算机 + + + + Enterprise-level Upgrade Management System + 企业级更新管理系统 + + + From 9cd59b72dbb39e3a0fec0ccb867882457cf0eed9 Mon Sep 17 00:00:00 2001 From: ut005973 Date: Wed, 1 Apr 2026 14:12:24 +0800 Subject: [PATCH 2/2] feat: [Update Delivery] Merge update delivery related functionality into Control Center update client MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Integrate the update delivery logic from v20 into the v25 Control Center update module: Add an update delivery master switch in the Control Center update advanced settings interface. When the switch is enabled, the interface displays two sub-items: Update Delivery - Upload Speed Limit and Update Delivery - Download Speed Limit. Each sub-item has its own control switch and speed limit input field. Simultaneously, call the relevant SetP2pEnable interface of lastore-daemon, which will attempt to start the upgrade-delivery system service during the next update check of lastore. The switches and input fields for Update Delivery - Upload Speed Limit and Update Delivery - Download Speed Limit independently control the enabling/disabling of upload/download speed limits and their specific values. These values are set by calling the SetDeliveryUploadSpeedLimit and SetDeliveryDownloadSpeedLimit interfaces of lastore-daemon, which are then applied by lastore. The update delivery speed limit configuration supports backend-driven busy/idle time speed limit policies via the update platform. When such policies are active, the Control Center will gray out the Update Delivery - Upload Speed Limit and Update Delivery - Download Speed Limit sub-items accordingly, preventing user modifications. Each operation on the master switch triggers a refresh of the status values for various sub-items. The status values are read from the upgrade-delivery service. Log: Integrate v20 update delivery functionality into v25 Task: https://pms.uniontech.com/task-view-387713.html Influence: New update delivery logic added to the Control Center update module feat: 【更新传递】控制中心更新客户端合入更新传递相关功能 v20的更新传递逻辑合入v25控制中心更新模块 1.添加更新传递总开关在控制中心更新高级设置界面。当开关打开时,界面展示更新传递-上传限速、更新传递-下载限速两个子项,每个子项有各自的控制开关和限速值输入框,同时调用lastore-daemon的SetP2pEnable相关接口,在lastore的下次检查更新时尝试拉起upgrade-delivery系统服务 2.更新传递-上传限速、更新传递-下载限速两个子项的开关和输入框各自控制上传、下载限速的开启和关闭以及具体数值,具体通过调用lastore-daemon的SetDeliveryUploadSpeedLimit和SetDeliveryDownloadSpeedLimit接口交由lastore来实际设置值。 3.更新传递的限速配置支持通过更新平台后端下发忙闲时的限速策略,此时控制中心会根据这一策略适时置灰更新传递-上传限速、更新传递-下载限速两个子项,禁止用户修改 4.每次操作总开关时会适时的刷新各种子项的状态值,具体是从upgrade-delivery服务中读取对应的属性值 Log: v20更新传递功能合入v25 Task: https://pms.uniontech.com/task-view-387713.html Influence: 控制中心更新模块新增更新传递逻辑 --- src/common/dbus/updateassistant.cpp | 63 +++++ src/common/dbus/updateassistant.h | 84 +++++++ src/common/dbus/updatedbusproxy.cpp | 40 ++++ src/common/dbus/updatedbusproxy.h | 13 ++ .../operation/updatedatastructs.h | 123 +++++++++- .../operation/updatelistmodel.cpp | 2 +- .../operation/updatelistmodel.h | 2 +- .../operation/updatemodel.cpp | 94 ++++++++ src/dcc-update-plugin/operation/updatemodel.h | 34 ++- .../operation/updatework.cpp | 113 ++++++++- src/dcc-update-plugin/operation/updatework.h | 11 +- src/dcc-update-plugin/operation/utils.h | 2 +- src/dcc-update-plugin/qml/UpdateControl.qml | 111 +-------- src/dcc-update-plugin/qml/UpdateMain.qml | 12 +- src/dcc-update-plugin/qml/UpdateSetting.qml | 221 +++++++++++++++++- .../translations/update_zh_CN.ts | 28 ++- .../translations/update_zh_HK.ts | 28 ++- .../translations/update_zh_TW.ts | 28 ++- src/private-lastore-tray/tipswidget.cpp | 1 - 19 files changed, 853 insertions(+), 157 deletions(-) create mode 100644 src/common/dbus/updateassistant.cpp create mode 100644 src/common/dbus/updateassistant.h diff --git a/src/common/dbus/updateassistant.cpp b/src/common/dbus/updateassistant.cpp new file mode 100644 index 000000000..d4c0cba93 --- /dev/null +++ b/src/common/dbus/updateassistant.cpp @@ -0,0 +1,63 @@ +// // SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later +#include "updateassistant.h" + +UpdateAssistant::UpdateAssistant(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent) + : QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent) + , d_ptr(new UpdateAssistantPrivate) +{ + QDBusConnection::systemBus().connect("org.deepin.upgradedelivery", "/org/deepin/upgradedelivery", "org.freedesktop.DBus.Properties", "PropertiesChanged", this, SLOT(onPropertyChanged(QString, QVariantMap, QStringList))); +} + +UpdateAssistant::~UpdateAssistant() +{ +} + +void UpdateAssistant::onPropertyChanged(const QString& interfaceName, + const QVariantMap& changedProperties, + const QStringList& invalidatedProperties) +{ + if (interfaceName != staticInterfaceName()) + return; + + if (changedProperties.contains("UploadLimitSpeed")) { + Q_EMIT UploadLimitSpeedChanged(uploadLimitSpeed()); + } + if (changedProperties.contains("DownloadLimitSpeed")) { + Q_EMIT DownloadLimitSpeedChanged(downloadLimitSpeed()); + } + + return; +} + +void UpdateAssistant::CallQueued(const QString &callName, const QList &args) +{ + if (d_ptr->m_waittingCalls.contains(callName)) { + d_ptr->m_waittingCalls[callName] = args; + return; + } + if (d_ptr->m_processingCalls.contains(callName)) { + d_ptr->m_waittingCalls.insert(callName, args); + } else { + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(asyncCallWithArgumentList(callName, args)); + connect(watcher, &QDBusPendingCallWatcher::finished, this, &UpdateAssistant::onPendingCallFinished); + d_ptr->m_processingCalls.insert(callName, watcher); + } +} + +void UpdateAssistant::onPendingCallFinished(QDBusPendingCallWatcher *w) +{ + if (!w) + return; + w->deleteLater(); + const auto callName = d_ptr->m_processingCalls.key(w); + Q_ASSERT(!callName.isEmpty()); + if (callName.isEmpty()) + return; + d_ptr->m_processingCalls.remove(callName); + if (!d_ptr->m_waittingCalls.contains(callName)) + return; + const auto args = d_ptr->m_waittingCalls.take(callName); + CallQueued(callName, args); +} \ No newline at end of file diff --git a/src/common/dbus/updateassistant.h b/src/common/dbus/updateassistant.h new file mode 100644 index 000000000..f404af316 --- /dev/null +++ b/src/common/dbus/updateassistant.h @@ -0,0 +1,84 @@ +// // SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later +#ifndef UPDATEASSISTANT_H +#define UPDATEASSISTANT_H + +#include +#include +#include +#include +#include +#include +#include +#include + +class UpdateAssistantPrivate +{ +public: + UpdateAssistantPrivate() = default; + +public: + QMap m_processingCalls; + QMap> m_waittingCalls; +}; + +/* + * Proxy class for interface org.deepin.updateassistant + */ +class UpdateAssistant: public QDBusAbstractInterface +{ + Q_OBJECT +public: + static inline const char *staticInterfaceName() + { return "org.deepin.upgradedelivery"; } + +public: + UpdateAssistant(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = nullptr); + + ~UpdateAssistant(); + + Q_PROPERTY(QString UploadLimitSpeed READ uploadLimitSpeed NOTIFY UploadLimitSpeedChanged) + inline QString uploadLimitSpeed() const + { return qvariant_cast(internalPropGet("UploadLimitSpeed")); } + + Q_PROPERTY(QString DownloadLimitSpeed READ downloadLimitSpeed NOTIFY DownloadLimitSpeedChanged) + inline QString downloadLimitSpeed() const + { return qvariant_cast(internalPropGet("DownloadLimitSpeed")); } + +public Q_SLOTS: // METHODS + + inline QDBusPendingReply SetUploadRateLimit(int speed) + { + QList argumentList; + argumentList << QVariant::fromValue(speed); + return asyncCallWithArgumentList(QStringLiteral("SetUploadRateLimit"), argumentList); + } + + inline QDBusPendingReply SetDownloadRateLimit(int speed) + { + QList argumentList; + argumentList << QVariant::fromValue(speed); + return asyncCallWithArgumentList(QStringLiteral("SetDownloadRateLimit"), argumentList); + } + +Q_SIGNALS: // SIGNALS + // begin property changed signals + void propertyChanged(const QString &propertyName, const QVariant &value); + void UploadLimitSpeedChanged(const QString & speed) const; + void DownloadLimitSpeedChanged(const QString & speed) const; + +public Q_SLOTS: + void CallQueued(const QString &callName, const QList &args); + +private Q_SLOTS: + void onPendingCallFinished(QDBusPendingCallWatcher *w); + void onPropertyChanged(const QString& interfaceName, + const QVariantMap& changedProperties, + const QStringList& invalidatedProperties); + +private: + UpdateAssistantPrivate *d_ptr; +}; + +#endif // UPDATEASSISTANT_H diff --git a/src/common/dbus/updatedbusproxy.cpp b/src/common/dbus/updatedbusproxy.cpp index c6cd74f6a..436b6ba18 100644 --- a/src/common/dbus/updatedbusproxy.cpp +++ b/src/common/dbus/updatedbusproxy.cpp @@ -207,6 +207,16 @@ QString UpdateDBusProxy::updateStatus() return qvariant_cast(m_managerInter->property("UpdateStatus")); } +bool UpdateDBusProxy::p2PUpdateEnable() +{ + return qvariant_cast(m_managerInter->property("P2PUpdateEnable")); +} + +bool UpdateDBusProxy::p2PUpdateSupport() +{ + return qvariant_cast(m_managerInter->property("P2PUpdateSupport")); +} + bool UpdateDBusProxy::immutableAutoRecovery() { return qvariant_cast(m_managerInter->property("ImmutableAutoRecovery")); @@ -387,6 +397,36 @@ QDBusPendingReply UpdateDBusProxy::GetUpdateLogs(int updateType) return m_managerInter->asyncCallWithArgumentList(QStringLiteral("GetUpdateLogs"), argumentList); } +QDBusPendingReply UpdateDBusProxy::SetUpgradeDeliveryEnable(bool enable) +{ + qCDebug(logCommon) << "Setting upgrade delivery enable :" << enable; + QList argumentList; + argumentList << QVariant::fromValue(enable); + return m_updateInter->asyncCallWithArgumentList(QStringLiteral("SetP2PUpdateEnable"), argumentList); +} + +QDBusPendingReply UpdateDBusProxy::SetUpgradeDeliveryDownloadSpeedLimit(const QString& downloadLimit) +{ + qCDebug(logCommon) << "Setting upgrade delivery download speed limit : " << downloadLimit; + QList argumentList; + argumentList << QVariant::fromValue(downloadLimit); + return m_updateInter->asyncCallWithArgumentList(QStringLiteral("SetDeliveryDownloadSpeedLimit"), argumentList); +} + +QDBusPendingReply UpdateDBusProxy::SetUpgradeDeliveryUploadSpeedLimit(const QString& uploadLimit) +{ + qCDebug(logCommon) << "Setting upgrade delivery upload speed limit : " << uploadLimit; + QList argumentList; + argumentList << QVariant::fromValue(uploadLimit); + return m_updateInter->asyncCallWithArgumentList(QStringLiteral("SetDeliveryUploadSpeedLimit"), argumentList); +} + +QDBusPendingReply UpdateDBusProxy::ClearUpgradeDeliveryCache() +{ + qCDebug(logCommon) << "Clearing upgrade delivery cache"; + return m_updateInter->asyncCall(QStringLiteral("CleanTransmissionFiles")); +} + QDBusPendingReply UpdateDBusProxy::SetIdleDownloadConfig(const QString& config) { qCDebug(logCommon) << "Setting idle download config:" << config; diff --git a/src/common/dbus/updatedbusproxy.h b/src/common/dbus/updatedbusproxy.h index 8c15215da..32263948d 100644 --- a/src/common/dbus/updatedbusproxy.h +++ b/src/common/dbus/updatedbusproxy.h @@ -70,6 +70,12 @@ class UpdateDBusProxy : public QObject Q_PROPERTY(bool ImmutableAutoRecovery READ immutableAutoRecovery NOTIFY ImmutableAutoRecoveryChanged) bool immutableAutoRecovery(); + Q_PROPERTY(bool P2PUpdateEnable READ p2PUpdateEnable NOTIFY P2PUpdateEnableChanged) + bool p2PUpdateEnable(); + + Q_PROPERTY(bool P2PUpdateSupport READ p2PUpdateSupport NOTIFY P2PUpdateSupportChanged) + bool p2PUpdateSupport(); + QString hardwareId(); quint64 checkUpdateMode(); @@ -103,6 +109,10 @@ class UpdateDBusProxy : public QObject QDBusPendingReply SetDownloadSpeedLimit(const QString &config); QDBusPendingReply QueryAllSizeWithSource(int updateType); QDBusPendingReply GetUpdateLogs(int updateType); + QDBusPendingReply SetUpgradeDeliveryEnable(bool enable); + QDBusPendingReply SetUpgradeDeliveryDownloadSpeedLimit(const QString& downloadLimit); + QDBusPendingReply SetUpgradeDeliveryUploadSpeedLimit(const QString& uploadLimit); + QDBusPendingReply ClearUpgradeDeliveryCache(); QDBusPendingReply SetIdleDownloadConfig(const QString &config); QDBusPendingReply PrepareDistUpgradePartly(int updateMode); QDBusPendingReply fixError(const QString &errorType); @@ -137,6 +147,7 @@ class UpdateDBusProxy : public QObject void AutoInstallUpdatesChanged(bool value) const; void AutoInstallUpdateTypeChanged(qulonglong value) const; void MirrorSourceChanged(const QString &value) const; + void UpgradeDeliveryEnabledChanged(bool value) const; void AutoCheckUpdatesChanged(bool value) const; void ClassifiedUpdatablePackagesChanged(LastoreUpdatePackagesInfo value) const; @@ -146,6 +157,8 @@ class UpdateDBusProxy : public QObject void UpdateModeChanged(qulonglong value) const; void UpdateStatusChanged(QString value) const; void ImmutableAutoRecoveryChanged(bool value) const; + void P2PUpdateEnableChanged(bool value) const; + void P2PUpdateSupportChanged(bool value) const; void managerInterServiceValidChanged(bool value) const; // Power diff --git a/src/dcc-update-plugin/operation/updatedatastructs.h b/src/dcc-update-plugin/operation/updatedatastructs.h index abd00dcae..d82ad99c5 100644 --- a/src/dcc-update-plugin/operation/updatedatastructs.h +++ b/src/dcc-update-plugin/operation/updatedatastructs.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2011 - 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2011-2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: LGPL-3.0-or-later @@ -65,12 +65,14 @@ struct IdleDownloadConfig { struct DownloadSpeedLimitConfig { bool downloadSpeedLimitEnabled = false; QString limitSpeed = "10240"; + bool isOnlineSpeedLimit = false; QString toJson() const { QJsonObject obj; obj.insert("DownloadSpeedLimitEnabled", downloadSpeedLimitEnabled); obj.insert("LimitSpeed", limitSpeed); + obj.insert("IsOnlineSpeedLimit", isOnlineSpeedLimit); QJsonDocument doc; doc.setObject(obj); return doc.toJson(); @@ -89,11 +91,130 @@ struct DownloadSpeedLimitConfig { QJsonObject obj = doc.object(); config.downloadSpeedLimitEnabled = obj.contains("DownloadSpeedLimitEnabled") ? obj.value("DownloadSpeedLimitEnabled").toBool() : false; config.limitSpeed = obj.contains("LimitSpeed") ? obj.value("LimitSpeed").toString() : "10240"; + config.isOnlineSpeedLimit = obj.contains("IsOnlineSpeedLimit") ? obj.value("IsOnlineSpeedLimit").toBool() : false; return config; } }; +/** + * @brief 通过lastore修改upgrade服务限速配置的数据结构 + */ +struct LastoreUpgradeSpeedLimitConfig { + bool speedLimitEnabled = false; + QString limitSpeed = "10240"; + bool isOnlineSpeedLimit = false; + + QString toJson() const + { + QJsonObject obj; + obj.insert("SpeedLimitEnabled", speedLimitEnabled); + obj.insert("LimitSpeed", limitSpeed); + obj.insert("IsOnlineSpeedLimit", isOnlineSpeedLimit); + QJsonDocument doc; + doc.setObject(obj); + return doc.toJson(); + } + + static LastoreUpgradeSpeedLimitConfig fromJson(const QByteArray& configStr) + { + LastoreUpgradeSpeedLimitConfig config; + QJsonParseError jsonParseError; + const QJsonDocument doc = QJsonDocument::fromJson(configStr, &jsonParseError); + if (jsonParseError.error != QJsonParseError::NoError || doc.isEmpty()) { + qWarning() << "Parse download speed limit config failed: " << jsonParseError.errorString(); + return config; + } + + QJsonObject obj = doc.object(); + config.speedLimitEnabled = obj.contains("SpeedLimitEnabled") ? obj.value("SpeedLimitEnabled").toBool() : false; + config.limitSpeed = obj.contains("LimitSpeed") ? obj.value("LimitSpeed").toString() : "10240"; + config.isOnlineSpeedLimit = obj.contains("IsOnlineSpeedLimit") ? obj.value("IsOnlineSpeedLimit").toBool() : false; + + return config; + } +}; + +/** + * @brief 传递优化upgrade服务上传下载限速配置 + */ +struct UpgradeSpeedLimitConfig { + int currentRate = 102400; + int limitRate = 102400; + int limitType = 0; // 限速类型 + int rateType = 0; // 速率类型 + int speed = 10240; // 速度值 + QDateTime startTime; // 开始时间 + QDateTime endTime; // 结束时间 + + bool ifInOnlineLimit() const { + if (limitType != 3) { + return false; + } + + if (!startTime.isValid() || !endTime.isValid()) { + return false; + } + + QDateTime currentTime = QDateTime::currentDateTime(); + return currentTime >= startTime && currentTime <= endTime; + } + + bool shouldLimitRate() const { + if (ifInOnlineLimit()) { + return true; + } + return limitType == 1; + } + + QString toJson() const + { + QJsonObject obj; + obj.insert("CurrentRate", currentRate); + obj.insert("LimitRate", limitRate); + obj.insert("LimitType", limitType); + obj.insert("RateType", rateType); + obj.insert("Speed", speed); + obj.insert("StartTime", startTime.toString(Qt::ISODate)); + obj.insert("EndTime", endTime.toString(Qt::ISODate)); + + QJsonDocument doc; + doc.setObject(obj); + return doc.toJson(); + } + + static UpgradeSpeedLimitConfig fromJson(const QByteArray& configStr) + { + UpgradeSpeedLimitConfig config; + QJsonParseError jsonParseError; + const QJsonDocument doc = QJsonDocument::fromJson(configStr, &jsonParseError); + + if (jsonParseError.error != QJsonParseError::NoError || doc.isEmpty()) { + // qCWarning(logDccUpdatePlugin) << "Parse upgrade speed limit config failed: " << jsonParseError.errorString(); + return config; + } + + QJsonObject obj = doc.object(); + config.currentRate = obj.contains("CurrentRate") ? obj.value("CurrentRate").toInt() : 10240; + config.limitRate = obj.contains("LimitRate") ? obj.value("LimitRate").toInt() : 10240; + config.limitType = obj.contains("LimitType") ? obj.value("LimitType").toInt() : 0; + config.rateType = obj.contains("RateType") ? obj.value("RateType").toInt() : 0; + config.speed = obj.contains("Speed") ? obj.value("Speed").toInt() : 10240; + + if (obj.contains("StartTime") && obj.value("StartTime").isString()) { + QString startTimeStr = obj.value("StartTime").toString(); + config.startTime = QDateTime::fromString(startTimeStr, Qt::ISODate); + } + + if (obj.contains("EndTime") && obj.value("EndTime").isString()) { + QString endTimeStr = obj.value("EndTime").toString(); + config.endTime = QDateTime::fromString(endTimeStr, Qt::ISODate); + } + + return config; + } +}; + struct LastoreDaemonUpdateStatus { UpdatesStatus backupStatus = UpdatesStatus::Default; UpdateErrorType backupError = UpdateErrorType::NoError; diff --git a/src/dcc-update-plugin/operation/updatelistmodel.cpp b/src/dcc-update-plugin/operation/updatelistmodel.cpp index b5fbdc7a1..bd040b991 100644 --- a/src/dcc-update-plugin/operation/updatelistmodel.cpp +++ b/src/dcc-update-plugin/operation/updatelistmodel.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024-2026 UnionTech Software Technology Co., Ltd. // SPDX-License-Identifier: GPL-3.0-or-later #include "updatelistmodel.h" #include diff --git a/src/dcc-update-plugin/operation/updatelistmodel.h b/src/dcc-update-plugin/operation/updatelistmodel.h index 1d26b8b37..51d3c9f53 100644 --- a/src/dcc-update-plugin/operation/updatelistmodel.h +++ b/src/dcc-update-plugin/operation/updatelistmodel.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024-2026 UnionTech Software Technology Co., Ltd. // SPDX-License-Identifier: GPL-3.0-or-later #ifndef UPDATELISTMODEL_H #define UPDATELISTMODEL_H diff --git a/src/dcc-update-plugin/operation/updatemodel.cpp b/src/dcc-update-plugin/operation/updatemodel.cpp index 861e12dea..8a1a538b8 100644 --- a/src/dcc-update-plugin/operation/updatemodel.cpp +++ b/src/dcc-update-plugin/operation/updatemodel.cpp @@ -1113,6 +1113,11 @@ bool UpdateModel::downloadSpeedLimitEnabled() const return DownloadSpeedLimitConfig::fromJson(m_speedLimitConfig).downloadSpeedLimitEnabled; } +bool UpdateModel::downloadIsOnlineSpeedLimit() const +{ + return DownloadSpeedLimitConfig::fromJson(m_speedLimitConfig).isOnlineSpeedLimit; +} + QString UpdateModel::downloadSpeedLimitSize() const { return DownloadSpeedLimitConfig::fromJson(m_speedLimitConfig).limitSpeed; @@ -1133,6 +1138,95 @@ void UpdateModel::setSpeedLimitConfig(const QByteArray& config) Q_EMIT downloadSpeedLimitConfigChanged(); } +void UpdateModel::setUpgradeDownloadSpeedLimitConfig(const QByteArray& config) +{ + qCInfo(logDccUpdatePlugin) << "setUpgradeDownloadSpeedLimitConfig" << config; + // if (m_upgradeDownloadSpeedLimitConfig == config) + // return; + + m_upgradeDownloadSpeedLimitConfig = config; + Q_EMIT upgradeDownloadSpeedLimitConfigChanged(); +} + +QString UpdateModel::upgradeDownloadSpeedCurrentRate() const +{ + qCInfo(logDccUpdatePlugin) << "upgradeDownloadSpeedCurrentRate " << UpgradeSpeedLimitConfig::fromJson(m_upgradeDownloadSpeedLimitConfig).currentRate; + return QString::number(UpgradeSpeedLimitConfig::fromJson(m_upgradeDownloadSpeedLimitConfig).currentRate / 1024); +} + +QString UpdateModel::upgradeDownloadSpeedLimitRate() const +{ + qCInfo(logDccUpdatePlugin) << "upgradeDownloadSpeedCurrentRate " << UpgradeSpeedLimitConfig::fromJson(m_upgradeDownloadSpeedLimitConfig).currentRate; + return QString::number(UpgradeSpeedLimitConfig::fromJson(m_upgradeDownloadSpeedLimitConfig).limitRate / 1024); +} + +bool UpdateModel::upgradeDownloadSpeedEnable() const +{ + qCInfo(logDccUpdatePlugin) << "xiongbo111 upgradeDownloadSpeedCurrentRate " << m_upgradeDownloadSpeedLimitConfig; + return UpgradeSpeedLimitConfig::fromJson(m_upgradeDownloadSpeedLimitConfig).shouldLimitRate(); +} + +bool UpdateModel::upgradeDownloadSpeedIsOnline() const +{ + return UpgradeSpeedLimitConfig::fromJson(m_upgradeDownloadSpeedLimitConfig).ifInOnlineLimit(); +} + +UpgradeSpeedLimitConfig UpdateModel::upgradeDownloadSpeedLimitConfig() const +{ + return UpgradeSpeedLimitConfig::fromJson(m_upgradeDownloadSpeedLimitConfig); +} + +void UpdateModel::setUpgradeUploadSpeedLimitConfig(const QByteArray& config) +{ + m_upgradeUploadSpeedLimitConfig = config; + Q_EMIT upgradeUploadSpeedLimitConfigChanged(); +} + +QString UpdateModel::upgradeUploadSpeedCurrentRate() const +{ + return QString::number(UpgradeSpeedLimitConfig::fromJson(m_upgradeUploadSpeedLimitConfig).currentRate / 1024); +} + +QString UpdateModel::upgradeUploadSpeedLimitRate() const +{ + return QString::number(UpgradeSpeedLimitConfig::fromJson(m_upgradeUploadSpeedLimitConfig).limitRate / 1024); +} + +bool UpdateModel::upgradeUploadSpeedEnable() const +{ + return UpgradeSpeedLimitConfig::fromJson(m_upgradeUploadSpeedLimitConfig).shouldLimitRate(); +} + +bool UpdateModel::upgradeUploadSpeedIsOnline() const +{ + return UpgradeSpeedLimitConfig::fromJson(m_upgradeUploadSpeedLimitConfig).ifInOnlineLimit(); +} + +UpgradeSpeedLimitConfig UpdateModel::upgradeUploadSpeedLimitConfig() const +{ + return UpgradeSpeedLimitConfig::fromJson(m_upgradeUploadSpeedLimitConfig); +} + +bool UpdateModel::upgradeDeliveryEnable() const +{ + return m_isUpgradeDeliveryEnable; +} + +void UpdateModel::setUpgradeDeliveryEnable(bool enable) +{ + m_isUpgradeDeliveryEnable = enable; + Q_EMIT upgradeDeliveryEnableChanged(); +} + +void UpdateModel::refreshUpgradeDeliveryEnable(bool enable) +{ + m_isUpgradeDeliveryEnable = enable; + if (enable) { + Q_EMIT upgradeDownloadSpeedLimitConfigChanged(); + Q_EMIT upgradeUploadSpeedLimitConfigChanged(); + } +} + void UpdateModel::setAutoDownloadUpdates(bool autoDownloadUpdates) { qCDebug(logDccUpdatePlugin) << "Set auto download updates:" << autoDownloadUpdates; diff --git a/src/dcc-update-plugin/operation/updatemodel.h b/src/dcc-update-plugin/operation/updatemodel.h index 6a5d8f4a7..52c9997c7 100644 --- a/src/dcc-update-plugin/operation/updatemodel.h +++ b/src/dcc-update-plugin/operation/updatemodel.h @@ -73,6 +73,7 @@ class UpdateModel : public QObject Q_PROPERTY(bool thirdPartyUpdate READ thirdPartyUpdate NOTIFY updateModeChanged FINAL) Q_PROPERTY(bool updateModeDisabled READ updateModeDisabled NOTIFY updateModeChanged FINAL) Q_PROPERTY(bool downloadSpeedLimitEnabled READ downloadSpeedLimitEnabled NOTIFY downloadSpeedLimitConfigChanged FINAL) + Q_PROPERTY(bool downloadIsOnlineSpeedLimit READ downloadIsOnlineSpeedLimit NOTIFY downloadIsOnlineSpeedLimitChanged FINAL) Q_PROPERTY(QString downloadSpeedLimitSize READ downloadSpeedLimitSize NOTIFY downloadSpeedLimitConfigChanged FINAL) Q_PROPERTY(bool autoDownloadUpdates READ autoDownloadUpdates WRITE setAutoDownloadUpdates NOTIFY autoDownloadUpdatesChanged FINAL) Q_PROPERTY(bool idleDownloadEnabled READ idleDownloadEnabled NOTIFY idleDownloadConfigChanged FINAL) @@ -82,7 +83,13 @@ class UpdateModel : public QObject Q_PROPERTY(bool autoCleanCache READ autoCleanCache WRITE setAutoCleanCache NOTIFY autoCleanCacheChanged FINAL) Q_PROPERTY(bool smartMirrorSwitch READ smartMirrorSwitch WRITE setSmartMirrorSwitch NOTIFY smartMirrorSwitchChanged FINAL) Q_PROPERTY(TestingChannelStatus testingChannelStatus READ testingChannelStatus WRITE setTestingChannelStatus NOTIFY testingChannelStatusChanged FINAL) - + Q_PROPERTY(QString upgradeDownloadSpeedCurrentRate READ upgradeDownloadSpeedCurrentRate NOTIFY upgradeDownloadSpeedLimitConfigChanged FINAL) + Q_PROPERTY(QString upgradeDownloadSpeedLimitRate READ upgradeDownloadSpeedLimitRate NOTIFY upgradeDownloadSpeedLimitConfigChanged FINAL) + Q_PROPERTY(bool upgradeDownloadSpeedEnable READ upgradeDownloadSpeedEnable NOTIFY upgradeDownloadSpeedLimitConfigChanged FINAL) + Q_PROPERTY(QString upgradeUploadSpeedCurrentRate READ upgradeUploadSpeedCurrentRate NOTIFY upgradeUploadSpeedLimitConfigChanged FINAL) + Q_PROPERTY(QString upgradeUploadSpeedLimitRate READ upgradeUploadSpeedLimitRate NOTIFY upgradeUploadSpeedLimitConfigChanged FINAL) + Q_PROPERTY(bool upgradeUploadSpeedEnable READ upgradeUploadSpeedEnable NOTIFY upgradeUploadSpeedLimitConfigChanged FINAL) + Q_PROPERTY(bool upgradeDeliveryEnable READ upgradeDeliveryEnable NOTIFY upgradeDeliveryEnableChanged FINAL) Q_PROPERTY(UpdateHistoryModel *historyModel READ historyModel NOTIFY historyModelChanged FINAL) @@ -262,10 +269,28 @@ class UpdateModel : public QObject void setUpdateItemEnabled(); bool downloadSpeedLimitEnabled() const; + bool downloadIsOnlineSpeedLimit() const; QString downloadSpeedLimitSize() const; DownloadSpeedLimitConfig speedLimitConfig() const; void setSpeedLimitConfig(const QByteArray &config); + void setUpgradeDownloadSpeedLimitConfig(const QByteArray& config); + QString upgradeDownloadSpeedCurrentRate() const; + QString upgradeDownloadSpeedLimitRate() const; + bool upgradeDownloadSpeedEnable() const; + bool upgradeDownloadSpeedIsOnline() const; + UpgradeSpeedLimitConfig upgradeDownloadSpeedLimitConfig() const; + void setUpgradeUploadSpeedLimitConfig(const QByteArray& config); + QString upgradeUploadSpeedCurrentRate() const; + QString upgradeUploadSpeedLimitRate() const; + bool upgradeUploadSpeedEnable() const; + bool upgradeUploadSpeedIsOnline() const; + UpgradeSpeedLimitConfig upgradeUploadSpeedLimitConfig() const; + bool upgradeDeliveryEnable() const; + + void setUpgradeDeliveryEnable(bool enable); + void refreshUpgradeDeliveryEnable(bool enable); + bool autoDownloadUpdates() const { return m_autoDownloadUpdates; } void setAutoDownloadUpdates(bool autoDownloadUpdates); @@ -383,7 +408,11 @@ public slots: void securityUpdateEnabledChanged(bool enable); void thirdPartyUpdateEnabledChanged(bool enable); void updateModeChanged(quint64 updateMode); + void upgradeDeliveryEnableChanged(); void downloadSpeedLimitConfigChanged(); + void downloadIsOnlineSpeedLimitChanged(); + void upgradeDownloadSpeedLimitConfigChanged(); + void upgradeUploadSpeedLimitConfigChanged(); void autoDownloadUpdatesChanged(bool autoDownloadUpdates); void idleDownloadConfigChanged(); void updateNotifyChanged(const bool notify); @@ -457,6 +486,9 @@ public slots: bool m_thirdPartyUpdateEnabled; quint64 m_updateMode; QByteArray m_speedLimitConfig; + bool m_isUpgradeDeliveryEnable; + QByteArray m_upgradeDownloadSpeedLimitConfig; + QByteArray m_upgradeUploadSpeedLimitConfig; bool m_autoDownloadUpdates; IdleDownloadConfig m_idleDownloadConfig; bool m_updateNotify; diff --git a/src/dcc-update-plugin/operation/updatework.cpp b/src/dcc-update-plugin/operation/updatework.cpp index 9b350cf79..c67fa3540 100644 --- a/src/dcc-update-plugin/operation/updatework.cpp +++ b/src/dcc-update-plugin/operation/updatework.cpp @@ -29,7 +29,6 @@ #include Q_DECLARE_LOGGING_CATEGORY(logDccUpdatePlugin) - using namespace DCC_NAMESPACE; const QString TestingChannel = "testing Channel"; @@ -129,6 +128,7 @@ UpdateWorker::UpdateWorker(UpdateModel* model, QObject* parent) , m_backupJob(nullptr) , m_installPackageJob(nullptr) , m_removePackageJob(nullptr) + , m_updateAssistant(nullptr) { qCDebug(logDccUpdatePlugin) << "Initializing UpdateWorker"; qRegisterMetaType("UpdatesStatus"); @@ -209,6 +209,15 @@ void UpdateWorker::initConnect() // m_model->setP2PUpdateEnabled(DConfigWatcher::instance()->getValue(DConfigWatcher::update, configName).toBool()); } }); + + m_updateAssistant = new UpdateAssistant("org.deepin.upgradedelivery", "/org/deepin/upgradedelivery", QDBusConnection::systemBus(), this); + connect(m_updateInter, &UpdateDBusProxy::AutoDownloadUpdatesChanged, m_model, &UpdateModel::setAutoDownloadUpdates); + connect(m_updateInter, &UpdateDBusProxy::MirrorSourceChanged, m_model, &UpdateModel::setDefaultMirror); + QDBusConnection::systemBus().connect("org.deepin.upgradedelivery", + "/org/deepin/upgradedelivery", + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + m_model, SLOT(onUpdatePropertiesChanged(QString, QVariantMap, QStringList))); } void UpdateWorker::activate() @@ -221,6 +230,7 @@ void UpdateWorker::activate() updateSystemVersion(); refreshLastTimeAndCheckCircle(); initTestingChannel(); + refreshUpgradeDeliveryInfo(); m_model->setIsPrivateUpdate(DConfigHelper::instance()->getConfig("org.deepin.dde.lastore", "org.deepin.dde.lastore", "","intranet-update", false).toString() == "true"); m_model->setUpdateMode(m_updateInter->updateMode()); @@ -905,6 +915,7 @@ void UpdateWorker::setDownloadSpeedLimitEnabled(bool enable) qCDebug(logDccUpdatePlugin) << "Set download speed limit enabled:" << enable; auto config = m_model->speedLimitConfig(); config.downloadSpeedLimitEnabled = enable; + config.isOnlineSpeedLimit = false; // dbus返回需要1s,导致界面更新慢,这里直接先更新model m_model->setSpeedLimitConfig(config.toJson().toUtf8()); setDownloadSpeedLimitConfig(config.toJson()); @@ -915,6 +926,7 @@ void UpdateWorker::setDownloadSpeedLimitSize(const QString& size) qCDebug(logDccUpdatePlugin) << "set download speed limit size" << size; auto config = m_model->speedLimitConfig(); config.limitSpeed = size; + config.isOnlineSpeedLimit = false; setDownloadSpeedLimitConfig(config.toJson()); } @@ -1271,6 +1283,14 @@ void UpdateWorker::initTestingChannel() }); } +void UpdateWorker::refreshUpgradeDeliveryInfo() +{ + qCDebug(logDccUpdatePlugin) << "Refresh upgrade delivery info"; + m_model->setUpgradeDownloadSpeedLimitConfig(m_updateAssistant->downloadLimitSpeed().toUtf8()); + m_model->setUpgradeUploadSpeedLimitConfig(m_updateAssistant->uploadLimitSpeed().toUtf8()); + m_model->setUpgradeDeliveryEnable(m_updateInter->p2pUpdateEnable()); +} + void UpdateWorker::checkTestingChannelStatus() { // Leave page @@ -1706,3 +1726,94 @@ void UpdateWorker::onRemovePackageStatusChanged(const QString& value) } } +//更新传递总开关 +void UpdateWorker::setUpgradeDeliveryEnabled(bool enabled) +{ + if (enabled == m_model->upgradeDeliveryEnable()) { + return; + } + + qCDebug(logDccUpdatePlugin) << "Set update assistant service, enabled: " << enabled; + auto func_set_success = [=](bool enabled) { + qCDebug(logDccUpdatePlugin) << "Set update assistant service succeed, enabled " << enabled; + if (m_updateAssistant && m_model) { + m_model->setUpgradeDownloadSpeedLimitConfig(m_updateAssistant->downloadLimitSpeed().toUtf8()); + m_model->setUpgradeUploadSpeedLimitConfig(m_updateAssistant->uploadLimitSpeed().toUtf8()); + m_model->setUpgradeDeliveryEnable(enabled); + } + }; + auto watcher = new QDBusPendingCallWatcher(m_updateInter->SetUpgradeDeliveryEnable(enabled), this); + connect(watcher, &QDBusPendingCallWatcher::finished, [this, watcher, enabled, func_set_success] { + watcher->deleteLater(); + if (watcher->isError()) { + qCWarning(logDccUpdatePlugin) << "Set update assistant service failed, enabled " << enabled << " error: " << watcher->error().message(); + m_model->setUpgradeDeliveryEnable(!enabled); + return; + } + func_set_success(enabled); + }); +} + +//设置更新传递下载限速 +void UpdateWorker::setUpgradeDeliveryDownloadLimitSpeed(const QString& speed, bool enable) +{ + LastoreUpgradeSpeedLimitConfig downloadSpeedLimitConfig; + downloadSpeedLimitConfig.isOnlineSpeedLimit = false; + downloadSpeedLimitConfig.speedLimitEnabled = enable; + downloadSpeedLimitConfig.limitSpeed = speed; + qCInfo(logDccUpdatePlugin) << "Set upgrade download speed limit: " << downloadSpeedLimitConfig.toJson(); + auto watcher = new QDBusPendingCallWatcher(m_updateInter->SetUpgradeDeliveryDownloadSpeedLimit(downloadSpeedLimitConfig.toJson()), this); + connect(watcher, &QDBusPendingCallWatcher::finished, [watcher, this] { + watcher->deleteLater(); + if (watcher->isError()) { + qCWarning(logDccUpdatePlugin) << "Set upgrade download speed limit config error: " << watcher->error().message(); + getUpgradeDeliveryDownloadLimitSpeed(); + } + }); +} + +//设置更新传递上传限速 +void UpdateWorker::setUpgradeDeliveryUploadLimitSpeed(const QString& speed, bool enable) +{ + LastoreUpgradeSpeedLimitConfig uploadSpeedLimitConfig; + uploadSpeedLimitConfig.isOnlineSpeedLimit = false; + uploadSpeedLimitConfig.speedLimitEnabled = enable; + uploadSpeedLimitConfig.limitSpeed = speed; + qCInfo(logDccUpdatePlugin) << "Set upgrade upload speed limit: " << uploadSpeedLimitConfig.toJson(); + auto watcher = new QDBusPendingCallWatcher(m_updateInter->SetUpgradeDeliveryUploadSpeedLimit(uploadSpeedLimitConfig.toJson()), this); + connect(watcher, &QDBusPendingCallWatcher::finished, [watcher, this] { + watcher->deleteLater(); + if (watcher->isError()) { + qCWarning(logDccUpdatePlugin) << "Set upgrade upload speed limit config error: " << watcher->error().message(); + getUpgradeDeliveryUploadLimitSpeed(); + } + }); +} + +void UpdateWorker::getUpgradeDeliveryDownloadLimitSpeed() +{ + if (m_updateAssistant && m_model) { + m_model->setUpgradeDownloadSpeedLimitConfig(m_updateAssistant->downloadLimitSpeed().toUtf8()); + } +} + +void UpdateWorker::getUpgradeDeliveryUploadLimitSpeed() +{ + if (m_updateAssistant && m_model) { + m_model->setUpgradeUploadSpeedLimitConfig(m_updateAssistant->uploadLimitSpeed().toUtf8()); + } +} + +void UpdateWorker::cleanUpgradeDeliveryCache() +{ + qCDebug(logDccUpdatePlugin) << "Clean update assistant cache succeed"; + auto watcher = new QDBusPendingCallWatcher(m_updateInter->ClearUpgradeDeliveryCache(), this); + connect(watcher, &QDBusPendingCallWatcher::finished, [this, watcher] { + watcher->deleteLater(); + if (watcher->isError()) { + qCWarning(logDccUpdatePlugin) << "Clean update assistant cache failed"; + return; + } + }); +} + diff --git a/src/dcc-update-plugin/operation/updatework.h b/src/dcc-update-plugin/operation/updatework.h index 376e4cc5a..35c623ee0 100644 --- a/src/dcc-update-plugin/operation/updatework.h +++ b/src/dcc-update-plugin/operation/updatework.h @@ -10,6 +10,7 @@ #include "common/common/logwatcherhelper.h" #include "common/dbus/updatedbusproxy.h" #include "common/dbus/updatejobdbusproxy.h" +#include "common/dbus/updateassistant.h" #include #include @@ -73,7 +74,13 @@ class UpdateWorker : public QObject Q_INVOKABLE void setIdleDownloadEnabled(bool enable); Q_INVOKABLE void setIdleDownloadBeginTime(QString time); Q_INVOKABLE void setIdleDownloadEndTime(QString time); - void setIdleDownloadConfig(const IdleDownloadConfig& config); + Q_INVOKABLE void setUpgradeDeliveryEnabled(bool enabled); + Q_INVOKABLE void setUpgradeDeliveryDownloadLimitSpeed(const QString& speed, bool enable); + Q_INVOKABLE void setUpgradeDeliveryUploadLimitSpeed(const QString& speed, bool enable); + Q_INVOKABLE void getUpgradeDeliveryDownloadLimitSpeed(); + Q_INVOKABLE void getUpgradeDeliveryUploadLimitSpeed(); + Q_INVOKABLE void cleanUpgradeDeliveryCache(); + Q_INVOKABLE void setIdleDownloadConfig(const IdleDownloadConfig& config); QString adjustTimeFunc(const QString& start, const QString& end, bool returnEndTime); // 更新设置-更新提醒 @@ -101,6 +108,7 @@ class UpdateWorker : public QObject void setInstallPackageJob(const QString& jobPath); void setRemovePackageJob(const QString& jobPath); QString getServiceUrlByRegion(); + void refreshUpgradeDeliveryInfo(); Q_INVOKABLE bool openUrl(const QString& url); Q_INVOKABLE void onRequestRetry(int type, int updateTypes); @@ -133,6 +141,7 @@ public Q_SLOTS: Dtk::Core::DConfig *m_lastoreDConfig; UpdateModel* m_model; UpdateDBusProxy *m_updateInter; + UpdateAssistant* m_updateAssistant; QTimer *m_lastoreHeartBeatTimer; // lastore-daemon 心跳信号,防止lastore-daemon自动退出 LogWatcherHelper *m_logWatcherHelper; diff --git a/src/dcc-update-plugin/operation/utils.h b/src/dcc-update-plugin/operation/utils.h index 44ad9696b..65e7448d2 100644 --- a/src/dcc-update-plugin/operation/utils.h +++ b/src/dcc-update-plugin/operation/utils.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2011 - 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2011-2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: LGPL-3.0-or-later diff --git a/src/dcc-update-plugin/qml/UpdateControl.qml b/src/dcc-update-plugin/qml/UpdateControl.qml index c2c4ee6d1..156336e40 100644 --- a/src/dcc-update-plugin/qml/UpdateControl.qml +++ b/src/dcc-update-plugin/qml/UpdateControl.qml @@ -118,115 +118,14 @@ ColumnLayout { Repeater { model: btnActions - delegate: Loader { - id: buttonLoader + delegate: D.Button { Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + text: modelData + font: D.DTK.fontManager.t6 visible: modelData.length !== 0 && !initAnimation.visible enabled: updatelistModel.model.isUpdateEnable - - Connections { - target: dccData.model() - function onIsPrivateUpdateChanged() { - updateButtonType() - } - function onIsUpdateDisabledChanged() { - updateButtonType() - } - function onShowCheckUpdateChanged() { - updateButtonType() - } - } - - Component.onCompleted: { - updateButtonType() - } - - function updateButtonType() { - var model = dccData.model() - if (!model) { - console.log("Model not ready yet") - return - } - if (index === 1) { - buttonLoader.sourceComponent = normalButtonComponent - } else if (model.isPrivateUpdate && - !model.isUpdateDisabled && - !model.showCheckUpdate && - model.preInstallListModel.anyVisible) { - buttonLoader.sourceComponent = dropdownButtonComponent - } else { - // 默认使用普通按钮 - buttonLoader.sourceComponent = normalButtonComponent - } - } - - Component { - id: normalButtonComponent - D.Button { - text: modelData - font: D.DTK.fontManager.t6 - onClicked: { - rootLayout.btnClicked(index, updateListModels.getAllUpdateType()) - } - } - } - - Component { - id: dropdownButtonComponent - Item { - implicitWidth: button.implicitWidth - implicitHeight: button.implicitHeight - - enum InstallType { - Now, - Shutdown - } - - property int curType: DropdownButton.InstallType.Now - - signal requestUpdateNow() - signal requestUpdateShutdown() - - D.Button { - id: button - text: qsTr("Install") - - Menu { - id: contextMenu - - MenuItem { - text: qsTr("Install Now") - onClicked: { - dccData.work().setShutdownAndUpgrade(false) - dccData.work().doUpgrade(updateListModels.getAllUpdateType(), true) - } - } - - MenuItem { - text: qsTr("Install Shutdown") - onClicked: { - dccData.work().setShutdownAndUpgrade(true) - } - } - } - - // 点击按钮显示菜单 - onClicked: { - contextMenu.open() - } - } - - // 连接内部信号到外部 - Connections { - target: parent - function onRequestUpdateNow() { - rootLayout.btnClicked(index, 0) - } - function onRequestUpdateShutdown() { - rootLayout.btnClicked(index, 1) - } - } - } + onClicked: { + rootLayout.btnClicked(index, updateListModels.getAllUpdateType()) } } } diff --git a/src/dcc-update-plugin/qml/UpdateMain.qml b/src/dcc-update-plugin/qml/UpdateMain.qml index b1b975f1a..c4db54eac 100644 --- a/src/dcc-update-plugin/qml/UpdateMain.qml +++ b/src/dcc-update-plugin/qml/UpdateMain.qml @@ -292,7 +292,7 @@ DccObject { updateListModels: dccData.model().preInstallListModel updateTitle: qsTr("Update download completed") btnActions: dccData.model().isPrivateUpdate ? [ - qsTr("Install updates"), + qsTr("Install Now"), qsTr("Check Again") ] : [qsTr("Install updates")] updateTips: { @@ -306,8 +306,12 @@ DccObject { onBtnClicked: function(index, updateType) { if (index === 0) { - updateSelectDialog.updateType = updateType - updateSelectDialog.show() + if (dccData.model().isPrivateUpdate) { + dccData.work().doUpgrade(updateListModels.getAllUpdateType(), true) + } else { + updateSelectDialog.updateType = updateType + updateSelectDialog.show() + } } else if (index === 1) { dccData.work().reCheckWithUi(); } @@ -400,7 +404,7 @@ DccObject { description: qsTr("Configure Update settings、Security Updates、Auto Download Updates and Updates Notification") icon: "update_set" weight: 120 - visible: dccData.model().systemActivation && !dccData.model().isPrivateUpdate + visible: dccData.model().systemActivation UpdateSetting {} } diff --git a/src/dcc-update-plugin/qml/UpdateSetting.qml b/src/dcc-update-plugin/qml/UpdateSetting.qml index 2f6ac9768..8652379dc 100644 --- a/src/dcc-update-plugin/qml/UpdateSetting.qml +++ b/src/dcc-update-plugin/qml/UpdateSetting.qml @@ -161,10 +161,221 @@ DccObject { } } + DccObject { + name: "upgradeDeliveryGrp" + parentName: "advancedSettingGroup" + weight: 40 + pageType: DccObject.Item + page: DccGroupView { + height: implicitHeight + 10 + spacing: 0 + } + + DccObject { + name: "upgradeDeliverySwitch" + parentName: "upgradeDeliveryGrp" + displayName: qsTr("Upgrade Delivery") + weight: 10 + enabled: !dccData.model().updateProhibited + pageType: DccObject.Editor + page: D.Switch { + id: upgradeDeliverySwitch + checked: dccData.model().upgradeDeliveryEnable + onCheckedChanged: { + dccData.work().setUpgradeDeliveryEnabled(checked) + } + Connections { + target: dccData.model() + function onUpgradeDeliveryEnableChanged() { + upgradeDeliverySwitch.checked = dccData.model().upgradeDeliveryEnable + } + } + } + } + + DccObject { + name: "upgradeDeliveryDownloadLimitSetting" + parentName: "upgradeDeliveryGrp" + displayName: qsTr("Upgrade Delivery Download Limit Setting") + visible: dccData.model().upgradeDeliveryEnable + weight: 20 + enabled: !dccData.model().upgradeDownloadSpeedIsOnline + pageType: DccObject.Item + property bool isInitializing: true + page: RowLayout { + D.CheckBox { + id: limitCheckBox + Layout.leftMargin: 14 + Layout.fillWidth: true + text: dccObj.displayName + checked: dccData.model().upgradeDownloadSpeedEnable + Connections { + target: dccData.model() + function onUpgradeDownloadSpeedLimitConfigChanged() { + limitCheckBox.checked = dccData.model().upgradeDownloadSpeedEnable + } + } + onCheckedChanged: { + if (dccObj.isInitializing) { + dccObj.isInitializing = false + return + } + if (limitCheckBox.checked) { + dccData.work().setUpgradeDeliveryDownloadLimitSpeed(lineEdit.text, limitCheckBox.checked) + } else { + dccData.work().setUpgradeDeliveryDownloadLimitSpeed("102400", limitCheckBox.checked) + } + } + } + + RowLayout { + spacing: 10 + Layout.topMargin: 5 + Layout.bottomMargin: 5 + Layout.rightMargin: 10 + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + D.LineEdit { + id: lineEdit + maximumLength: 5 + validator: RegularExpressionValidator { regularExpression: /^\d*$/ } + alertText: qsTr("Only numbers between 1-99999 are allowed") + alertDuration: 3000 + clearButton.active: lineEdit.activeFocus && (text.length !== 0) + text: dccData.model().upgradeDownloadSpeedCurrentRate + + onTextChanged: { + // 如果输入不为空且数字为0的情况,需要弹出提示且阻止继续输入 + if (lineEdit.text.length !== 0 && lineEdit.text[0] === "0") { + lineEdit.text = "" + lineEdit.showAlert = true + } else if (lineEdit.showAlert) { + lineEdit.showAlert = false + } + } + onEditingFinished: { + if (lineEdit.text.length === 0) { + lineEdit.text = dccData.model().upgradeDownloadSpeedLimitSize + return + } + dccData.work().setUpgradeDeliveryDownloadLimitSpeed(lineEdit.text, limitCheckBox.checked) + } + Keys.onPressed: { + if (event.key === Qt.Key_Return) { + lineEdit.forceActiveFocus(false); + } + } + Connections { + target: dccData.model() + function onUpgradeDownloadSpeedLimitConfigChanged() { + lineEdit.text = dccData.model().upgradeDownloadSpeedCurrentRate + } + } + } + + D.Label { + text: "KB/S" + } + } + } + } + + DccObject { + name: "upgradeDeliveryUploadLimitSetting" + parentName: "upgradeDeliveryGrp" + displayName: qsTr("Upgrade Delivery Upload Limit Setting") + visible: dccData.model().upgradeDeliveryEnable + weight: 20 + enabled: !dccData.model().upgradeUploadSpeedIsOnline + pageType: DccObject.Item + property bool isInitializing: true + page: RowLayout { + D.CheckBox { + id: limitCheckBox + Layout.leftMargin: 14 + Layout.fillWidth: true + text: dccObj.displayName + checked: dccData.model().upgradeUploadSpeedEnable + Connections { + target: dccData.model() + function onUpgradeUploadSpeedLimitConfigChanged() { + limitCheckBox.checked = dccData.model().upgradeUploadSpeedEnable + } + } + onCheckedChanged: { + console.log("xiongbo111") + if (dccObj.isInitializing) { + console.log("xiongbo222") + dccObj.isInitializing = false + return + } + console.log("xiongbo333") + if (limitCheckBox.checked) { + console.log("xiongbo444") + dccData.work().setUpgradeDeliveryUploadLimitSpeed(lineEdit.text, limitCheckBox.checked) + } else { + console.log("xiongbo555") + dccData.work().setUpgradeDeliveryUploadLimitSpeed("102400", limitCheckBox.checked) + } + } + } + + RowLayout { + spacing: 10 + Layout.topMargin: 5 + Layout.bottomMargin: 5 + Layout.rightMargin: 10 + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + D.LineEdit { + id: lineEdit + maximumLength: 5 + validator: RegularExpressionValidator { regularExpression: /^\d*$/ } + alertText: qsTr("Only numbers between 1-99999 are allowed") + alertDuration: 3000 + clearButton.active: lineEdit.activeFocus && (text.length !== 0) + text: dccData.model().upgradeUploadSpeedCurrentRate + + onTextChanged: { + // 如果输入不为空且数字为0的情况,需要弹出提示且阻止继续输入 + if (lineEdit.text.length !== 0 && lineEdit.text[0] === "0") { + lineEdit.text = "" + lineEdit.showAlert = true + } else if (lineEdit.showAlert) { + lineEdit.showAlert = false + } + } + onEditingFinished: { + if (lineEdit.text.length === 0) { + lineEdit.text = dccData.model().upgradeUploadSpeedLimitSize + return + } + dccData.work().setUpgradeDeliveryUploadLimitSpeed(lineEdit.text, limitCheckBox.checked) + } + Keys.onPressed: { + if (event.key === Qt.Key_Return) { + lineEdit.forceActiveFocus(false); + } + } + Connections { + target: dccData.model() + function onUpgradeUploadSpeedLimitConfigChanged() { + lineEdit.text = dccData.model().upgradeUploadSpeedCurrentRate + } + } + } + + D.Label { + text: "KB/S" + } + } + } + } + } + DccObject { name: "downloadLimitGrp" parentName: "advancedSettingGroup" weight: 40 + enabled: !dccData.model().downloadIsOnlineSpeedLimit pageType: DccObject.Item page: DccGroupView { height: implicitHeight + 10 @@ -179,7 +390,7 @@ DccObject { enabled: !dccData.model().updateProhibited pageType: DccObject.Editor page: D.Switch { - checked: dccData.model().downloadSpeedLimitEnabled + checked: dccData.model().downloadSpeedLimitEnabled || dccData.model().downloadIsOnlineSpeedLimit onCheckedChanged: { dccData.work().setDownloadSpeedLimitEnabled(checked) } @@ -238,6 +449,7 @@ DccObject { name: "autoDownloadGrp" parentName: "advancedSettingGroup" weight: 50 + visible: !dccData.model().isPrivateUpdate pageType: DccObject.Item page: DccGroupView { height: implicitHeight + 10 @@ -263,7 +475,7 @@ DccObject { name: "limitSetting" parentName: "autoDownloadGrp" displayName: qsTr("Download when Inactive") - visible: dccData.model().autoDownloadUpdates + visible: dccData.model().autoDownloadUpdates && !dccData.model().isPrivateUpdate weight: 20 enabled: !dccData.model().updateProhibited && !dccData.model().updateModeDisabled pageType: DccObject.Item @@ -346,6 +558,7 @@ DccObject { displayName: qsTr("Updates Notification") weight: 10 pageType: DccObject.Editor + visible: !dccData.model().isPrivateUpdate enabled: !dccData.model().updateProhibited && !dccData.model().updateModeDisabled page: D.Switch { checked: dccData.model().updateNotify @@ -360,6 +573,7 @@ DccObject { parentName: "advancedSettingGrp" displayName: qsTr("Clear Package Cache") weight: 20 + visible: !dccData.model().isPrivateUpdate pageType: DccObject.Editor page: D.Switch { checked: dccData.model().autoCleanCache @@ -374,6 +588,7 @@ DccObject { parentName: "advancedSettingGrp" displayName: qsTr("Update History") weight: 40 + visible: !dccData.model().isPrivateUpdate backgroundType: DccObject.Normal pageType: DccObject.Editor page: D.Button { @@ -403,7 +618,7 @@ DccObject { DccObject { name: "mirrorSettingGrp" parentName: "advancedSettingGroup" - visible: dccData.model().isCommunitySystem() + visible: dccData.model().isCommunitySystem() && !dccData.model().isPrivateUpdate weight: 70 pageType: DccObject.Item page: DccGroupView { diff --git a/src/dcc-update-plugin/translations/update_zh_CN.ts b/src/dcc-update-plugin/translations/update_zh_CN.ts index 7caeb2780..e87eea2e2 100644 --- a/src/dcc-update-plugin/translations/update_zh_CN.ts +++ b/src/dcc-update-plugin/translations/update_zh_CN.ts @@ -47,18 +47,6 @@ View Update Log 查看更新日志 - - Install - 更新 - - - Install Now - 立即更新 - - - Install Shutdown - 关机后更新 - UpdateHistoryDialog @@ -261,6 +249,10 @@ Install updates 安装更新 + + Install Now + 立即更新 + Check Again 重新检查更新 @@ -430,6 +422,18 @@ Delivers updates for additional repository sources 提供额外添加的仓库源更新内容 + + Upgrade Delivery + 更新传递 + + + Upgrade Delivery Download Limit Setting + 更新传递-下载限速 + + + Upgrade Delivery Upload Limit Setting + 更新传递-上传限速 + Limit Speed 下载限速 diff --git a/src/dcc-update-plugin/translations/update_zh_HK.ts b/src/dcc-update-plugin/translations/update_zh_HK.ts index 7d9e3fba1..037d968cd 100644 --- a/src/dcc-update-plugin/translations/update_zh_HK.ts +++ b/src/dcc-update-plugin/translations/update_zh_HK.ts @@ -47,18 +47,6 @@ View Update Log 查看更新日誌 - - Install - 更新 - - - Install Now - 立即更新 - - - Install Shutdown - 關機後更新 - UpdateHistoryDialog @@ -261,6 +249,10 @@ Install updates 安裝更新 + + Install Now + 立即更新 + Check Again 重新檢查更新 @@ -430,6 +422,18 @@ Delivers updates for additional repository sources 提供額外添加的倉庫源更新內容 + + Upgrade Delivery + 更新傳遞 + + + Upgrade Delivery Download Limit Setting + 更新傳遞-下載限速 + + + Upgrade Delivery Upload Limit Setting + 更新傳遞-上傳限速 + Limit Speed 下載限速 diff --git a/src/dcc-update-plugin/translations/update_zh_TW.ts b/src/dcc-update-plugin/translations/update_zh_TW.ts index 140666917..238dedeeb 100644 --- a/src/dcc-update-plugin/translations/update_zh_TW.ts +++ b/src/dcc-update-plugin/translations/update_zh_TW.ts @@ -47,18 +47,6 @@ View Update Log 檢視更新日誌 - - Install - 更新 - - - Install Now - 立即更新 - - - Install Shutdown - 關機後更新 - UpdateHistoryDialog @@ -261,6 +249,10 @@ Install updates 安裝更新 + + Install Now + 立即更新 + Check Again 重新檢查更新 @@ -430,6 +422,18 @@ Delivers updates for additional repository sources 提供額外新增的倉庫源更新內容 + + Upgrade Delivery + 更新傳遞 + + + Upgrade Delivery Download Limit Setting + 更新傳遞-下載限速 + + + Upgrade Delivery Upload Limit Setting + 更新傳遞-上傳限速 + Limit Speed 下載限速 diff --git a/src/private-lastore-tray/tipswidget.cpp b/src/private-lastore-tray/tipswidget.cpp index dee99773f..68a4a63eb 100644 --- a/src/private-lastore-tray/tipswidget.cpp +++ b/src/private-lastore-tray/tipswidget.cpp @@ -91,7 +91,6 @@ bool TipsWidget::checkRegularlyUpdate() void TipsWidget::onUpdatePropertiesChanged(const QString& interfaceName, const QVariantMap& changedProperties, const QStringList& invalidatedProperties) { Q_UNUSED(invalidatedProperties) - qCInfo(dockUpdatePlugin) << "xiongbo111 onUpdatePropertiesChanged " << interfaceName << " changedProperties " << changedProperties; if (interfaceName == "org.deepin.dde.Lastore1.Job") { if (changedProperties.contains("Speed")) {