Akonadi

core/agentmanager.cpp
1 /*
2  SPDX-FileCopyrightText: 2006-2008 Tobias Koenig <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "agentmanager.h"
8 #include "agentmanager_p.h"
9 
10 #include "agenttype_p.h"
11 #include "agentinstance_p.h"
12 #include <QDBusConnection>
13 #include "servermanager.h"
14 #include "collection.h"
15 
16 #include "shared/akranges.h"
17 
18 #include <QDBusServiceWatcher>
19 #include <QWidget>
20 
21 using namespace Akonadi;
22 using namespace AkRanges;
23 
24 // @cond PRIVATE
25 
26 AgentInstance AgentManagerPrivate::createInstance(const AgentType &type)
27 {
28  const QString &identifier = mManager->createAgentInstance(type.identifier());
29  if (identifier.isEmpty()) {
30  return AgentInstance();
31  }
32 
33  return fillAgentInstanceLight(identifier);
34 }
35 
36 void AgentManagerPrivate::agentTypeAdded(const QString &identifier)
37 {
38  // Ignore agent types we already know about, for example because we called
39  // readAgentTypes before.
40  if (mTypes.contains(identifier)) {
41  return;
42  }
43 
44  if (mTypes.isEmpty()) {
45  // The Akonadi ServerManager assumes that the server is up and running as soon
46  // as it knows about at least one agent type.
47  // If we Q_EMIT the typeAdded() signal here, it therefore thinks the server is
48  // running. However, the AgentManager does not know about all agent types yet,
49  // as the server might still have pending agentTypeAdded() signals, even though
50  // it internally knows all agent types already.
51  // This can cause situations where the client gets told by the ServerManager that
52  // the server is running, yet the client will encounter an error because the
53  // AgentManager doesn't know all types yet.
54  //
55  // Therefore, we read all agent types from the server here so they are known.
56  readAgentTypes();
57  }
58 
59  const AgentType type = fillAgentType(identifier);
60  if (type.isValid()) {
61  mTypes.insert(identifier, type);
62 
63  Q_EMIT mParent->typeAdded(type);
64  }
65 }
66 
67 void AgentManagerPrivate::agentTypeRemoved(const QString &identifier)
68 {
69  if (!mTypes.contains(identifier)) {
70  return;
71  }
72 
73  const AgentType type = mTypes.take(identifier);
74  Q_EMIT mParent->typeRemoved(type);
75 }
76 
77 void AgentManagerPrivate::agentInstanceAdded(const QString &identifier)
78 {
79  const AgentInstance instance = fillAgentInstance(identifier);
80  if (instance.isValid()) {
81 
82  // It is possible that this function is called when the instance is already
83  // in our list we filled initially in the constructor.
84  // This happens when the constructor is called during Akonadi startup, when
85  // the agent processes are not fully loaded and have no D-Bus interface yet.
86  // The server-side agent manager then emits the instance added signal when
87  // the D-Bus interface for the agent comes up.
88  // In this case, we simply notify that the instance status has changed.
89  const bool newAgentInstance = !mInstances.contains(identifier);
90  if (newAgentInstance) {
91  mInstances.insert(identifier, instance);
92  Q_EMIT mParent->instanceAdded(instance);
93  } else {
94  mInstances.remove(identifier);
95  mInstances.insert(identifier, instance);
96  Q_EMIT mParent->instanceStatusChanged(instance);
97  }
98  }
99 }
100 
101 void AgentManagerPrivate::agentInstanceRemoved(const QString &identifier)
102 {
103  if (!mInstances.contains(identifier)) {
104  return;
105  }
106 
107  const AgentInstance instance = mInstances.take(identifier);
108  Q_EMIT mParent->instanceRemoved(instance);
109 }
110 
111 void AgentManagerPrivate::agentInstanceStatusChanged(const QString &identifier, int status, const QString &msg)
112 {
113  if (!mInstances.contains(identifier)) {
114  return;
115  }
116 
117  AgentInstance &instance = mInstances[identifier];
118  instance.d->mStatus = status;
119  instance.d->mStatusMessage = msg;
120 
121  Q_EMIT mParent->instanceStatusChanged(instance);
122 }
123 
124 void AgentManagerPrivate::agentInstanceProgressChanged(const QString &identifier, uint progress, const QString &msg)
125 {
126  if (!mInstances.contains(identifier)) {
127  return;
128  }
129 
130  AgentInstance &instance = mInstances[identifier];
131  instance.d->mProgress = progress;
132  if (!msg.isEmpty()) {
133  instance.d->mStatusMessage = msg;
134  }
135 
136  Q_EMIT mParent->instanceProgressChanged(instance);
137 }
138 
139 void AgentManagerPrivate::agentInstanceWarning(const QString &identifier, const QString &msg)
140 {
141  if (!mInstances.contains(identifier)) {
142  return;
143  }
144 
145  AgentInstance &instance = mInstances[identifier];
146  Q_EMIT mParent->instanceWarning(instance, msg);
147 }
148 
149 void AgentManagerPrivate::agentInstanceError(const QString &identifier, const QString &msg)
150 {
151  if (!mInstances.contains(identifier)) {
152  return;
153  }
154 
155  AgentInstance &instance = mInstances[identifier];
156  Q_EMIT mParent->instanceError(instance, msg);
157 }
158 
159 void AgentManagerPrivate::agentInstanceOnlineChanged(const QString &identifier, bool state)
160 {
161  if (!mInstances.contains(identifier)) {
162  return;
163  }
164 
165  AgentInstance &instance = mInstances[identifier];
166  instance.d->mIsOnline = state;
167  Q_EMIT mParent->instanceOnline(instance, state);
168 }
169 
170 void AgentManagerPrivate::agentInstanceNameChanged(const QString &identifier, const QString &name)
171 {
172  if (!mInstances.contains(identifier)) {
173  return;
174  }
175 
176  AgentInstance &instance = mInstances[identifier];
177  instance.d->mName = name;
178 
179  Q_EMIT mParent->instanceNameChanged(instance);
180 }
181 
182 void AgentManagerPrivate::readAgentTypes()
183 {
184  const QDBusReply<QStringList> types = mManager->agentTypes();
185  if (types.isValid()) {
186  const QStringList lst = types.value();
187  for (const QString &type : lst) {
188  const AgentType agentType = fillAgentType(type);
189  if (agentType.isValid()) {
190  mTypes.insert(type, agentType);
191  Q_EMIT mParent->typeAdded(agentType);
192  }
193  }
194  }
195 }
196 
197 void AgentManagerPrivate::readAgentInstances()
198 {
199  const QDBusReply<QStringList> instances = mManager->agentInstances();
200  if (instances.isValid()) {
201  const QStringList lst = instances.value();
202  for (const QString &instance : lst) {
203  const AgentInstance agentInstance = fillAgentInstance(instance);
204  if (agentInstance.isValid()) {
205  mInstances.insert(instance, agentInstance);
206  Q_EMIT mParent->instanceAdded(agentInstance);
207  }
208  }
209  }
210 }
211 
212 AgentType AgentManagerPrivate::fillAgentType(const QString &identifier) const
213 {
214  AgentType type;
215  type.d->mIdentifier = identifier;
216  type.d->mName = mManager->agentName(identifier);
217  type.d->mDescription = mManager->agentComment(identifier);
218  type.d->mIconName = mManager->agentIcon(identifier);
219  type.d->mMimeTypes = mManager->agentMimeTypes(identifier);
220  type.d->mCapabilities = mManager->agentCapabilities(identifier);
221  type.d->mCustomProperties = mManager->agentCustomProperties(identifier);
222 
223  return type;
224 }
225 
226 void AgentManagerPrivate::setName(const AgentInstance &instance, const QString &name)
227 {
228  mManager->setAgentInstanceName(instance.identifier(), name);
229 }
230 
231 void AgentManagerPrivate::setOnline(const AgentInstance &instance, bool state)
232 {
233  mManager->setAgentInstanceOnline(instance.identifier(), state);
234 }
235 
236 void AgentManagerPrivate::configure(const AgentInstance &instance, QWidget *parent)
237 {
238  qlonglong winId = 0;
239  if (parent) {
240  winId = static_cast<qlonglong>(parent->window()->winId());
241  }
242 
243  mManager->agentInstanceConfigure(instance.identifier(), winId);
244 }
245 
246 void AgentManagerPrivate::synchronize(const AgentInstance &instance)
247 {
248  mManager->agentInstanceSynchronize(instance.identifier());
249 }
250 
251 void AgentManagerPrivate::synchronizeCollectionTree(const AgentInstance &instance)
252 {
253  mManager->agentInstanceSynchronizeCollectionTree(instance.identifier());
254 }
255 
256 void AgentManagerPrivate::synchronizeTags(const AgentInstance &instance)
257 {
258  mManager->agentInstanceSynchronizeTags(instance.identifier());
259 }
260 
261 void AgentManagerPrivate::synchronizeRelations(const AgentInstance &instance)
262 {
263  mManager->agentInstanceSynchronizeRelations(instance.identifier());
264 }
265 
266 AgentInstance AgentManagerPrivate::fillAgentInstance(const QString &identifier) const
267 {
268  AgentInstance instance;
269 
270  const QString agentTypeIdentifier = mManager->agentInstanceType(identifier);
271  if (!mTypes.contains(agentTypeIdentifier)) {
272  return instance;
273  }
274 
275  instance.d->mType = mTypes.value(agentTypeIdentifier);
276  instance.d->mIdentifier = identifier;
277  instance.d->mName = mManager->agentInstanceName(identifier);
278  instance.d->mStatus = mManager->agentInstanceStatus(identifier);
279  instance.d->mStatusMessage = mManager->agentInstanceStatusMessage(identifier);
280  instance.d->mProgress = mManager->agentInstanceProgress(identifier);
281  instance.d->mIsOnline = mManager->agentInstanceOnline(identifier);
282 
283  return instance;
284 }
285 
286 AgentInstance AgentManagerPrivate::fillAgentInstanceLight(const QString &identifier) const
287 {
288  AgentInstance instance;
289 
290  const QString agentTypeIdentifier = mManager->agentInstanceType(identifier);
291  Q_ASSERT_X(mTypes.contains(agentTypeIdentifier), "fillAgentInstanceLight", "Requests non-existing agent type");
292 
293  instance.d->mType = mTypes.value(agentTypeIdentifier);
294  instance.d->mIdentifier = identifier;
295 
296  return instance;
297 }
298 
299 void AgentManagerPrivate::createDBusInterface()
300 {
301  mTypes.clear();
302  mInstances.clear();
303 
304  using AgentManagerIface = org::freedesktop::Akonadi::AgentManager;
305  mManager = std::make_unique<AgentManagerIface>(ServerManager::serviceName(ServerManager::Control),
306  QStringLiteral("/AgentManager"),
307  QDBusConnection::sessionBus(), mParent);
308 
309  connect(mManager.get(), &AgentManagerIface::agentTypeAdded, this, &AgentManagerPrivate::agentTypeAdded);
310  connect(mManager.get(), &AgentManagerIface::agentTypeRemoved, this, &AgentManagerPrivate::agentTypeRemoved);
311  connect(mManager.get(), &AgentManagerIface::agentInstanceAdded, this, &AgentManagerPrivate::agentInstanceAdded);
312  connect(mManager.get(), &AgentManagerIface::agentInstanceRemoved, this, &AgentManagerPrivate::agentInstanceRemoved);
313  connect(mManager.get(), &AgentManagerIface::agentInstanceStatusChanged, this, &AgentManagerPrivate::agentInstanceStatusChanged);
314  connect(mManager.get(), &AgentManagerIface::agentInstanceProgressChanged, this, &AgentManagerPrivate::agentInstanceProgressChanged);
315  connect(mManager.get(), &AgentManagerIface::agentInstanceNameChanged, this, &AgentManagerPrivate::agentInstanceNameChanged);
316  connect(mManager.get(), &AgentManagerIface::agentInstanceWarning, this, &AgentManagerPrivate::agentInstanceWarning);
317  connect(mManager.get(), &AgentManagerIface::agentInstanceError, this, &AgentManagerPrivate::agentInstanceError);
318  connect(mManager.get(), &AgentManagerIface::agentInstanceOnlineChanged, this, &AgentManagerPrivate::agentInstanceOnlineChanged);
319 
320  if (mManager->isValid()) {
321  readAgentTypes();
322  readAgentInstances();
323  }
324 }
325 
326 AgentManager *AgentManagerPrivate::mSelf = nullptr;
327 
329  : QObject(nullptr)
330  , d(new AgentManagerPrivate(this))
331 {
332  // needed for queued connections on our signals
333  qRegisterMetaType<Akonadi::AgentType>();
334  qRegisterMetaType<Akonadi::AgentInstance>();
335 
336  d->createDBusInterface();
337 
338  d->mServiceWatcher = std::make_unique<QDBusServiceWatcher>(
339  ServerManager::serviceName(ServerManager::Control), QDBusConnection::sessionBus(),
341  connect(d->mServiceWatcher.get(), &QDBusServiceWatcher::serviceRegistered,
342  this, [this]() {
343  if (d->mTypes.isEmpty()) { // just to be safe
344  d->readAgentTypes();
345  }
346  if (d->mInstances.isEmpty()) {
347  d->readAgentInstances();
348  }
349  });
350 }
351 
352 // @endcond
353 
354 AgentManager::~AgentManager() = default;
355 
356 AgentManager *AgentManager::self()
357 {
358  if (!AgentManagerPrivate::mSelf) {
359  AgentManagerPrivate::mSelf = new AgentManager();
360  }
361 
362  return AgentManagerPrivate::mSelf;
363 }
364 
365 AgentType::List AgentManager::types() const
366 {
367  // Maybe the Control process is up and ready but we haven't been to the event loop yet so
368  // QDBusServiceWatcher hasn't notified us yet.
369  // In that case make sure to do it here, to avoid going into Broken state.
370  if (d->mTypes.isEmpty()) {
371  d->readAgentTypes();
372  }
373  return d->mTypes | Views::values | Actions::toQVector;
374 }
375 
376 AgentType AgentManager::type(const QString &identifier) const
377 {
378  return d->mTypes.value(identifier);
379 }
380 
381 AgentInstance::List AgentManager::instances() const
382 {
383  return d->mInstances | Views::values | Actions::toQVector;
384 }
385 
386 AgentInstance AgentManager::instance(const QString &identifier) const
387 {
388  return d->mInstances.value(identifier);
389 }
390 
391 void AgentManager::removeInstance(const AgentInstance &instance)
392 {
393  d->mManager->removeAgentInstance(instance.identifier());
394 }
395 
396 void AgentManager::synchronizeCollection(const Collection &collection)
397 {
398  synchronizeCollection(collection, false);
399 }
400 
401 void AgentManager::synchronizeCollection(const Collection &collection, bool recursive)
402 {
403  const QString resId = collection.resource();
404  Q_ASSERT(!resId.isEmpty());
405  d->mManager->agentInstanceSynchronizeCollection(resId, collection.id(), recursive);
406 }
407 
408 #include "moc_agentmanager.cpp"
void serviceRegistered(const QString &serviceName)
Provides an interface to retrieve agent types and manage agent instances.
QWidget * window() const const
AgentManager(bool verbose, QObject *parent=nullptr)
Creates a new agent manager.
~AgentManager()
Destroys the agent manager.
Represents a collection of PIM items.
Definition: collection.h:63
bool isValid() const
Returns whether the agent type is valid.
bool isValid() const const
QString identifier() const
Returns the unique identifier of the agent instance.
QDBusConnection sessionBus()
static QString serviceName(ServiceType serviceType)
Returns the namespaced D-Bus service name for serviceType.
QString identifier() const
Returns the unique identifier of the agent type.
A representation of an agent type.
QDBusReply::Type value() const const
bool isValid() const
Returns whether the agent instance object is valid.
bool isEmpty() const const
WId winId() const const
Id id() const
Returns the unique identifier of the collection.
Definition: collection.cpp:98
Helper integration between Akonadi and Qt.
A representation of an agent instance.
QString resource() const
Returns the identifier of the resource owning the collection.
Definition: collection.cpp:304
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
Types types(const QStringList &mimeTypes)
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Tue Jan 19 2021 23:20:39 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.