File indexing completed on 2023-05-30 11:40:28
0001 /* 0002 Class for displaying connection errors 0003 Copyright (C) 2011 Martin Klapetek <martin.klapetek@gmail.com> 0004 0005 This library is free software; you can redistribute it and/or 0006 modify it under the terms of the GNU Lesser General Public 0007 License as published by the Free Software Foundation; either 0008 version 2.1 of the License, or (at your option) any later version. 0009 0010 This library is distributed in the hope that it will be useful, 0011 but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0013 Lesser General Public License for more details. 0014 0015 You should have received a copy of the GNU Lesser General Public 0016 License along with this library; if not, write to the Free Software 0017 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 0018 */ 0019 0020 0021 #include "error-handler.h" 0022 0023 #include <KTp/core.h> 0024 #include <KTp/error-dictionary.h> 0025 0026 #include <TelepathyQt/Account> 0027 #include <TelepathyQt/AccountManager> 0028 #include <TelepathyQt/Connection> 0029 0030 #include <KLocalizedString> 0031 #include <KNotification> 0032 0033 #include <QScopedPointer> 0034 #include <QTimer> 0035 #include <QNetworkConfigurationManager> 0036 0037 /** Stores the last error message for an account 0038 For every new error if we're online we wait 30 seconds and show 1 notification for all errors. This will be the only error we show for that account until the user reconnects. 0039 */ 0040 0041 class ConnectionError 0042 { 0043 public: 0044 ConnectionError(Tp::ConnectionStatusReason connectionStatusReason, 0045 const QString &connectionError, 0046 const Tp::Connection::ErrorDetails &connectionErrorDetails); 0047 0048 Tp::ConnectionStatusReason connectionStatusReason() const; 0049 QString connectionError() const; 0050 Tp::Connection::ErrorDetails connectionErrorDetails() const; 0051 0052 bool shown() const; 0053 void setShown(bool); 0054 0055 QDateTime errorTime() const; 0056 0057 private: 0058 bool m_shown; 0059 Tp::ConnectionStatusReason m_connectionStatusReason; 0060 Tp::Connection::ErrorDetails m_connectionErrorDetails; 0061 QString m_connectionError; 0062 QDateTime m_errorTime; 0063 }; 0064 0065 ConnectionError::ConnectionError(Tp::ConnectionStatusReason connectionStatusReason, const QString &connectionError, const Tp::Connection::ErrorDetails &connectionErrorDetails): 0066 m_connectionStatusReason(connectionStatusReason), 0067 m_connectionErrorDetails(connectionErrorDetails), 0068 m_connectionError(connectionError) 0069 { 0070 m_shown = false; 0071 m_errorTime = QDateTime::currentDateTime(); 0072 } 0073 0074 Tp::ConnectionStatusReason ConnectionError::connectionStatusReason() const 0075 { 0076 return m_connectionStatusReason; 0077 } 0078 0079 QString ConnectionError::connectionError() const 0080 { 0081 return m_connectionError; 0082 } 0083 0084 Tp::Connection::ErrorDetails ConnectionError::connectionErrorDetails() const 0085 { 0086 return m_connectionErrorDetails; 0087 } 0088 0089 QDateTime ConnectionError::errorTime() const 0090 { 0091 return m_errorTime; 0092 } 0093 0094 bool ConnectionError::shown() const 0095 { 0096 return m_shown; 0097 } 0098 0099 void ConnectionError::setShown(bool) 0100 { 0101 m_shown = true; 0102 } 0103 0104 0105 ErrorHandler::ErrorHandler(QObject *parent) 0106 : QObject(parent) 0107 { 0108 Q_FOREACH(const Tp::AccountPtr &account, KTp::accountManager()->allAccounts()) { 0109 onNewAccount(account); 0110 } 0111 0112 connect(KTp::accountManager().data(), SIGNAL(newAccount(Tp::AccountPtr)), 0113 this, SLOT(onNewAccount(Tp::AccountPtr))); 0114 } 0115 0116 ErrorHandler::~ErrorHandler() 0117 { 0118 0119 } 0120 0121 void ErrorHandler::showErrorNotification() 0122 { 0123 //if we're not currently connected to the network, any older errors were probably related to this, ignore them. 0124 QNetworkConfigurationManager network; 0125 if (!network.isOnline()) { 0126 return; 0127 } 0128 0129 QString errorMessage; 0130 0131 QHash<Tp::AccountPtr, ConnectionError>::iterator i = m_errorMap.begin(); 0132 while (i != m_errorMap.end()) { 0133 const Tp::AccountPtr account = i.key(); 0134 ConnectionError &error = i.value(); 0135 0136 //try to group as many error messages as we can, but we still want to give accounts a chance to reconnect 0137 //only want to show messages that are at least 20 seconds old to give the account a chance to connect. 0138 if (!error.shown() && error.errorTime().secsTo(QDateTime::currentDateTime()) > 20) { 0139 switch (error.connectionStatusReason()) { 0140 case Tp::ConnectionStatusReasonNetworkError: 0141 errorMessage += i18nc("%1 is the account name", "Could not connect %1. There was a network error, check your connection", account->displayName()) + QLatin1String("<br>"); 0142 break; 0143 default: 0144 if (error.connectionError() == QLatin1String(TP_QT_ERROR_CANCELLED)) { 0145 break; 0146 } 0147 if (error.connectionErrorDetails().hasServerMessage()) { 0148 errorMessage += i18nc("%1 is the account name, %2 the error message", "There was a problem while trying to connect %1 - %2", account->displayName(), error.connectionErrorDetails().serverMessage()) + QLatin1String("<br>"); 0149 } else { 0150 errorMessage += i18nc("%1 is the account name, %2 the error message", "There was a problem while trying to connect %1 - %2", account->displayName(), KTp::ErrorDictionary::displayVerboseErrorMessage(error.connectionError())) + QLatin1String("<br>"); 0151 } 0152 break; 0153 } 0154 error.setShown(true); 0155 } 0156 ++i; 0157 } 0158 0159 if (!errorMessage.isEmpty()) { 0160 if (errorMessage.endsWith(QLatin1String("<br>"))) { 0161 errorMessage.chop(4); 0162 } 0163 0164 showMessageToUser(errorMessage, ErrorHandler::SystemMessageError); 0165 } 0166 } 0167 0168 void ErrorHandler::onConnectionStatusChanged(const Tp::ConnectionStatus status) 0169 { 0170 Tp::AccountPtr account(qobject_cast< Tp::Account* >(sender())); 0171 0172 //if we're not connected to the network, errors are pointless 0173 QNetworkConfigurationManager network; 0174 if (!network.isOnline()) { 0175 return; 0176 } 0177 0178 if (status == Tp::ConnectionStatusDisconnected) { 0179 //if we're deliberately disconnected do nothing 0180 //else if this is the first error for this account, store the details of the error to show 0181 if (account->connectionStatusReason() == Tp::ConnectionStatusReasonRequested) { 0182 m_errorMap.remove(account); 0183 } else if (!m_errorMap.contains(account)) { 0184 m_errorMap.insert(account, ConnectionError(account->connectionStatusReason(), account->connectionError(), account->connectionErrorDetails())); 0185 QTimer::singleShot(30 * 1000, this, SLOT(showErrorNotification())); //a timer is kept per account because we want to show 30 seconds after the first still valid error. 0186 account->reconnect(); 0187 } 0188 0189 } else if (status == Tp::ConnectionStatusConnected) { 0190 //we are now connected, removed pending error messages 0191 m_errorMap.remove(account); 0192 } 0193 } 0194 0195 void ErrorHandler::onRequestedPresenceChanged() 0196 { 0197 Tp::AccountPtr account(qobject_cast< Tp::Account* >(sender())); 0198 m_errorMap.remove(account); 0199 } 0200 0201 void ErrorHandler::showMessageToUser(const QString &text, const ErrorHandler::SystemMessageType type) 0202 { 0203 //The pointer is automatically deleted when the event is closed 0204 KNotification *notification; 0205 if (type == ErrorHandler::SystemMessageError) { 0206 notification = new KNotification(QLatin1String("telepathyError"), KNotification::Persistent); 0207 } else { 0208 notification = new KNotification(QLatin1String("telepathyInfo"), KNotification::CloseOnTimeout); 0209 } 0210 0211 notification->setComponentName(QStringLiteral("ktelepathy")); 0212 0213 notification->setText(text); 0214 notification->sendEvent(); 0215 } 0216 0217 void ErrorHandler::onNewAccount(const Tp::AccountPtr &account) 0218 { 0219 connect(account.data(), SIGNAL(connectionStatusChanged(Tp::ConnectionStatus)), 0220 this, SLOT(onConnectionStatusChanged(Tp::ConnectionStatus))); 0221 0222 connect(account.data(), SIGNAL(requestedPresenceChanged(Tp::Presence)), SLOT(onRequestedPresenceChanged())); 0223 connect(account.data(), SIGNAL(removed()), SLOT(onAccountRemoved())); 0224 } 0225 0226 void ErrorHandler::onAccountRemoved() 0227 { 0228 Tp::AccountPtr account(qobject_cast<Tp::Account*>(sender())); 0229 Q_ASSERT(account); 0230 m_errorMap.remove(account); 0231 }