File indexing completed on 2024-04-14 05:26:59

0001 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0002 // SPDX-FileCopyrightText: 2020-2021 Harald Sitter <sitter@kde.org>
0003 
0004 #include "smartnotifier.h"
0005 
0006 #include <KIO/ApplicationLauncherJob>
0007 #include <KLocalizedString>
0008 #include <KNotification>
0009 #include <KService>
0010 
0011 #include <QDebug>
0012 
0013 #include "device.h"
0014 #include "smartmonitor.h"
0015 
0016 class FailureNotification : public QObject
0017 {
0018     Q_OBJECT
0019 public:
0020     FailureNotification(const Device *device, QObject *parent = nullptr)
0021         : QObject(parent)
0022     {
0023         m_notification->setComponentName(QStringLiteral("org.kde.kded.smart"));
0024         if (device->failed()) {
0025             m_notification->setIconName(QStringLiteral("data-warning"));
0026         } else {
0027             m_notification->setIconName(QStringLiteral("data-information"));
0028         }
0029         m_notification->setTitle(i18nc("@title notification", "Storage Device Problems"));
0030         if (device->failed()) {
0031             m_notification->setText(xi18nc("@info notification; text %1 is a pretty product name; %2 the device path e.g. /dev/sda",
0032                                            "The storage device <emphasis>%1</emphasis> (<filename>%2</filename>) is likely to fail soon!",
0033                                            device->product(),
0034                                            device->path()));
0035         } else {
0036             m_notification->setText(xi18nc("@info notification; text %1 is a pretty product name; %2 the device path e.g. /dev/sda",
0037                                            "The storage device <emphasis>%1</emphasis> (<filename>%2</filename>) is showing indications of instability.",
0038                                            device->product(),
0039                                            device->path()));
0040         }
0041 
0042         KService::Ptr kcm = KService::serviceByDesktopName(QStringLiteral("kcm_disks"));
0043         Q_ASSERT(kcm); // there's a bug or installation is broken; mustn't happen in production
0044         auto manageAction = m_notification->addAction(i18nc("@action:button notification action to manage device problems", "Manage"));
0045         connect(manageAction, &KNotificationAction::activated, this, [kcm] {
0046             KIO::ApplicationLauncherJob(kcm).start();
0047         });
0048 
0049         connect(m_notification, &KNotification::closed, this, [this] {
0050             deleteLater();
0051             m_notification = nullptr;
0052         });
0053 
0054         m_notification->sendEvent();
0055     }
0056 
0057     ~FailureNotification() override
0058     {
0059         if (m_notification) {
0060             m_notification->close();
0061         }
0062     }
0063 
0064 private:
0065     KNotification *m_notification = new KNotification{QStringLiteral("imminentDeviceFailure"), KNotification::Persistent, nullptr};
0066 };
0067 
0068 SMARTNotifier::SMARTNotifier(SMARTMonitor *monitor, QObject *parent)
0069     : QObject(parent)
0070 {
0071     connect(monitor, &SMARTMonitor::deviceAdded, this, [this](const Device *device) {
0072         connect(device, &Device::failedChanged, this, &SMARTNotifier::onMaybeFailed);
0073         // The device may already be in failure state. Make sure we display it if applicable.
0074         maybeFailed(device);
0075     });
0076     // upon removal the devices are deleted which takes care of disconnecting
0077 }
0078 
0079 void SMARTNotifier::onMaybeFailed()
0080 {
0081     maybeFailed(qobject_cast<Device *>(sender()));
0082 }
0083 
0084 void SMARTNotifier::maybeFailed(const Device *device)
0085 {
0086     Q_ASSERT(device);
0087     // NB: do not notify on instabilities in 5.22 it's shown to raise false positives or at least be annoying when
0088     //   failure seems unlikely. fixing it properly requires larger changes to the UI and strings
0089     // https://bugs.kde.org/show_bug.cgi?id=438539
0090     if ((!device->failed() /*&& device->instabilities().isEmpty()*/) || device->ignore()) {
0091         return;
0092     }
0093 
0094     new FailureNotification(device, this); // auto-deletes
0095     // once displayed we'll not want to trigger any more notifications
0096     device->disconnect(this);
0097 }
0098 
0099 #include "smartnotifier.moc"
0100 
0101 #include "moc_smartnotifier.cpp"