Akonadi

akonadicontrol/accountsintegration.cpp
1/***************************************************************************
2 * SPDX-FileCopyrightText: 2019 Daniel Vrátil <dvratil@kde.org> *
3 * *
4 * SPDX-License-Identifier: LGPL-2.0-or-later *
5 ***************************************************************************/
6
7#include "accountsintegration.h"
8#include "accountsinterface.h"
9#include "agentmanager.h"
10#include "akonadicontrol_debug.h"
11
12#include "private/dbus_p.h"
13
14#include <QDBusConnection>
15#include <QTimer>
16
17#include <Accounts/Account>
18#include <Accounts/Service>
19
20using namespace Akonadi;
21using namespace std::chrono_literals;
22
23namespace
24{
25const auto akonadiAgentType = QStringLiteral("akonadi/agentType");
26
27}
28
29AccountsIntegration::AccountsIntegration(AgentManager &agentManager)
30 : mAgentManager(agentManager)
31{
32 connect(&mAccountsManager, &Accounts::Manager::accountCreated, this, &AccountsIntegration::onAccountAdded);
33 connect(&mAccountsManager, &Accounts::Manager::accountRemoved, this, &AccountsIntegration::onAccountRemoved);
34
35 const auto accounts = mAccountsManager.accountList();
36 for (const auto account : accounts) {
37 connect(mAccountsManager.account(account), &Accounts::Account::enabledChanged, this, &AccountsIntegration::onAccountServiceEnabled);
38 }
39}
40
41std::optional<QString> AccountsIntegration::agentForAccount(const QString &agentType, Accounts::AccountId accountId) const
42{
43 const auto instances = mAgentManager.agentInstances();
44 for (const auto &identifier : instances) {
45 if (mAgentManager.agentInstanceType(identifier) == agentType) {
46 const auto serviceName = Akonadi::DBus::agentServiceName(identifier, Akonadi::DBus::Resource);
47 org::kde::Akonadi::Accounts accountsIface(serviceName, QStringLiteral("/Accounts"), QDBusConnection::sessionBus());
48 if (!accountsIface.isValid()) {
49 continue;
50 }
51
52 if (accountsIface.getAccountId() == accountId) {
53 return identifier;
54 }
55 }
56 }
57 return std::nullopt;
58}
59
60void AccountsIntegration::configureAgentInstance(const QString &identifier, Accounts::AccountId accountId, int attempt)
61{
62 const auto serviceName = Akonadi::DBus::agentServiceName(identifier, Akonadi::DBus::Resource);
63 org::kde::Akonadi::Accounts accountsIface(serviceName, QStringLiteral("/Accounts"), QDBusConnection::sessionBus());
64 if (!accountsIface.isValid()) {
65 if (attempt >= 3) {
66 qCWarning(AKONADICONTROL_LOG) << "The resource" << identifier << "does not provide the Accounts DBus interface. Will remove the agent";
67 mAgentManager.removeAgentInstance(identifier);
68 } else {
69 QTimer::singleShot(2s, this, [this, identifier, accountId, attempt]() {
70 configureAgentInstance(identifier, accountId, attempt + 1);
71 });
72 }
73 return;
74 }
75
76 accountsIface.setAccountId(accountId);
77 qCDebug(AKONADICONTROL_LOG) << "Configured resource" << identifier << "for account" << accountId;
78}
79
80void AccountsIntegration::createAgent(const QString &agentType, Accounts::AccountId accountId)
81{
82 const auto identifier = mAgentManager.createAgentInstance(agentType);
83 qCDebug(AKONADICONTROL_LOG) << "Created resource" << identifier << "for account" << accountId;
84 configureAgentInstance(identifier, accountId);
85}
86
87void AccountsIntegration::removeAgentInstance(const QString &identifier)
88{
89 mAgentManager.removeAgentInstance(identifier);
90}
91
92void AccountsIntegration::onAccountAdded(Accounts::AccountId accId)
93{
94 qCDebug(AKONADICONTROL_LOG) << "Online account ID" << accId << "added.";
95 auto account = mAccountsManager.account(accId);
96 if (!account || !account->isEnabled()) {
97 return;
98 }
99
100 const auto enabledServices = account->enabledServices();
101 for (const auto &service : enabledServices) {
102 account->selectService(service);
103 const auto agentType = account->valueAsString(akonadiAgentType);
104 if (agentType.isEmpty()) {
105 continue; // doesn't support Akonadi :-(
106 }
107 const auto agent = agentForAccount(agentType, accId);
108 if (!agent.has_value()) {
109 createAgent(agentType, account->id());
110 }
111 // Always go through all services, there may be more!
112 }
113 account->selectService();
114
115 connect(account, &Accounts::Account::enabledChanged, this, &AccountsIntegration::onAccountServiceEnabled);
116}
117
118void AccountsIntegration::onAccountRemoved(Accounts::AccountId accId)
119{
120 qCDebug(AKONADICONTROL_LOG) << "Online account ID" << accId << "removed.";
121
122 const auto instances = mAgentManager.agentInstances();
123 for (const auto &instance : instances) {
124 const auto serviceName = DBus::agentServiceName(instance, DBus::Resource);
125 org::kde::Akonadi::Accounts accountIface(serviceName, QStringLiteral("/Accounts"), QDBusConnection::sessionBus());
126 if (!accountIface.isValid()) {
127 continue;
128 }
129
130 if (accountIface.getAccountId() == accId) {
131 removeAgentInstance(instance);
132 }
133 }
134}
135
136void AccountsIntegration::onAccountServiceEnabled(const QString &serviceType, bool enabled)
137{
138 if (serviceType.isEmpty()) {
139 return;
140 }
141
143 qCDebug(AKONADICONTROL_LOG) << "Online account ID" << account->id() << "service" << serviceType << "has been" << (enabled ? "enabled" : "disabled");
144
145 const auto service = mAccountsManager.service(serviceType);
146 account->selectService(service);
147 const auto agentType = account->valueAsString(akonadiAgentType);
148 account->selectService();
149 if (agentType.isEmpty()) {
150 return; // this service does not support Akonadi (yet -;)
151 }
152
153 const auto identifier = agentForAccount(agentType, account->id());
154 if (enabled && !identifier.has_value()) {
155 createAgent(agentType, account->id());
156 } else if (!enabled && identifier.has_value()) {
157 removeAgentInstance(identifier.value());
158 }
159}
160
161#include "moc_accountsintegration.cpp"
The agent manager has knowledge about all available agents (it scans for .desktop files in the agent ...
Helper integration between Akonadi and Qt.
QDBusConnection sessionBus()
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
T qobject_cast(QObject *object)
QObject * sender() const const
bool isEmpty() const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 17:01:42 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.