File indexing completed on 2024-05-12 05:20:46

0001 /*
0002    SPDX-FileCopyrightText: 2018-2024 Laurent Montel <montel@kde.org>
0003 
0004    SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "unityservicemanager.h"
0008 #include "kmail_debug.h"
0009 #include "kmkernel.h"
0010 #include "kmsystemtray.h"
0011 #include "settings/kmailsettings.h"
0012 #include <MailCommon/MailKernel>
0013 #include <MailCommon/MailUtil>
0014 
0015 #include <QApplication>
0016 #include <QDBusConnection>
0017 #include <QDBusConnectionInterface>
0018 #include <QDBusMessage>
0019 #include <QDBusPendingCallWatcher>
0020 #include <QDBusPendingReply>
0021 #include <QDBusServiceWatcher>
0022 #include <QTimer>
0023 
0024 #include <Akonadi/ChangeRecorder>
0025 #include <Akonadi/CollectionStatistics>
0026 #include <Akonadi/EntityMimeTypeFilterModel>
0027 #include <Akonadi/EntityTreeModel>
0028 #include <Akonadi/NewMailNotifierAttribute>
0029 #include <chrono>
0030 
0031 using namespace std::chrono_literals;
0032 
0033 using namespace KMail;
0034 
0035 UnityServiceManager::UnityServiceManager(QObject *parent)
0036     : QObject(parent)
0037     , mUnityServiceWatcher(new QDBusServiceWatcher(this))
0038 {
0039     connect(kmkernel->folderCollectionMonitor(), &Akonadi::Monitor::collectionStatisticsChanged, this, &UnityServiceManager::slotCollectionStatisticsChanged);
0040 
0041     connect(kmkernel->folderCollectionMonitor(), &Akonadi::Monitor::collectionAdded, this, &UnityServiceManager::initListOfCollection);
0042     connect(kmkernel->folderCollectionMonitor(), &Akonadi::Monitor::collectionRemoved, this, &UnityServiceManager::initListOfCollection);
0043     connect(kmkernel->folderCollectionMonitor(), &Akonadi::Monitor::collectionSubscribed, this, &UnityServiceManager::initListOfCollection);
0044     connect(kmkernel->folderCollectionMonitor(), &Akonadi::Monitor::collectionUnsubscribed, this, &UnityServiceManager::initListOfCollection);
0045     initListOfCollection();
0046     initUnity();
0047 }
0048 
0049 UnityServiceManager::~UnityServiceManager()
0050 {
0051     mSystemTray = nullptr;
0052 }
0053 
0054 bool UnityServiceManager::excludeFolder(const Akonadi::Collection &collection) const
0055 {
0056     if (!collection.isValid() || !collection.contentMimeTypes().contains(KMime::Message::mimeType())) {
0057         return true;
0058     }
0059     if (CommonKernel->outboxCollectionFolder() == collection || CommonKernel->sentCollectionFolder() == collection
0060         || CommonKernel->templatesCollectionFolder() == collection || CommonKernel->trashCollectionFolder() == collection
0061         || CommonKernel->draftsCollectionFolder() == collection) {
0062         return true;
0063     }
0064 
0065     if (MailCommon::Util::isVirtualCollection(collection)) {
0066         return true;
0067     }
0068     return false;
0069 }
0070 
0071 void UnityServiceManager::unreadMail(const QAbstractItemModel *model, const QModelIndex &parentIndex)
0072 {
0073     const int rowCount = model->rowCount(parentIndex);
0074     for (int row = 0; row < rowCount; ++row) {
0075         const QModelIndex index = model->index(row, 0, parentIndex);
0076         const auto collection = model->data(index, Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>();
0077 
0078         if (!excludeFolder(collection)) {
0079             const Akonadi::CollectionStatistics statistics = collection.statistics();
0080             const qint64 count = qMax(0LL, statistics.unreadCount());
0081 
0082             if (count > 0) {
0083                 if (!ignoreNewMailInFolder(collection)) {
0084                     mCount += count;
0085                 }
0086             }
0087         }
0088         if (model->hasChildren(index)) {
0089             unreadMail(model, index);
0090         }
0091     }
0092     if (mSystemTray) {
0093         // Update tooltip to reflect count of unread messages
0094         mSystemTray->updateToolTip(mCount);
0095     }
0096 }
0097 
0098 void UnityServiceManager::updateSystemTray()
0099 {
0100     initListOfCollection();
0101 }
0102 
0103 void UnityServiceManager::initListOfCollection()
0104 {
0105     mCount = 0;
0106     const QAbstractItemModel *model = kmkernel->collectionModel();
0107     if (model->rowCount() == 0) {
0108         QTimer::singleShot(1s, this, &UnityServiceManager::initListOfCollection);
0109         return;
0110     }
0111     unreadMail(model);
0112     if (mSystemTray) {
0113         mSystemTray->updateStatus(mCount);
0114     }
0115 
0116     // qCDebug(KMAIL_LOG)<<" mCount :"<<mCount;
0117     updateCount();
0118 }
0119 
0120 void UnityServiceManager::slotCollectionStatisticsChanged(Akonadi::Collection::Id id, const Akonadi::CollectionStatistics &)
0121 {
0122     // Exclude sent mail folder
0123 
0124     if (CommonKernel->outboxCollectionFolder().id() == id || CommonKernel->sentCollectionFolder().id() == id
0125         || CommonKernel->templatesCollectionFolder().id() == id || CommonKernel->trashCollectionFolder().id() == id
0126         || CommonKernel->draftsCollectionFolder().id() == id) {
0127         return;
0128     }
0129     initListOfCollection();
0130 }
0131 
0132 void UnityServiceManager::updateCount()
0133 {
0134     if (mSystemTray) {
0135         mSystemTray->updateCount(mCount);
0136     }
0137 
0138     if (mUnityServiceAvailable) {
0139         const QString launcherId = qApp->desktopFileName() + QLatin1StringView(".desktop");
0140         const int unreadEmail = KMailSettings::self()->showUnreadInTaskbar() ? mCount : 0;
0141         const QVariantMap properties{{QStringLiteral("count-visible"), unreadEmail > 0}, {QStringLiteral("count"), unreadEmail}};
0142 
0143         QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/org/kmail2/UnityLauncher"),
0144                                                           QStringLiteral("com.canonical.Unity.LauncherEntry"),
0145                                                           QStringLiteral("Update"));
0146         message.setArguments({launcherId, properties});
0147         QDBusConnection::sessionBus().send(message);
0148     }
0149 }
0150 
0151 void UnityServiceManager::setSystemTryAssociatedWindow(QWindow *window)
0152 {
0153     if (!mSystemTray) {
0154         return;
0155     }
0156     mSystemTray->setAssociatedWindow(window);
0157 }
0158 
0159 void UnityServiceManager::initUnity()
0160 {
0161     mUnityServiceWatcher->setConnection(QDBusConnection::sessionBus());
0162     mUnityServiceWatcher->setWatchMode(QDBusServiceWatcher::WatchForUnregistration | QDBusServiceWatcher::WatchForRegistration);
0163     mUnityServiceWatcher->addWatchedService(QStringLiteral("com.canonical.Unity"));
0164     connect(mUnityServiceWatcher, &QDBusServiceWatcher::serviceRegistered, this, [this](const QString &service) {
0165         Q_UNUSED(service)
0166         mUnityServiceAvailable = true;
0167         updateCount();
0168     });
0169 
0170     connect(mUnityServiceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, [this](const QString &service) {
0171         Q_UNUSED(service)
0172         mUnityServiceAvailable = false;
0173     });
0174 
0175     // QDBusConnectionInterface::isServiceRegistered blocks
0176     QDBusPendingCall listNamesCall = QDBusConnection::sessionBus().interface()->asyncCall(QStringLiteral("ListNames"));
0177     auto callWatcher = new QDBusPendingCallWatcher(listNamesCall, this);
0178     connect(callWatcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) {
0179         QDBusPendingReply<QStringList> reply = *watcher;
0180         watcher->deleteLater();
0181 
0182         if (reply.isError()) {
0183             qCWarning(KMAIL_LOG) << "DBus reported an error " << reply.error().message();
0184             return;
0185         }
0186 
0187         const QStringList &services = reply.value();
0188 
0189         mUnityServiceAvailable = services.contains(QLatin1StringView("com.canonical.Unity"));
0190         if (mUnityServiceAvailable) {
0191             updateCount();
0192         }
0193     });
0194 }
0195 
0196 bool UnityServiceManager::ignoreNewMailInFolder(const Akonadi::Collection &collection)
0197 {
0198     if (collection.hasAttribute<Akonadi::NewMailNotifierAttribute>()) {
0199         if (collection.attribute<Akonadi::NewMailNotifierAttribute>()->ignoreNewMail()) {
0200             return true;
0201         }
0202     }
0203     return false;
0204 }
0205 
0206 bool UnityServiceManager::haveSystemTrayApplet() const
0207 {
0208     return mSystemTray != nullptr;
0209 }
0210 
0211 bool UnityServiceManager::hasUnreadMail() const
0212 {
0213     return mCount != 0;
0214 }
0215 
0216 bool UnityServiceManager::canQueryClose()
0217 {
0218     if (!mSystemTray) {
0219         return true;
0220     }
0221     if (hasUnreadMail()) {
0222         mSystemTray->setStatus(KStatusNotifierItem::Active);
0223     }
0224     mSystemTray->hideKMail();
0225     return false;
0226 }
0227 
0228 void UnityServiceManager::toggleSystemTray(QWidget *widget)
0229 {
0230     if (widget) {
0231         if (!mSystemTray && KMailSettings::self()->systemTrayEnabled()) {
0232             mSystemTray = new KMail::KMSystemTray(widget);
0233             mSystemTray->setUnityServiceManager(this);
0234             mSystemTray->initialize(mCount);
0235         } else if (mSystemTray && !KMailSettings::self()->systemTrayEnabled()) {
0236             // Get rid of system tray on user's request
0237             qCDebug(KMAIL_LOG) << "deleting systray";
0238             delete mSystemTray;
0239             mSystemTray = nullptr;
0240         }
0241     }
0242 }
0243 
0244 #include "moc_unityservicemanager.cpp"