1 /*
2 SPDX-License-Identifier: GPL-2.0-or-later
3
4 SPDX-FileCopyrightText: 1999 Martin R. Jones <mjones@kde.org>
5 SPDX-FileCopyrightText: 2008 Eike Hein <hein@kde.org>
6 SPDX-FileCopyrightText: 2010 Martin Blumenstingl <darklight.xdarklight@googlemail.com>
7 */
8
9 #include "awaymanager.h"
10 #include "application.h"
11 #include "connectionmanager.h"
12 #include "server.h"
13 #include "preferences.h"
14 #include "trayicon.h"
15
16 #include <KActionCollection>
17 #include <QIcon>
18 #include <KIdleTime>
19 #include <KToggleAction>
20
AwayManager(QObject * parent)21 AwayManager::AwayManager(QObject* parent) : QObject(parent)
22 {
23 m_connectionManager = Application::instance()->getConnectionManager();
24
25 connect(KIdleTime::instance(), &KIdleTime::resumingFromIdle, this, &AwayManager::resumeFromIdle);
26 connect(KIdleTime::instance(), QOverload<int, int>::of(&KIdleTime::timeoutReached), this, &AwayManager::idleTimeoutReached);
27
28 // Catch the first "resume event" (= user input) so we correctly catch the first
29 // resume event in case the user is already idle on startup).
30 KIdleTime::instance()->catchNextResumeEvent();
31 }
32
~AwayManager()33 AwayManager::~AwayManager()
34 {
35 }
36
minutesToMilliseconds(int minutes)37 int AwayManager::minutesToMilliseconds(int minutes)
38 {
39 return minutes * 60 * 1000;
40 }
41
identitiesChanged()42 void AwayManager::identitiesChanged()
43 {
44 QHash<int, int> newIdentityWithIdleTimeMapping;
45
46 const QList<Server*> serverList = m_connectionManager->getServerList();
47
48 for (Server* server : serverList) {
49 IdentityPtr identity = server->getIdentity();
50 int identityId = identity->id();
51
52 // Calculate the auto-away time in milliseconds.
53 int identityIdleTime = minutesToMilliseconds(identity->getAwayInactivity());
54
55 if (identity && identity->getAutomaticAway() && server->isConnected())
56 newIdentityWithIdleTimeMapping[identityId] = identityIdleTime;
57 }
58
59 m_identitiesWithIdleTimesOnAutoAway = newIdentityWithIdleTimeMapping;
60
61 identitiesOnAutoAwayChanged();
62 }
63
identityOnline(int identityId)64 void AwayManager::identityOnline(int identityId)
65 {
66 IdentityPtr identity = Preferences::identityById(identityId);
67
68 if (identity && identity->getAutomaticAway() &&
69 !m_identitiesWithIdleTimesOnAutoAway.contains(identityId))
70 {
71 m_identitiesWithIdleTimesOnAutoAway[identityId] = minutesToMilliseconds(identity->getAwayInactivity());
72
73 // Notify the AwayManager implementation that a user which has auto-away enabled is not online.
74 identityOnAutoAwayWentOnline(identityId);
75 }
76 }
77
identityOffline(int identityId)78 void AwayManager::identityOffline(int identityId)
79 {
80 if (m_identitiesWithIdleTimesOnAutoAway.remove(identityId))
81 // Notify the AwayManager implementation that a user which has auto-away enabled is now offline.
82 identityOnAutoAwayWentOffline(identityId);
83 }
84
implementManagedAway(int identityId)85 void AwayManager::implementManagedAway(int identityId)
86 {
87 const QList<Server*> serverList = m_connectionManager->getServerList();
88
89 for (Server* server : serverList) {
90 if (server->getIdentity()->id() == identityId && server->isConnected() && !server->isAway())
91 server->requestAway();
92 }
93 }
94
setManagedIdentitiesAway()95 void AwayManager::setManagedIdentitiesAway()
96 {
97 QHash<int, int>::ConstIterator itr = m_identitiesWithIdleTimesOnAutoAway.constBegin();
98
99 for (; itr != m_identitiesWithIdleTimesOnAutoAway.constEnd(); ++itr)
100 implementManagedAway(itr.key());
101 }
102
implementManagedUnaway(const QList<int> & identityList)103 void AwayManager::implementManagedUnaway(const QList<int>& identityList)
104 {
105 const QList<Server*> serverList = m_connectionManager->getServerList();
106
107 for (Server* server : serverList) {
108 IdentityPtr identity = server->getIdentity();
109
110 if (identityList.contains(identity->id()) && identity->getAutomaticUnaway()
111 && server->isConnected() && server->isAway())
112 {
113 server->requestUnaway();
114 }
115 }
116 }
117
setManagedIdentitiesUnaway()118 void AwayManager::setManagedIdentitiesUnaway()
119 {
120 // Set the "not away" status for all identities which have
121 // auto-away enabled.
122 implementManagedUnaway(m_identitiesWithIdleTimesOnAutoAway.keys());
123 }
124
requestAllAway(const QString & reason)125 void AwayManager::requestAllAway(const QString& reason)
126 {
127 const QList<Server*> serverList = m_connectionManager->getServerList();
128
129 for (Server* server : serverList)
130 if (server->isConnected())
131 server->requestAway(reason);
132 }
133
requestAllUnaway()134 void AwayManager::requestAllUnaway()
135 {
136 const QList<Server*> serverList = m_connectionManager->getServerList();
137
138 for (Server* server : serverList)
139 if (server->isConnected() && server->isAway())
140 server->requestUnaway();
141 }
142
setGlobalAway(bool away)143 void AwayManager::setGlobalAway(bool away)
144 {
145 if (away)
146 requestAllAway();
147 else
148 requestAllUnaway();
149 }
150
updateGlobalAwayAction(bool away)151 void AwayManager::updateGlobalAwayAction(bool away)
152 {
153 // Regardless of any implementation: If the given parameter indicates
154 // that the user is not away we should simulate user activity to
155 // ensure that the away-status of the user is really reset.
156 if (!away)
157 simulateUserActivity();
158
159 Application* konvApp = Application::instance();
160 auto* awayAction = qobject_cast<KToggleAction*>(konvApp->getMainWindow()->actionCollection()->action(QStringLiteral("toggle_away")));
161 Konversation::TrayIcon* trayIcon = konvApp->getMainWindow()->systemTrayIcon();
162
163 if (!awayAction)
164 return;
165
166 if (away)
167 {
168 const QList<Server*> serverList = m_connectionManager->getServerList();
169 int awayCount = 0;
170
171 for (Server* server : serverList) {
172 if (server->isAway())
173 awayCount++;
174 }
175
176 if (awayCount == serverList.count())
177 {
178 awayAction->setChecked(true);
179 awayAction->setIcon(QIcon::fromTheme(QStringLiteral("im-user")));
180 if (trayIcon) trayIcon->setAway(true);
181 }
182 }
183 else
184 {
185 awayAction->setChecked(false);
186 awayAction->setIcon(QIcon::fromTheme(QStringLiteral("im-user-away")));
187 if (trayIcon) trayIcon->setAway(false);
188 }
189 }
190
calculateRemainingTime(int identityId)191 int AwayManager::calculateRemainingTime(int identityId)
192 {
193 // Get the idle time for the identity.
194 int identityIdleTimeout = m_identitiesWithIdleTimesOnAutoAway[identityId];
195
196 // The remaining time until the user will be marked as "auto-away".
197 int remainingTime = identityIdleTimeout - KIdleTime::instance()->idleTime();
198
199 return remainingTime;
200 }
201
implementUpdateIdleTimeout(int identityId)202 void AwayManager::implementUpdateIdleTimeout(int identityId)
203 {
204 const QHash<int, int> idleTimeouts = KIdleTime::instance()->idleTimeouts();
205
206 // calculate the remaining time until the user will be marked as away.
207 int remainingTime = calculateRemainingTime(identityId);
208
209 // Check if the user should be away right now.
210 if (remainingTime <= 0)
211 {
212 implementMarkIdentityAway(identityId);
213
214 // Since the user is away right now the next auto-away should occur
215 // in X minutes (where X is the timeout which the user has
216 // configured for the identity).
217 remainingTime = m_identitiesWithIdleTimesOnAutoAway[identityId];
218 }
219
220 // Get the timer which is currently active for the identity.
221 int timerId = m_timerForIdentity.key(identityId, -1);
222
223 // Checks if we already have a timer for the given identity.
224 // If we already have a timer we need to make sure that we're always
225 // waiting for the remaining time.
226 if (idleTimeouts[timerId] != remainingTime)
227 {
228 // Remove the idle timeout.
229 implementRemoveIdleTimeout(timerId);
230
231 // Then also reset the timer ID (as the timer does not exist anymore).
232 timerId = -1;
233 }
234
235 // Check if we already have a timer.
236 if (timerId == -1)
237 // If not create a new timer.
238 implementAddIdleTimeout(identityId, remainingTime);
239 }
240
implementAddIdleTimeout(int identityId,int idleTime)241 void AwayManager::implementAddIdleTimeout(int identityId, int idleTime)
242 {
243 // Create a new timer.
244 int newTimerId = KIdleTime::instance()->addIdleTimeout(idleTime);
245
246 // Make sure we keep track of the identity <-> KIdleTimer mapping.
247 m_timerForIdentity[newTimerId] = identityId;
248 }
249
implementRemoveIdleTimeout(int timerId)250 void AwayManager::implementRemoveIdleTimeout(int timerId)
251 {
252 // Make sure we got a valid timer ID.
253 if (timerId != -1)
254 {
255 // Remove the idle timeout.
256 KIdleTime::instance()->removeIdleTimeout(timerId);
257
258 // Also remove the timer/identity mapping from our hashtable.
259 m_timerForIdentity.remove(timerId);
260 }
261 }
262
implementMarkIdentityAway(int identityId)263 void AwayManager::implementMarkIdentityAway(int identityId)
264 {
265 // Mark the current identity as away.
266 implementManagedAway(identityId);
267
268 // As at least one identity is away we have to catch the next
269 // resume event.
270 KIdleTime::instance()->catchNextResumeEvent();
271 }
272
simulateUserActivity()273 void AwayManager::simulateUserActivity()
274 {
275 // Tell KIdleTime that it should reset the user's idle status.
276 KIdleTime::instance()->simulateUserActivity();
277 }
278
resumeFromIdle()279 void AwayManager::resumeFromIdle()
280 {
281 // We are not idle anymore.
282 setManagedIdentitiesUnaway();
283
284 QHash<int, int>::ConstIterator itr = m_identitiesWithIdleTimesOnAutoAway.constBegin();
285
286 for (; itr != m_identitiesWithIdleTimesOnAutoAway.constEnd(); ++itr)
287 // Update the idle timeout for the identity to the configured timeout.
288 // This is needed in case the timer is not set to fire after the full
289 // away time but a shorter time (for example if the user was away on startup
290 // we want the timer to fire after "configured away-time" minus "time the user
291 // is already idle"). Then the timer's interval is wrong.
292 // Now (if needed) we simply remove the old timer and add a new one with the
293 // correct interval.
294 implementUpdateIdleTimeout(itr.key());
295 }
296
idleTimeoutReached(int timerId)297 void AwayManager::idleTimeoutReached(int timerId)
298 {
299 // Get the identity ID for the given timer ID.
300 int identityId = m_timerForIdentity[timerId];
301
302 // Mark the identity as away.
303 implementMarkIdentityAway(identityId);
304 }
305
identitiesOnAutoAwayChanged()306 void AwayManager::identitiesOnAutoAwayChanged()
307 {
308 const QList<Server*> serverList = m_connectionManager->getServerList();
309
310 // Since the list of identities has changed we want to drop all timers.
311 KIdleTime::instance()->removeAllIdleTimeouts();
312
313 // Also clear the list of identity <-> timer mappings.
314 m_timerForIdentity.clear();
315
316 for (Server* server : serverList) {
317 IdentityPtr identity = server->getIdentity();
318 int identityId = identity->id();
319
320 // Only add idle timeouts for identities which have auto-away
321 // enabled.
322 if (m_identitiesWithIdleTimesOnAutoAway.contains(identityId))
323 // Update the idle timeout for the current identity.
324 implementUpdateIdleTimeout(identityId);
325 }
326 }
327
identityOnAutoAwayWentOnline(int identityId)328 void AwayManager::identityOnAutoAwayWentOnline(int identityId)
329 {
330 // Simply update the idle timeout for the identity (this will
331 // take care of all necessary calculations).
332 implementUpdateIdleTimeout(identityId);
333 }
334
identityOnAutoAwayWentOffline(int identityId)335 void AwayManager::identityOnAutoAwayWentOffline(int identityId)
336 {
337 // Get the timer for the given identity.
338 int timerId = m_timerForIdentity.key(identityId, -1);
339
340 // Then remove the timer.
341 implementRemoveIdleTimeout(timerId);
342 }
343
344
345