File indexing completed on 2023-10-01 08:41:44

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 &currentPresence)
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 }