File indexing completed on 2025-04-27 08:12:25
0001 /* 0002 * SPDX-FileCopyrightText: 2020 Dimitris Kardarakos <dimkard@posteo.net> 0003 * 0004 * SPDX-License-Identifier: GPL-3.0-or-later 0005 */ 0006 0007 #include "calalarmclient.h" 0008 #include "alarmnotification.h" 0009 #include "alarmsmodel.h" 0010 #include "notificationhandler.h" 0011 #include "calindacadaptor.h" 0012 #include "solidwakeupbackend.h" 0013 #include "wakeupmanager.h" 0014 #include <KSharedConfig> 0015 #include <KConfigGroup> 0016 #include <QDebug> 0017 #include <QVariantMap> 0018 #include <QDateTime> 0019 #include <KLocalizedString> 0020 0021 using namespace KCalendarCore; 0022 0023 CalAlarmClient::CalAlarmClient(QObject *parent) 0024 : QObject(parent), m_alarms_model {new AlarmsModel(this)}, m_notification_handler {new NotificationHandler(this)}, m_wakeup_manager {new WakeupManager(this)} 0025 { 0026 new CalindacAdaptor(this); 0027 0028 QDBusConnection::sessionBus().registerObject(QStringLiteral("/calindac"), this); 0029 0030 KConfigGroup generalGroup(KSharedConfig::openConfig(), QStringLiteral("General")); 0031 m_check_interval = generalGroup.readEntry("CheckInterval", 45); 0032 m_suspend_seconds = generalGroup.readEntry("SuspendSeconds", 60); 0033 m_last_check = generalGroup.readEntry("CalendarsLastChecked", QDateTime()); 0034 0035 qDebug() << "CalAlarmClient:lastChecked:" << m_last_check.toString(QStringLiteral("dd.MM.yyyy hh:mm:ss")); 0036 0037 restoreSuspendedFromConfig(); 0038 saveCheckInterval(); 0039 saveSuspendSeconds(); 0040 checkAlarms(); 0041 0042 connect(m_notification_handler, &NotificationHandler::scheduleAlarmCheck, this, &CalAlarmClient::scheduleAlarmCheck); 0043 connect(&m_check_timer, &QTimer::timeout, this, &CalAlarmClient::checkAlarms); 0044 connect(m_wakeup_manager, &WakeupManager::wakeupAlarmClient, this, &CalAlarmClient::wakeupCallback); 0045 connect(m_wakeup_manager, &WakeupManager::activeChanged, this, &CalAlarmClient::setupShceduler); 0046 setupShceduler((m_wakeup_manager != nullptr) && (m_wakeup_manager->active())); 0047 } 0048 0049 CalAlarmClient::~CalAlarmClient() = default; 0050 0051 QStringList CalAlarmClient::calendarFileList() const 0052 { 0053 auto filesList { QStringList() }; 0054 KConfigGroup calindoriCfgGeneral(KSharedConfig::openConfig(QStringLiteral("calindorirc")), QStringLiteral("general")); 0055 auto iCalendars = calindoriCfgGeneral.readEntry("calendars", QString()); 0056 auto eCalendars = calindoriCfgGeneral.readEntry("externalCalendars", QString()); 0057 0058 auto calendarsList = iCalendars.isEmpty() ? QStringList() : iCalendars.split(QStringLiteral(";")); 0059 if (!(eCalendars.isEmpty())) { 0060 calendarsList.append(eCalendars.split(QStringLiteral(";"))); 0061 } 0062 0063 for (const auto &c : qAsConst(calendarsList)) { 0064 QString fileName = KSharedConfig::openConfig(QStringLiteral("calindorirc"))->group(c).readEntry("file"); 0065 0066 if (!(fileName.isNull())) { 0067 filesList.append(fileName); 0068 } 0069 } 0070 0071 return filesList; 0072 } 0073 0074 void CalAlarmClient::checkAlarms() 0075 { 0076 KConfigGroup cfg(KSharedConfig::openConfig(), QStringLiteral("General")); 0077 0078 if (!cfg.readEntry("Enabled", true)) { 0079 return; 0080 } 0081 0082 auto checkFrom = m_last_check.addSecs(1); 0083 m_last_check = QDateTime::currentDateTime(); 0084 0085 qDebug() << "\ncheckAlarms:Check:" << checkFrom.toString() << " -" << m_last_check.toString(); 0086 0087 FilterPeriod fPeriod { .from = checkFrom, .to = m_last_check }; 0088 m_alarms_model->setCalendarFiles(calendarFileList()); 0089 m_alarms_model->setPeriod(fPeriod); 0090 m_notification_handler->setPeriod(fPeriod); 0091 0092 auto alarms = m_alarms_model->alarms(); 0093 qDebug() << "checkAlarms:Alarms Found: " << alarms.count(); 0094 0095 for (const auto &alarm : qAsConst(alarms)) { 0096 m_notification_handler->addActiveNotification(alarm->parentUid(), QStringLiteral("%1\n%2").arg(alarm->time().toString(QStringLiteral("hh:mm")), alarm->text())); 0097 } 0098 m_notification_handler->sendNotifications(); 0099 saveLastCheckTime(); 0100 flushSuspendedToConfig(); 0101 } 0102 0103 void CalAlarmClient::saveLastCheckTime() 0104 { 0105 KConfigGroup generalGroup(KSharedConfig::openConfig(), QStringLiteral("General")); 0106 generalGroup.writeEntry("CalendarsLastChecked", m_last_check); 0107 KSharedConfig::openConfig()->sync(); 0108 } 0109 0110 void CalAlarmClient::saveCheckInterval() 0111 { 0112 KConfigGroup generalGroup(KSharedConfig::openConfig(), QStringLiteral("General")); 0113 generalGroup.writeEntry("CheckInterval", m_check_interval); 0114 KSharedConfig::openConfig()->sync(); 0115 } 0116 0117 void CalAlarmClient::saveSuspendSeconds() 0118 { 0119 KConfigGroup generalGroup(KSharedConfig::openConfig(), QStringLiteral("General")); 0120 generalGroup.writeEntry("SuspendSeconds", m_suspend_seconds); 0121 KSharedConfig::openConfig()->sync(); 0122 } 0123 0124 void CalAlarmClient::quit() 0125 { 0126 flushSuspendedToConfig(); 0127 saveLastCheckTime(); 0128 qDebug("\nquit"); 0129 qApp->quit(); 0130 } 0131 0132 void CalAlarmClient::forceAlarmCheck() 0133 { 0134 checkAlarms(); 0135 saveLastCheckTime(); 0136 } 0137 0138 QString CalAlarmClient::dumpLastCheck() const 0139 { 0140 KConfigGroup cfg(KSharedConfig::openConfig(), QStringLiteral("General")); 0141 const QDateTime lastChecked = cfg.readEntry("CalendarsLastChecked", QDateTime()); 0142 0143 return QStringLiteral("Last Check: %1").arg(lastChecked.toString()); 0144 } 0145 0146 QStringList CalAlarmClient::dumpAlarms() const 0147 { 0148 const auto start = QDateTime(QDate::currentDate(), QTime(0, 0), Qt::LocalTime); 0149 const auto end = start.date().endOfDay(); 0150 0151 AlarmsModel model {}; 0152 model.setCalendarFiles(calendarFileList()); 0153 model.setPeriod({.from = start, .to = end}); 0154 0155 auto lst = QStringList(); 0156 const auto alarms = model.alarms(); 0157 0158 for (const auto &alarm : qAsConst(alarms)) { 0159 lst << QStringLiteral("%1: \"%2\"").arg(alarm->time().toString(QStringLiteral("hh:mm")), alarm->parentUid()); 0160 } 0161 0162 return lst; 0163 } 0164 0165 void CalAlarmClient::restoreSuspendedFromConfig() 0166 { 0167 qDebug() << "\nrestoreSuspendedFromConfig:Restore suspended alarms from config"; 0168 KConfigGroup suspendedGroup(KSharedConfig::openConfig(), QStringLiteral("Suspended")); 0169 const auto suspendedAlarms = suspendedGroup.groupList(); 0170 0171 for (const auto &s : suspendedAlarms) { 0172 KConfigGroup suspendedAlarm(&suspendedGroup, s); 0173 QString uid = suspendedAlarm.readEntry("UID"); 0174 QString txt = alarmText(uid); 0175 QDateTime remindAt = QDateTime::fromString(suspendedAlarm.readEntry("RemindAt"), QStringLiteral("yyyy,M,d,HH,m,s")); 0176 qDebug() << "restoreSuspendedFromConfig:Restoring alarm" << uid << "," << txt << "," << remindAt.toString(); 0177 0178 if (!(uid.isEmpty() && remindAt.isValid() && !(txt.isEmpty()))) { 0179 m_notification_handler->addSuspendedNotification(uid, txt, remindAt); 0180 } 0181 } 0182 } 0183 0184 QString CalAlarmClient::alarmText(const QString &uid) const 0185 { 0186 AlarmsModel model {}; 0187 model.setCalendarFiles(calendarFileList()); 0188 model.setPeriod({.from = QDateTime(), .to = QDateTime::currentDateTime()}); 0189 const auto alarms = model.alarms(); 0190 0191 for (const auto &alarm : qAsConst(alarms)) { 0192 if (alarm->parentUid() == uid) { 0193 return alarm->text(); 0194 } 0195 } 0196 0197 return QString(); 0198 } 0199 0200 void CalAlarmClient::flushSuspendedToConfig() 0201 { 0202 KConfigGroup suspendedGroup(KSharedConfig::openConfig(), QStringLiteral("Suspended")); 0203 suspendedGroup.deleteGroup(); 0204 0205 const auto suspendedNotifications = m_notification_handler->suspendedNotifications(); 0206 0207 if (suspendedNotifications.isEmpty()) { 0208 qDebug() << "flushSuspendedToConfig:No suspended notification exists, nothing to write to config"; 0209 KSharedConfig::openConfig()->sync(); 0210 0211 return; 0212 } 0213 0214 for (const auto &s : suspendedNotifications) { 0215 qDebug() << "flushSuspendedToConfig:Flushing suspended alarm" << s->uid() << " to config"; 0216 KConfigGroup notificationGroup(&suspendedGroup, s->uid()); 0217 notificationGroup.writeEntry("UID", s->uid()); 0218 notificationGroup.writeEntry("RemindAt", s->remindAt()); 0219 } 0220 KSharedConfig::openConfig()->sync(); 0221 } 0222 0223 void CalAlarmClient::scheduleAlarmCheck() 0224 { 0225 if ((m_wakeup_manager == nullptr) || !(m_wakeup_manager->active())) { 0226 qDebug() << "Wakeup manager is not active, alarms are handled by a timer"; 0227 return; 0228 } 0229 0230 // Look for alarms over the next days 0231 AlarmsModel model {}; 0232 model.setCalendarFiles(calendarFileList()); 0233 model.setPeriod({.from = m_last_check.addSecs(1), .to = (m_last_check.addDays(15)).date().startOfDay()}); 0234 0235 // Next schedule time: the trigger time of the first alarm or scheduled notification 0236 QDateTime scheduleWakeupAt {}; 0237 auto firstAlarmTime = model.firstAlarmTime(); 0238 auto firstSuspendedTime = m_notification_handler->firstSuspended(); 0239 0240 if (firstAlarmTime.isValid() && firstSuspendedTime.isValid()) { 0241 scheduleWakeupAt = (firstAlarmTime < firstSuspendedTime) ? firstAlarmTime : firstSuspendedTime; 0242 } else { 0243 scheduleWakeupAt = firstAlarmTime.isValid() ? firstAlarmTime : firstSuspendedTime; 0244 } 0245 0246 if (scheduleWakeupAt.isValid()) { 0247 qDebug() << "scheduleAlarmCheck:" << "Shecdule next alarm check at" << scheduleWakeupAt.addSecs(1).toString(QStringLiteral("dd.MM.yyyy hh:mm:ss")); 0248 m_wakeup_manager->scheduleWakeup(scheduleWakeupAt.addSecs(1)); 0249 } else { // If no alarms/suspended notifications exist, do not schedule anything and remove any scheduled wakeup call. 0250 qDebug() << "scheduleAlarmCheck:" << "Cancel scheduled wake up"; 0251 m_wakeup_manager->removeWakeup(); 0252 } 0253 } 0254 0255 void CalAlarmClient::wakeupCallback() 0256 { 0257 qDebug() << "CalAlarmClient wakeupCallback"; 0258 0259 checkAlarms(); 0260 scheduleAlarmCheck(); 0261 } 0262 0263 void CalAlarmClient::setupShceduler(const bool wakeupManagerActive) 0264 { 0265 if (wakeupManagerActive && m_wakeup_manager->hasWakeupFeatures()) { 0266 qDebug() << "setupShceduler: wake up manager offers an active backend with wakeup features"; 0267 if (m_check_timer.isActive()) { 0268 m_check_timer.stop(); 0269 } 0270 scheduleAlarmCheck(); 0271 } else { 0272 qDebug() << "setupShceduler: No wakeup backend, alarms will be checked by a timer"; 0273 if (!m_check_timer.isActive()) { 0274 m_check_timer.start(1000 * m_check_interval); 0275 } 0276 } 0277 }