File indexing completed on 2024-05-05 17:43:52

0001 /*
0002     SPDX-FileCopyrightText: 2006 Lukas Tinkl <ltinkl@suse.cz>
0003     SPDX-FileCopyrightText: 2008 Lubos Lunak <l.lunak@suse.cz>
0004     SPDX-FileCopyrightText: 2009 Ivo Anjo <knuckles@gmail.com>
0005     SPDX-FileCopyrightText: 2020 Kai Uwe Broulik <kde@broulik.de>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "freespacenotifier.h"
0011 
0012 #include <KNotification>
0013 #include <KNotificationJobUiDelegate>
0014 
0015 #include <KIO/ApplicationLauncherJob>
0016 #include <KIO/FileSystemFreeSpaceJob>
0017 #include <KIO/OpenUrlJob>
0018 
0019 #include <chrono>
0020 
0021 #include "settings.h"
0022 
0023 FreeSpaceNotifier::FreeSpaceNotifier(const QString &path, const KLocalizedString &notificationText, QObject *parent)
0024     : QObject(parent)
0025     , m_path(path)
0026     , m_notificationText(notificationText)
0027 {
0028     connect(&m_timer, &QTimer::timeout, this, &FreeSpaceNotifier::checkFreeDiskSpace);
0029     m_timer.start(std::chrono::minutes(1));
0030 }
0031 
0032 FreeSpaceNotifier::~FreeSpaceNotifier()
0033 {
0034     if (m_notification) {
0035         m_notification->close();
0036     }
0037 }
0038 
0039 void FreeSpaceNotifier::checkFreeDiskSpace()
0040 {
0041     if (!FreeSpaceNotifierSettings::enableNotification()) {
0042         // do nothing if notifying is disabled;
0043         // also stop the timer that probably got us here in the first place
0044         m_timer.stop();
0045         return;
0046     }
0047 
0048     auto *job = KIO::fileSystemFreeSpace(QUrl::fromLocalFile(m_path));
0049     connect(job, &KIO::FileSystemFreeSpaceJob::result, this, [this](KIO::Job *job, KIO::filesize_t size, KIO::filesize_t available) {
0050         if (job->error()) {
0051             return;
0052         }
0053 
0054         const int limit = FreeSpaceNotifierSettings::minimumSpace(); // MiB
0055         const qint64 avail = available / (1024 * 1024); // to MiB
0056 
0057         if (avail >= limit) {
0058             if (m_notification) {
0059                 m_notification->close();
0060             }
0061             return;
0062         }
0063 
0064         const int availPercent = int(100 * available / size);
0065         const QString text = m_notificationText.subs(avail).subs(availPercent).toString();
0066 
0067         // Make sure the notification text is always up to date whenever we checked free space
0068         if (m_notification) {
0069             m_notification->setText(text);
0070         }
0071 
0072         // User freed some space, warn if it goes low again
0073         if (m_lastAvail > -1 && avail > m_lastAvail) {
0074             m_lastAvail = avail;
0075             return;
0076         }
0077 
0078         // Always warn the first time or when available space dropped to half of the previous time
0079         const bool warn = (m_lastAvail < 0 || avail < m_lastAvail / 2);
0080         if (!warn) {
0081             return;
0082         }
0083 
0084         m_lastAvail = avail;
0085 
0086         if (!m_notification) {
0087             m_notification = new KNotification(QStringLiteral("freespacenotif"));
0088             m_notification->setComponentName(QStringLiteral("freespacenotifier"));
0089             m_notification->setText(text);
0090 
0091             QStringList actions = {i18n("Configure Warning…")};
0092 
0093             auto filelight = filelightService();
0094             if (filelight) {
0095                 actions.prepend(i18n("Open in Filelight"));
0096             } else {
0097                 // Do we really want the user opening Root in a file manager?
0098                 actions.prepend(i18n("Open in File Manager"));
0099             }
0100 
0101             m_notification->setActions(actions);
0102 
0103             connect(m_notification, &KNotification::activated, this, [this](uint actionId) {
0104                 if (actionId == 1) {
0105                     exploreDrive();
0106                     // TODO once we have "configure" action support in KNotification, wire it up instead of a button
0107                 } else if (actionId == 2) {
0108                     Q_EMIT configureRequested();
0109                 }
0110             });
0111 
0112             connect(m_notification, &KNotification::closed, this, &FreeSpaceNotifier::onNotificationClosed);
0113             m_notification->sendEvent();
0114         }
0115     });
0116 }
0117 
0118 KService::Ptr FreeSpaceNotifier::filelightService() const
0119 {
0120     return KService::serviceByDesktopName(QStringLiteral("org.kde.filelight"));
0121 }
0122 
0123 void FreeSpaceNotifier::exploreDrive()
0124 {
0125     auto service = filelightService();
0126     if (!service) {
0127         auto *job = new KIO::OpenUrlJob({QUrl::fromLocalFile(m_path)});
0128         job->setUiDelegate(new KNotificationJobUiDelegate(KJobUiDelegate::AutoErrorHandlingEnabled));
0129         job->start();
0130         return;
0131     }
0132 
0133     auto *job = new KIO::ApplicationLauncherJob(service);
0134     job->setUrls({QUrl::fromLocalFile(m_path)});
0135     job->setUiDelegate(new KNotificationJobUiDelegate(KJobUiDelegate::AutoErrorHandlingEnabled));
0136     job->start();
0137 }
0138 
0139 void FreeSpaceNotifier::onNotificationClosed()
0140 {
0141     // warn again if constantly below limit for too long
0142     if (!m_lastAvailTimer) {
0143         m_lastAvailTimer = new QTimer(this);
0144         connect(m_lastAvailTimer, &QTimer::timeout, this, &FreeSpaceNotifier::resetLastAvailable);
0145     }
0146     m_lastAvailTimer->start(std::chrono::hours(1));
0147 }
0148 
0149 void FreeSpaceNotifier::resetLastAvailable()
0150 {
0151     m_lastAvail = -1;
0152     m_lastAvailTimer->deleteLater();
0153     m_lastAvailTimer = nullptr;
0154 }