diff --git a/.tx/transifex.yaml b/.tx/transifex.yaml index 9e49a36b0..fd75e9017 100644 --- a/.tx/transifex.yaml +++ b/.tx/transifex.yaml @@ -19,5 +19,11 @@ filters: file_format: QT source_language: en_US translation_files_expression: src/ddock-update-plugin/translations/dock-update-plugin_.ts + - filter_type: file + source_file: src/private-lastore-tray/translations/private-lastore-tray_en.ts + file_format: QT + source_language: en_US + translation_files_expression: src/private-lastore-tray/translations/private-lastore-tray_.ts + settings: pr_branch_name: transifex_update_ \ No newline at end of file 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/updateassistant.cpp b/src/common/dbus/updateassistant.cpp new file mode 100644 index 000000000..71736dc78 --- /dev/null +++ b/src/common/dbus/updateassistant.cpp @@ -0,0 +1,65 @@ +// 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() +{ + delete d_ptr; + d_ptr = nullptr; +} + +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..e309feec1 --- /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 e582a2271..8093b0806 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" @@ -207,6 +207,21 @@ QString UpdateDBusProxy::updateStatus() return qvariant_cast(m_managerInter->property("UpdateStatus")); } +bool UpdateDBusProxy::downloadLimitOnChanging() +{ + return qvariant_cast(m_managerInter->property("downloadLimitOnChanging")); +} + +bool UpdateDBusProxy::p2PUpdateEnable() +{ + return qvariant_cast(m_updateInter->property("P2PUpdateEnable")); +} + +bool UpdateDBusProxy::p2PUpdateSupport() +{ + return qvariant_cast(m_updateInter->property("P2PUpdateSupport")); +} + bool UpdateDBusProxy::immutableAutoRecovery() { return qvariant_cast(m_managerInter->property("ImmutableAutoRecovery")); @@ -387,6 +402,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; @@ -428,6 +473,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..a42b37525 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 @@ -67,9 +67,18 @@ class UpdateDBusProxy : public QObject Q_PROPERTY(QString updateStatus READ updateStatus NOTIFY UpdateStatusChanged) QString updateStatus(); + Q_PROPERTY(bool downloadLimitOnChanging READ downloadLimitOnChanging NOTIFY DownloadLimitOnChangingChanged) + bool downloadLimitOnChanging(); + 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,12 +112,16 @@ 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); QDBusPendingCall CheckUpgrade(int checkMode, int checkOrder); QDBusPendingReply GetUpdateDetails(int fd, bool realtime); - + QDBusPendingReply SetShutdownForceUpdate(bool isShutdownUpdate); // Power bool onBattery(); @@ -137,6 +150,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; @@ -145,7 +159,10 @@ class UpdateDBusProxy : public QObject void AutoCleanChanged(bool value) const; void UpdateModeChanged(qulonglong value) const; void UpdateStatusChanged(QString value) const; + void DownloadLimitOnChangingChanged(bool 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/common/dbus/updatejobdbusproxy.cpp b/src/common/dbus/updatejobdbusproxy.cpp index 5e2e40760..9a2af4078 100644 --- a/src/common/dbus/updatejobdbusproxy.cpp +++ b/src/common/dbus/updatejobdbusproxy.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 "updatejobdbusproxy.h" @@ -103,3 +103,8 @@ QString UpdateJobDBusProxy::type() { return qvariant_cast(m_updateJobInter->property("Type")); } + +QString UpdateJobDBusProxy::proto() +{ + return qvariant_cast(m_updateJobInter->property("Proto")); +} \ No newline at end of file diff --git a/src/common/dbus/updatejobdbusproxy.h b/src/common/dbus/updatejobdbusproxy.h index 111014fae..cc3cd4a67 100644 --- a/src/common/dbus/updatejobdbusproxy.h +++ b/src/common/dbus/updatejobdbusproxy.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 UPDATEJOBDBUSPROXY_H @@ -56,6 +56,9 @@ class UpdateJobDBusProxy : public QObject Q_PROPERTY(QString Type READ type NOTIFY TypeChanged) QString type(); + Q_PROPERTY(QString Proto READ proto NOTIFY ProtoChanged) + QString proto(); + inline QString path() const { return m_path; }; signals: @@ -73,6 +76,7 @@ class UpdateJobDBusProxy : public QObject void SpeedChanged(qlonglong value) const; void StatusChanged(const QString & value) const; void TypeChanged(const QString & value) const; + void ProtoChanged(const QString & value) const; private: DDBusInterface *m_updateJobInter; diff --git a/src/dcc-update-plugin/operation/updatedatastructs.h b/src/dcc-update-plugin/operation/updatedatastructs.h index abd00dcae..8582e1ace 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,123 @@ 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 == 2) + return true; + + return false; + } + + 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()) { + 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; @@ -196,6 +310,20 @@ struct LastoreDaemonUpdateStatus { } }; +inline QString transferDeliveryConfigToLastoreDeliveryConfig(const QString& deliveryConfig) +{ + LastoreUpgradeSpeedLimitConfig lastoreDeliveryConfig; + auto config = UpgradeSpeedLimitConfig::fromJson(deliveryConfig.toUtf8()); + lastoreDeliveryConfig.isOnlineSpeedLimit = config.ifInOnlineLimit(); + lastoreDeliveryConfig.speedLimitEnabled = config.shouldLimitRate(); + if (lastoreDeliveryConfig.isOnlineSpeedLimit) { + lastoreDeliveryConfig.limitSpeed = QString::number(config.currentRate); + } else { + lastoreDeliveryConfig.limitSpeed = QString::number(config.limitRate); + } + return lastoreDeliveryConfig.toJson(); +} + const int INSTALLATION_IS_READY = 1 << 0; // 是否可以安装 const int UPDATE_IS_DISABLED = 1 << 1; // 更新功能是否被禁用 struct LastoreDaemonDConfigStatusHelper { diff --git a/src/dcc-update-plugin/operation/updatemodel.cpp b/src/dcc-update-plugin/operation/updatemodel.cpp index b7a7dcb5e..f3da10379 100644 --- a/src/dcc-update-plugin/operation/updatemodel.cpp +++ b/src/dcc-update-plugin/operation/updatemodel.cpp @@ -1,13 +1,16 @@ -// 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 +#include "common/common/dconfig_helper.h" #include "updatemodel.h" #include "updatehistorymodel.h" #include "operation/common.h" +#include "common/common/dconfig_helper.h" #include "utils.h" #include +#include #include using namespace dcc::update::common; @@ -15,6 +18,7 @@ using namespace dcc::update::common; Q_DECLARE_LOGGING_CATEGORY(logDccUpdatePlugin) DCORE_USE_NAMESPACE +static constexpr int ShutdownUpdateStatus = 5; static const QMap ControlPanelTypeMapping = { { Default, CPT_Invalid }, { UpdatesAvailable, CPT_Available }, @@ -63,6 +67,7 @@ UpdateModel::UpdateModel(QObject* parent) , m_downloadWaiting(false) , m_downloadPaused(false) , m_upgradeWaiting(false) + , m_forceUpdateText("") , m_downloadProgress(0.0) , m_distUpgradeProgress(0.0) , m_backupProgress(0.0) @@ -72,6 +77,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) @@ -87,6 +93,7 @@ UpdateModel::UpdateModel(QObject* parent) , m_showVersion("") , m_baseline("") , m_p2pUpdateEnabled(false) + , m_forceUpdate(false) , m_historyModel(new UpdateHistoryModel(this)) { qCDebug(logDccUpdatePlugin) << "Initialize UpdateModel"; @@ -214,6 +221,27 @@ void UpdateModel::setBatterIsOK(bool ok) Q_EMIT batterIsOKChanged(ok); } +void UpdateModel::setForceUpdateText(const QString& updateTime, int lastoreStatus) +{ + if (!updateTime.isEmpty()) { + QDateTime dateTime = QDateTime::fromString(updateTime, Qt::ISODate); + if (dateTime.isValid()) { + QString formattedDateTime = dateTime.toString("HH:mm:ss"); + qCDebug(logDccUpdatePlugin) << "Set scheduled upgrade time:" << formattedDateTime; + if (m_forceUpdateText == formattedDateTime) { + return; + } + + m_forceUpdateText = tr("will upgrade at %1").arg(formattedDateTime); + } + } else if (lastoreStatus == ShutdownUpdateStatus) { + m_forceUpdateText = tr("will upgrade when shutdown"); + } else { + m_forceUpdateText = ""; + } + Q_EMIT forceUpdateTextChanged(); +} + void UpdateModel::setLastStatus(const UpdatesStatus& status, int line, int types) { qCInfo(logDccUpdatePlugin) << "Status: " << status << ", types:" << types << ", line:" << line; @@ -300,12 +328,18 @@ void UpdateModel::updateCheckUpdateUi() setCheckBtnText(tr("Check Again")); break; case Updated: - case UpdatesAvailable: + case UpdatesAvailable: { qCDebug(logDccUpdatePlugin) << "Setting UI for Updated status"; setCheckBtnText(tr("Check Again")); setCheckUpdateErrTips(tr("Your system is up to date")); setCheckUpdateIcon("update_abreast_of_time"); + QString versionInfo = tr("Current Edition") + ": " + m_systemVersionInfo; + if (!m_baseline.isEmpty()) { + versionInfo = versionInfo + "\n" + tr("Baseline") + ": " + m_baseline; + } + setVersionInfo(versionInfo); break; + } default: qCDebug(logDccUpdatePlugin) << "Unknown status in switch, clearing text"; setCheckBtnText(tr("")); @@ -313,6 +347,17 @@ void UpdateModel::updateCheckUpdateUi() } } +void UpdateModel::setVersionInfo(const QString &versionInfo) +{ + qCDebug(logDccUpdatePlugin) << "Setting version info:" << versionInfo; + if (m_versionInfo == versionInfo) { + return; + } + + m_versionInfo = versionInfo; + emit versionInfoChanged(); +} + void UpdateModel::setCheckUpdateErrTips(const QString &newCheckUpdateErrTips) { qCDebug(logDccUpdatePlugin) << "Setting check update error tips:" << newCheckUpdateErrTips; @@ -969,6 +1014,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; @@ -1101,6 +1157,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; @@ -1111,9 +1172,11 @@ DownloadSpeedLimitConfig UpdateModel::speedLimitConfig() const return DownloadSpeedLimitConfig::fromJson(m_speedLimitConfig); } -void UpdateModel::setSpeedLimitConfig(const QByteArray& config) +void UpdateModel::setSpeedLimitConfig(const QByteArray& config, bool isFromQml) { qCDebug(logDccUpdatePlugin) << "Set speed limit config:" << config; + if (DownloadSpeedLimitConfig::fromJson(m_speedLimitConfig).isOnlineSpeedLimit && isFromQml) + return; if (m_speedLimitConfig == config) return; @@ -1121,6 +1184,102 @@ void UpdateModel::setSpeedLimitConfig(const QByteArray& config) Q_EMIT downloadSpeedLimitConfigChanged(); } +void UpdateModel::setUpgradeDownloadSpeedLimitConfig(const QByteArray& config, bool needEmitSignal) +{ + qCInfo(logDccUpdatePlugin) << "Set upgrade download speed limit config" << config; + m_upgradeDownloadSpeedLimitConfig = config; + if (needEmitSignal) + Q_EMIT upgradeDownloadSpeedLimitConfigChanged(); +} + +QString UpdateModel::upgradeDownloadSpeedCurrentRate() const +{ + qCInfo(logDccUpdatePlugin) << "Upgrade download speed current rate " << UpgradeSpeedLimitConfig::fromJson(m_upgradeDownloadSpeedLimitConfig).currentRate; + return QString::number(LastoreUpgradeSpeedLimitConfig::fromJson(m_upgradeDownloadSpeedLimitConfig).limitSpeed.toInt() / 1024); +} + +QString UpdateModel::upgradeDownloadSpeedLimitRate() const +{ + qCInfo(logDccUpdatePlugin) << "Upgrade download speed limit rate " << UpgradeSpeedLimitConfig::fromJson(m_upgradeDownloadSpeedLimitConfig).limitRate; + return LastoreUpgradeSpeedLimitConfig::fromJson(m_upgradeDownloadSpeedLimitConfig).limitSpeed; +} + +bool UpdateModel::upgradeDownloadSpeedEnable() const +{ + qCInfo(logDccUpdatePlugin) << "Upgrade download speed enable " << m_upgradeDownloadSpeedLimitConfig; + return LastoreUpgradeSpeedLimitConfig::fromJson(m_upgradeDownloadSpeedLimitConfig).speedLimitEnabled; +} + +bool UpdateModel::upgradeDownloadSpeedIsOnline() const +{ + qCInfo(logDccUpdatePlugin) << "Upgrade download speed is online " << m_upgradeDownloadSpeedLimitConfig; + return LastoreUpgradeSpeedLimitConfig::fromJson(m_upgradeDownloadSpeedLimitConfig).isOnlineSpeedLimit; +} + +LastoreUpgradeSpeedLimitConfig UpdateModel::upgradeDownloadSpeedLimitConfig() const +{ + qCInfo(logDccUpdatePlugin) << "Upgrade download speed limit config " << m_upgradeDownloadSpeedLimitConfig; + return LastoreUpgradeSpeedLimitConfig::fromJson(m_upgradeDownloadSpeedLimitConfig); +} + +void UpdateModel::setUpgradeUploadSpeedLimitConfig(const QByteArray& config, bool needEmitSignal) +{ + qCInfo(logDccUpdatePlugin) << "Set upgrade upload speed limit config" << config; + m_upgradeUploadSpeedLimitConfig = config; + if (needEmitSignal) + Q_EMIT upgradeUploadSpeedLimitConfigChanged(); +} + +QString UpdateModel::upgradeUploadSpeedCurrentRate() const +{ + qCInfo(logDccUpdatePlugin) << "Upgrade upload speed current rate " << UpgradeSpeedLimitConfig::fromJson(m_upgradeUploadSpeedLimitConfig).currentRate; + return QString::number(LastoreUpgradeSpeedLimitConfig::fromJson(m_upgradeUploadSpeedLimitConfig).limitSpeed.toInt() / 1024); +} + +QString UpdateModel::upgradeUploadSpeedLimitRate() const +{ + qCInfo(logDccUpdatePlugin) << "Upgrade upload speed limit rate " << UpgradeSpeedLimitConfig::fromJson(m_upgradeUploadSpeedLimitConfig).limitRate; + return LastoreUpgradeSpeedLimitConfig::fromJson(m_upgradeUploadSpeedLimitConfig).limitSpeed; +} + +bool UpdateModel::upgradeUploadSpeedEnable() const +{ + qCInfo(logDccUpdatePlugin) << "Upgrade upload speed enable " << m_upgradeUploadSpeedLimitConfig; + return LastoreUpgradeSpeedLimitConfig::fromJson(m_upgradeUploadSpeedLimitConfig).speedLimitEnabled; +} + +bool UpdateModel::upgradeUploadSpeedIsOnline() const +{ + qCInfo(logDccUpdatePlugin) << "Upgrade upload speed is online " << m_upgradeDownloadSpeedLimitConfig; + return LastoreUpgradeSpeedLimitConfig::fromJson(m_upgradeUploadSpeedLimitConfig).isOnlineSpeedLimit; +} + +LastoreUpgradeSpeedLimitConfig UpdateModel::upgradeUploadSpeedLimitConfig() const +{ + qCInfo(logDccUpdatePlugin) << "Upgrade upload speed limit config " << m_upgradeUploadSpeedLimitConfig; + return LastoreUpgradeSpeedLimitConfig::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; @@ -1308,6 +1467,29 @@ void UpdateModel::setP2PUpdateEnabled(bool enabled) } } +void UpdateModel::setForceUpdate() +{ + bool forceUpdate = false; + const int lastoreStatus = DConfigHelper::instance()->getConfig("org.deepin.dde.lastore", "org.deepin.dde.lastore", "", "lastore-daemon-status", 0).toInt(); + + if (lastoreStatus == ShutdownUpdateStatus) { + forceUpdate = true; + } else { + const QString updateTime = DConfigHelper::instance()->getConfig("org.deepin.dde.lastore", "org.deepin.dde.lastore", "", "update-time", "").toString(); + if (!updateTime.isEmpty()) { + forceUpdate = QDateTime::fromString(updateTime, Qt::ISODate).isValid(); + } + } + + qCDebug(logDccUpdatePlugin) << "Set force update:" << forceUpdate << "old value:" << m_forceUpdate; + if (m_forceUpdate == forceUpdate) { + return; + } + + m_forceUpdate = forceUpdate; + Q_EMIT forceUpdateChanged(m_forceUpdate); +} + bool UpdateModel::isCommunitySystem() const { return Dtk::Core::DSysInfo::UosCommunity == Dtk::Core::DSysInfo::DSysInfo::uosEditionType(); @@ -1346,7 +1528,6 @@ void UpdateModel::onUpdatePropertiesChanged(const QString& interfaceName, const { qCDebug(logDccUpdatePlugin) << "Update properties changed for interface:" << interfaceName << "properties count:" << changedProperties.size(); Q_UNUSED(invalidatedProperties) - if (interfaceName == "org.deepin.dde.Lastore1.Manager") { qCDebug(logDccUpdatePlugin) << "Handling Lastore Manager property changes"; if (changedProperties.contains("CheckUpdateMode")) { @@ -1384,7 +1565,21 @@ void UpdateModel::onUpdatePropertiesChanged(const QString& interfaceName, const if (changedProperties.contains("P2PUpdateEnable")) { qCDebug(logDccUpdatePlugin) << "P2PUpdateEnable property changed"; - setP2PUpdateEnabled(changedProperties.value("P2PUpdateEnable").toBool()); + setUpgradeDeliveryEnable(changedProperties.value("P2PUpdateEnable").toBool()); + } + } + + if (interfaceName == "org.deepin.upgradedelivery") { + qCDebug(logDccUpdatePlugin) << "Handling upgrade delivery property changes"; + if (changedProperties.contains("DownloadLimitSpeed")) { + qCDebug(logDccUpdatePlugin) << "P2PUpgradeDownloadSpeedLimitConfig property changed"; + qCDebug(logDccUpdatePlugin) << "P2PUpgradeDownloadSpeedLimitConfig property changed " << changedProperties.value("DownloadLimitSpeed"); + setUpgradeDownloadSpeedLimitConfig(transferDeliveryConfigToLastoreDeliveryConfig(changedProperties.value("DownloadLimitSpeed").toByteArray()).toUtf8()); + } + + if (changedProperties.contains("UploadLimitSpeed")) { + qCDebug(logDccUpdatePlugin) << "P2PUpgradeUploadSpeedLimitConfig property changed"; + setUpgradeUploadSpeedLimitConfig(transferDeliveryConfigToLastoreDeliveryConfig(changedProperties.value("UploadLimitSpeed").toByteArray()).toUtf8()); } } } diff --git a/src/dcc-update-plugin/operation/updatemodel.h b/src/dcc-update-plugin/operation/updatemodel.h index 6e8bede2c..7f2a6e66c 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,12 +29,14 @@ 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) Q_PROPERTY(QString checkUpdateIcon READ checkUpdateIcon NOTIFY checkUpdateIconChanged FINAL) Q_PROPERTY(double checkUpdateProgress READ checkUpdateProgress NOTIFY checkUpdateProgressChanged FINAL) Q_PROPERTY(UpdatesStatus checkUpdateStatus READ checkUpdateStatus NOTIFY checkUpdateStatusChanged FINAL) + Q_PROPERTY(QString versionInfo READ versionInfo NOTIFY versionInfoChanged FINAL) Q_PROPERTY(QString checkUpdateErrTips READ checkUpdateErrTips NOTIFY checkUpdateErrTipsChanged FINAL) Q_PROPERTY(QString checkBtnText READ checkBtnText NOTIFY checkBtnTextChanged FINAL) Q_PROPERTY(QString lastCheckUpdateTime READ lastCheckUpdateTime NOTIFY lastCheckUpdateTimeChanged FINAL) @@ -53,6 +55,7 @@ class UpdateModel : public QObject Q_PROPERTY(bool downloadWaiting READ downloadWaiting NOTIFY downloadWaitingChanged FINAL) Q_PROPERTY(bool downloadPaused READ downloadPaused NOTIFY downloadPausedChanged FINAL) Q_PROPERTY(bool upgradeWaiting READ upgradeWaiting NOTIFY upgradeWaitingChanged FINAL) + Q_PROPERTY(QString forceUpdateText READ forceUpdateText NOTIFY forceUpdateTextChanged FINAL) Q_PROPERTY(double downloadProgress READ downloadProgress NOTIFY downloadProgressChanged FINAL) Q_PROPERTY(double backupProgress READ backupProgress NOTIFY backupProgressChanged FINAL) @@ -72,6 +75,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) @@ -81,7 +85,17 @@ 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(bool upgradeDownloadSpeedIsOnline READ upgradeDownloadSpeedIsOnline NOTIFY upgradeDownloadSpeedLimitConfigChanged FINAL) + Q_PROPERTY(QString upgradeUploadSpeedCurrentRate READ upgradeUploadSpeedCurrentRate NOTIFY upgradeUploadSpeedLimitConfigChanged FINAL) + Q_PROPERTY(QString upgradeUploadSpeedLimitRate READ upgradeUploadSpeedLimitRate NOTIFY upgradeUploadSpeedLimitConfigChanged FINAL) + Q_PROPERTY(bool upgradeUploadSpeedIsOnline READ upgradeUploadSpeedIsOnline NOTIFY upgradeUploadSpeedLimitConfigChanged FINAL) + Q_PROPERTY(bool upgradeUploadSpeedEnable READ upgradeUploadSpeedEnable NOTIFY upgradeUploadSpeedLimitConfigChanged FINAL) + Q_PROPERTY(bool upgradeDeliveryEnable READ upgradeDeliveryEnable NOTIFY upgradeDeliveryEnableChanged FINAL) + Q_PROPERTY(bool p2pUpdateEnabled READ isP2PUpdateEnabled NOTIFY p2pUpdateEnableStateChanged FINAL) + Q_PROPERTY(bool forceUpdate READ forceUpdate NOTIFY forceUpdateChanged FINAL) Q_PROPERTY(UpdateHistoryModel *historyModel READ historyModel NOTIFY historyModelChanged FINAL) @@ -133,6 +147,9 @@ class UpdateModel : public QObject void setCheckUpdateStatus(UpdatesStatus newCheckUpdateStatus); void updateCheckUpdateUi(); + QString versionInfo() const { return m_versionInfo; } + void setVersionInfo(const QString &newCheckUpdateErrTips); + QString checkUpdateErrTips() const { return m_checkUpdateErrTips; } void setCheckUpdateErrTips(const QString &newCheckUpdateErrTips); @@ -183,6 +200,9 @@ class UpdateModel : public QObject bool upgradeWaiting() const { return m_upgradeWaiting; } void setUpgradeWaiting(bool waiting); + QString forceUpdateText() const { return m_forceUpdateText; } + void setForceUpdateText(const QString& updateTime, int lastoreStatus); + double downloadProgress() const { return m_downloadProgress; } void setDownloadProgress(double downloadProgress); @@ -234,6 +254,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; @@ -258,9 +281,27 @@ class UpdateModel : public QObject void setUpdateItemEnabled(); bool downloadSpeedLimitEnabled() const; + bool downloadIsOnlineSpeedLimit() const; QString downloadSpeedLimitSize() const; DownloadSpeedLimitConfig speedLimitConfig() const; - void setSpeedLimitConfig(const QByteArray &config); + void setSpeedLimitConfig(const QByteArray &config, bool isFromQml = false); + + void setUpgradeDownloadSpeedLimitConfig(const QByteArray& config, bool needEmitSignal = true); + QString upgradeDownloadSpeedCurrentRate() const; + QString upgradeDownloadSpeedLimitRate() const; + bool upgradeDownloadSpeedEnable() const; + bool upgradeDownloadSpeedIsOnline() const; + LastoreUpgradeSpeedLimitConfig upgradeDownloadSpeedLimitConfig() const; + void setUpgradeUploadSpeedLimitConfig(const QByteArray& config, bool needEmitSignal = true); + QString upgradeUploadSpeedCurrentRate() const; + QString upgradeUploadSpeedLimitRate() const; + bool upgradeUploadSpeedEnable() const; + bool upgradeUploadSpeedIsOnline() const; + LastoreUpgradeSpeedLimitConfig upgradeUploadSpeedLimitConfig() const; + bool upgradeDeliveryEnable() const; + + void setUpgradeDeliveryEnable(bool enable); + void refreshUpgradeDeliveryEnable(bool enable); bool autoDownloadUpdates() const { return m_autoDownloadUpdates; } void setAutoDownloadUpdates(bool autoDownloadUpdates); @@ -310,6 +351,9 @@ class UpdateModel : public QObject bool isP2PUpdateEnabled() const { return m_p2pUpdateEnabled; } void setP2PUpdateEnabled(bool enabled); + bool forceUpdate() const { return m_forceUpdate; } + void setForceUpdate(); + Q_INVOKABLE bool isCommunitySystem() const; Q_INVOKABLE QString privacyAgreementText() const; @@ -337,6 +381,7 @@ public slots: void checkUpdateIconChanged(); void checkUpdateProgressChanged(); void checkUpdateStatusChanged(); + void versionInfoChanged(); void checkUpdateErrTipsChanged(); void checkBtnTextChanged(); void lastCheckUpdateTimeChanged(); @@ -356,6 +401,7 @@ public slots: void downloadWaitingChanged(bool waiting); void downloadPausedChanged(bool paused); void upgradeWaitingChanged(bool waiting); + void forceUpdateTextChanged(); void downloadProgressChanged(const double &progress); void backupProgressChanged(double progress); @@ -369,6 +415,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); @@ -378,7 +425,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); @@ -392,6 +443,7 @@ public slots: void showVersionChanged(QString version); void baselineChanged(const QString &baseline); void p2pUpdateEnableStateChanged(bool enabled); + void forceUpdateChanged(bool forceUpdate); void historyModelChanged(); private: @@ -410,6 +462,7 @@ public slots: QString m_checkUpdateIcon; double m_checkUpdateProgress; UpdatesStatus m_checkUpdateStatus; + QString m_versionInfo; QString m_checkUpdateErrTips; QString m_checkBtnText; QString m_lastCheckUpdateTime; @@ -428,6 +481,7 @@ public slots: bool m_downloadWaiting; bool m_downloadPaused; bool m_upgradeWaiting; + QString m_forceUpdateText; double m_downloadProgress; double m_distUpgradeProgress; double m_backupProgress; @@ -443,6 +497,7 @@ public slots: QByteArray m_updateStatus; // lastore daemon发上来的原始json数据 bool m_isUpdatable; // 是否有包可更新 + bool m_isPrivateUpdate; //当前是否接入私有化更新 QMap>> m_controlStatusMap; QMap m_waitingStatusMap; @@ -451,6 +506,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; @@ -466,6 +524,7 @@ public slots: QString m_showVersion; QString m_baseline; bool m_p2pUpdateEnabled; + bool m_forceUpdate; // update history qml data UpdateHistoryModel *m_historyModel; diff --git a/src/dcc-update-plugin/operation/updatework.cpp b/src/dcc-update-plugin/operation/updatework.cpp index 2a8c04feb..5cf424d51 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 @@ -28,7 +29,6 @@ #include Q_DECLARE_LOGGING_CATEGORY(logDccUpdatePlugin) - using namespace DCC_NAMESPACE; const QString TestingChannel = "testing Channel"; @@ -38,6 +38,7 @@ const QString ServiceLinkCN = QStringLiteral("https://insider.deepin.org.cn"); const QString ChangeLogFile = "/usr/share/deepin/release-note/UpdateInfo.json"; const QString ChangeLogDic = "/usr/share/deepin/"; const QString UpdateLogTmpFile = "/tmp/deepin-update-log.json"; +const QString AuthFailed = "authentication failed"; const int LOWEST_BATTERY_PERCENT = 60; @@ -116,6 +117,7 @@ UpdateWorker::UpdateWorker(UpdateModel* model, QObject* parent) , m_lastoreDConfig(DConfig::create("org.deepin.dde.lastore", "org.deepin.dde.lastore", "", this)) , m_model(model) , m_updateInter(new UpdateDBusProxy(this)) + , m_updateAssistant(nullptr) , m_lastoreHeartBeatTimer(new QTimer(this)) , m_logWatcherHelper(new LogWatcherHelper(m_updateInter, this)) , m_machineid(std::nullopt) @@ -181,6 +183,9 @@ void UpdateWorker::initConnect() connect(m_updateInter, &UpdateDBusProxy::AutoCleanChanged, m_model, &UpdateModel::setAutoCleanCache); connect(m_updateInter, &UpdateDBusProxy::AutoDownloadUpdatesChanged, m_model, &UpdateModel::setAutoDownloadUpdates); connect(m_updateInter, &UpdateDBusProxy::MirrorSourceChanged, m_model, &UpdateModel::setDefaultMirror); + connect(m_updateInter, &UpdateDBusProxy::P2PUpdateSupportChanged, this, [this](bool supported) { + Q_EMIT p2PUpdateSupportChanged(supported); + }); if (IsCommunitySystem) { connect(m_updateInter, &UpdateDBusProxy::EnableChanged, m_model, &UpdateModel::setSmartMirrorSwitch); } @@ -208,6 +213,13 @@ 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); + QDBusConnection::systemBus().connect("org.deepin.upgradedelivery", + "/org/deepin/upgradedelivery", + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + m_model, SLOT(onUpdatePropertiesChanged(QString, QVariantMap, QStringList))); } void UpdateWorker::activate() @@ -220,7 +232,9 @@ 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()); m_model->setCheckUpdateMode(m_updateInter->checkUpdateMode()); m_model->setSecurityUpdateEnabled(DConfigWatcher::instance()->getValue(DConfigWatcher::update, "updateSafety").toString() != "Hidden"); @@ -232,6 +246,7 @@ void UpdateWorker::activate() m_model->setUpdateNotify(m_updateInter->updateNotify()); m_model->setAutoCleanCache(m_updateInter->autoClean()); m_model->setP2PUpdateEnabled(m_updateInter->p2pUpdateEnable()); + m_model->setForceUpdate(); m_model->setImmutableAutoRecovery(m_updateInter->immutableAutoRecovery()); if (IsCommunitySystem) { qCDebug(logDccUpdatePlugin) << "community system, enable smarrt mirror switch"; @@ -282,6 +297,7 @@ void UpdateWorker::initConfig() qCDebug(logDccUpdatePlugin) << "Initialize lastore daemon configuration"; if (m_lastoreDConfig && m_lastoreDConfig->isValid()) { m_model->setLastoreDaemonStatus(m_lastoreDConfig->value("lastore-daemon-status").toInt()); + m_model->setForceUpdateText(m_lastoreDConfig->value("update-time").toString(), m_lastoreDConfig->value("lastore-daemon-status").toInt()); connect(m_lastoreDConfig, &DConfig::valueChanged, this, [this](const QString& key) { if ("lastore-daemon-status" == key) { bool ok; @@ -290,6 +306,12 @@ void UpdateWorker::initConfig() qCDebug(logDccUpdatePlugin) << "Lastore daemon status changed:" << value; m_model->setLastoreDaemonStatus(value); } + m_model->setForceUpdateText(m_lastoreDConfig->value("update-time").toString(), m_lastoreDConfig->value("lastore-daemon-status").toInt()); + m_model->setForceUpdate(); + } + if ("update-time" == key) { + m_model->setForceUpdateText(m_lastoreDConfig->value("update-time").toString(), m_lastoreDConfig->value("lastore-daemon-status").toInt()); + m_model->setForceUpdate(); } }); } else { @@ -494,6 +516,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 +815,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; @@ -891,8 +925,9 @@ 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()); + m_model->setSpeedLimitConfig(config.toJson().toUtf8(), true); setDownloadSpeedLimitConfig(config.toJson()); } @@ -901,6 +936,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()); } @@ -1257,6 +1293,22 @@ void UpdateWorker::initTestingChannel() }); } +void UpdateWorker::refreshUpgradeDeliveryInfo() +{ + qCDebug(logDccUpdatePlugin) << "Refresh upgrade delivery info"; + if (!m_updateAssistant || !m_model) { + return; + } + m_model->setUpgradeDownloadSpeedLimitConfig(transferDeliveryConfigToLastoreDeliveryConfig(m_updateAssistant->downloadLimitSpeed()).toUtf8()); + m_model->setUpgradeUploadSpeedLimitConfig(transferDeliveryConfigToLastoreDeliveryConfig(m_updateAssistant->uploadLimitSpeed()).toUtf8()); + m_model->setUpgradeDeliveryEnable(m_updateInter->p2pUpdateEnable()); +} + +bool UpdateWorker::p2pUpdateSupported() const +{ + return m_updateInter && m_updateInter->p2PUpdateSupport(); +} + void UpdateWorker::checkTestingChannelStatus() { // Leave page @@ -1692,3 +1744,99 @@ void UpdateWorker::onRemovePackageStatusChanged(const QString& value) } } +//更新传递总开关 +void UpdateWorker::setUpgradeDeliveryEnabled(bool enabled, bool fromRetryDialog) +{ + if (enabled == m_model->upgradeDeliveryEnable() && !fromRetryDialog) { + 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(transferDeliveryConfigToLastoreDeliveryConfig(m_updateAssistant->downloadLimitSpeed()).toUtf8()); + m_model->setUpgradeUploadSpeedLimitConfig(transferDeliveryConfigToLastoreDeliveryConfig(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); + if (watcher->error().message() != AuthFailed) + Q_EMIT upgradeDeliveryEnableSetFailed(); + 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, downloadSpeedLimitConfig] { + watcher->deleteLater(); + if (watcher->isError()) { + qCWarning(logDccUpdatePlugin) << "Set upgrade download speed limit config error: " << watcher->error().message(); + m_model->setUpgradeDownloadSpeedLimitConfig(m_model->upgradeDownloadSpeedLimitConfig().toJson().toUtf8()); + Q_EMIT upgradeDeliveryConfigSetFailed(); + return; + } + }); +} + +//设置更新传递上传限速 +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, uploadSpeedLimitConfig] { + watcher->deleteLater(); + if (watcher->isError()) { + qCWarning(logDccUpdatePlugin) << "Set upgrade upload speed limit config error: " << watcher->error().message(); + m_model->setUpgradeUploadSpeedLimitConfig(m_model->upgradeUploadSpeedLimitConfig().toJson().toUtf8()); + Q_EMIT upgradeDeliveryConfigSetFailed(); + return; + } + }); +} + +void UpdateWorker::getUpgradeDeliveryDownloadLimitSpeed() +{ + if (m_updateAssistant && m_model) { + m_model->setUpgradeDownloadSpeedLimitConfig(transferDeliveryConfigToLastoreDeliveryConfig(m_updateAssistant->downloadLimitSpeed()).toUtf8()); + } +} + +void UpdateWorker::getUpgradeDeliveryUploadLimitSpeed() +{ + if (m_updateAssistant && m_model) { + m_model->setUpgradeUploadSpeedLimitConfig(transferDeliveryConfigToLastoreDeliveryConfig(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 571de400e..88bf5f240 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 @@ -37,6 +38,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 +56,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(); @@ -71,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, bool fromRetryDialog = false); + 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); // 更新设置-更新提醒 @@ -92,6 +101,7 @@ class UpdateWorker : public QObject Q_INVOKABLE void setTestingChannelEnable(const bool& enable); Q_INVOKABLE bool openTestingChannelUrl(); Q_INVOKABLE void exitTestingChannel(bool value); + Q_INVOKABLE bool p2pUpdateSupported() const; std::optional getMachineId(); std::optional getTestingChannelUrl(); void initTestingChannel(); @@ -99,6 +109,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); @@ -126,11 +137,15 @@ public Q_SLOTS: Q_SIGNALS: void requestCloseTestingChannel(); void startDoUpgrade(); + void upgradeDeliveryEnableSetFailed(); + void upgradeDeliveryConfigSetFailed(); + void p2PUpdateSupportChanged(bool supported); private: 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/CheckUpdate.qml b/src/dcc-update-plugin/qml/CheckUpdate.qml index 35ed9c0e8..b1aa53368 100644 --- a/src/dcc-update-plugin/qml/CheckUpdate.qml +++ b/src/dcc-update-plugin/qml/CheckUpdate.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 2.0 import QtQuick.Controls 2.0 @@ -23,6 +23,14 @@ ColumnLayout { width: parent.width spacing: 10 + D.Label { + Layout.alignment: Qt.AlignHCenter + width: implicitWidth + visible: dccData.model().isPrivateUpdate + text: dccData.model().versionInfo + font: D.DTK.fontManager.t8 + } + D.DciIcon { id: checkUpdateIcon Layout.alignment: Qt.AlignHCenter diff --git a/src/dcc-update-plugin/qml/UpdateControl.qml b/src/dcc-update-plugin/qml/UpdateControl.qml index da873075c..6e4c37428 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 @@ -15,12 +15,16 @@ ColumnLayout { property string updateStateIcon: "" property string updateTitle : "" property string updateTips: "" + property string secondaryUpdateTips: "" property var btnActions: [] + property bool showActionButtons: true property string processTitle: "" property double processValue: 0 property bool processState: false property bool busyState: false property bool updateListEnable: true + property bool buttonsEnabled: true + property var buttonEnabledStates: [] property bool updateListcheck: true property bool isDownloading: false property bool isPauseOrNot: false @@ -89,6 +93,14 @@ ColumnLayout { } } + D.Label { + Layout.alignment: Qt.AlignLeft | Qt.AlignTop + visible: secondaryUpdateTips.length !== 0 + text: secondaryUpdateTips + font: D.DTK.fontManager.t8 + Layout.fillWidth: true + } + D.Button { Layout.alignment: Qt.AlignLeft | Qt.AlignTop visible: showLogButton @@ -122,8 +134,10 @@ ColumnLayout { Layout.alignment: Qt.AlignRight | Qt.AlignVCenter text: modelData font: D.DTK.fontManager.t6 - visible: modelData.length !== 0 && !initAnimation.visible - enabled: updatelistModel.model.isUpdateEnable + visible: rootLayout.showActionButtons && modelData.length !== 0 && !initAnimation.visible + enabled: rootLayout.buttonsEnabled + && updatelistModel.model.isUpdateEnable + && (rootLayout.buttonEnabledStates.length <= index || rootLayout.buttonEnabledStates[index]) onClicked: { rootLayout.btnClicked(index, updateListModels.getAllUpdateType()) } @@ -180,7 +194,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 548e5dc42..f543f44f9 100644 --- a/src/dcc-update-plugin/qml/UpdateMain.qml +++ b/src/dcc-update-plugin/qml/UpdateMain.qml @@ -233,7 +233,7 @@ DccObject { return dccData.model().installFailedTips } btnActions: [ qsTr("Continue Update") ] - + showActionButtons: !dccData.model().isPrivateUpdate onBtnClicked: function(index, updateType) { dccData.work().onRequestRetry(Common.CPT_UpgradeFailed, updateType) } @@ -289,21 +289,41 @@ DccObject { pageType: DccObject.Item page: UpdateControl { + id: preInstallUpdateControl updateListModels: dccData.model().preInstallListModel updateTitle: qsTr("Update download completed") - btnActions: [ qsTr("Install updates") ] + btnActions: dccData.model().isPrivateUpdate ? [ + qsTr("Install Now"), + qsTr("Check Again") + ] : [qsTr("Install updates")] + function updateSecondaryTips() { + secondaryUpdateTips = dccData.model().forceUpdateText + } updateTips: { if (!dccData.model().batterIsOK) { return qsTr("The battery capacity is lower than 60%. To get successful updates, please plug in.") } return qsTr("Update size: ") + dccData.model().preInstallListModel.downloadSize } + secondaryUpdateTips: "" + Component.onCompleted: updateSecondaryTips() busyState: dccData.model().upgradeWaiting updateListEnable: !dccData.model().upgradeWaiting + buttonEnabledStates: dccData.model().isPrivateUpdate + ? [!dccData.model().forceUpdate, true] + : [true] onBtnClicked: function(index, updateType) { - updateSelectLoader.updateType = updateType - updateSelectLoader.active = true + if (index === 0) { + if (dccData.model().isPrivateUpdate) { + dccData.work().doUpgrade(updateListModels.getAllUpdateType(), true) + } else { + updateSelectLoader.updateType = updateType + updateSelectLoader.active = true + } + } else if (index === 1) { + dccData.work().reCheckWithUi(); + } } Loader { @@ -330,6 +350,13 @@ DccObject { } } + Connections { + target: dccData.model() + function onForceUpdateTextChanged() { + preInstallUpdateControl.updateSecondaryTips() + } + } + Connections { target: dccData.work() function onStartDoUpgrade() { @@ -377,7 +404,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 @@ -386,6 +413,12 @@ DccObject { onBtnClicked: function(index, updateType) { dccData.work().startDownload(updateType) } + + Component.onCompleted: { + if (dccData.model().isPrivateUpdate) { + dccData.work().startDownload(1) + } + } } } diff --git a/src/dcc-update-plugin/qml/UpdateSetting.qml b/src/dcc-update-plugin/qml/UpdateSetting.qml index f7809e7d1..b43a77850 100644 --- a/src/dcc-update-plugin/qml/UpdateSetting.qml +++ b/src/dcc-update-plugin/qml/UpdateSetting.qml @@ -12,14 +12,172 @@ import org.deepin.dcc 1.0 import org.deepin.dcc.update 1.0 DccObject { + id: root + property bool syncingUpgradeDeliverySwitch: false + FontMetrics { id: fm } + + D.DialogWindow { + id: upgradeDeliverySetConfigFailedDialog + width: 360 + icon: "preferences-system" + modality: Qt.ApplicationModal + visible: false + + ColumnLayout { + width: parent.width + + D.Label { + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + text: qsTr("Failed to change Delivery Optimization setting") + } + + Item { + Layout.fillHeight: true + Layout.preferredHeight: 22 + } + + RowLayout { + Layout.fillWidth: true + Layout.bottomMargin: 10 + spacing: 10 + + ButtonWithToolTip { + text: qsTr("Cancel") + Layout.fillWidth: true + focus: upgradeDeliverySetConfigFailedDialog.visible + onClicked: { + upgradeDeliverySetConfigFailedDialog.close() + } + Keys.onEscapePressed: { + upgradeDeliverySetConfigFailedDialog.close() + } + } + + component ButtonWithToolTip: D.Button { + id: customButton + + contentItem: Text { + id: buttonText + text: customButton.text + font: customButton.font + color: customButton.D.ColorSelector.textColor + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + width: customButton.width + } + + hoverEnabled: true + + ToolTip { + visible: customButton.hovered && buttonText.truncated + delay: 500 + text: customButton.text + } + } + } + } + } + + Connections { + target: dccData.work() + function onUpgradeDeliveryConfigSetFailed() { + upgradeDeliverySetConfigFailedDialog.show() + } + } + + D.DialogWindow { + id: upgradeDeliverySetEnableFailedDialog + width: 360 + icon: "preferences-system" + modality: Qt.ApplicationModal + visible: false + + ColumnLayout { + width: parent.width + + D.Label { + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + text: qsTr("Update Delivery Optimization service exception") + } + + Item { + Layout.fillHeight: true + Layout.preferredHeight: 22 + } + + RowLayout { + Layout.fillWidth: true + Layout.bottomMargin: 10 + spacing: 10 + + EnableFailedDialogButton { + text: qsTr("Try again") + Layout.fillWidth: true + focus: upgradeDeliverySetEnableFailedDialog.visible + onClicked: { + upgradeDeliverySetEnableFailedDialog.close() + dccData.work().setUpgradeDeliveryEnabled(!dccData.model().upgradeDeliveryEnable, true) + } + Keys.onEscapePressed: { + upgradeDeliverySetEnableFailedDialog.close() + } + } + + EnableFailedDialogButton { + text: qsTr("Cancel") + Layout.fillWidth: true + onClicked: { + upgradeDeliverySetEnableFailedDialog.close() + } + } + + component EnableFailedDialogButton: D.Button { + id: customButton + + contentItem: Text { + id: buttonText + text: customButton.text + font: customButton.font + color: customButton.D.ColorSelector.textColor + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + width: customButton.width + } + + hoverEnabled: true + + ToolTip { + visible: customButton.hovered && buttonText.truncated + delay: 500 + text: customButton.text + } + } + } + } + } + + Connections { + target: dccData.work() + function onUpgradeDeliveryEnableSetFailed() { + upgradeDeliverySetEnableFailedDialog.show() + } + } + DccTitleObject { name: "updateTypeGrpTitle" parentName: "updateSettingsPage" displayName: qsTr("Update Type") weight: 10 + visible: !dccData.model().isPrivateUpdate } DccObject { @@ -28,6 +186,7 @@ DccObject { parentName: "updateSettingsPage" weight: 20 pageType: DccObject.Item + visible: !dccData.model().isPrivateUpdate page: DccGroupView { height: implicitHeight + 10 spacing: 0 @@ -98,7 +257,7 @@ DccObject { DccObject { id: advancedSetting - property bool showDetails: false + property bool showDetails: dccData.model().isPrivateUpdate name: "advancedSettingTitle" parentName: "updateSettingsPage" displayName: qsTr("Advanced Settings") @@ -106,7 +265,7 @@ DccObject { pageType: DccObject.Item page: RowLayout { Component.onDestruction: { - advancedSetting.showDetails = false + advancedSetting.showDetails = false || dccData.model().isPrivateUpdate } DccLabel { property D.Palette textColor: D.Palette { @@ -171,14 +330,277 @@ DccObject { } DccObject { - name: "downloadLimitGrp" + id: upgradeDeliveryGrp + name: "upgradeDeliveryGrp" parentName: "advancedSettingGroup" weight: 40 + visible: dccData.work().p2pUpdateSupported() pageType: DccObject.Item page: DccGroupView { height: implicitHeight + 10 spacing: 0 } + Connections { + target: dccData.work() + function onP2PUpdateSupportChanged() { + upgradeDeliveryGrp.visible = dccData.work().p2pUpdateSupported() + } + } + + DccObject { + name: "upgradeDeliverySwitch" + parentName: "upgradeDeliveryGrp" + displayName: qsTr("Delivery Optimization") + description: qsTr("When enabled, your device may share previously downloaded system updates with other devices on your local network.When you turn it off, cached files from update delivery will be cleared during the next restart.") + weight: 10 + enabled: !dccData.model().isPrivateUpdate + pageType: DccObject.Editor + page: D.Switch { + id: upgradeDeliverySwitch + checked: dccData.model().upgradeDeliveryEnable + onCheckedChanged: { + if (root.syncingUpgradeDeliverySwitch) { + return + } + dccData.work().setUpgradeDeliveryEnabled(checked) + } + Connections { + target: dccData.model() + function onUpgradeDeliveryEnableChanged() { + root.syncingUpgradeDeliverySwitch = true + upgradeDeliverySwitch.checked = dccData.model().upgradeDeliveryEnable + root.syncingUpgradeDeliverySwitch = false + } + } + } + } + + DccObject { + id: upgradeDeliveryUploadLimitSetting + name: "upgradeDeliveryUploadLimitSetting" + parentName: "upgradeDeliveryGrp" + displayName: qsTr("Delivery Optimization-Upload throttling") + visible: dccData.model().upgradeDeliveryEnable + weight: 20 + enabled: !dccData.model().upgradeUploadSpeedIsOnline + pageType: DccObject.Item + Connections { + target: dccData.model() + function onUpgradeUploadSpeedLimitConfigChanged() { + upgradeDeliveryUploadLimitSetting.enabled = !dccData.model().upgradeUploadSpeedIsOnline + } + } + page: RowLayout { + D.CheckBox { + id: uploadLimitCheckBox + Layout.leftMargin: 14 + Layout.fillWidth: true + text: dccObj.displayName + ToolTip { + visible: uploadLimitCheckBox.hovered && uploadLimitCheckBox.width < uploadLimitCheckBox.implicitWidth + text: uploadLimitCheckBox.text + } + checked: dccData.model().upgradeUploadSpeedEnable + Connections { + target: dccData.model() + function onUpgradeUploadSpeedLimitConfigChanged() { + uploadLimitCheckBox.checked = dccData.model().upgradeUploadSpeedEnable + } + } + onCheckedChanged: { + dccData.work().setUpgradeDeliveryUploadLimitSpeed(uploadLineEdit.text, uploadLimitCheckBox.checked) + } + } + + RowLayout { + spacing: 10 + Layout.topMargin: 5 + Layout.bottomMargin: 5 + Layout.rightMargin: 10 + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + D.LineEdit { + id: uploadLineEdit + maximumLength: 6 + validator: RegularExpressionValidator { regularExpression: /^\d*$/ } + alertText: qsTr("Only numbers between 10-999999 are allowed") + alertDuration: 3000 + enabled: uploadLimitCheckBox.checked + clearButton.active: uploadLineEdit.activeFocus && (text.length !== 0) + text: dccData.model().upgradeUploadSpeedCurrentRate + + function triggerAlert() { + uploadLineEdit.showAlert = true + uploadAlertResetTimer.restart() + } + + Timer { + id: uploadAlertResetTimer + interval: uploadLineEdit.alertDuration + onTriggered: uploadLineEdit.showAlert = false + } + + onTextChanged: { + if (uploadLineEdit.text.length !== 0 && (uploadLineEdit.text[0] === "0" || Number(uploadLineEdit.text) > 999999)) { + uploadLineEdit.text = "" + uploadLineEdit.triggerAlert() + } else if (uploadLineEdit.showAlert && (uploadLineEdit.text.length === 0 || (Number(uploadLineEdit.text) >= 10 && Number(uploadLineEdit.text) <= 999999))) { + uploadLineEdit.showAlert = false + } + } + onEditingFinished: { + if (uploadLineEdit.text.length === 0) { + uploadLineEdit.text = dccData.model().upgradeUploadSpeedCurrentRate + return + } + if (Number(uploadLineEdit.text) < 10 || Number(uploadLineEdit.text) > 999999) { + uploadLineEdit.text = dccData.model().upgradeUploadSpeedCurrentRate + uploadLineEdit.triggerAlert() + return + } + dccData.work().setUpgradeDeliveryUploadLimitSpeed(uploadLineEdit.text, uploadLimitCheckBox.checked) + } + Keys.onPressed: { + if (event.key === Qt.Key_Return) { + uploadLineEdit.forceActiveFocus(false); + } + } + Connections { + target: dccData.model() + function onUpgradeUploadSpeedLimitConfigChanged() { + uploadLineEdit.text = dccData.model().upgradeUploadSpeedCurrentRate + } + } + } + + D.Label { + text: "KB/S" + } + } + } + } + + DccObject { + id: upgradeDeliveryDownloadLimitSetting + name: "upgradeDeliveryDownloadLimitSetting" + parentName: "upgradeDeliveryGrp" + displayName: qsTr("Delivery Optimization-Limit Speed") + visible: dccData.model().upgradeDeliveryEnable + weight: 30 + enabled: !dccData.model().upgradeDownloadSpeedIsOnline + pageType: DccObject.Item + Connections { + target: dccData.model() + function onUpgradeDownloadSpeedLimitConfigChanged() { + upgradeDeliveryDownloadLimitSetting.enabled = !dccData.model().upgradeDownloadSpeedIsOnline + } + } + page: RowLayout { + D.CheckBox { + id: downloadLimitCheckBox + Layout.leftMargin: 14 + Layout.fillWidth: true + text: dccObj.displayName + ToolTip { + visible: downloadLimitCheckBox.hovered && downloadLimitCheckBox.width < downloadLimitCheckBox.implicitWidth + text: downloadLimitCheckBox.text + } + checked: dccData.model().upgradeDownloadSpeedEnable + Connections { + target: dccData.model() + function onUpgradeDownloadSpeedLimitConfigChanged() { + downloadLimitCheckBox.checked = dccData.model().upgradeDownloadSpeedEnable + } + } + onCheckedChanged: { + dccData.work().setUpgradeDeliveryDownloadLimitSpeed(downloadLineEdit.text, downloadLimitCheckBox.checked) + } + } + + RowLayout { + spacing: 10 + Layout.topMargin: 5 + Layout.bottomMargin: 5 + Layout.rightMargin: 10 + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + D.LineEdit { + id: downloadLineEdit + maximumLength: 6 + validator: RegularExpressionValidator { regularExpression: /^\d*$/ } + alertText: qsTr("Only numbers between 10-999999 are allowed") + alertDuration: 3000 + enabled: downloadLimitCheckBox.checked + clearButton.active: downloadLineEdit.activeFocus && (text.length !== 0) + text: dccData.model().upgradeDownloadSpeedCurrentRate + + function triggerAlert() { + downloadLineEdit.showAlert = true + downloadAlertResetTimer.restart() + } + + Timer { + id: downloadAlertResetTimer + interval: downloadLineEdit.alertDuration + onTriggered: downloadLineEdit.showAlert = false + } + + onTextChanged: { + if (downloadLineEdit.text.length !== 0 && (downloadLineEdit.text[0] === "0" || Number(downloadLineEdit.text) > 999999)) { + downloadLineEdit.text = "" + downloadLineEdit.triggerAlert() + } else if (downloadLineEdit.showAlert && (downloadLineEdit.text.length === 0 || (Number(downloadLineEdit.text) >= 10 && Number(downloadLineEdit.text) <= 999999))) { + downloadLineEdit.showAlert = false + } + } + onEditingFinished: { + if (downloadLineEdit.text.length === 0) { + downloadLineEdit.text = dccData.model().upgradeDownloadSpeedCurrentRate + return + } + if (Number(downloadLineEdit.text) < 10 || Number(downloadLineEdit.text) > 999999) { + downloadLineEdit.text = dccData.model().upgradeDownloadSpeedCurrentRate + downloadLineEdit.triggerAlert() + return + } + dccData.work().setUpgradeDeliveryDownloadLimitSpeed(downloadLineEdit.text, downloadLimitCheckBox.checked) + } + Keys.onPressed: { + if (event.key === Qt.Key_Return) { + downloadLineEdit.forceActiveFocus(false); + } + } + Connections { + target: dccData.model() + function onUpgradeDownloadSpeedLimitConfigChanged() { + downloadLineEdit.text = dccData.model().upgradeDownloadSpeedCurrentRate + } + } + } + + D.Label { + text: "KB/S" + } + } + } + } + } + + DccObject { + id: downloadLimitGrp + name: "downloadLimitGrp" + parentName: "advancedSettingGroup" + weight: 50 + enabled: !dccData.model().downloadIsOnlineSpeedLimit + pageType: DccObject.Item + page: DccGroupView { + height: implicitHeight + 10 + spacing: 0 + } + Connections { + target: dccData.model() + function onDownloadSpeedLimitConfigChanged() { + downloadLimitGrp.enabled = !dccData.model().downloadIsOnlineSpeedLimit + } + } DccObject { name: "downloadLimit" @@ -188,7 +610,7 @@ DccObject { enabled: !dccData.model().updateProhibited pageType: DccObject.Editor page: D.Switch { - checked: dccData.model().downloadSpeedLimitEnabled + checked: dccData.model().downloadSpeedLimitEnabled || dccData.model().downloadIsOnlineSpeedLimit onCheckedChanged: { if (checked !== dccData.model().downloadSpeedLimitEnabled) { dccData.work().setDownloadSpeedLimitEnabled(checked) @@ -208,19 +630,29 @@ DccObject { page: RowLayout { D.LineEdit { id: lineEdit - maximumLength: 5 + maximumLength: 6 validator: RegularExpressionValidator { regularExpression: /^\d*$/ } - alertText: qsTr("Only numbers between 1-99999 are allowed") + alertText: qsTr("Only numbers between 10-999999 are allowed") alertDuration: 3000 clearButton.active: lineEdit.activeFocus && (text.length !== 0) text: dccData.model().downloadSpeedLimitSize + function triggerAlert() { + lineEdit.showAlert = true + alertResetTimer.restart() + } + + Timer { + id: alertResetTimer + interval: lineEdit.alertDuration + onTriggered: lineEdit.showAlert = false + } + onTextChanged: { - // 如果输入不为空且数字为0的情况,需要弹出提示且阻止继续输入 - if (lineEdit.text.length !== 0 && lineEdit.text[0] === "0") { + if (lineEdit.text.length !== 0 && (lineEdit.text[0] === "0" || Number(lineEdit.text) > 999999)) { lineEdit.text = "" - lineEdit.showAlert = true - } else if (lineEdit.showAlert) { + lineEdit.triggerAlert() + } else if (lineEdit.showAlert && (lineEdit.text.length === 0 || (Number(lineEdit.text) >= 10 && Number(lineEdit.text) <= 999999))) { lineEdit.showAlert = false } } @@ -229,6 +661,11 @@ DccObject { lineEdit.text = dccData.model().downloadSpeedLimitSize return } + if (Number(lineEdit.text) < 10 || Number(lineEdit.text) > 999999) { + lineEdit.text = dccData.model().downloadSpeedLimitSize + lineEdit.triggerAlert() + return + } dccData.work().setDownloadSpeedLimitSize(lineEdit.text) } Keys.onPressed: { @@ -249,6 +686,7 @@ DccObject { name: "autoDownloadGrp" parentName: "advancedSettingGroup" weight: 50 + visible: !dccData.model().isPrivateUpdate pageType: DccObject.Item page: DccGroupView { height: implicitHeight + 10 @@ -276,7 +714,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 @@ -361,6 +799,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 @@ -377,6 +816,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 @@ -393,6 +833,7 @@ DccObject { parentName: "advancedSettingGrp" displayName: qsTr("Update History") weight: 40 + visible: !dccData.model().isPrivateUpdate backgroundType: DccObject.Normal pageType: DccObject.Editor page: D.Button { @@ -422,7 +863,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_az.ts b/src/dcc-update-plugin/translations/update_az.ts index 5d627f93d..d9b82408e 100644 --- a/src/dcc-update-plugin/translations/update_az.ts +++ b/src/dcc-update-plugin/translations/update_az.ts @@ -91,6 +91,14 @@ Collapse طي + + View More + + + + Collapse + + UpdateLogDialog diff --git a/src/dcc-update-plugin/translations/update_bo.ts b/src/dcc-update-plugin/translations/update_bo.ts index 5c7c6ed8d..3ff6f285c 100644 --- a/src/dcc-update-plugin/translations/update_bo.ts +++ b/src/dcc-update-plugin/translations/update_bo.ts @@ -91,6 +91,14 @@ Collapse + + View More + + + + Collapse + + UpdateLogDialog diff --git a/src/dcc-update-plugin/translations/update_cs.ts b/src/dcc-update-plugin/translations/update_cs.ts index 3c438d6a7..e8f492e77 100644 --- a/src/dcc-update-plugin/translations/update_cs.ts +++ b/src/dcc-update-plugin/translations/update_cs.ts @@ -91,6 +91,14 @@ Collapse + + View More + + + + Collapse + + UpdateLogDialog diff --git a/src/dcc-update-plugin/translations/update_de.ts b/src/dcc-update-plugin/translations/update_de.ts index 28a2ea598..10958d3db 100644 --- a/src/dcc-update-plugin/translations/update_de.ts +++ b/src/dcc-update-plugin/translations/update_de.ts @@ -91,6 +91,14 @@ Collapse + + View More + + + + Collapse + + UpdateLogDialog diff --git a/src/dcc-update-plugin/translations/update_en.ts b/src/dcc-update-plugin/translations/update_en.ts index 6265817c1..67d981c3a 100644 --- a/src/dcc-update-plugin/translations/update_en.ts +++ b/src/dcc-update-plugin/translations/update_en.ts @@ -422,6 +422,22 @@ Delivers updates for additional repository sources + + Delivery Optimization + + + + When enabled, your device may share previously downloaded system updates with other devices on your local network.When you turn it off, cached files from update delivery will be cleared during the next restart. + + + + Delivery Optimization-Limit Speed + + + + Delivery Optimization-Upload throttling + + Limit Speed @@ -490,6 +506,26 @@ Only numbers between 1-99999 are allowed + + Only numbers between 10-999999 are allowed + + + + Failed to change Delivery Optimization setting + + + + Update Delivery Optimization service exception + + + + Try again + + + + Cancel + + Join the internal testing channel to get deepin latest updates. diff --git a/src/dcc-update-plugin/translations/update_en_US.ts b/src/dcc-update-plugin/translations/update_en_US.ts index 0ffd28707..4c76dff05 100644 --- a/src/dcc-update-plugin/translations/update_en_US.ts +++ b/src/dcc-update-plugin/translations/update_en_US.ts @@ -91,6 +91,14 @@ Collapse + + View More + + + + Collapse + + UpdateLogDialog @@ -420,6 +428,22 @@ Delivers updates for additional repository sources + + Delivery Optimization + + + + When enabled, your device may share previously downloaded system updates with other devices on your local network.When you turn it off, cached files from update delivery will be cleared during the next restart. + + + + Delivery Optimization-Limit Speed + + + + Delivery Optimization-Upload throttling + + Limit Speed @@ -488,6 +512,26 @@ Only numbers between 1-99999 are allowed + + Only numbers between 10-999999 are allowed + + + + Failed to change Delivery Optimization setting + + + + Update Delivery Optimization service exception + + + + Try again + + + + Cancel + + Join the internal testing channel to get deepin latest updates. diff --git a/src/dcc-update-plugin/translations/update_fr.ts b/src/dcc-update-plugin/translations/update_fr.ts index 7c6cb442f..8031ef36d 100644 --- a/src/dcc-update-plugin/translations/update_fr.ts +++ b/src/dcc-update-plugin/translations/update_fr.ts @@ -85,11 +85,11 @@ View More - + Collapse - Réduire + Réduire diff --git a/src/dcc-update-plugin/translations/update_hu.ts b/src/dcc-update-plugin/translations/update_hu.ts index 7f59322a4..8e58ff0b5 100644 --- a/src/dcc-update-plugin/translations/update_hu.ts +++ b/src/dcc-update-plugin/translations/update_hu.ts @@ -91,6 +91,14 @@ Collapse + + View More + + + + Collapse + + UpdateLogDialog diff --git a/src/dcc-update-plugin/translations/update_it.ts b/src/dcc-update-plugin/translations/update_it.ts index 19193dae6..9679fe133 100644 --- a/src/dcc-update-plugin/translations/update_it.ts +++ b/src/dcc-update-plugin/translations/update_it.ts @@ -91,6 +91,14 @@ Collapse + + View More + + + + Collapse + + UpdateLogDialog diff --git a/src/dcc-update-plugin/translations/update_lt.ts b/src/dcc-update-plugin/translations/update_lt.ts index db2e49439..27cd15ae0 100644 --- a/src/dcc-update-plugin/translations/update_lt.ts +++ b/src/dcc-update-plugin/translations/update_lt.ts @@ -91,6 +91,14 @@ Collapse + + View More + + + + Collapse + + UpdateLogDialog diff --git a/src/dcc-update-plugin/translations/update_sl.ts b/src/dcc-update-plugin/translations/update_sl.ts index 40f181c1a..56e202e58 100644 --- a/src/dcc-update-plugin/translations/update_sl.ts +++ b/src/dcc-update-plugin/translations/update_sl.ts @@ -91,6 +91,14 @@ Collapse Zaklopi + + View More + + + + Collapse + + UpdateLogDialog diff --git a/src/dcc-update-plugin/translations/update_ug.ts b/src/dcc-update-plugin/translations/update_ug.ts index 9838b9d3f..6da555415 100644 --- a/src/dcc-update-plugin/translations/update_ug.ts +++ b/src/dcc-update-plugin/translations/update_ug.ts @@ -91,6 +91,14 @@ Collapse + + View More + + + + Collapse + + UpdateLogDialog diff --git a/src/dcc-update-plugin/translations/update_zh_CN.ts b/src/dcc-update-plugin/translations/update_zh_CN.ts index 8d04a66ad..5cf9abf3e 100644 --- a/src/dcc-update-plugin/translations/update_zh_CN.ts +++ b/src/dcc-update-plugin/translations/update_zh_CN.ts @@ -255,6 +255,14 @@ Install updates 安装更新 + + Install Now + 立即更新 + + + Check Again + 重新检查更新 + Update download failed 更新下载失败 @@ -282,6 +290,14 @@ UpdateModel + + Current Edition + 私有化更新版本号 + + + Baseline + 补丁版本号 + Checking for updates, please wait… 正在检查更新,请等待… @@ -354,6 +370,14 @@ Turn on the switches under Update Content to get better experiences 开启更新内容开关,可以获得更优质的功能体验哦 + + will upgrade when shutdown + 系统将在计算机关机或重启时进行更新 + + + will upgrade at %1 + 将于%1开始系统更新 + Your system is not activated, and it failed to connect to update services 当前系统未激活,无法启动更新服务 @@ -420,6 +444,22 @@ Delivers updates for additional repository sources 提供额外添加的仓库源更新内容 + + Delivery Optimization + 传递优化 + + + When enabled, your device may share previously downloaded system updates with other devices on your local network.When you turn it off, cached files from update delivery will be cleared during the next restart. + 开启此功能,你的设备可能会将以前下载的部分系统更新发送到本地网络的设备上。关闭此功能后,将在重启时清除传递优化时缓存的文件 + + + Delivery Optimization-Limit Speed + 传递优化-下载限速 + + + Delivery Optimization-Upload throttling + 传递优化-上传限速 + Limit Speed 下载限速 @@ -488,6 +528,26 @@ Only numbers between 1-99999 are allowed 只允许输入1-99999 + + Only numbers between 10-999999 are allowed + 只允许输入10-999999 + + + Failed to change Delivery Optimization setting + 更新传递优化服务异常 + + + Update Delivery Optimization service exception + 更新传递优化服务异常 + + + Try again + 再试一次 + + + Cancel + 取消 + Join the internal testing channel to get deepin latest updates. 加入deepin内测通道,以获取deepin最新更新内容 diff --git a/src/dcc-update-plugin/translations/update_zh_HK.ts b/src/dcc-update-plugin/translations/update_zh_HK.ts index 69de83ec6..008e2cfcc 100644 --- a/src/dcc-update-plugin/translations/update_zh_HK.ts +++ b/src/dcc-update-plugin/translations/update_zh_HK.ts @@ -255,6 +255,14 @@ Install updates 安裝更新 + + Install Now + 立即更新 + + + Check Again + 重新檢查更新 + Update download failed 更新下載失敗 @@ -282,6 +290,14 @@ UpdateModel + + Current Edition + 私有化更新版本號 + + + Baseline + 補丁版本號 + Checking for updates, please wait… 正在檢查更新,請等待… @@ -354,6 +370,14 @@ Turn on the switches under Update Content to get better experiences 開啓更新內容開關,可以獲得更優質的功能體驗哦 + + will upgrade when shutdown + 系統將在計算機關機或重啓時進行更新 + + + will upgrade at %1 + 將於%1開始系統更新 + Your system is not activated, and it failed to connect to update services 當前系統未激活,無法啓動更新服務 @@ -420,6 +444,22 @@ Delivers updates for additional repository sources 提供額外添加的倉庫源更新內容 + + Delivery Optimization + 傳遞優化 + + + When enabled, your device may share previously downloaded system updates with other devices on your local network.When you turn it off, cached files from update delivery will be cleared during the next restart. + 開啟此功能,你的裝置可能會將以前下載的部分系統更新傳送到本地網絡的裝置上。關閉此功能後,將在重新啟動時清除更新傳遞時快取的檔案 + + + Delivery Optimization-Limit Speed + 傳遞優化-下載限速 + + + Delivery Optimization-Upload throttling + 傳遞優化-上傳限速 + Limit Speed 下載限速 @@ -488,6 +528,26 @@ Only numbers between 1-99999 are allowed 只允許輸入1-99999 + + Only numbers between 10-999999 are allowed + 只允許輸入10-999999 + + + Failed to change Delivery Optimization setting + 更新傳遞優化服務異常 + + + Update Delivery Optimization service exception + 更新傳遞優化服務異常 + + + Try again + 再試一次 + + + Cancel + 取消 + Join the internal testing channel to get deepin latest updates. 加入deepin內測通道,以獲取deepin最新更新內容 diff --git a/src/dcc-update-plugin/translations/update_zh_TW.ts b/src/dcc-update-plugin/translations/update_zh_TW.ts index 839f0219f..70d5deeeb 100644 --- a/src/dcc-update-plugin/translations/update_zh_TW.ts +++ b/src/dcc-update-plugin/translations/update_zh_TW.ts @@ -255,6 +255,14 @@ Install updates 安裝更新 + + Install Now + 立即更新 + + + Check Again + 重新檢查更新 + Update download failed 更新下載失敗 @@ -282,6 +290,14 @@ UpdateModel + + Current Edition + 私有化更新版本號 + + + Baseline + 補丁版本號 + Checking for updates, please wait… 正在檢查更新,請等待… @@ -354,6 +370,14 @@ Turn on the switches under Update Content to get better experiences 開啟更新內容開關,可以獲得更優質的功能體驗哦 + + will upgrade when shutdown + 系統將在計算機關機或重啟時進行更新 + + + will upgrade at %1 + 將於%1開始系統更新 + Your system is not activated, and it failed to connect to update services 當前系統未啟用,無法啟動更新服務 @@ -420,6 +444,22 @@ Delivers updates for additional repository sources 提供額外新增的倉庫源更新內容 + + Delivery Optimization + 傳遞優化 + + + When enabled, your device may share previously downloaded system updates with other devices on your local network.When you turn it off, cached files from update delivery will be cleared during the next restart. + 開啟此功能,你的裝置可能會將以前下載的部分系統更新傳送到本機網路的裝置上。關閉此功能後,將在重新啟動時清除更新傳遞時快取的檔案 + + + Delivery Optimization-Limit Speed + 傳遞優化-下載限速 + + + Delivery Optimization-Upload throttling + 傳遞優化-上傳限速 + Limit Speed 下載限速 @@ -488,6 +528,26 @@ Only numbers between 1-99999 are allowed 只允許輸入1-99999 + + Only numbers between 10-999999 are allowed + 只允許輸入10-999999 + + + Failed to change Delivery Optimization setting + 更新傳遞優化服務異常 + + + Update Delivery Optimization service exception + 更新傳遞優化服務異常 + + + Try again + 再試一次 + + + Cancel + 取消 + Join the internal testing channel to get deepin latest updates. 加入deepin內測通道,以獲取deepin最新更新內容 diff --git a/src/dock-update-plugin/pluginupdateplugin.cpp b/src/dock-update-plugin/pluginupdateplugin.cpp index 9195ace5d..74262ebb3 100644 --- a/src/dock-update-plugin/pluginupdateplugin.cpp +++ b/src/dock-update-plugin/pluginupdateplugin.cpp @@ -40,6 +40,7 @@ PluginUpdatePlugin::PluginUpdatePlugin(QObject *parent) , m_currentState(UpdateState::UpdatesAvailable) , m_updateMode(0) , m_shouldShow(false) + , m_isPrivateUpdate(false) { m_tipsLabel->setVisible(false); m_tipsLabel->setAccessibleName("plugin-update"); @@ -104,6 +105,7 @@ void PluginUpdatePlugin::init(PluginProxyInterface *proxyInter) m_updateMode = m_dconfig->value("update-mode").toInt(); m_updateStatus = m_dconfig->value("update-status"); + m_isPrivateUpdate = m_dconfig->value("intranet-update", false).toBool(); connect(m_dconfig.data(), &DConfig::valueChanged, this, &PluginUpdatePlugin::onConfigChanged); @@ -239,7 +241,7 @@ void PluginUpdatePlugin::loadPlugin() void PluginUpdatePlugin::refreshPluginItemsVisible() { - bool shouldShow = !pluginIsDisable() && m_shouldShow; + bool shouldShow = !pluginIsDisable() && m_shouldShow && !m_isPrivateUpdate; if (!shouldShow) { if (m_pluginLoaded) { @@ -288,6 +290,8 @@ void PluginUpdatePlugin::onConfigChanged(const QString &key) } else if (key == "update-status") { m_updateStatus = m_dconfig->value("update-status", QVariant()); qCInfo(dockUpdatePlugin) << "UpdateStatus changed to:" << m_updateStatus; + } else if (key == "intranet-update") { + m_isPrivateUpdate = m_dconfig->value("intranet-update", false).toBool(); } //对dconfig获取的数据进行解析,从而处理更新的状态和是否显示 updateStateFromUpdateStatus(); diff --git a/src/dock-update-plugin/pluginupdateplugin.h b/src/dock-update-plugin/pluginupdateplugin.h index 1f73ed669..af3f60fad 100644 --- a/src/dock-update-plugin/pluginupdateplugin.h +++ b/src/dock-update-plugin/pluginupdateplugin.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2025-2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -76,6 +76,7 @@ class PluginUpdatePlugin : public QObject, PluginsItemInterfaceV2 int m_updateMode; QVariant m_updateStatus; bool m_shouldShow; + bool m_isPrivateUpdate; }; Q_DECLARE_METATYPE(UpdateState) 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..4ba0f1fb7 --- /dev/null +++ b/src/private-lastore-tray/commoniconbutton.cpp @@ -0,0 +1,303 @@ +// 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) + , m_animLabel1(new QLabel(this)) + , m_animLabel2(new QLabel(this)) + , m_effect1(new QGraphicsOpacityEffect(this)) + , m_effect2(new QGraphicsOpacityEffect(this)) + , m_fadeOutAnim(new QPropertyAnimation) + , m_fadeInAnim(new QPropertyAnimation) + , m_animTimer(new QTimer(this)) + , m_showingFirst(true) +{ + setAccessibleName("IconButton"); + setFixedSize(Dock::DOCK_PLUGIN_ITEM_FIXED_SIZE); + if (parent) + setForegroundRole(parent->foregroundRole()); + + m_defaultPalette = palette(); + + m_animLabel1->setGraphicsEffect(m_effect1); + m_animLabel2->setGraphicsEffect(m_effect2); + m_animLabel1->setScaledContents(true); + m_animLabel2->setScaledContents(true); + m_animLabel1->setGeometry(this->rect()); + m_animLabel2->setGeometry(this->rect()); + m_animLabel1->setPixmap(QPixmap(":resources/private-lastore-sleep_16px.svg")); + m_animLabel2->setPixmap(QPixmap(":resources/private-lastore-active_16px.svg")); + m_animLabel1->setFixedSize(Dock::DOCK_PLUGIN_ITEM_FIXED_SIZE); + m_animLabel2->setFixedSize(Dock::DOCK_PLUGIN_ITEM_FIXED_SIZE); + m_animLabel1->hide(); + m_animLabel2->hide(); + + m_effect1->setOpacity(1.0); + m_effect2->setOpacity(0.0); + + m_fadeOutAnim->setDuration(500); + m_fadeOutAnim->setPropertyName("opacity"); + m_fadeInAnim->setDuration(500); + m_fadeInAnim->setPropertyName("opacity"); + + m_fadeOutAnim->setEasingCurve(QEasingCurve::InOutQuad); + m_fadeInAnim->setEasingCurve(QEasingCurve::InOutQuad); + + connect(m_animTimer, &QTimer::timeout, this, &CommonIconButton::switchIcon); + connect(DGuiApplicationHelper::instance(), &DGuiApplicationHelper::themeTypeChanged, this, &CommonIconButton::refreshIcon); +} + +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::startAnimation() +{ + // 通过两张图标交替淡入淡出显示活动状态。 + m_showingFirst = true; + m_effect1->setOpacity(1.0); + m_effect2->setOpacity(0.0); + m_animLabel1->setVisible(true); + m_animLabel2->setVisible(true); + if (!m_animTimer->isActive()) { + m_animTimer->start(2000); + } +} + +void CommonIconButton::stopAnimation() +{ + m_animLabel1->setVisible(false); + m_animLabel2->setVisible(false); + m_animTimer->stop(); +} + +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::switchIcon() +{ + // 切换当前显示的图标层并启动透明度动画。 + if (m_showingFirst) { + m_fadeOutAnim->setTargetObject(m_effect1); + m_fadeOutAnim->setStartValue(1.0); + m_fadeOutAnim->setEndValue(0.0); + + m_fadeInAnim->setTargetObject(m_effect2); + m_fadeInAnim->setStartValue(0.0); + m_fadeInAnim->setEndValue(1.0); + } else { + m_fadeOutAnim->setTargetObject(m_effect2); + m_fadeOutAnim->setStartValue(1.0); + m_fadeOutAnim->setEndValue(0.0); + + m_fadeInAnim->setTargetObject(m_effect1); + m_fadeInAnim->setStartValue(0.0); + m_fadeInAnim->setEndValue(1.0); + } + + m_fadeOutAnim->start(); + m_fadeInAnim->start(); + + m_showingFirst = !m_showingFirst; +} + +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; + + // 浅色主题下优先使用带 `-dark` 后缀的图标资源。 + 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); + + if (m_animLabel1->isVisible() || m_animLabel2->isVisible()) { + return; + } + + 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..88cec92db --- /dev/null +++ b/src/private-lastore-tray/commoniconbutton.h @@ -0,0 +1,99 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#ifndef ICONBUTTON_H +#define ICONBUTTON_H +#include "tipswidget.h" + +#include +#include +#include +#include +#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;} + void startAnimation(); + void stopAnimation(); + +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(); + // 在两张动画帧之间切换透明度。 + void switchIcon(); + +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; + + QLabel *m_animLabel1; + QLabel *m_animLabel2; + QGraphicsOpacityEffect* m_effect1; + QGraphicsOpacityEffect* m_effect2; + QPropertyAnimation* m_fadeOutAnim; + QPropertyAnimation* m_fadeInAnim; + QTimer* m_animTimer; + bool m_showingFirst; +}; + +#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..11ef54f17 --- /dev/null +++ b/src/private-lastore-tray/pluginproxyinterface.h @@ -0,0 +1,43 @@ +// 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: + // 向 dock 添加条目。 + virtual void itemAdded(PluginsItemInterface * const itemInter, const QString &itemKey) = 0; + + // 通知 dock 刷新条目。 + virtual void itemUpdate(PluginsItemInterface * const itemInter, const QString &itemKey) = 0; + + // 从 dock 移除条目。 + virtual void itemRemoved(PluginsItemInterface * const itemInter, const QString &itemKey) = 0; + + // 请求显示条目上下文菜单。 + //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; + + // 保存插件配置。 + virtual void saveValue(PluginsItemInterface * const itemInter, const QString &key, const QVariant &value) = 0; + + // 读取插件配置。 + virtual const QVariant getValue(PluginsItemInterface *const itemInter, const QString &key, const QVariant& fallback = QVariant()) = 0; + + // 删除插件配置。 + 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..75c30fc8c --- /dev/null +++ b/src/private-lastore-tray/pluginsiteminterface.h @@ -0,0 +1,118 @@ +// 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 + +// Dock 插件条目接口,定义条目显示和交互能力。 +class PluginsItemInterface +{ +public: + enum PluginType { + Normal, + Fixed + }; + + // 条目尺寸跟随系统或使用自定义策略。 + enum PluginSizePolicy { + System = 1 << 0, + Custom = 1 << 1 + }; + + // 销毁插件条目对象。 + virtual ~PluginsItemInterface() {} + + // 返回插件唯一标识。 + virtual const QString pluginName() const = 0; + virtual const QString pluginDisplayName() const { return QString(); } + + // 初始化条目并接收 dock 代理接口。 + virtual void init(PluginProxyInterface *proxyInter) = 0; + + // 返回条目控件。 + virtual QWidget *itemWidget(const QString &itemKey) = 0; + + // 返回条目悬浮提示控件。 + virtual QWidget *itemTipsWidget(const QString &itemKey) {Q_UNUSED(itemKey); return nullptr;} + + // 返回条目点击后弹出的内容控件。 + virtual QWidget *itemPopupApplet(const QString &itemKey) {Q_UNUSED(itemKey); return nullptr;} + + // 返回点击条目时执行的命令。 + virtual const QString itemCommand(const QString &itemKey) {Q_UNUSED(itemKey); return QString();} + + // 返回条目上下文菜单定义。 + virtual const QString itemContextMenu(const QString &itemKey) {Q_UNUSED(itemKey); return QString();} + + // 处理上下文菜单点击事件。 + virtual void invokedMenuItem(const QString &itemKey, const QString &menuId, const bool checked) {Q_UNUSED(itemKey); Q_UNUSED(menuId); Q_UNUSED(checked);} + + // 返回条目排序位置。 + virtual int itemSortKey(const QString &itemKey) {Q_UNUSED(itemKey); return 1;} + + // 保存条目排序位置。 + virtual void setSortKey(const QString &itemKey, const int order) {Q_UNUSED(itemKey); Q_UNUSED(order);} + + // 返回条目是否允许进入容器区域。 + virtual bool itemAllowContainer(const QString &itemKey) {Q_UNUSED(itemKey); return false;} + + // 返回条目当前是否在容器中。 + virtual bool itemIsInContainer(const QString &itemKey) {Q_UNUSED(itemKey); return false;} + + // 保存条目容器状态。 + 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() {} + + // 处理 dock 显示模式变化。 + virtual void displayModeChanged(const Dock::DisplayMode displayMode) {Q_UNUSED(displayMode);} + + // 处理 dock 停靠位置变化。 + virtual void positionChanged(const Dock::Position position) {Q_UNUSED(position);} + + // 刷新条目图标。 + virtual void refreshIcon(const QString &itemKey) { Q_UNUSED(itemKey); } + + // 返回当前 dock 显示模式。 + inline Dock::DisplayMode displayMode() const + { + return qApp->property(PROP_DISPLAY_MODE).value(); + } + + // 返回当前 dock 停靠位置。 + inline Dock::Position position() const + { + return qApp->property(PROP_POSITION).value(); + } + + // 处理插件设置变化。 + virtual void pluginSettingsChanged() {} + + // 返回插件类型。 + QT_DEPRECATED_X("Use flags()") + virtual PluginType type() { return Normal; } + + // 返回条目尺寸策略。 + virtual PluginSizePolicy pluginSizePolicy() const { return System; } + +protected: + // 保存 dock 代理接口。 + 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..6ab692906 --- /dev/null +++ b/src/private-lastore-tray/privatelastoreitem.cpp @@ -0,0 +1,115 @@ +// 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 + +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(), this)) +{ + 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); + onStartAnimation(""); + connect(m_managerInter, &UpdateDBusProxy::JobListChanged, this, &PrivateLastoreItem::onRefreshIcon); + connect(m_managerInter, &UpdateDBusProxy::UpdateStatusChanged, this, &PrivateLastoreItem::onStartAnimation); +} + +QWidget* PrivateLastoreItem::tipsWidget() +{ + m_tipsLabel->refreshContent(); + return m_tipsLabel; +} + +void PrivateLastoreItem::refreshTrayIcon() +{ + 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); +} + +void PrivateLastoreItem::onRefreshIcon(const QList &jobs) +{ + qInfo() << "Update job list changed"; + + for (const auto &job : jobs) { + const QString &jobPath = job.path(); + qInfo() << "Update job path:" << jobPath; + UpdateJobDBusProxy jobInter(jobPath, this); + if (!jobInter.isValid()) { + qWarning() << "Invalid update job"; + continue; + } + + const QString &id = jobInter.id(); + qInfo() << "Update job id:" << id; + // 下载或安装任务存在时显示更新动画。 + if (id == "dist_upgrade" || id == "prepare_dist_upgrade") { + m_icon->startAnimation(); + return; + } + } + m_icon->setIcon(QIcon(":resources/private-lastore-sleep_16px.svg")); +} + +void PrivateLastoreItem::onStartAnimation(const QString &updateStatus) +{ + Q_UNUSED(updateStatus) + + // 从更新状态中提取 system_upgrade 状态控制动画。 + QString systemUpgradeStatus = checkHasSystemUpdate(m_managerInter->updateStatus()); + if (systemUpgradeStatus == UPDATE_STATUS_UpdatesAvailable || systemUpgradeStatus == UPDATE_STATUS_Downloading || + systemUpgradeStatus == UPDATE_STATUS_DownloadPaused || systemUpgradeStatus == UPDATE_STATUS_UpgradeReady + || systemUpgradeStatus == UPDATE_STATUS_Upgrading || systemUpgradeStatus == UPDATE_STATUS_Downloaded) + m_icon->startAnimation(); + else + m_icon->stopAnimation(); +} + +void PrivateLastoreItem::resizeEvent(QResizeEvent* e) +{ + QWidget::resizeEvent(e); + + // 按 dock 停靠方向将条目约束为正方形区域。 + 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..2e177459f --- /dev/null +++ b/src/private-lastore-tray/privatelastoreitem.h @@ -0,0 +1,46 @@ +// 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 PrivateLastoreItem : public QWidget +{ + Q_OBJECT + +public: + explicit PrivateLastoreItem(QWidget *parent = nullptr); + + QWidget *tipsWidget(); + void refreshTrayIcon(); + +protected: + void resizeEvent(QResizeEvent *e); + void mouseReleaseEvent(QMouseEvent *event) override; + +private slots: + void onRefreshIcon(const QList &jobs); + void onStartAnimation(const QString &updateStatus); + +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..872d9823b --- /dev/null +++ b/src/private-lastore-tray/privatelastoreplugin.cpp @@ -0,0 +1,134 @@ +// 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 + +#define PRIVATE_LASTORE_KEY "private-lastore-key" +#define STATE_KEY "enabled" + +DCORE_USE_NAMESPACE +Q_LOGGING_CATEGORY(dockPrivateUpdatePlugin, "org.deepin.dde.dock.update") + +PrivateLastorePlugin::PrivateLastorePlugin(QObject *parent) + : QObject(parent) + , m_item(new PrivateLastoreItem) + , m_pluginLoaded(false) + , m_shouldShow(false) + , m_dconfig(DConfig::create("org.deepin.dde.lastore", "org.deepin.dde.lastore", QString(), this)) + , m_dockTrayConfig(DConfig::create("org.deepin.dde.shell", "org.deepin.ds.dock.tray", QString(), this)) +{ + QTranslator *translator = new QTranslator(this); + const bool loaded = translator->load(QLocale(), "private-lastore-tray", "_", "/usr/share/private-lastore-tray/translations"); + if (!loaded) { + qCWarning(dockPrivateUpdatePlugin) << "Failed to load private-lastore-tray translations"; + } + QCoreApplication::installTranslator(translator); + + connect(m_dconfig.data(), &DConfig::valueChanged, this, &PrivateLastorePlugin::onConfigChanged); +} + +PrivateLastorePlugin::~PrivateLastorePlugin() +{ + if (m_item) { + delete m_item; + m_item = nullptr; + } +} + +const QString PrivateLastorePlugin::pluginName() const +{ + return "private-lastore"; +} + +const QString PrivateLastorePlugin::pluginDisplayName() const +{ + return tr("Private Lastore"); +} + +void PrivateLastorePlugin::init(PluginProxyInterface *proxyInter) +{ + if (!m_dconfig) { + qCWarning(dockPrivateUpdatePlugin) << "Failed to create DConfig for org.deepin.dde.lastore"; + return; + } + if (!m_dockTrayConfig) { + qCWarning(dockPrivateUpdatePlugin) << "Failed to create DConfig for org.deepin.ds.dock.tray"; + } + m_proxyInter = proxyInter; + loadPlugin(); + + m_shouldShow = m_dconfig->value("intranet-update", false).toBool(); + updateDockHiddenSurfaceIds(!m_shouldShow); +} + +QWidget *PrivateLastorePlugin::itemWidget(const QString &itemKey) +{ + return m_item; +} + +QWidget *PrivateLastorePlugin::itemTipsWidget(const QString &itemKey) +{ + return m_item->tipsWidget(); +} + +void PrivateLastorePlugin::loadPlugin() +{ + if (m_pluginLoaded) { + return; + } + m_proxyInter->itemAdded(this, pluginName()); + m_proxyInter->saveValue(this, STATE_KEY, true); + m_pluginLoaded = true; + + m_item->refreshTrayIcon(); +} + +void PrivateLastorePlugin::onConfigChanged(const QString &key) +{ + if (key == "intranet-update") { + m_shouldShow = m_dconfig->value("intranet-update", false).toBool(); + } + refreshPluginItemsVisible(); +} + +void PrivateLastorePlugin::refreshPluginItemsVisible() +{ + if (!m_shouldShow) { + updateDockHiddenSurfaceIds(true); + } else { + if (!m_pluginLoaded) { + loadPlugin(); + return; + } + updateDockHiddenSurfaceIds(false); + m_proxyInter->itemAdded(this, pluginName()); + } +} + +void PrivateLastorePlugin::updateDockHiddenSurfaceIds(bool shouldHide) +{ + const QString surfaceId = "private-lastore::private-lastore"; + QStringList hiddenIds = m_dockTrayConfig->value("dockHiddenSurfaceIds").toStringList(); + + if (shouldHide) { + // 添加到隐藏列表 + if (!hiddenIds.contains(surfaceId)) { + hiddenIds.append(surfaceId); + m_dockTrayConfig->setValue("dockHiddenSurfaceIds", hiddenIds); + qCInfo(dockPrivateUpdatePlugin) << "Added" << surfaceId << "to dockHiddenSurfaceIds"; + } + } else { + // 从隐藏列表移除 + if (hiddenIds.contains(surfaceId)) { + hiddenIds.removeAll(surfaceId); + m_dockTrayConfig->setValue("dockHiddenSurfaceIds", hiddenIds); + qCInfo(dockPrivateUpdatePlugin) << "Removed" << surfaceId << "from dockHiddenSurfaceIds"; + } + } +} \ No newline at end of file diff --git a/src/private-lastore-tray/privatelastoreplugin.h b/src/private-lastore-tray/privatelastoreplugin.h new file mode 100644 index 000000000..867539716 --- /dev/null +++ b/src/private-lastore-tray/privatelastoreplugin.h @@ -0,0 +1,49 @@ +// 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 + +#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(); + void refreshPluginItemsVisible(); + void updateDockHiddenSurfaceIds(bool shouldHide); + void onConfigChanged(const QString &key); + +private: + PrivateLastoreItem *m_item; + QSharedPointer m_dconfig; + QSharedPointer m_dockTrayConfig; + bool m_shouldShow; + bool m_pluginLoaded; +}; + +#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..72be64a1d --- /dev/null +++ b/src/private-lastore-tray/tipswidget.cpp @@ -0,0 +1,348 @@ +// 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 + +Q_LOGGING_CATEGORY(dockUpdatePlugin, "org.deepin.dde.dock.update") +#define PADDING 4 +#define SHUTDOWNUPDATESTATUS 5 + +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); + connect(m_managerInter, &UpdateDBusProxy::DownloadLimitOnChangingChanged, this, &TipsWidget::onDownloadLimitChanged); + } +} + +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() +{ + QString systemUpgradeStatus = checkHasSystemUpdate(m_managerInter->updateStatus()); + if (systemUpgradeStatus != UPDATE_STATUS_Downloaded) { + return false; + } + 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 systemUpgradeStatus = checkHasSystemUpdate(m_managerInter->updateStatus()); + if (systemUpgradeStatus != UPDATE_STATUS_Downloaded) { + return false; + } + + 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; +} + +bool TipsWidget::hasUnfinishedJob(const QList &jobs) const +{ + for (const auto &job : jobs) { + UpdateJobDBusProxy jobInter(job.path()); + if (!jobInter.isValid()) { + continue; + } + + QString curJobId = jobInter.id(); + if (curJobId != "prepare_dist_upgrade" + && curJobId != "dist_upgrade" + && curJobId != "update_source") { + continue; + } + + const QString status = jobInter.status(); + if (status != "failed" && status != "end") { + return true; + } + } + + return false; +} + +void TipsWidget::onSetUpdateProto(const QString& proto) +{ + m_proto = proto; +} + +void TipsWidget::onSetUpdateSpeed(qlonglong speed) +{ + m_speed = speed; +} + +void TipsWidget::onDownloadLimitChanged(bool value) { + m_downloadLimitOnChanging = value; +} + +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("Changing download speed limit. Please wait")); + return; + } + + // 优先根据当前任务生成提示,没有任务时再根据总体状态生成提示。 + if (m_managerInter) { + QList jobList = m_managerInter->jobList(); + if (jobList.isEmpty() || !hasUnfinishedJob(jobList)) { + // 没有任务时优先显示下载完成后的后续状态。 + if (checkShutdownUpdate()) return; + if (checkRegularlyUpdate()) return; + QString systemUpgradeStatus = checkHasSystemUpdate(m_managerInter->updateStatus()); + if (systemUpgradeStatus == UPDATE_STATUS_BackupFailed || systemUpgradeStatus == UPDATE_STATUS_UpgradeFailed) { + m_textList.append(tr("Upgrade failed. Please check.")); + return; + } else if (systemUpgradeStatus == UPDATE_STATUS_UpgradeSuccess) { + m_textList.append(tr("Upgrade complete. Please reboot.")); + return; + } else if (systemUpgradeStatus == UPDATE_STATUS_Downloaded) { + m_textList.append(tr("Download complete. Please open Control Center to check.")); + } else if (systemUpgradeStatus == UPDATE_STATUS_UpdatesAvailable || systemUpgradeStatus == UPDATE_STATUS_Downloading|| + systemUpgradeStatus == UPDATE_STATUS_DownloadPaused || systemUpgradeStatus == UPDATE_STATUS_UpgradeReady + || systemUpgradeStatus == UPDATE_STATUS_Upgrading) { + m_textList.append(tr("New version available. Please check.")); + return; + } + } else { + for (const auto &job : jobList) { + const QString &jobPath = job.path(); + qInfo() << "Update job path:" << jobPath; + UpdateJobDBusProxy jobInter(jobPath, this); + if (!jobInter.isValid()) { + qWarning() << "Invalid update job"; + 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 progress"); + 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("Downloading"); + QString curProgress = tr("Current download progress"); + 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 progress"); + curStatus = tr("Installing"); + 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 == UPDATE_STATUS_UpdatesAvailable || systemUpgradeStatus == UPDATE_STATUS_Downloading|| + systemUpgradeStatus == UPDATE_STATUS_DownloadPaused || systemUpgradeStatus == UPDATE_STATUS_UpgradeReady + || systemUpgradeStatus == UPDATE_STATUS_Upgrading || systemUpgradeStatus == UPDATE_STATUS_Downloaded) { + // 更新源检查任务存在时仍保持新版本提示。 + m_textList.append(tr("New version available. Please check.")); + } + } + } else { + // 非 failed/end 且暂不展示进度的任务先跳过。 + 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::onRefreshJobList(const QList &jobs) +{ + qInfo() << "Update 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") { + if (m_updateJobInter) { + disconnect(m_updateJobInter, nullptr, this, nullptr); + m_updateJobInter->deleteLater(); + } + m_updateJobInter = new UpdateJobDBusProxy(jobPath, this); + connect(m_updateJobInter, &UpdateJobDBusProxy::ProgressChanged, this, &TipsWidget::onSetUpdateProgress); + connect(m_updateJobInter, &UpdateJobDBusProxy::ProtoChanged, this, &TipsWidget::onSetUpdateProto); + connect(m_updateJobInter, &UpdateJobDBusProxy::SpeedChanged, this, &TipsWidget::onSetUpdateSpeed); + } else if (id == "backup") { + m_backupJobInter = new UpdateJobDBusProxy(jobPath, this); + connect(m_backupJobInter, &UpdateJobDBusProxy::ProgressChanged, this, &TipsWidget::onSetBackUpProgress); + } + } +} +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 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..c3435e4b5 --- /dev/null +++ b/src/private-lastore-tray/tipswidget.h @@ -0,0 +1,101 @@ +// 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" + +#define UPDATE_STATUS_Default "noUpdate" +#define UPDATE_STATUS_UpdatesAvailable "notDownload" +#define UPDATE_STATUS_Downloading "isDownloading" +#define UPDATE_STATUS_DownloadPaused "downloadPause" +#define UPDATE_STATUS_DownloadFailed "downloadFailed" +#define UPDATE_STATUS_Downloaded "downloaded" +#define UPDATE_STATUS_BackingUp "backingUp" +#define UPDATE_STATUS_BackupFailed "backupFailed" +#define UPDATE_STATUS_BackupSuccess "hasBackedUp" +#define UPDATE_STATUS_UpgradeReady "upgradeReady" +#define UPDATE_STATUS_Upgrading "upgrading" +#define UPDATE_STATUS_UpgradeFailed "upgradeFailed" +#define UPDATE_STATUS_UpgradeSuccess "needReboot" + +// 从更新状态 JSON 中提取 system_upgrade 字段。 +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(); + // 判断任务列表中是否仍有未进入 failed/end 的任务。 + bool hasUnfinishedJob(const QList &jobs) const; + // 将下载速度格式化为展示文本。 + QString regulateSpeed(); + +private slots: + void onRefreshJobList(const QList &jobs); + void onSetUpdateProgress(double progress); + void onSetBackUpProgress(double progress); + void onSetUpdateProto(const QString &proto); + void onSetUpdateSpeed(qlonglong speed); + void onDownloadLimitChanged(bool value); + +private: + UpdateDBusProxy *m_managerInter = nullptr; + UpdateJobDBusProxy *m_updateJobInter = nullptr; + UpdateJobDBusProxy *m_backupJobInter = nullptr; + QString m_text; + QStringList m_textList; + ShowType m_type; + qlonglong 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_en.ts b/src/private-lastore-tray/translations/private-lastore-tray_en.ts new file mode 100644 index 000000000..782539a02 --- /dev/null +++ b/src/private-lastore-tray/translations/private-lastore-tray_en.ts @@ -0,0 +1,74 @@ + + + + + PrivateLastorePlugin + + Private Lastore + + + + + TipsWidget + + Download complete + + + + Shutdown update + + + + Will upgrade at %1 + + + + Download complete. Please open Control Center to check. + + + + Changing download speed limit. Please wait + + + + Upgrade complete. Please reboot. + + + + Upgrade failed. Please check. + + + + New version available. Please check. + + + + Current upgrade progress + + + + Backing up + + + + Downloading + + + + Current download progress + + + + Current speed + + + + Installing + + + + Enterprise Upgrade Management System + + + + diff --git a/src/private-lastore-tray/translations/private-lastore-tray_en_US.ts b/src/private-lastore-tray/translations/private-lastore-tray_en_US.ts new file mode 100644 index 000000000..782539a02 --- /dev/null +++ b/src/private-lastore-tray/translations/private-lastore-tray_en_US.ts @@ -0,0 +1,74 @@ + + + + + PrivateLastorePlugin + + Private Lastore + + + + + TipsWidget + + Download complete + + + + Shutdown update + + + + Will upgrade at %1 + + + + Download complete. Please open Control Center to check. + + + + Changing download speed limit. Please wait + + + + Upgrade complete. Please reboot. + + + + Upgrade failed. Please check. + + + + New version available. Please check. + + + + Current upgrade progress + + + + Backing up + + + + Downloading + + + + Current download progress + + + + Current speed + + + + Installing + + + + Enterprise 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..16bf9bdb2 --- /dev/null +++ b/src/private-lastore-tray/translations/private-lastore-tray_zh_CN.ts @@ -0,0 +1,74 @@ + + + + + PrivateLastorePlugin + + Private Lastore + 更新独立客户端 + + + + TipsWidget + + Download complete + 更新包下载完成 + + + Shutdown update + 将在下次关机/重启时开始更新 + + + Will upgrade at %1 + 将于%1开始系统更新 + + + Download complete. Please open Control Center to check. + 更新包下载完成,点击图标进入更新界面进行查看与安装更新 + + + Changing download speed limit. Please wait + 下载限速配置切换中,切换完成后将会继续下载更新资源 + + + Upgrade complete. Please reboot. + 更新完成,请重启系统 + + + Upgrade failed. Please check. + 更新失败,点击查看 + + + New version available. Please check. + 有新版本,点击图标查看 + + + Current upgrade progress + 更新进度 + + + Backing up + 备份中 + + + Downloading + 更新包下载中 + + + Current download progress + 已下载 + + + Current speed + 下载速度 + + + Installing + 系统更新中,请勿关闭计算机 + + + Enterprise Upgrade Management System + 企业级更新管理系统 + + + diff --git a/src/private-lastore-tray/translations/private-lastore-tray_zh_HK.ts b/src/private-lastore-tray/translations/private-lastore-tray_zh_HK.ts new file mode 100644 index 000000000..7789dc2ac --- /dev/null +++ b/src/private-lastore-tray/translations/private-lastore-tray_zh_HK.ts @@ -0,0 +1,74 @@ + + + + + PrivateLastorePlugin + + Private Lastore + 更新獨立客戶端 + + + + TipsWidget + + Download complete + 更新包下載完成 + + + Shutdown update + 將在下次關機/重啟時開始更新 + + + Will upgrade at %1 + 將於%1開始系統更新 + + + Download complete. Please open Control Center to check. + 更新包下載完成,點擊圖標進入更新界面進行查看與安裝更新 + + + Changing download speed limit. Please wait + 下載限速配置切換中,切換完成後將會繼續下載更新資源 + + + Upgrade complete. Please reboot. + 更新完成,請重啟系統 + + + Upgrade failed. Please check. + 更新失敗,點擊查看 + + + New version available. Please check. + 有新版本,點擊圖標查看 + + + Current upgrade progress + 更新進度 + + + Backing up + 備份中 + + + Downloading + 更新包下載中 + + + Current download progress + 已下載 + + + Current speed + 下載速度 + + + Installing + 系統更新中,請勿關閉計算機 + + + Enterprise Upgrade Management System + 企業級更新管理系統 + + + diff --git a/src/private-lastore-tray/translations/private-lastore-tray_zh_TW.ts b/src/private-lastore-tray/translations/private-lastore-tray_zh_TW.ts new file mode 100644 index 000000000..7112f93e1 --- /dev/null +++ b/src/private-lastore-tray/translations/private-lastore-tray_zh_TW.ts @@ -0,0 +1,74 @@ + + + + + PrivateLastorePlugin + + Private Lastore + 更新獨立客戶端 + + + + TipsWidget + + Download complete + 更新包下載完成 + + + Shutdown update + 將在下次關機/重新啟動時開始更新 + + + Will upgrade at %1 + 將於%1開始系統更新 + + + Download complete. Please open Control Center to check. + 更新包下載完成,點擊圖標進入更新介面進行查看與安裝更新 + + + Changing download speed limit. Please wait + 下載限速配置切換中,切換完成後將會繼續下載更新資源 + + + Upgrade complete. Please reboot. + 更新完成,請重新啟動系統 + + + Upgrade failed. Please check. + 更新失敗,點擊查看 + + + New version available. Please check. + 有新版本,點擊圖標查看 + + + Current upgrade progress + 更新進度 + + + Backing up + 備份中 + + + Downloading + 更新包下載中 + + + Current download progress + 已下載 + + + Current speed + 下載速度 + + + Installing + 系統更新中,請勿關閉電腦 + + + Enterprise Upgrade Management System + 企業級更新管理系統 + + +