File indexing completed on 2024-11-17 05:15:20
0001 /* 0002 * Copyright 2020 Han Young <hanyoung@protonmail.com> 0003 * Copyright 2021 Boris Petrov <boris.v.petrov@protonmail.com> 0004 * Copyright 2022 Devin Lin <devin@kde.org> 0005 * 0006 * SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "timer.h" 0010 0011 Timer::Timer(int length, const QString &label, const QString &commandTimeout, bool running, QObject *parent) 0012 : QObject{parent} 0013 , m_uuid{QUuid::createUuid()} 0014 , m_length{length} 0015 , m_label{label} 0016 , m_commandTimeout{commandTimeout} 0017 { 0018 init(); 0019 0020 // start timer if requested 0021 if (running) { 0022 this->toggleRunning(); 0023 } 0024 } 0025 0026 Timer::Timer(const QJsonObject &obj, QObject *parent) 0027 : QObject{parent} 0028 , m_uuid{QUuid(obj[QStringLiteral("uuid")].toString())} 0029 , m_length{obj[QStringLiteral("length")].toInt()} 0030 , m_label{obj[QStringLiteral("label")].toString()} 0031 , m_commandTimeout{obj[QStringLiteral("commandTimeout")].toString()} 0032 , m_looping{obj[QStringLiteral("looping")].toBool()} 0033 { 0034 init(); 0035 } 0036 0037 Timer::~Timer() 0038 { 0039 if (!m_running) { 0040 // stop wakeup if timer is being deleted 0041 setRunning(false); 0042 } 0043 } 0044 0045 void Timer::init() 0046 { 0047 // connect signals 0048 connect(&Utilities::instance(), &Utilities::wakeup, this, &Timer::timeUp); 0049 connect(&Utilities::instance(), &Utilities::needsReschedule, this, &Timer::reschedule); 0050 0051 // initialize notification 0052 m_notification->setIconName(QStringLiteral("kclock")); 0053 m_notification->setTitle(i18n("Timer complete")); 0054 m_notification->setText(i18n("Your timer %1 has finished!", label())); 0055 m_notification->setUrgency(KNotification::HighUrgency); 0056 m_notification->setAutoDelete(false); // don't auto-delete when closing 0057 0058 auto defaultAction = m_notification->addDefaultAction(i18n("View")); 0059 connect(defaultAction, &KNotificationAction::activated, this, &Timer::dismiss); 0060 0061 connect(m_notification, &KNotification::closed, this, &Timer::dismiss); 0062 0063 // initialize DBus object 0064 QDBusConnection::sessionBus().registerObject(QStringLiteral("/Timers/") + m_uuid.toString(QUuid::Id128), 0065 this, 0066 QDBusConnection::ExportScriptableContents | QDBusConnection::ExportAllProperties); 0067 connect(this, &QObject::destroyed, [this] { 0068 QDBusConnection::sessionBus().unregisterObject(QStringLiteral("/Timers/") + m_uuid.toString(QUuid::Id128), QDBusConnection::UnregisterNode); 0069 }); 0070 } 0071 0072 QJsonObject Timer::serialize() 0073 { 0074 QJsonObject obj; 0075 obj[QStringLiteral("length")] = m_length; 0076 obj[QStringLiteral("label")] = m_label; 0077 obj[QStringLiteral("uuid")] = m_uuid.toString(); 0078 obj[QStringLiteral("looping")] = m_looping; 0079 obj[QStringLiteral("commandTimeout")] = m_commandTimeout; 0080 return obj; 0081 } 0082 0083 void Timer::toggleRunning() 0084 { 0085 setRunning(!m_running); 0086 } 0087 0088 void Timer::toggleLooping() 0089 { 0090 m_looping = !m_looping; 0091 Q_EMIT loopingChanged(); 0092 0093 TimerModel::instance()->save(); 0094 } 0095 0096 void Timer::reset() 0097 { 0098 setRunning(false); 0099 m_hasElapsed = 0; 0100 0101 // ensure UI keeps up to date with hasElapsed 0102 Q_EMIT runningChanged(); 0103 } 0104 0105 int Timer::elapsed() const 0106 { 0107 if (running()) { 0108 return QDateTime::currentSecsSinceEpoch() - m_startTime; 0109 } else { 0110 return m_hasElapsed; 0111 } 0112 } 0113 0114 QString Timer::uuid() const 0115 { 0116 return m_uuid.toString(); 0117 } 0118 0119 int Timer::length() const 0120 { 0121 return m_length; 0122 } 0123 0124 void Timer::setLength(int length) 0125 { 0126 if (length != m_length) { 0127 m_length = length; 0128 Q_EMIT lengthChanged(); 0129 0130 TimerModel::instance()->save(); 0131 } 0132 } 0133 0134 QString Timer::label() const 0135 { 0136 return m_label; 0137 } 0138 0139 void Timer::setLabel(const QString &label) 0140 { 0141 if (label != m_label) { 0142 m_label = label; 0143 Q_EMIT labelChanged(); 0144 0145 TimerModel::instance()->save(); 0146 } 0147 } 0148 0149 QString Timer::commandTimeout() const 0150 { 0151 return m_commandTimeout; 0152 } 0153 0154 void Timer::setCommandTimeout(const QString &commandTimeout) 0155 { 0156 if (m_commandTimeout != commandTimeout) { 0157 m_commandTimeout = commandTimeout; 0158 Q_EMIT commandTimeoutChanged(); 0159 0160 TimerModel::instance()->save(); 0161 } 0162 } 0163 0164 bool Timer::looping() const 0165 { 0166 return m_looping; 0167 } 0168 0169 bool Timer::running() const 0170 { 0171 return m_running; 0172 } 0173 0174 bool Timer::ringing() const 0175 { 0176 return m_ringing; 0177 } 0178 0179 void Timer::timeUp(int cookie) 0180 { 0181 if (cookie == m_cookie) { 0182 ring(); 0183 0184 // clear wakeup if it's somehow still there 0185 if (m_cookie > 0) { 0186 Utilities::instance().clearWakeup(m_cookie); 0187 m_cookie = -1; 0188 } 0189 0190 // run command since timer has ended 0191 qDebug() << "Running command:" << m_commandTimeout; 0192 if (!m_commandTimeout.isEmpty()) { 0193 const QStringList commandAndArguments = QProcess::splitCommand(m_commandTimeout); 0194 QProcess::execute(commandAndArguments.front(), commandAndArguments.mid(1)); 0195 } 0196 0197 // loop if it is set 0198 if (m_looping) { 0199 reset(); 0200 setRunning(true); 0201 } else { 0202 setRunning(false); 0203 m_hasElapsed = m_length; 0204 } 0205 } 0206 } 0207 0208 void Timer::setRunning(bool running) 0209 { 0210 if (m_running == running) { 0211 return; 0212 } 0213 0214 if (m_running) { 0215 m_hasElapsed = QDateTime::currentSecsSinceEpoch() - m_startTime; 0216 0217 Utilities::instance().decfActiveCount(); 0218 0219 // clear wakeup 0220 if (m_cookie > 0) { 0221 Utilities::instance().clearWakeup(m_cookie); 0222 m_cookie = -1; 0223 } 0224 } else { 0225 if (m_hasElapsed == m_length) { 0226 // reset elapsed if the timer was already finished 0227 m_hasElapsed = 0; 0228 } 0229 0230 // if we scheduled a wakeup before, cancel it first 0231 if (m_cookie > 0) { 0232 Utilities::instance().clearWakeup(m_cookie); 0233 } 0234 0235 Utilities::instance().incfActiveCount(); 0236 0237 m_startTime = QDateTime::currentSecsSinceEpoch() - m_hasElapsed; 0238 m_cookie = Utilities::instance().scheduleWakeup(m_startTime + m_length); 0239 } 0240 0241 m_running = running; 0242 Q_EMIT runningChanged(); 0243 0244 TimerModel::instance()->save(); 0245 } 0246 0247 void Timer::ring() 0248 { 0249 // if there were other ring events running, close them 0250 m_notification->close(); 0251 0252 qDebug("Timer finished, sending notification..."); 0253 m_notification->sendEvent(); 0254 0255 Utilities::pauseMprisSources(); 0256 0257 m_ringing = true; 0258 Q_EMIT ringingChanged(); 0259 } 0260 0261 void Timer::dismiss() 0262 { 0263 qDebug() << "Timer dismissed."; 0264 m_notification->close(); 0265 0266 Utilities::resumeMprisSources(); 0267 0268 m_ringing = false; 0269 Q_EMIT ringingChanged(); 0270 } 0271 0272 void Timer::reschedule() 0273 { 0274 if (m_running) { 0275 m_cookie = Utilities::instance().scheduleWakeup(m_startTime + m_length); 0276 } 0277 }