File indexing completed on 2023-12-10 04:56:58
0001 /* 0002 * Global Presence - wraps calls to set and get presence for all accounts. 0003 * 0004 * Copyright (C) 2011 David Edmundson <kde@davidedmundson.co.uk> 0005 * 0006 * This library is free software; you can redistribute it and/or 0007 * modify it under the terms of the GNU Lesser General Public 0008 * License as published by the Free Software Foundation; either 0009 * version 2.1 of the License, or (at your option) any later version. 0010 * 0011 * This library is distributed in the hope that it will be useful, 0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0014 * Lesser General Public License for more details. 0015 * 0016 * You should have received a copy of the GNU Lesser General Public 0017 * License along with this library; if not, write to the Free Software 0018 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 0019 */ 0020 0021 #include "global-presence.h" 0022 0023 #include <KTp/presence.h> 0024 0025 #include <QDBusPendingCall> 0026 #include <QVariant> 0027 0028 #include <TelepathyQt/Account> 0029 #include <TelepathyQt/AccountSet> 0030 #include <TelepathyQt/AccountManager> 0031 #include <TelepathyQt/PendingReady> 0032 #include <TelepathyQt/Types> 0033 0034 #include "types.h" 0035 #include "ktp-debug.h" 0036 0037 namespace KTp 0038 { 0039 0040 GlobalPresence::GlobalPresence(QObject *parent) 0041 : QObject(parent), 0042 m_connectionStatus(GlobalPresence::Disconnected), 0043 m_changingPresence(false), 0044 m_hasEnabledAccounts(false) 0045 { 0046 Tp::registerTypes(); 0047 0048 m_statusHandlerInterface = new QDBusInterface(QLatin1String("org.freedesktop.Telepathy.Client.KTp.KdedIntegrationModule"), 0049 QLatin1String("/StatusHandler"), 0050 QString(), 0051 QDBusConnection::sessionBus(), this); 0052 0053 m_requestedPresence.setStatus(Tp::ConnectionPresenceTypeUnset, QLatin1String("unset"), QString()); 0054 m_currentPresence.setStatus(Tp::ConnectionPresenceTypeUnset, QLatin1String("unset"), QString()); 0055 } 0056 0057 void GlobalPresence::setAccountManager(const Tp::AccountManagerPtr &accountManager) 0058 { 0059 m_accountManager = accountManager; 0060 0061 m_enabledAccounts = m_accountManager->enabledAccounts(); 0062 m_onlineAccounts = m_accountManager->onlineAccounts(); 0063 0064 for (const Tp::AccountPtr &account : m_enabledAccounts->accounts()) { 0065 onAccountEnabledChanged(account); 0066 } 0067 0068 connect(m_enabledAccounts.data(), &Tp::AccountSet::accountAdded, this, &GlobalPresence::onAccountEnabledChanged); 0069 connect(m_enabledAccounts.data(), &Tp::AccountSet::accountRemoved, this, &GlobalPresence::onAccountEnabledChanged); 0070 0071 if (!m_accountManager->isReady()) { 0072 qCWarning(KTP_COMMONINTERNALS) << "GlobalPresence used with unready account manager"; 0073 } else { 0074 Q_EMIT accountManagerReady(); 0075 } 0076 } 0077 0078 void GlobalPresence::addAccountManager(const Tp::AccountManagerPtr &accountManager) 0079 { 0080 connect(accountManager->becomeReady(), &Tp::PendingReady::finished, [=] (Tp::PendingOperation* op) { 0081 if (op->isError()) { 0082 qCDebug(KTP_COMMONINTERNALS) << op->errorName(); 0083 qCDebug(KTP_COMMONINTERNALS) << op->errorMessage(); 0084 0085 qCDebug(KTP_COMMONINTERNALS) << "Something unexpected happened to" 0086 << "the core part of your Instant Messaging system and it couldn't" 0087 << "be initialized. Try restarting the client."; 0088 0089 return; 0090 } 0091 0092 setAccountManager(accountManager); 0093 }); 0094 } 0095 0096 Tp::AccountManagerPtr GlobalPresence::accountManager() const 0097 { 0098 return m_accountManager; 0099 } 0100 0101 GlobalPresence::ConnectionStatus GlobalPresence::connectionStatus() const 0102 { 0103 return m_connectionStatus; 0104 } 0105 0106 KTp::Presence GlobalPresence::currentPresence() const 0107 { 0108 return m_currentPresence; 0109 } 0110 0111 QString GlobalPresence::currentPresenceMessage() const 0112 { 0113 return m_currentPresence.statusMessage(); 0114 } 0115 0116 QIcon GlobalPresence::currentPresenceIcon() const 0117 { 0118 return m_currentPresence.icon(); 0119 } 0120 0121 QString GlobalPresence::currentPresenceIconName() const 0122 { 0123 return m_currentPresence.iconName(); 0124 } 0125 0126 QString GlobalPresence::currentPresenceName() const 0127 { 0128 return m_currentPresence.displayString(); 0129 } 0130 0131 GlobalPresence::ConnectionPresenceType GlobalPresence::currentPresenceType() const 0132 { 0133 switch(m_currentPresence.type()) { 0134 case Tp::ConnectionPresenceTypeAvailable: 0135 return GlobalPresence::Available; 0136 case Tp::ConnectionPresenceTypeBusy: 0137 return GlobalPresence::Busy; 0138 case Tp::ConnectionPresenceTypeAway: 0139 return GlobalPresence::Away; 0140 case Tp::ConnectionPresenceTypeExtendedAway: 0141 return GlobalPresence::ExtendedAway; 0142 case Tp::ConnectionPresenceTypeHidden: 0143 return GlobalPresence::Hidden; 0144 case Tp::ConnectionPresenceTypeOffline: 0145 return GlobalPresence::Offline; 0146 default: 0147 return GlobalPresence::Unknown; 0148 } 0149 } 0150 0151 KTp::Presence GlobalPresence::requestedPresence() const 0152 { 0153 return m_requestedPresence; 0154 } 0155 0156 QString GlobalPresence::requestedPresenceName() const 0157 { 0158 return m_requestedPresence.displayString(); 0159 } 0160 0161 bool GlobalPresence::isChangingPresence() const 0162 { 0163 return m_changingPresence; 0164 } 0165 0166 KTp::Presence GlobalPresence::globalPresence() const 0167 { 0168 KTp::Presence globalPresence; 0169 globalPresence.setStatus(Tp::ConnectionPresenceTypeUnset, QLatin1String("unset"), QString()); 0170 0171 if (m_statusHandlerInterface->property("requestedGlobalPresence").isValid()) { 0172 globalPresence = KTp::Presence(qdbus_cast<Tp::SimplePresence>(m_statusHandlerInterface->property("requestedGlobalPresence"))); 0173 } 0174 0175 return globalPresence; 0176 } 0177 0178 void GlobalPresence::setPresence(const KTp::Presence &presence, PresenceClass presenceClass) 0179 { 0180 if (m_enabledAccounts.isNull()) { 0181 qCWarning(KTP_COMMONINTERNALS) << "Requested presence change on empty accounts set"; 0182 return; 0183 } 0184 0185 if (!presence.isValid()) { 0186 qCWarning(KTP_COMMONINTERNALS) << "Invalid requested presence"; 0187 return; 0188 } 0189 0190 QDBusPendingCall call = m_statusHandlerInterface->asyncCall(QLatin1String("setRequestedGlobalPresence"), QVariant::fromValue<Tp::SimplePresence>(presence.barePresence()), QVariant::fromValue<uint>(presenceClass)); 0191 } 0192 0193 void GlobalPresence::setPresence(GlobalPresence::ConnectionPresenceType type, QString message, PresenceClass presenceClass) 0194 { 0195 KTp::Presence presence; 0196 0197 switch (type) { 0198 case GlobalPresence::Available: 0199 presence = KTp::Presence::available(message); 0200 break; 0201 case GlobalPresence::Busy: 0202 presence = KTp::Presence::busy(message); 0203 break; 0204 case GlobalPresence::Away: 0205 presence = KTp::Presence::away(message); 0206 break; 0207 case GlobalPresence::ExtendedAway: 0208 presence = KTp::Presence::xa(message); 0209 break; 0210 case GlobalPresence::Hidden: 0211 presence = KTp::Presence::hidden(message); 0212 break; 0213 case GlobalPresence::Offline: 0214 presence = KTp::Presence::offline(message); 0215 break; 0216 case GlobalPresence::Unknown: 0217 presence = KTp::Presence(Tp::Presence(Tp::ConnectionPresenceTypeUnknown, QLatin1String("unknown"), message)); 0218 break; 0219 case GlobalPresence::Unset: 0220 presence = KTp::Presence(Tp::Presence(Tp::ConnectionPresenceTypeUnset, QLatin1String("unset"), message)); 0221 break; 0222 default: 0223 qCDebug(KTP_COMMONINTERNALS) << "You should not be here!"; 0224 } 0225 0226 setPresence(presence, presenceClass); 0227 } 0228 0229 void GlobalPresence::onAccountEnabledChanged(const Tp::AccountPtr &account) 0230 { 0231 if (account->isEnabled()) { 0232 connect(account.data(), &Tp::Account::connectionStatusChanged, this, &GlobalPresence::onConnectionStatusChanged); 0233 connect(account.data(), &Tp::Account::changingPresence, this, &GlobalPresence::onChangingPresence); 0234 connect(account.data(), &Tp::Account::requestedPresenceChanged, this, &GlobalPresence::onRequestedPresenceChanged); 0235 connect(account.data(), &Tp::Account::currentPresenceChanged, this, &GlobalPresence::onCurrentPresenceChanged); 0236 } else { 0237 disconnect(account.data()); 0238 } 0239 0240 onCurrentPresenceChanged(account->currentPresence()); 0241 onRequestedPresenceChanged(account->requestedPresence()); 0242 onChangingPresence(account->isChangingPresence()); 0243 onConnectionStatusChanged(account->connectionStatus()); 0244 0245 if (m_hasEnabledAccounts != !m_enabledAccounts.data()->accounts().isEmpty()) { 0246 m_hasEnabledAccounts = !m_enabledAccounts.data()->accounts().isEmpty(); 0247 Q_EMIT enabledAccountsChanged(m_hasEnabledAccounts); 0248 } 0249 0250 qCDebug(KTP_COMMONINTERNALS) << "Account" << account->uniqueIdentifier() 0251 << "enabled:" << account->isEnabled(); 0252 } 0253 0254 void GlobalPresence::onCurrentPresenceChanged(const Tp::Presence ¤tPresence) 0255 { 0256 /* basic idea of choosing global presence it to make it reflects the presence 0257 * over all accounts, usually this is used to indicates user the whole system 0258 * status. 0259 * 0260 * If there isn't any account, currentPresence should be offline, since there is nothing 0261 * online. 0262 * If there's only one account, then currentPresence should represent the presence 0263 * of this account. 0264 * If there're more than one accounts, the situation is more complicated. 0265 * There can be some accounts is still connecting (thus it's offline), and there can be 0266 * some accounts doesn't support the presence you're choosing. The user-chosen presence 0267 * priority will be higher than standard presence order. 0268 * 0269 * Example: 0270 * user choose to be online, 1 account online, 1 account offline, current presence 0271 * should be online, since online priority is higher than offline. 0272 * user chooses a presence supported by part of the account, current presence will be 0273 * the one chosen by user, to indicate there is at least one account supports it. 0274 * user choose a presence supported by no account, current presence will be chosen 0275 * from all accounts based on priority, and it also indicates there is no account support 0276 * the user-chosen presence. 0277 */ 0278 KTp::Presence highestCurrentPresence = KTp::Presence::offline(); 0279 0280 if (m_currentPresence == KTp::Presence(currentPresence)) { 0281 return; 0282 } else { 0283 for (const Tp::AccountPtr &account : m_enabledAccounts->accounts()) { 0284 if (KTp::Presence(account->currentPresence()) > highestCurrentPresence) { 0285 highestCurrentPresence = KTp::Presence(account->currentPresence()); 0286 } 0287 } 0288 } 0289 0290 if (m_currentPresence != highestCurrentPresence) { 0291 m_currentPresence = highestCurrentPresence; 0292 Q_EMIT currentPresenceChanged(m_currentPresence); 0293 qCDebug(KTP_COMMONINTERNALS) << "Current presence changed:" 0294 << m_currentPresence.status() << m_currentPresence.statusMessage(); 0295 } 0296 } 0297 0298 void GlobalPresence::onRequestedPresenceChanged(const Tp::Presence &requestedPresence) 0299 { 0300 KTp::Presence highestRequestedPresence = KTp::Presence::offline(); 0301 0302 if (m_requestedPresence == KTp::Presence(requestedPresence)) { 0303 return; 0304 } else { 0305 for (const Tp::AccountPtr &account : m_enabledAccounts->accounts()) { 0306 if (KTp::Presence(account->requestedPresence()) > highestRequestedPresence) { 0307 highestRequestedPresence = KTp::Presence(account->requestedPresence()); 0308 } 0309 } 0310 } 0311 0312 if (m_requestedPresence != highestRequestedPresence) { 0313 m_requestedPresence = highestRequestedPresence; 0314 Q_EMIT requestedPresenceChanged(m_requestedPresence); 0315 qCDebug(KTP_COMMONINTERNALS) << "Requested presence changed:" 0316 << m_requestedPresence.status() << m_requestedPresence.statusMessage(); 0317 } 0318 } 0319 0320 void GlobalPresence::onChangingPresence(bool isChangingPresence) 0321 { 0322 bool changing = false; 0323 0324 if (m_changingPresence == isChangingPresence) { 0325 return; 0326 } else { 0327 for (const Tp::AccountPtr &account : m_enabledAccounts->accounts()) { 0328 changing = account->isChangingPresence(); 0329 0330 if (account->isChangingPresence()) { 0331 break; 0332 } 0333 } 0334 } 0335 0336 if (m_changingPresence != changing) { 0337 m_changingPresence = changing; 0338 Q_EMIT changingPresence(m_changingPresence); 0339 qCDebug(KTP_COMMONINTERNALS) << "Presence changing:" << m_changingPresence; 0340 } 0341 } 0342 0343 void GlobalPresence::onConnectionStatusChanged(Tp::ConnectionStatus connectionStatus) 0344 { 0345 GlobalPresence::ConnectionStatus changedConnectionStatus = GlobalPresence::Disconnected; 0346 QList<GlobalPresence::ConnectionStatus> accountConnectionStatuses; 0347 bool hasConnectionError = false; 0348 0349 if (m_connectionStatus == ConnectionStatus(connectionStatus)) { 0350 return; 0351 } else { 0352 for (const Tp::AccountPtr &account : m_enabledAccounts->accounts()) { 0353 accountConnectionStatuses << ConnectionStatus(account->connectionStatus()); 0354 if (!account->connectionError().isEmpty()) { 0355 hasConnectionError = true; 0356 } 0357 } 0358 } 0359 0360 if (accountConnectionStatuses.contains(GlobalPresence::Connecting)) { 0361 changedConnectionStatus = GlobalPresence::Connecting; 0362 } else if (accountConnectionStatuses.contains(GlobalPresence::Connected)) { 0363 changedConnectionStatus = GlobalPresence::Connected; 0364 } 0365 0366 m_hasConnectionError = hasConnectionError; 0367 0368 if (m_connectionStatus != changedConnectionStatus) { 0369 m_connectionStatus = changedConnectionStatus; 0370 Q_EMIT connectionStatusChanged(m_connectionStatus); 0371 qCDebug(KTP_COMMONINTERNALS) << "Connection status changed:" << m_connectionStatus; 0372 } 0373 } 0374 0375 bool GlobalPresence::hasEnabledAccounts() const 0376 { 0377 return m_hasEnabledAccounts; 0378 } 0379 0380 bool GlobalPresence::hasConnectionError() const 0381 { 0382 return m_hasConnectionError; 0383 } 0384 0385 Tp::AccountSetPtr GlobalPresence::onlineAccounts() const 0386 { 0387 return m_onlineAccounts; 0388 } 0389 0390 Tp::AccountSetPtr GlobalPresence::enabledAccounts() const 0391 { 0392 return m_enabledAccounts; 0393 } 0394 0395 }