File indexing completed on 2024-11-17 05:15:20
0001 /* 0002 * Copyright 2020 Han Young <hanyoung@protonmail.com> 0003 * Copyright 2020-2021 Devin Lin <devin@kde.org> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "alarmmodel.h" 0009 0010 #include "alarmmodeladaptor.h" 0011 #include "utilities.h" 0012 0013 #include <KConfigGroup> 0014 #include <KLocalizedString> 0015 #include <KSharedConfig> 0016 0017 #include <QDBusConnection> 0018 #include <QDBusReply> 0019 #include <QDebug> 0020 #include <QLocale> 0021 0022 AlarmModel *AlarmModel::instance() 0023 { 0024 static AlarmModel *singleton = new AlarmModel(); 0025 return singleton; 0026 } 0027 0028 AlarmModel::AlarmModel(QObject *parent) 0029 : QObject{parent} 0030 { 0031 // DBus 0032 new AlarmModelAdaptor(this); 0033 QDBusConnection::sessionBus().registerObject(QStringLiteral("/Alarms"), this); 0034 0035 // load alarms from config 0036 load(); 0037 0038 // update notify icon in systemtray 0039 connect(this, &AlarmModel::nextAlarm, this, &AlarmModel::updateNotifierItem); 0040 0041 // alarm wakeup behaviour 0042 connect(&Utilities::instance(), &Utilities::wakeup, this, &AlarmModel::wakeupCallback); 0043 connect(&Utilities::instance(), &Utilities::needsReschedule, this, &AlarmModel::scheduleAlarm); 0044 } 0045 0046 void AlarmModel::load() 0047 { 0048 auto config = KSharedConfig::openConfig(); 0049 KConfigGroup group = config->group(ALARM_CFG_GROUP); 0050 QStringList list = group.keyList(); 0051 0052 for (const QString &key : list) { 0053 QString json = group.readEntry(key, QString()); 0054 if (!json.isEmpty()) { 0055 Alarm *alarm = new Alarm(json, this); 0056 m_alarmsList.append(alarm); 0057 } 0058 } 0059 } 0060 0061 void AlarmModel::save() 0062 { 0063 std::for_each(m_alarmsList.begin(), m_alarmsList.end(), [](Alarm *alarm) { 0064 alarm->save(); 0065 }); 0066 } 0067 0068 void AlarmModel::configureWakeups() 0069 { 0070 // start alarm polling 0071 scheduleAlarm(); 0072 } 0073 0074 quint64 AlarmModel::getNextAlarm() 0075 { 0076 return m_nextAlarmTime; 0077 } 0078 0079 void AlarmModel::scheduleAlarm() 0080 { 0081 // if there are no alarms, return 0082 if (m_alarmsList.isEmpty()) { 0083 m_nextAlarmTime = 0; 0084 Q_EMIT nextAlarm(0); 0085 return; 0086 } 0087 0088 alarmsToRing.clear(); 0089 0090 // get the next minimum time for a wakeup (next alarm ring), and add alarms that will needed to be woken up to the list 0091 quint64 minTime = std::numeric_limits<quint64>::max(); 0092 for (auto *alarm : std::as_const(m_alarmsList)) { 0093 if (alarm->nextRingTime() > 0) { 0094 if (alarm->nextRingTime() == minTime) { 0095 alarmsToRing.append(alarm); 0096 } else if (alarm->nextRingTime() < minTime) { 0097 alarmsToRing.clear(); 0098 alarmsToRing.append(alarm); 0099 minTime = alarm->nextRingTime(); 0100 } 0101 } 0102 } 0103 0104 int activeAlarmCount = alarmsToRing.size(); 0105 while (activeAlarmCount--) { 0106 Utilities::instance().incfActiveCount(); 0107 } 0108 0109 // if there is an alarm that needs to rung 0110 if (minTime != std::numeric_limits<quint64>::max()) { 0111 qDebug() << "Scheduled alarm wakeup at" << QDateTime::fromSecsSinceEpoch(minTime).toString() << "."; 0112 m_nextAlarmTime = minTime; 0113 0114 // if we scheduled a wakeup before, cancel it first 0115 if (m_cookie > 0) { 0116 Utilities::instance().clearWakeup(m_cookie); 0117 } 0118 0119 m_cookie = Utilities::instance().scheduleWakeup(minTime); 0120 } else { 0121 // this doesn't explicitly cancel the alarm currently waiting in m_worker if disabled by user 0122 // because alarm->ring() will return immediately if disabled 0123 qDebug() << "No alarms scheduled."; 0124 0125 m_nextAlarmTime = 0; 0126 Utilities::instance().clearWakeup(m_cookie); 0127 m_cookie = -1; 0128 } 0129 Q_EMIT nextAlarm(m_nextAlarmTime); 0130 } 0131 0132 void AlarmModel::wakeupCallback(int cookie) 0133 { 0134 if (m_cookie == cookie) { 0135 for (auto alarm : std::as_const(alarmsToRing)) { 0136 alarm->ring(); 0137 } 0138 scheduleAlarm(); 0139 } 0140 } 0141 void AlarmModel::removeAlarm(const QString &uuid) 0142 { 0143 for (int i = 0; i < m_alarmsList.size(); i++) { 0144 if (m_alarmsList[i]->uuid() == uuid) { 0145 removeAlarm(i); 0146 break; 0147 } 0148 } 0149 } 0150 0151 void AlarmModel::removeAlarm(int index) 0152 { 0153 if (index < 0 || index >= this->m_alarmsList.size()) { 0154 return; 0155 } 0156 0157 Q_EMIT alarmRemoved(m_alarmsList.at(index)->uuid()); 0158 0159 Alarm *alarmPointer = m_alarmsList.at(index); 0160 0161 // remove from list of alarms to ring 0162 for (int i = 0; i < alarmsToRing.size(); i++) { 0163 if (alarmsToRing.at(i) == alarmPointer) { 0164 alarmsToRing.removeAt(i); 0165 i--; 0166 } 0167 } 0168 0169 // write to config 0170 auto config = KSharedConfig::openConfig(); 0171 KConfigGroup group = config->group(ALARM_CFG_GROUP); 0172 group.deleteEntry(m_alarmsList.at(index)->uuid()); 0173 m_alarmsList.at(index)->deleteLater(); // delete object 0174 m_alarmsList.removeAt(index); 0175 0176 config->sync(); 0177 scheduleAlarm(); 0178 } 0179 0180 void AlarmModel::addAlarm(const QString &name, int hours, int minutes, int daysOfWeek, const QString &audioPath, int ringDuration, int snoozeDuration) 0181 { 0182 Alarm *alarm = new Alarm(name, hours, minutes, daysOfWeek, audioPath, ringDuration, snoozeDuration, this); 0183 alarm->save(); 0184 0185 // insert new alarm in order by time of day 0186 int i = 0; 0187 while (i < m_alarmsList.size() && !(m_alarmsList[i]->hours() == hours && m_alarmsList[i]->minutes() >= minutes)) { 0188 ++i; 0189 } 0190 m_alarmsList.insert(i, alarm); 0191 0192 scheduleAlarm(); 0193 Q_EMIT alarmAdded(alarm->uuid()); 0194 } 0195 0196 void AlarmModel::initNotifierItem() 0197 { 0198 if (!m_item) { 0199 m_item = new KStatusNotifierItem(this); 0200 m_item->setIconByName(QStringLiteral("clock")); 0201 m_item->setStandardActionsEnabled(false); 0202 m_item->setCategory(KStatusNotifierItem::SystemServices); 0203 m_item->setStatus(KStatusNotifierItem::Passive); 0204 } 0205 } 0206 0207 void AlarmModel::updateNotifierItem(quint64 time) 0208 { 0209 if (time == 0) { 0210 // no alarm waiting, only set notifier if we have one 0211 if (m_item) { 0212 m_item->setStatus(KStatusNotifierItem::Passive); 0213 m_item->setToolTip(QStringLiteral("clock"), QStringLiteral("KClock"), QString()); 0214 } 0215 } else { 0216 auto dateTime = QDateTime::fromSecsSinceEpoch(time).toLocalTime(); 0217 initNotifierItem(); 0218 m_item->setStatus(KStatusNotifierItem::Active); 0219 m_item->setToolTip(QStringLiteral("clock"), 0220 QStringLiteral("KClock"), 0221 xi18nc("@info", 0222 "Alarm: <shortcut>%1 %2</shortcut>", 0223 QLocale::system().standaloneDayName(dateTime.date().dayOfWeek()), 0224 QLocale::system().toString(dateTime.time(), QLocale::ShortFormat))); 0225 } 0226 }