File: freespacenotifier.cpp

package info (click to toggle)
plasma-workspace 4%3A6.3.6-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 104,900 kB
  • sloc: cpp: 125,434; xml: 31,579; python: 3,976; perl: 572; sh: 234; javascript: 74; ruby: 39; ansic: 13; makefile: 9
file content (183 lines) | stat: -rw-r--r-- 6,304 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
/*
    SPDX-FileCopyrightText: 2006 Lukas Tinkl <ltinkl@suse.cz>
    SPDX-FileCopyrightText: 2008 Lubos Lunak <l.lunak@suse.cz>
    SPDX-FileCopyrightText: 2009 Ivo Anjo <knuckles@gmail.com>
    SPDX-FileCopyrightText: 2020 Kai Uwe Broulik <kde@broulik.de>

    SPDX-License-Identifier: GPL-2.0-or-later
*/

#include "freespacenotifier.h"
#include "freespacenotifier_logging.h"

#include <KNotification>
#include <KNotificationJobUiDelegate>

#include <KIO/ApplicationLauncherJob>
#include <KIO/FileSystemFreeSpaceJob>
#include <KIO/OpenUrlJob>

#include <Solid/Device>
#include <Solid/StorageAccess>

#include <QFileInfo>

#include <chrono>

#include "settings.h"

FreeSpaceNotifier::FreeSpaceNotifier(const QString &udi, const QString &path, const KLocalizedString &notificationText, QObject *parent)
    : QObject(parent)
    , m_udi(udi)
    , m_path(path)
    , m_notificationText(notificationText)
{
    checkFreeDiskSpace();
    connect(&m_timer, &QTimer::timeout, this, &FreeSpaceNotifier::checkFreeDiskSpace);
    m_timer.start(std::chrono::minutes(1));
}

FreeSpaceNotifier::~FreeSpaceNotifier()
{
    if (m_notification) {
        m_notification->close();
    }
}

void FreeSpaceNotifier::checkFreeDiskSpace()
{
    if (!FreeSpaceNotifierSettings::enableNotification()) {
        // do nothing if notifying is disabled;
        // also stop the timer that probably got us here in the first place
        m_timer.stop();
        return;
    }

    Solid::Device device(m_udi);

    Solid::StorageAccess *storageaccess = device.as<Solid::StorageAccess>();
    if (!storageaccess || !storageaccess->isAccessible()) {
        qCDebug(FSN) << "Space Monitor: failed to get storage access " << m_udi;
        return;
    }

    QString path = storageaccess->filePath();

    // create job
    KIO::FileSystemFreeSpaceJob *job = KIO::fileSystemFreeSpace(QUrl::fromLocalFile(path));

    // collect and process info
    connect(job, &KJob::result, this, [this, job]() {
        if (job->error()) {
            qCDebug(FSN) << "Space Monitor: failed to get storage access " << m_udi;
            return;
        }
        KIO::filesize_t size = job->size();
        KIO::filesize_t available = job->availableSize();
        const qint64 totalSpaceMB = size / (1024 * 1024); // to MiB
        const int percLimit = (FreeSpaceNotifierSettings::minimumSpacePercentage() * totalSpaceMB) / 100;
        const int fixedLimit = FreeSpaceNotifierSettings::minimumSpace();
        const int limit = qMin(fixedLimit, percLimit);
        const qint64 avail = available / (1024 * 1024); // to MiB

        if (avail >= limit) {
            if (m_notification) {
                m_notification->close();
            }
            m_lastAvail = avail;
            return;
        }

        const int availPercent = int(100 * available / size);
        const QString text = m_notificationText.subs(avail).subs(availPercent).toString();
        qCDebug(FSN) << "Available percentage for" << m_udi << ":" << availPercent;

        // Make sure the notification text is always up to date whenever we checked free space
        if (m_notification) {
            m_notification->setText(text);
        }

        // User freed some space, warn if it goes low again
        if (m_lastAvail > -1 && avail > m_lastAvail) {
            m_lastAvail = avail;
            return;
        }

        // Always warn the first time or when available space dropped to half of the previous time
        const bool warn = (m_lastAvail >= limit || avail < m_lastAvail / 2);
        if (!warn) {
            return;
        }

        m_lastAvail = avail;

        if (!m_notification) {
            m_notification = new KNotification(QStringLiteral("freespacenotif"));
            m_notification->setComponentName(QStringLiteral("freespacenotifier"));
            m_notification->setText(text);

            auto filelight = filelightService();
            if (filelight) {
                auto filelightAction = m_notification->addAction(i18n("Open in Filelight"));
                connect(filelightAction, &KNotificationAction::activated, this, [this] {
                    exploreDrive();
                });
            } else {
                // Do we really want the user opening Root in a file manager?
                auto fileManagerAction = m_notification->addAction(i18n("Open in File Manager"));
                connect(fileManagerAction, &KNotificationAction::activated, this, [this] {
                    exploreDrive();
                });
            }

            // TODO once we have "configure" action support in KNotification, wire it up instead of a button
            auto configureAction = m_notification->addAction(i18n("Configure Warning…"));
            connect(configureAction, &KNotificationAction::activated, this, [this] {
                Q_EMIT configureRequested();
            });

            connect(m_notification, &KNotification::closed, this, &FreeSpaceNotifier::onNotificationClosed);
            m_notification->sendEvent();
        }
    });
}

KService::Ptr FreeSpaceNotifier::filelightService() const
{
    return KService::serviceByDesktopName(QStringLiteral("org.kde.filelight"));
}

void FreeSpaceNotifier::exploreDrive()
{
    auto service = filelightService();
    if (!service) {
        auto *job = new KIO::OpenUrlJob({QUrl::fromLocalFile(m_path)});
        job->setUiDelegate(new KNotificationJobUiDelegate(KJobUiDelegate::AutoErrorHandlingEnabled));
        job->start();
        return;
    }

    auto *job = new KIO::ApplicationLauncherJob(service);
    job->setUrls({QUrl::fromLocalFile(m_path)});
    job->setUiDelegate(new KNotificationJobUiDelegate(KJobUiDelegate::AutoErrorHandlingEnabled));
    job->start();
}

void FreeSpaceNotifier::onNotificationClosed()
{
    // warn again if constantly below limit for too long
    if (!m_lastAvailTimer) {
        m_lastAvailTimer = new QTimer(this);
        connect(m_lastAvailTimer, &QTimer::timeout, this, &FreeSpaceNotifier::resetLastAvailable);
    }
    m_lastAvailTimer->start(std::chrono::hours(1));
}

void FreeSpaceNotifier::resetLastAvailable()
{
    m_lastAvail = FreeSpaceNotifierSettings::minimumSpace();
    m_lastAvailTimer->deleteLater();
    m_lastAvailTimer = nullptr;
}

#include "moc_freespacenotifier.cpp"