2
* This file is part of TelepathyQt
4
* @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
5
* @copyright Copyright (C) 2008 Nokia Corporation
8
* This library is free software; you can redistribute it and/or
9
* modify it under the terms of the GNU Lesser General Public
10
* License as published by the Free Software Foundation; either
11
* version 2.1 of the License, or (at your option) any later version.
13
* This library is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
* Lesser General Public License for more details.
18
* You should have received a copy of the GNU Lesser General Public
19
* License along with this library; if not, write to the Free Software
20
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23
#include <TelepathyQt/Channel>
24
#include "TelepathyQt/channel-internal.h"
26
#include "TelepathyQt/_gen/cli-channel-body.hpp"
27
#include "TelepathyQt/_gen/cli-channel.moc.hpp"
28
#include "TelepathyQt/_gen/channel.moc.hpp"
29
#include "TelepathyQt/_gen/channel-internal.moc.hpp"
31
#include "TelepathyQt/debug-internal.h"
33
#include "TelepathyQt/future-internal.h"
35
#include <TelepathyQt/ChannelFactory>
36
#include <TelepathyQt/Connection>
37
#include <TelepathyQt/ConnectionCapabilities>
38
#include <TelepathyQt/ConnectionLowlevel>
39
#include <TelepathyQt/ContactManager>
40
#include <TelepathyQt/PendingContacts>
41
#include <TelepathyQt/PendingFailure>
42
#include <TelepathyQt/PendingOperation>
43
#include <TelepathyQt/PendingReady>
44
#include <TelepathyQt/PendingSuccess>
45
#include <TelepathyQt/StreamTubeChannel>
46
#include <TelepathyQt/ReferencedHandles>
47
#include <TelepathyQt/Constants>
51
#include <QSharedData>
57
using TpFuture::Client::ChannelInterfaceMergeableConferenceInterface;
58
using TpFuture::Client::ChannelInterfaceSplittableInterface;
60
struct TP_QT_NO_EXPORT Channel::Private
62
Private(Channel *parent, const ConnectionPtr &connection,
63
const QVariantMap &immutableProperties);
66
static void introspectMain(Private *self);
67
void introspectMainProperties();
68
void introspectMainFallbackChannelType();
69
void introspectMainFallbackHandle();
70
void introspectMainFallbackInterfaces();
71
void introspectGroup();
72
void introspectGroupFallbackFlags();
73
void introspectGroupFallbackMembers();
74
void introspectGroupFallbackLocalPendingWithInfo();
75
void introspectGroupFallbackSelfHandle();
76
void introspectConference();
78
static void introspectConferenceInitialInviteeContacts(Private *self);
80
void continueIntrospection();
82
void extractMainProps(const QVariantMap &props);
83
void extract0176GroupProps(const QVariantMap &props);
85
void nowHaveInterfaces();
86
void nowHaveInitialMembers();
88
bool setGroupFlags(uint groupFlags);
91
void doMembersChangedDetailed(const UIntList &, const UIntList &, const UIntList &,
92
const UIntList &, const QVariantMap &);
93
void processMembersChanged();
94
void updateContacts(const QList<ContactPtr> &contacts =
96
bool fakeGroupInterfaceIfNeeded();
99
QString groupMemberChangeDetailsTelepathyError(
100
const GroupMemberChangeDetails &details);
102
inline ChannelInterfaceMergeableConferenceInterface *mergeableConferenceInterface(
103
InterfaceSupportedChecking check = CheckInterfaceSupported) const
105
return parent->optionalInterface<ChannelInterfaceMergeableConferenceInterface>(check);
108
inline ChannelInterfaceSplittableInterface *splittableInterface(
109
InterfaceSupportedChecking check = CheckInterfaceSupported) const
111
return parent->optionalInterface<ChannelInterfaceSplittableInterface>(check);
114
void processConferenceChannelRemoved();
116
struct GroupMembersChangedInfo;
117
struct ConferenceChannelRemovedInfo;
122
// Instance of generated interface class
123
Client::ChannelInterface *baseInterface;
125
// Mandatory properties interface proxy
126
Client::DBus::PropertiesInterface *properties;
128
// Owning connection - it can be a SharedPtr as Connection does not cache
130
ConnectionPtr connection;
132
QVariantMap immutableProperties;
134
// Optional interface proxies
135
Client::ChannelInterfaceGroupInterface *group;
136
Client::ChannelInterfaceConferenceInterface *conference;
138
ReadinessHelper *readinessHelper;
141
QQueue<void (Private::*)()> introspectQueue;
143
// Introspected properties
147
uint targetHandleType;
150
ContactPtr targetContact;
152
uint initiatorHandle;
153
ContactPtr initiatorContact;
157
bool usingMembersChangedDetailed;
159
// Group member introspection
160
bool groupHaveMembers;
161
bool buildingContacts;
163
// Queue of received MCD signals to process
164
QQueue<GroupMembersChangedInfo *> groupMembersChangedQueue;
165
GroupMembersChangedInfo *currentGroupMembersChangedInfo;
167
// Pending from the MCD signal currently processed, but contacts not yet built
168
QSet<uint> pendingGroupMembers;
169
QSet<uint> pendingGroupLocalPendingMembers;
170
QSet<uint> pendingGroupRemotePendingMembers;
171
UIntList groupMembersToRemove;
172
UIntList groupLocalPendingMembersToRemove;
173
UIntList groupRemotePendingMembersToRemove;
176
UIntList groupInitialMembers;
177
LocalPendingInfoList groupInitialLP;
178
UIntList groupInitialRP;
181
QHash<uint, ContactPtr> groupContacts;
182
QHash<uint, ContactPtr> groupLocalPendingContacts;
183
QHash<uint, ContactPtr> groupRemotePendingContacts;
185
// Stored change info
186
QHash<uint, GroupMemberChangeDetails> groupLocalPendingContactsChangeInfo;
187
GroupMemberChangeDetails groupSelfContactRemoveInfo;
189
// Group handle owners
190
bool groupAreHandleOwnersAvailable;
191
HandleOwnerMap groupHandleOwners;
193
// Group self identity
194
bool pendingRetrieveGroupSelfContact;
195
bool groupIsSelfHandleTracked;
196
uint groupSelfHandle;
197
ContactPtr groupSelfContact;
200
bool introspectingConference;
201
QHash<QString, ChannelPtr> conferenceChannels;
202
QHash<QString, ChannelPtr> conferenceInitialChannels;
203
QString conferenceInvitationMessage;
204
QHash<uint, ChannelPtr> conferenceOriginalChannels;
205
UIntList conferenceInitialInviteeHandles;
206
Contacts conferenceInitialInviteeContacts;
207
QQueue<ConferenceChannelRemovedInfo *> conferenceChannelRemovedQueue;
208
bool buildingConferenceChannelRemovedActorContact;
210
static const QString keyActor;
213
struct TP_QT_NO_EXPORT Channel::Private::GroupMembersChangedInfo
215
GroupMembersChangedInfo(const UIntList &added, const UIntList &removed,
216
const UIntList &localPending, const UIntList &remotePending,
217
const QVariantMap &details)
220
localPending(localPending),
221
remotePending(remotePending),
223
// TODO most of these probably should be removed once the rest of the code using them is sanitized
224
actor(qdbus_cast<uint>(details.value(keyActor))),
225
reason(qdbus_cast<uint>(details.value(keyChangeReason))),
226
message(qdbus_cast<QString>(details.value(keyMessage)))
232
UIntList localPending;
233
UIntList remotePending;
239
static const QString keyChangeReason;
240
static const QString keyMessage;
241
static const QString keyContactIds;
244
struct TP_QT_NO_EXPORT Channel::Private::ConferenceChannelRemovedInfo
246
ConferenceChannelRemovedInfo(const QDBusObjectPath &channelPath, const QVariantMap &details)
247
: channelPath(channelPath),
252
QDBusObjectPath channelPath;
256
const QString Channel::Private::keyActor(QLatin1String("actor"));
257
const QString Channel::Private::GroupMembersChangedInfo::keyChangeReason(
258
QLatin1String("change-reason"));
259
const QString Channel::Private::GroupMembersChangedInfo::keyMessage(QLatin1String("message"));
260
const QString Channel::Private::GroupMembersChangedInfo::keyContactIds(QLatin1String("contact-ids"));
262
Channel::Private::Private(Channel *parent, const ConnectionPtr &connection,
263
const QVariantMap &immutableProperties)
265
baseInterface(new Client::ChannelInterface(parent)),
266
properties(parent->interface<Client::DBus::PropertiesInterface>()),
267
connection(connection),
268
immutableProperties(immutableProperties),
271
readinessHelper(parent->readinessHelper()),
277
usingMembersChangedDetailed(false),
278
groupHaveMembers(false),
279
buildingContacts(false),
280
currentGroupMembersChangedInfo(0),
281
groupAreHandleOwnersAvailable(false),
282
pendingRetrieveGroupSelfContact(false),
283
groupIsSelfHandleTracked(false),
285
introspectingConference(false),
286
buildingConferenceChannelRemovedActorContact(false)
288
debug() << "Creating new Channel:" << parent->objectPath();
290
if (connection->isValid()) {
291
debug() << " Connecting to Channel::Closed() signal";
292
parent->connect(baseInterface,
296
debug() << " Connection to owning connection's lifetime signals";
297
parent->connect(connection.data(),
298
SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)),
299
SLOT(onConnectionInvalidated()));
302
warning() << "Connection given as the owner for a Channel was "
303
"invalid! Channel will be stillborn.";
304
parent->invalidate(TP_QT_ERROR_INVALID_ARGUMENT,
305
QLatin1String("Connection given as the owner of this channel was invalid"));
308
ReadinessHelper::Introspectables introspectables;
310
// As Channel does not have predefined statuses let's simulate one (0)
311
ReadinessHelper::Introspectable introspectableCore(
312
QSet<uint>() << 0, // makesSenseForStatuses
313
Features(), // dependsOnFeatures
314
QStringList(), // dependsOnInterfaces
315
(ReadinessHelper::IntrospectFunc) &Private::introspectMain,
317
introspectables[FeatureCore] = introspectableCore;
319
// As Channel does not have predefined statuses let's simulate one (0)
320
ReadinessHelper::Introspectable introspectableConferenceInitialInviteeContacts(
321
QSet<uint>() << 0, // makesSenseForStatuses
322
Features() << FeatureCore, // dependsOnFeatures
323
QStringList() << TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE, // dependsOnInterfaces
324
(ReadinessHelper::IntrospectFunc) &Private::introspectConferenceInitialInviteeContacts,
326
introspectables[FeatureConferenceInitialInviteeContacts] =
327
introspectableConferenceInitialInviteeContacts;
329
readinessHelper->addIntrospectables(introspectables);
332
Channel::Private::~Private()
334
delete currentGroupMembersChangedInfo;
335
foreach (GroupMembersChangedInfo *info, groupMembersChangedQueue) {
338
foreach (ConferenceChannelRemovedInfo *info, conferenceChannelRemovedQueue) {
343
void Channel::Private::introspectMain(Channel::Private *self)
345
// Make sure connection object is ready, as we need to use some methods that
346
// are only available after connection object gets ready.
347
debug() << "Calling Connection::becomeReady()";
348
self->parent->connect(self->connection->becomeReady(),
349
SIGNAL(finished(Tp::PendingOperation*)),
350
SLOT(onConnectionReady(Tp::PendingOperation*)));
353
void Channel::Private::introspectMainProperties()
357
bool needIntrospectMainProps = false;
358
const unsigned numNames = 8;
359
const static QString names[numNames] = {
360
QLatin1String("ChannelType"),
361
QLatin1String("Interfaces"),
362
QLatin1String("TargetHandleType"),
363
QLatin1String("TargetHandle"),
364
QLatin1String("TargetID"),
365
QLatin1String("Requested"),
366
QLatin1String("InitiatorHandle"),
367
QLatin1String("InitiatorID")
369
const static QString qualifiedNames[numNames] = {
370
TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType"),
371
TP_QT_IFACE_CHANNEL + QLatin1String(".Interfaces"),
372
TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandleType"),
373
TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandle"),
374
TP_QT_IFACE_CHANNEL + QLatin1String(".TargetID"),
375
TP_QT_IFACE_CHANNEL + QLatin1String(".Requested"),
376
TP_QT_IFACE_CHANNEL + QLatin1String(".InitiatorHandle"),
377
TP_QT_IFACE_CHANNEL + QLatin1String(".InitiatorID")
379
for (unsigned i = 0; i < numNames; ++i) {
380
const QString &qualified = qualifiedNames[i];
381
if (!immutableProperties.contains(qualified)) {
382
needIntrospectMainProps = true;
385
props.insert(names[i], immutableProperties.value(qualified));
388
// Save Requested and InitiatorHandle here, so even if the GetAll return doesn't have them but
389
// the given immutable props do (eg. due to the PendingChannel fallback guesses) we use them
390
requested = qdbus_cast<bool>(props[QLatin1String("Requested")]);
391
initiatorHandle = qdbus_cast<uint>(props[QLatin1String("InitiatorHandle")]);
393
if (props.contains(QLatin1String("InitiatorID"))) {
394
QString initiatorId = qdbus_cast<QString>(props[QLatin1String("InitiatorID")]);
395
connection->lowlevel()->injectContactId(initiatorHandle, initiatorId);
398
if (needIntrospectMainProps) {
399
debug() << "Calling Properties::GetAll(Channel)";
400
QDBusPendingCallWatcher *watcher =
401
new QDBusPendingCallWatcher(
402
properties->GetAll(TP_QT_IFACE_CHANNEL),
404
parent->connect(watcher,
405
SIGNAL(finished(QDBusPendingCallWatcher*)),
406
SLOT(gotMainProperties(QDBusPendingCallWatcher*)));
408
extractMainProps(props);
409
continueIntrospection();
413
void Channel::Private::introspectMainFallbackChannelType()
415
debug() << "Calling Channel::GetChannelType()";
416
QDBusPendingCallWatcher *watcher =
417
new QDBusPendingCallWatcher(baseInterface->GetChannelType(), parent);
418
parent->connect(watcher,
419
SIGNAL(finished(QDBusPendingCallWatcher*)),
420
SLOT(gotChannelType(QDBusPendingCallWatcher*)));
423
void Channel::Private::introspectMainFallbackHandle()
425
debug() << "Calling Channel::GetHandle()";
426
QDBusPendingCallWatcher *watcher =
427
new QDBusPendingCallWatcher(baseInterface->GetHandle(), parent);
428
parent->connect(watcher,
429
SIGNAL(finished(QDBusPendingCallWatcher*)),
430
SLOT(gotHandle(QDBusPendingCallWatcher*)));
433
void Channel::Private::introspectMainFallbackInterfaces()
435
debug() << "Calling Channel::GetInterfaces()";
436
QDBusPendingCallWatcher *watcher =
437
new QDBusPendingCallWatcher(baseInterface->GetInterfaces(), parent);
438
parent->connect(watcher,
439
SIGNAL(finished(QDBusPendingCallWatcher*)),
440
SLOT(gotInterfaces(QDBusPendingCallWatcher*)));
443
void Channel::Private::introspectGroup()
445
Q_ASSERT(properties != 0);
448
group = parent->interface<Client::ChannelInterfaceGroupInterface>();
449
Q_ASSERT(group != 0);
452
debug() << "Introspecting Channel.Interface.Group for" << parent->objectPath();
454
parent->connect(group,
455
SIGNAL(GroupFlagsChanged(uint,uint)),
456
SLOT(onGroupFlagsChanged(uint,uint)));
458
parent->connect(group,
459
SIGNAL(MembersChanged(QString,Tp::UIntList,
460
Tp::UIntList,Tp::UIntList,
461
Tp::UIntList,uint,uint)),
462
SLOT(onMembersChanged(QString,Tp::UIntList,
463
Tp::UIntList,Tp::UIntList,
464
Tp::UIntList,uint,uint)));
465
parent->connect(group,
466
SIGNAL(MembersChangedDetailed(Tp::UIntList,
467
Tp::UIntList,Tp::UIntList,
468
Tp::UIntList,QVariantMap)),
469
SLOT(onMembersChangedDetailed(Tp::UIntList,
470
Tp::UIntList,Tp::UIntList,
471
Tp::UIntList,QVariantMap)));
473
parent->connect(group,
474
SIGNAL(HandleOwnersChanged(Tp::HandleOwnerMap,
476
SLOT(onHandleOwnersChanged(Tp::HandleOwnerMap,
479
parent->connect(group,
480
SIGNAL(SelfHandleChanged(uint)),
481
SLOT(onSelfHandleChanged(uint)));
483
debug() << "Calling Properties::GetAll(Channel.Interface.Group)";
484
QDBusPendingCallWatcher *watcher =
485
new QDBusPendingCallWatcher(
486
properties->GetAll(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP),
488
parent->connect(watcher,
489
SIGNAL(finished(QDBusPendingCallWatcher*)),
490
SLOT(gotGroupProperties(QDBusPendingCallWatcher*)));
493
void Channel::Private::introspectGroupFallbackFlags()
495
Q_ASSERT(group != 0);
497
debug() << "Calling Channel.Interface.Group::GetGroupFlags()";
498
QDBusPendingCallWatcher *watcher =
499
new QDBusPendingCallWatcher(group->GetGroupFlags(), parent);
500
parent->connect(watcher,
501
SIGNAL(finished(QDBusPendingCallWatcher*)),
502
SLOT(gotGroupFlags(QDBusPendingCallWatcher*)));
505
void Channel::Private::introspectGroupFallbackMembers()
507
Q_ASSERT(group != 0);
509
debug() << "Calling Channel.Interface.Group::GetAllMembers()";
510
QDBusPendingCallWatcher *watcher =
511
new QDBusPendingCallWatcher(group->GetAllMembers(), parent);
512
parent->connect(watcher,
513
SIGNAL(finished(QDBusPendingCallWatcher*)),
514
SLOT(gotAllMembers(QDBusPendingCallWatcher*)));
517
void Channel::Private::introspectGroupFallbackLocalPendingWithInfo()
519
Q_ASSERT(group != 0);
521
debug() << "Calling Channel.Interface.Group::GetLocalPendingMembersWithInfo()";
522
QDBusPendingCallWatcher *watcher =
523
new QDBusPendingCallWatcher(group->GetLocalPendingMembersWithInfo(),
525
parent->connect(watcher,
526
SIGNAL(finished(QDBusPendingCallWatcher*)),
527
SLOT(gotLocalPendingMembersWithInfo(QDBusPendingCallWatcher*)));
530
void Channel::Private::introspectGroupFallbackSelfHandle()
532
Q_ASSERT(group != 0);
534
debug() << "Calling Channel.Interface.Group::GetSelfHandle()";
535
QDBusPendingCallWatcher *watcher =
536
new QDBusPendingCallWatcher(group->GetSelfHandle(), parent);
537
parent->connect(watcher,
538
SIGNAL(finished(QDBusPendingCallWatcher*)),
539
SLOT(gotSelfHandle(QDBusPendingCallWatcher*)));
542
void Channel::Private::introspectConference()
544
Q_ASSERT(properties != 0);
545
Q_ASSERT(conference == 0);
547
debug() << "Introspecting Conference interface";
548
conference = parent->interface<Client::ChannelInterfaceConferenceInterface>();
549
Q_ASSERT(conference != 0);
551
introspectingConference = true;
553
debug() << "Connecting to Channel.Interface.Conference.ChannelMerged/Removed";
554
parent->connect(conference,
555
SIGNAL(ChannelMerged(QDBusObjectPath,uint,QVariantMap)),
556
SLOT(onConferenceChannelMerged(QDBusObjectPath,uint,QVariantMap)));
557
parent->connect(conference,
558
SIGNAL(ChannelRemoved(QDBusObjectPath,QVariantMap)),
559
SLOT(onConferenceChannelRemoved(QDBusObjectPath,QVariantMap)));
561
debug() << "Calling Properties::GetAll(Channel.Interface.Conference)";
562
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
563
properties->GetAll(TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE),
565
parent->connect(watcher,
566
SIGNAL(finished(QDBusPendingCallWatcher*)),
567
SLOT(gotConferenceProperties(QDBusPendingCallWatcher*)));
570
void Channel::Private::introspectConferenceInitialInviteeContacts(Private *self)
572
if (!self->conferenceInitialInviteeHandles.isEmpty()) {
573
ContactManagerPtr manager = self->connection->contactManager();
574
PendingContacts *pendingContacts = manager->contactsForHandles(
575
self->conferenceInitialInviteeHandles);
576
self->parent->connect(pendingContacts,
577
SIGNAL(finished(Tp::PendingOperation *)),
578
SLOT(gotConferenceInitialInviteeContacts(Tp::PendingOperation *)));
580
self->readinessHelper->setIntrospectCompleted(
581
FeatureConferenceInitialInviteeContacts, true);
585
void Channel::Private::continueIntrospection()
587
if (introspectQueue.isEmpty()) {
588
// this should always be true, but let's make sure
589
if (!parent->isReady(Channel::FeatureCore)) {
590
if (groupMembersChangedQueue.isEmpty() && !buildingContacts &&
591
!introspectingConference) {
592
debug() << "Both the IS and the MCD queue empty for the first time. Ready.";
595
debug() << "Introspection done before contacts done - contacts sets ready";
599
(this->*(introspectQueue.dequeue()))();
603
void Channel::Private::extractMainProps(const QVariantMap &props)
605
const static QString keyChannelType(QLatin1String("ChannelType"));
606
const static QString keyInterfaces(QLatin1String("Interfaces"));
607
const static QString keyTargetHandle(QLatin1String("TargetHandle"));
608
const static QString keyTargetHandleType(QLatin1String("TargetHandleType"));
610
bool haveProps = props.size() >= 4
611
&& props.contains(keyChannelType)
612
&& !qdbus_cast<QString>(props[keyChannelType]).isEmpty()
613
&& props.contains(keyInterfaces)
614
&& props.contains(keyTargetHandle)
615
&& props.contains(keyTargetHandleType);
618
warning() << "Channel properties specified in 0.17.7 not found";
620
introspectQueue.enqueue(&Private::introspectMainFallbackChannelType);
621
introspectQueue.enqueue(&Private::introspectMainFallbackHandle);
622
introspectQueue.enqueue(&Private::introspectMainFallbackInterfaces);
624
parent->setInterfaces(qdbus_cast<QStringList>(props[keyInterfaces]));
625
readinessHelper->setInterfaces(parent->interfaces());
626
channelType = qdbus_cast<QString>(props[keyChannelType]);
627
targetHandle = qdbus_cast<uint>(props[keyTargetHandle]);
628
targetHandleType = qdbus_cast<uint>(props[keyTargetHandleType]);
630
const static QString keyTargetId(QLatin1String("TargetID"));
631
const static QString keyRequested(QLatin1String("Requested"));
632
const static QString keyInitiatorHandle(QLatin1String("InitiatorHandle"));
633
const static QString keyInitiatorId(QLatin1String("InitiatorID"));
635
if (props.contains(keyTargetId)) {
636
targetId = qdbus_cast<QString>(props[keyTargetId]);
638
if (targetHandleType == HandleTypeContact) {
639
connection->lowlevel()->injectContactId(targetHandle, targetId);
643
if (props.contains(keyRequested)) {
644
requested = qdbus_cast<uint>(props[keyRequested]);
647
if (props.contains(keyInitiatorHandle)) {
648
initiatorHandle = qdbus_cast<uint>(props[keyInitiatorHandle]);
651
if (props.contains(keyInitiatorId)) {
652
QString initiatorId = qdbus_cast<QString>(props[keyInitiatorId]);
653
connection->lowlevel()->injectContactId(initiatorHandle, initiatorId);
656
if (!fakeGroupInterfaceIfNeeded() &&
657
!parent->interfaces().contains(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP) &&
659
// No group interface, so nobody will build the poor fellow for us. Will do it ourselves
660
// out of pity for him.
661
// TODO: needs testing. I would imagine some of the elaborate updateContacts logic
662
// tripping over with just this.
669
debug() << "Have initiator handle:" << (initiatorHandle ? "yes" : "no");
672
void Channel::Private::extract0176GroupProps(const QVariantMap &props)
674
const static QString keyGroupFlags(QLatin1String("GroupFlags"));
675
const static QString keyHandleOwners(QLatin1String("HandleOwners"));
676
const static QString keyLPMembers(QLatin1String("LocalPendingMembers"));
677
const static QString keyMembers(QLatin1String("Members"));
678
const static QString keyRPMembers(QLatin1String("RemotePendingMembers"));
679
const static QString keySelfHandle(QLatin1String("SelfHandle"));
681
bool haveProps = props.size() >= 6
682
&& (props.contains(keyGroupFlags)
683
&& (qdbus_cast<uint>(props[keyGroupFlags]) &
684
ChannelGroupFlagProperties))
685
&& props.contains(keyHandleOwners)
686
&& props.contains(keyLPMembers)
687
&& props.contains(keyMembers)
688
&& props.contains(keyRPMembers)
689
&& props.contains(keySelfHandle);
692
warning() << " Properties specified in 0.17.6 not found";
693
warning() << " Handle owners and self handle tracking disabled";
695
introspectQueue.enqueue(&Private::introspectGroupFallbackFlags);
696
introspectQueue.enqueue(&Private::introspectGroupFallbackMembers);
697
introspectQueue.enqueue(&Private::introspectGroupFallbackLocalPendingWithInfo);
698
introspectQueue.enqueue(&Private::introspectGroupFallbackSelfHandle);
700
debug() << " Found properties specified in 0.17.6";
702
groupAreHandleOwnersAvailable = true;
703
groupIsSelfHandleTracked = true;
705
setGroupFlags(qdbus_cast<uint>(props[keyGroupFlags]));
706
groupHandleOwners = qdbus_cast<HandleOwnerMap>(props[keyHandleOwners]);
708
groupInitialMembers = qdbus_cast<UIntList>(props[keyMembers]);
709
groupInitialLP = qdbus_cast<LocalPendingInfoList>(props[keyLPMembers]);
710
groupInitialRP = qdbus_cast<UIntList>(props[keyRPMembers]);
712
uint propSelfHandle = qdbus_cast<uint>(props[keySelfHandle]);
713
// Don't overwrite the self handle we got from the Connection with 0
714
if (propSelfHandle) {
715
groupSelfHandle = propSelfHandle;
718
nowHaveInitialMembers();
722
void Channel::Private::nowHaveInterfaces()
724
debug() << "Channel has" << parent->interfaces().size() <<
725
"optional interfaces:" << parent->interfaces();
727
QStringList interfaces = parent->interfaces();
729
if (interfaces.contains(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP)) {
730
introspectQueue.enqueue(&Private::introspectGroup);
733
if (interfaces.contains(TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE)) {
734
introspectQueue.enqueue(&Private::introspectConference);
738
void Channel::Private::nowHaveInitialMembers()
740
// Must be called with no contacts anywhere in the first place
741
Q_ASSERT(!parent->isReady(Channel::FeatureCore));
742
Q_ASSERT(!buildingContacts);
744
Q_ASSERT(pendingGroupMembers.isEmpty());
745
Q_ASSERT(pendingGroupLocalPendingMembers.isEmpty());
746
Q_ASSERT(pendingGroupRemotePendingMembers.isEmpty());
748
Q_ASSERT(groupContacts.isEmpty());
749
Q_ASSERT(groupLocalPendingContacts.isEmpty());
750
Q_ASSERT(groupRemotePendingContacts.isEmpty());
752
// Set groupHaveMembers so we start queueing fresh MCD signals
753
Q_ASSERT(!groupHaveMembers);
754
groupHaveMembers = true;
756
// Synthesize MCD for current + RP
757
groupMembersChangedQueue.enqueue(new GroupMembersChangedInfo(
758
groupInitialMembers, // Members
759
UIntList(), // Removed - obviously, none
760
UIntList(), // LP - will be handled separately, see below
761
groupInitialRP, // Remote pending
762
QVariantMap())); // No details for members + RP
764
// Synthesize one MCD for each initial LP member - they might have different details
765
foreach (const LocalPendingInfo &info, groupInitialLP) {
768
if (info.actor != 0) {
769
details.insert(QLatin1String("actor"), info.actor);
772
if (info.reason != ChannelGroupChangeReasonNone) {
773
details.insert(QLatin1String("change-reason"), info.reason);
776
if (!info.message.isEmpty()) {
777
details.insert(QLatin1String("message"), info.message);
780
groupMembersChangedQueue.enqueue(new GroupMembersChangedInfo(UIntList(), UIntList(),
781
UIntList() << info.toBeAdded, UIntList(), details));
784
// At least our added MCD event to process
785
processMembersChanged();
788
bool Channel::Private::setGroupFlags(uint newGroupFlags)
790
if (groupFlags == newGroupFlags) {
794
groupFlags = newGroupFlags;
796
// this shouldn't happen but let's make sure
797
if (!parent->interfaces().contains(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP)) {
801
if ((groupFlags & ChannelGroupFlagMembersChangedDetailed) &&
802
!usingMembersChangedDetailed) {
803
usingMembersChangedDetailed = true;
804
debug() << "Starting to exclusively listen to MembersChangedDetailed for" <<
805
parent->objectPath();
806
parent->disconnect(group,
807
SIGNAL(MembersChanged(QString,Tp::UIntList,
808
Tp::UIntList,Tp::UIntList,
809
Tp::UIntList,uint,uint)),
811
SLOT(onMembersChanged(QString,Tp::UIntList,
812
Tp::UIntList,Tp::UIntList,
813
Tp::UIntList,uint,uint)));
814
} else if (!(groupFlags & ChannelGroupFlagMembersChangedDetailed) &&
815
usingMembersChangedDetailed) {
816
warning() << " Channel service did spec-incompliant removal of MCD from GroupFlags";
817
usingMembersChangedDetailed = false;
818
parent->connect(group,
819
SIGNAL(MembersChanged(QString,Tp::UIntList,
820
Tp::UIntList,Tp::UIntList,
821
Tp::UIntList,uint,uint)),
823
SLOT(onMembersChanged(QString,Tp::UIntList,
824
Tp::UIntList,Tp::UIntList,
825
Tp::UIntList,uint,uint)));
831
void Channel::Private::buildContacts()
833
buildingContacts = true;
835
ContactManagerPtr manager = connection->contactManager();
836
UIntList toBuild = QSet<uint>(pendingGroupMembers +
837
pendingGroupLocalPendingMembers +
838
pendingGroupRemotePendingMembers).toList();
840
if (currentGroupMembersChangedInfo &&
841
currentGroupMembersChangedInfo->actor != 0) {
842
toBuild.append(currentGroupMembersChangedInfo->actor);
845
if (!initiatorContact && initiatorHandle) {
846
// No initiator contact, but Yes initiator handle - might do something about it with just
848
toBuild.append(initiatorHandle);
851
if (!targetContact && targetHandleType == HandleTypeContact && targetHandle != 0) {
852
toBuild.append(targetHandle);
855
// always try to retrieve selfContact and check if it changed on
856
// updateContacts or on gotContacts, in case we were not able to retrieve it
857
if (groupSelfHandle) {
858
toBuild.append(groupSelfHandle);
861
// group self handle changed to 0 <- strange but it may happen, and contacts
862
// were being built at the time, so check now
863
if (toBuild.isEmpty()) {
864
if (!groupSelfHandle && groupSelfContact) {
865
groupSelfContact.reset();
866
if (parent->isReady(Channel::FeatureCore)) {
867
emit parent->groupSelfContactChanged();
871
buildingContacts = false;
875
PendingContacts *pendingContacts = manager->contactsForHandles(
877
parent->connect(pendingContacts,
878
SIGNAL(finished(Tp::PendingOperation*)),
879
SLOT(gotContacts(Tp::PendingOperation*)));
882
void Channel::Private::processMembersChanged()
884
Q_ASSERT(!buildingContacts);
886
if (groupMembersChangedQueue.isEmpty()) {
887
if (pendingRetrieveGroupSelfContact) {
888
pendingRetrieveGroupSelfContact = false;
889
// nothing queued but selfContact changed
894
if (!parent->isReady(Channel::FeatureCore)) {
895
if (introspectQueue.isEmpty()) {
896
debug() << "Both the MCD and the introspect queue empty for the first time. Ready!";
898
if (initiatorHandle && !initiatorContact) {
899
warning() << " Unable to create contact object for initiator with handle" <<
903
if (targetHandleType == HandleTypeContact && targetHandle != 0 && !targetContact) {
904
warning() << " Unable to create contact object for target with handle" <<
908
if (groupSelfHandle && !groupSelfContact) {
909
warning() << " Unable to create contact object for self handle" <<
913
continueIntrospection();
915
debug() << "Contact queue empty but introspect queue isn't. IS will set ready.";
922
Q_ASSERT(pendingGroupMembers.isEmpty());
923
Q_ASSERT(pendingGroupLocalPendingMembers.isEmpty());
924
Q_ASSERT(pendingGroupRemotePendingMembers.isEmpty());
926
// always set this to false here, as buildContacts will always try to
927
// retrieve the selfContact and updateContacts will check if the built
928
// contact is the same as the current contact.
929
pendingRetrieveGroupSelfContact = false;
931
currentGroupMembersChangedInfo = groupMembersChangedQueue.dequeue();
933
foreach (uint handle, currentGroupMembersChangedInfo->added) {
934
if (!groupContacts.contains(handle)) {
935
pendingGroupMembers.insert(handle);
938
// the member was added to current members, check if it was in the
939
// local/pending lists and if true, schedule for removal from that list
940
if (groupLocalPendingContacts.contains(handle)) {
941
groupLocalPendingMembersToRemove.append(handle);
942
} else if(groupRemotePendingContacts.contains(handle)) {
943
groupRemotePendingMembersToRemove.append(handle);
947
foreach (uint handle, currentGroupMembersChangedInfo->localPending) {
948
if (!groupLocalPendingContacts.contains(handle)) {
949
pendingGroupLocalPendingMembers.insert(handle);
953
foreach (uint handle, currentGroupMembersChangedInfo->remotePending) {
954
if (!groupRemotePendingContacts.contains(handle)) {
955
pendingGroupRemotePendingMembers.insert(handle);
959
foreach (uint handle, currentGroupMembersChangedInfo->removed) {
960
groupMembersToRemove.append(handle);
963
// Always go through buildContacts - we might have a self/initiator/whatever handle to build
967
void Channel::Private::updateContacts(const QList<ContactPtr> &contacts)
969
Contacts groupContactsAdded;
970
Contacts groupLocalPendingContactsAdded;
971
Contacts groupRemotePendingContactsAdded;
972
ContactPtr actorContact;
973
bool selfContactUpdated = false;
975
debug() << "Entering Chan::Priv::updateContacts() with" << contacts.size() << "contacts";
977
// FIXME: simplify. Some duplication of logic present.
978
foreach (ContactPtr contact, contacts) {
979
uint handle = contact->handle()[0];
980
if (pendingGroupMembers.contains(handle)) {
981
groupContactsAdded.insert(contact);
982
groupContacts[handle] = contact;
983
} else if (pendingGroupLocalPendingMembers.contains(handle)) {
984
groupLocalPendingContactsAdded.insert(contact);
985
groupLocalPendingContacts[handle] = contact;
986
// FIXME: should set the details and actor here too
987
groupLocalPendingContactsChangeInfo[handle] = GroupMemberChangeDetails();
988
} else if (pendingGroupRemotePendingMembers.contains(handle)) {
989
groupRemotePendingContactsAdded.insert(contact);
990
groupRemotePendingContacts[handle] = contact;
993
if (groupSelfHandle == handle && groupSelfContact != contact) {
994
groupSelfContact = contact;
995
selfContactUpdated = true;
998
if (!initiatorContact && initiatorHandle == handle) {
999
// No initiator contact stored, but there's a contact for the initiator handle
1001
initiatorContact = contact;
1004
if (!targetContact && targetHandleType == HandleTypeContact && targetHandle == handle) {
1005
targetContact = contact;
1007
if (targetId.isEmpty()) {
1008
// For some reason, TargetID was missing from the property map. We can initialize it
1009
// here in that case.
1010
targetId = targetContact->id();
1014
if (currentGroupMembersChangedInfo &&
1015
currentGroupMembersChangedInfo->actor == contact->handle()[0]) {
1016
actorContact = contact;
1020
if (!groupSelfHandle && groupSelfContact) {
1021
groupSelfContact.reset();
1022
selfContactUpdated = true;
1025
pendingGroupMembers.clear();
1026
pendingGroupLocalPendingMembers.clear();
1027
pendingGroupRemotePendingMembers.clear();
1029
// FIXME: This shouldn't be needed. Clearer would be to first scan for the actor being present
1030
// in the contacts supplied.
1031
foreach (ContactPtr contact, contacts) {
1032
uint handle = contact->handle()[0];
1033
if (groupLocalPendingContactsChangeInfo.contains(handle)) {
1034
groupLocalPendingContactsChangeInfo[handle] =
1035
GroupMemberChangeDetails(actorContact,
1036
currentGroupMembersChangedInfo ? currentGroupMembersChangedInfo->details : QVariantMap());
1040
Contacts groupContactsRemoved;
1041
ContactPtr contactToRemove;
1042
foreach (uint handle, groupMembersToRemove) {
1043
if (groupContacts.contains(handle)) {
1044
contactToRemove = groupContacts[handle];
1045
groupContacts.remove(handle);
1046
} else if (groupLocalPendingContacts.contains(handle)) {
1047
contactToRemove = groupLocalPendingContacts[handle];
1048
groupLocalPendingContacts.remove(handle);
1049
} else if (groupRemotePendingContacts.contains(handle)) {
1050
contactToRemove = groupRemotePendingContacts[handle];
1051
groupRemotePendingContacts.remove(handle);
1054
if (groupLocalPendingContactsChangeInfo.contains(handle)) {
1055
groupLocalPendingContactsChangeInfo.remove(handle);
1058
if (contactToRemove) {
1059
groupContactsRemoved.insert(contactToRemove);
1062
groupMembersToRemove.clear();
1064
// FIXME: drop the LPToRemove and RPToRemove sets - they're redundant
1065
foreach (uint handle, groupLocalPendingMembersToRemove) {
1066
groupLocalPendingContacts.remove(handle);
1068
groupLocalPendingMembersToRemove.clear();
1070
foreach (uint handle, groupRemotePendingMembersToRemove) {
1071
groupRemotePendingContacts.remove(handle);
1073
groupRemotePendingMembersToRemove.clear();
1075
if (!groupContactsAdded.isEmpty() ||
1076
!groupLocalPendingContactsAdded.isEmpty() ||
1077
!groupRemotePendingContactsAdded.isEmpty() ||
1078
!groupContactsRemoved.isEmpty()) {
1079
GroupMemberChangeDetails details(
1081
currentGroupMembersChangedInfo ? currentGroupMembersChangedInfo->details : QVariantMap());
1083
if (currentGroupMembersChangedInfo
1084
&& currentGroupMembersChangedInfo->removed.contains(groupSelfHandle)) {
1085
// Update groupSelfContactRemoveInfo with the proper actor in case
1086
// the actor was not available by the time onMembersChangedDetailed
1088
groupSelfContactRemoveInfo = details;
1091
if (parent->isReady(Channel::FeatureCore)) {
1092
// Channel is ready, we can signal membership changes to the outside world without
1093
// confusing anyone's fragile logic.
1094
emit parent->groupMembersChanged(
1096
groupLocalPendingContactsAdded,
1097
groupRemotePendingContactsAdded,
1098
groupContactsRemoved,
1102
delete currentGroupMembersChangedInfo;
1103
currentGroupMembersChangedInfo = 0;
1105
if (selfContactUpdated && parent->isReady(Channel::FeatureCore)) {
1106
emit parent->groupSelfContactChanged();
1109
processMembersChanged();
1112
bool Channel::Private::fakeGroupInterfaceIfNeeded()
1114
if (parent->interfaces().contains(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP)) {
1116
} else if (targetHandleType != HandleTypeContact) {
1120
// fake group interface
1121
if (connection->selfHandle() && targetHandle) {
1122
// Fake groupSelfHandle and initial members, let the MCD handling take care of the rest
1123
// TODO connect to Connection::selfHandleChanged
1124
groupSelfHandle = connection->selfHandle();
1125
groupInitialMembers = UIntList() << groupSelfHandle << targetHandle;
1127
debug().nospace() << "Faking a group on channel with self handle=" <<
1128
groupSelfHandle << " and other handle=" << targetHandle;
1130
nowHaveInitialMembers();
1132
warning() << "Connection::selfHandle is 0 or targetHandle is 0, "
1133
"not faking a group on channel";
1139
void Channel::Private::setReady()
1141
Q_ASSERT(!parent->isReady(Channel::FeatureCore));
1143
debug() << "Channel fully ready";
1144
debug() << " Channel type" << channelType;
1145
debug() << " Target handle" << targetHandle;
1146
debug() << " Target handle type" << targetHandleType;
1148
if (parent->interfaces().contains(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP)) {
1149
debug() << " Group: flags" << groupFlags;
1150
if (groupAreHandleOwnersAvailable) {
1151
debug() << " Group: Number of handle owner mappings" <<
1152
groupHandleOwners.size();
1155
debug() << " Group: No handle owners property present";
1157
debug() << " Group: Number of current members" <<
1158
groupContacts.size();
1159
debug() << " Group: Number of local pending members" <<
1160
groupLocalPendingContacts.size();
1161
debug() << " Group: Number of remote pending members" <<
1162
groupRemotePendingContacts.size();
1163
debug() << " Group: Self handle" << groupSelfHandle <<
1164
"tracked:" << (groupIsSelfHandleTracked ? "yes" : "no");
1167
readinessHelper->setIntrospectCompleted(FeatureCore, true);
1170
QString Channel::Private::groupMemberChangeDetailsTelepathyError(
1171
const GroupMemberChangeDetails &details)
1174
uint reason = details.reason();
1176
case ChannelGroupChangeReasonOffline:
1177
error = TP_QT_ERROR_OFFLINE;
1179
case ChannelGroupChangeReasonKicked:
1180
error = TP_QT_ERROR_CHANNEL_KICKED;
1182
case ChannelGroupChangeReasonBanned:
1183
error = TP_QT_ERROR_CHANNEL_BANNED;
1185
case ChannelGroupChangeReasonBusy:
1186
error = TP_QT_ERROR_BUSY;
1188
case ChannelGroupChangeReasonNoAnswer:
1189
error = TP_QT_ERROR_NO_ANSWER;
1191
case ChannelGroupChangeReasonPermissionDenied:
1192
error = TP_QT_ERROR_PERMISSION_DENIED;
1194
case ChannelGroupChangeReasonInvalidContact:
1195
error = TP_QT_ERROR_DOES_NOT_EXIST;
1197
// The following change reason are being mapped to default
1198
// case ChannelGroupChangeReasonNone:
1199
// case ChannelGroupChangeReasonInvited: // shouldn't happen
1200
// case ChannelGroupChangeReasonError:
1201
// case ChannelGroupChangeReasonRenamed:
1202
// case ChannelGroupChangeReasonSeparated: // shouldn't happen
1204
// let's use the actor handle and selfHandle here instead of the
1205
// contacts, as the contacts may not be ready.
1206
error = ((qdbus_cast<uint>(details.allDetails().value(QLatin1String("actor"))) == groupSelfHandle) ?
1207
TP_QT_ERROR_CANCELLED :
1208
TP_QT_ERROR_TERMINATED);
1215
void Channel::Private::processConferenceChannelRemoved()
1217
if (buildingConferenceChannelRemovedActorContact ||
1218
conferenceChannelRemovedQueue.isEmpty()) {
1222
ConferenceChannelRemovedInfo *info = conferenceChannelRemovedQueue.first();
1223
if (!conferenceChannels.contains(info->channelPath.path())) {
1224
info = conferenceChannelRemovedQueue.dequeue();
1226
processConferenceChannelRemoved();
1230
buildingConferenceChannelRemovedActorContact = true;
1232
if (info->details.contains(keyActor)) {
1233
ContactManagerPtr manager = connection->contactManager();
1234
PendingContacts *pendingContacts = manager->contactsForHandles(
1235
UIntList() << qdbus_cast<uint>(info->details.value(keyActor)));
1236
parent->connect(pendingContacts,
1237
SIGNAL(finished(Tp::PendingOperation*)),
1238
SLOT(gotConferenceChannelRemovedActorContact(Tp::PendingOperation*)));
1240
parent->gotConferenceChannelRemovedActorContact(0);
1244
struct TP_QT_NO_EXPORT Channel::GroupMemberChangeDetails::Private : public QSharedData
1246
Private(const ContactPtr &actor, const QVariantMap &details)
1247
: actor(actor), details(details) {}
1250
QVariantMap details;
1254
* \class Channel::GroupMemberChangeDetails
1255
* \ingroup clientchannel
1256
* \headerfile TelepathyQt/channel.h <TelepathyQt/Channel>
1258
* \brief The Channel::GroupMemberChangeDetails class represents the details of a group membership
1261
* Extended information is not always available; this will be reflected by
1262
* the return value of isValid().
1266
* Constructs a new invalid GroupMemberChangeDetails instance.
1268
Channel::GroupMemberChangeDetails::GroupMemberChangeDetails()
1275
Channel::GroupMemberChangeDetails::GroupMemberChangeDetails(const GroupMemberChangeDetails &other)
1276
: mPriv(other.mPriv)
1283
Channel::GroupMemberChangeDetails::~GroupMemberChangeDetails()
1288
* Assigns all information (validity, details) from other to this.
1290
Channel::GroupMemberChangeDetails &Channel::GroupMemberChangeDetails::operator=(
1291
const GroupMemberChangeDetails &other)
1293
this->mPriv = other.mPriv;
1298
* \fn bool Channel::GroupMemberChangeDetails::isValid() const
1300
* Return whether the details are valid (have actually been received from the service).
1302
* \return \c true if valid, \c false otherwise.
1306
* Return whether the details specify an actor.
1308
* If present, actor() will return the contact object representing the person who made the change.
1310
* \return \c true if the actor is known, \c false otherwise.
1313
bool Channel::GroupMemberChangeDetails::hasActor() const
1315
return isValid() && !mPriv->actor.isNull();
1319
* Return the contact object representing the person who made the change (actor), if known.
1321
* \return A pointer to the Contact object, or a null ContactPtr if the actor is unknown.
1324
ContactPtr Channel::GroupMemberChangeDetails::actor() const
1326
return isValid() ? mPriv->actor : ContactPtr();
1330
* \fn bool Channel::GroupMemberChangeDetails::hasReason() const
1332
* Return whether the details specify the reason for the change.
1334
* \return \c true if the reason is known, \c false otherwise.
1339
* \fn ChannelGroupChangeReason Channel::GroupMemberChangeDetails::reason() const
1341
* Return the reason for the change, if known.
1343
* \return The change reason as #ChannelGroupChangeReason, or #ChannelGroupChangeReasonNone
1344
* if the reason is unknown.
1349
* \fn bool Channel::GroupMemberChangeDetails::hasMessage() const
1351
* Return whether the details specify a human-readable message from the contact represented by
1352
* actor() pertaining to the change.
1354
* \return \c true if the message is known, \c false otherwise.
1359
* \fn QString Channel::GroupMemberChangeDetails::message() const
1361
* Return a human-readable message from the contact represented by actor() pertaining to the change,
1364
* \return The message, or an empty string if the message is unknown.
1369
* \fn bool Channel::GroupMemberChangeDetails::hasError() const
1371
* Return whether the details specify a D-Bus error describing the change.
1373
* \return \c true if the error is known, \c false otherwise.
1378
* \fn QString Channel::GroupMemberChangeDetails::error() const
1380
* Return the D-Bus error describing the change, if known.
1382
* The D-Bus error provides more specific information than the reason() and should be used if
1385
* \return A D-Bus error describing the change, or an empty string if the error is unknown.
1390
* \fn bool Channel::GroupMemberChangeDetails::hasDebugMessage() const
1392
* Return whether the details specify a debug message.
1394
* \return \c true if debug message is present, \c false otherwise.
1395
* \sa debugMessage()
1399
* \fn QString Channel::GroupMemberChangeDetails::debugMessage() const
1401
* Return the debug message specified by the details, if any.
1403
* The debug message is purely informational, offered for display for bug reporting purposes, and
1404
* should not be attempted to be parsed.
1406
* \return The debug message, or an empty string if there is none.
1407
* \sa hasDebugMessage()
1411
* Return a map containing all details of the group members change.
1413
* This is useful for accessing domain-specific additional details.
1415
* \return The details of the group members change as QVariantMap.
1417
QVariantMap Channel::GroupMemberChangeDetails::allDetails() const
1419
return isValid() ? mPriv->details : QVariantMap();
1422
Channel::GroupMemberChangeDetails::GroupMemberChangeDetails(const ContactPtr &actor,
1423
const QVariantMap &details)
1424
: mPriv(new Private(actor, details))
1430
* \ingroup clientchannel
1431
* \headerfile TelepathyQt/channel.h <TelepathyQt/Channel>
1433
* \brief The Channel class represents a Telepathy channel.
1435
* All communication in the Telepathy framework is carried out via channel
1436
* objects. Specialized classes for some specific channel types such as
1437
* StreamedMediaChannel, TextChannel, FileTransferChannel are provided.
1439
* The remote object accessor functions on this object (channelType(), targetHandleType(),
1440
* and so on) don't make any D-Bus calls; instead, they return/use
1441
* values cached from a previous introspection run. The introspection process
1442
* populates their values in the most efficient way possible based on what the
1443
* service implements.
1445
* To avoid unnecessary D-Bus traffic, some accessors only return valid
1446
* information after specific features have been enabled.
1447
* For instance, to retrieve the initial invitee contacts in a conference channel,
1448
* it is necessary to enable the feature Channel::FeatureConferenceInitialInviteeContacts.
1449
* See the individual methods descriptions for more details.
1451
* Channel features can be enabled by constructing a ChannelFactory and enabling
1452
* the desired features, and passing it to AccountManager, Account or ClientRegistrar
1453
* when creating them as appropriate. However, if a particular
1454
* feature is only ever used in a specific circumstance, such as an user opening
1455
* some settings dialog separate from the general view of the application,
1456
* features can be later enabled as needed by calling becomeReady() with the additional
1457
* features, and waiting for the resulting PendingOperation to finish.
1459
* Each channel is owned by a connection. If the Connection object becomes invalidated
1460
* the Channel object will also get invalidated.
1462
* \section chan_usage_sec Usage
1464
* \subsection chan_create_sec Creating a channel object
1466
* Channel objects can be created in various ways, but the preferred way is
1467
* trough Account channel creation methods such as Account::ensureTextChat(),
1468
* Account::createFileTransfer(), which uses the channel dispatcher.
1470
* If you already know the object path, you can just call create().
1475
* ChannelPtr chan = Channel::create(connection, objectPath,
1476
* immutableProperties);
1480
* \subsection chan_ready_sec Making channel ready to use
1482
* A Channel object needs to become ready before usage, meaning that the
1483
* introspection process finished and the object accessors can be used.
1485
* To make the object ready, use becomeReady() and wait for the
1486
* PendingOperation::finished() signal to be emitted.
1490
* class MyClass : public QObject
1495
* MyClass(QObject *parent = 0);
1499
* void onChannelReady(Tp::PendingOperation*);
1505
* MyClass::MyClass(const ConnectionPtr &connection,
1506
* const QString &objectPath, const QVariantMap &immutableProperties)
1508
* chan(Channel::create(connection, objectPath, immutableProperties))
1510
* connect(chan->becomeReady(),
1511
* SIGNAL(finished(Tp::PendingOperation*)),
1512
* SLOT(onChannelReady(Tp::PendingOperation*)));
1515
* void MyClass::onChannelReady(Tp::PendingOperation *op)
1517
* if (op->isError()) {
1518
* qWarning() << "Channel cannot become ready:" <<
1519
* op->errorName() << "-" << op->errorMessage();
1523
* // Channel is now ready
1528
* See \ref async_model, \ref shared_ptr
1532
* Feature representing the core that needs to become ready to make the Channel
1535
* Note that this feature must be enabled in order to use most Channel methods.
1536
* See specific methods documentation for more details.
1538
* When calling isReady(), becomeReady(), this feature is implicitly added
1539
* to the requested features.
1541
const Feature Channel::FeatureCore = Feature(QLatin1String(Channel::staticMetaObject.className()), 0, true);
1544
* Feature used in order to access the conference initial invitee contacts info.
1546
* \sa conferenceInitialInviteeContacts()
1548
const Feature Channel::FeatureConferenceInitialInviteeContacts = Feature(QLatin1String(Channel::staticMetaObject.className()), 1, true);
1551
* Create a new Channel object.
1553
* \param connection Connection owning this channel, and specifying the
1555
* \param objectPath The channel object path.
1556
* \param immutableProperties The channel immutable properties.
1557
* \return A ChannelPtr object pointing to the newly created Channel object.
1559
* \todo \a immutableProperties should be used to populate the corresponding accessors (such as
1560
* channelType()) already on construction, not only when making FeatureCore ready (fd.o #41654)
1562
ChannelPtr Channel::create(const ConnectionPtr &connection,
1563
const QString &objectPath, const QVariantMap &immutableProperties)
1565
return ChannelPtr(new Channel(connection, objectPath, immutableProperties,
1566
Channel::FeatureCore));
1570
* Construct a new Channel object.
1572
* \param connection Connection owning this channel, and specifying the
1574
* \param objectPath The channel object path.
1575
* \param immutableProperties The channel immutable properties.
1576
* \param coreFeature The core feature of the channel type. The corresponding introspectable should
1577
* depend on Channel::FeatureCore.
1579
Channel::Channel(const ConnectionPtr &connection,
1580
const QString &objectPath,
1581
const QVariantMap &immutableProperties,
1582
const Feature &coreFeature)
1583
: StatefulDBusProxy(connection->dbusConnection(), connection->busName(),
1584
objectPath, coreFeature),
1585
OptionalInterfaceFactory<Channel>(this),
1586
mPriv(new Private(this, connection, immutableProperties))
1599
* Return the connection owning this channel.
1601
* \return A pointer to the Connection object.
1603
ConnectionPtr Channel::connection() const
1605
return mPriv->connection;
1609
* Return the immutable properties of the channel.
1611
* If the channel is ready (isReady(Channel::FeatureCore) returns true), the following keys are
1612
* guaranteed to be present:
1613
* org.freedesktop.Telepathy.Channel.ChannelType,
1614
* org.freedesktop.Telepathy.Channel.TargetHandleType,
1615
* org.freedesktop.Telepathy.Channel.TargetHandle and
1616
* org.freedesktop.Telepathy.Channel.Requested.
1618
* The keys and values in this map are defined by the \telepathy_spec,
1619
* or by third-party extensions to that specification.
1620
* These are the properties that cannot change over the lifetime of the
1621
* channel; they're announced in the result of the request, for efficiency.
1623
* \return The immutable properties as QVariantMap.
1625
QVariantMap Channel::immutableProperties() const
1627
if (isReady(Channel::FeatureCore)) {
1630
key = TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType");
1631
if (!mPriv->immutableProperties.contains(key)) {
1632
mPriv->immutableProperties.insert(key, mPriv->channelType);
1635
key = TP_QT_IFACE_CHANNEL + QLatin1String(".Interfaces");
1636
if (!mPriv->immutableProperties.contains(key)) {
1637
mPriv->immutableProperties.insert(key, interfaces());
1640
key = TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandleType");
1641
if (!mPriv->immutableProperties.contains(key)) {
1642
mPriv->immutableProperties.insert(key, mPriv->targetHandleType);
1645
key = TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandle");
1646
if (!mPriv->immutableProperties.contains(key)) {
1647
mPriv->immutableProperties.insert(key, mPriv->targetHandle);
1650
key = TP_QT_IFACE_CHANNEL + QLatin1String(".TargetID");
1651
if (!mPriv->immutableProperties.contains(key)) {
1652
mPriv->immutableProperties.insert(key, mPriv->targetId);
1655
key = TP_QT_IFACE_CHANNEL + QLatin1String(".Requested");
1656
if (!mPriv->immutableProperties.contains(key)) {
1657
mPriv->immutableProperties.insert(key, mPriv->requested);
1660
key = TP_QT_IFACE_CHANNEL + QLatin1String(".InitiatorHandle");
1661
if (!mPriv->immutableProperties.contains(key)) {
1662
mPriv->immutableProperties.insert(key, mPriv->initiatorHandle);
1665
key = TP_QT_IFACE_CHANNEL + QLatin1String(".InitiatorID");
1666
if (!mPriv->immutableProperties.contains(key) && !mPriv->initiatorContact.isNull()) {
1667
mPriv->immutableProperties.insert(key, mPriv->initiatorContact->id());
1671
return mPriv->immutableProperties;
1675
* Return the D-Bus interface name for the type of this channel.
1677
* This method requires Channel::FeatureCore to be ready.
1679
* \return The D-Bus interface name for the type of the channel.
1681
QString Channel::channelType() const
1683
// Similarly, we don't want warnings triggered when using the type interface
1684
// proxies internally.
1685
if (!isReady(Channel::FeatureCore) && mPriv->channelType.isEmpty()) {
1686
warning() << "Channel::channelType() before the channel type has "
1689
else if (!isValid()) {
1690
warning() << "Channel::channelType() used with channel closed";
1693
return mPriv->channelType;
1697
* Return the type of the handle returned by targetHandle() as specified in
1700
* This method requires Channel::FeatureCore to be ready.
1702
* \return The target handle type as #HandleType.
1703
* \sa targetHandle(), targetId()
1705
HandleType Channel::targetHandleType() const
1707
if (!isReady(Channel::FeatureCore)) {
1708
warning() << "Channel::targetHandleType() used channel not ready";
1711
return (HandleType) mPriv->targetHandleType;
1715
* Return the handle of the remote party with which this channel
1718
* This method requires Channel::FeatureCore to be ready.
1720
* \return An integer representing the target handle, which is of the type
1721
* targetHandleType() indicates.
1722
* \sa targetHandleType(), targetId()
1724
uint Channel::targetHandle() const
1726
if (!isReady(Channel::FeatureCore)) {
1727
warning() << "Channel::targetHandle() used channel not ready";
1730
return mPriv->targetHandle;
1734
* Return the persistent unique ID of the remote party with which this channel communicates.
1736
* If targetHandleType() is #HandleTypeContact, this will be the ID of the remote contact, and
1737
* similarly the unique ID of the room when targetHandleType() is #HandleTypeRoom.
1739
* This is not necessarily the best identifier to display to the user, though. In particular, for
1740
* contacts, their alias should be displayed instead. It can be used for matching channels and UI
1741
* elements for them across reconnects, though, at which point the old channels and contacts are
1744
* This method requires Channel::FeatureCore to be ready.
1746
* \return The target identifier.
1747
* \sa targetHandle(), targetContact()
1749
QString Channel::targetId() const
1751
if (!isReady(Channel::FeatureCore)) {
1752
warning() << "Channel::targetId() used, but the channel is not ready";
1755
return mPriv->targetId;
1759
* Return the contact with which this channel communicates for its lifetime, if applicable.
1761
* This method requires Channel::FeatureCore to be ready.
1763
* \return A pointer to the Contact object, or a null ContactPtr if targetHandleType() is not
1764
* #HandleTypeContact.
1765
* \sa targetHandle(), targetId()
1767
ContactPtr Channel::targetContact() const
1769
if (!isReady(Channel::FeatureCore)) {
1770
warning() << "Channel::targetContact() used, but the channel is not ready";
1771
} else if (targetHandleType() != HandleTypeContact) {
1772
warning() << "Channel::targetContact() used with targetHandleType() != Contact";
1775
return mPriv->targetContact;
1779
* Return whether this channel was created in response to a
1782
* This method requires Channel::FeatureCore to be ready.
1784
* \return \c true if the channel was created in response to a local request,
1785
* \c false otherwise.
1787
bool Channel::isRequested() const
1789
if (!isReady(Channel::FeatureCore)) {
1790
warning() << "Channel::isRequested() used channel not ready";
1793
return mPriv->requested;
1797
* Return the contact who initiated this channel.
1799
* This method requires Channel::FeatureCore to be ready.
1801
* \return A pointer to the Contact object representing the contact who initiated the channel,
1802
* or a null ContactPtr if it can't be retrieved.
1804
ContactPtr Channel::initiatorContact() const
1806
if (!isReady(Channel::FeatureCore)) {
1807
warning() << "Channel::initiatorContact() used channel not ready";
1810
return mPriv->initiatorContact;
1814
* Start an asynchronous request that this channel be closed.
1816
* The returned PendingOperation object will signal the success or failure
1817
* of this request; under normal circumstances, it can be expected to
1820
* \return A PendingOperation which will emit PendingOperation::finished
1821
* when the call has finished.
1822
* \sa requestLeave()
1824
PendingOperation *Channel::requestClose()
1826
// Closing a channel does not make sense if it is already closed,
1827
// just silently Return.
1829
return new PendingSuccess(ChannelPtr(this));
1832
return new PendingVoid(mPriv->baseInterface->Close(), ChannelPtr(this));
1835
Channel::PendingLeave::PendingLeave(const ChannelPtr &chan, const QString &message,
1836
ChannelGroupChangeReason reason)
1837
: PendingOperation(chan)
1839
Q_ASSERT(chan->mPriv->group != NULL);
1841
QDBusPendingCall call =
1842
chan->mPriv->group->RemoveMembersWithReason(
1843
UIntList() << chan->mPriv->groupSelfHandle,
1847
connect(chan.data(),
1848
SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)),
1850
SLOT(onChanInvalidated(Tp::DBusProxy*)));
1852
connect(new PendingVoid(call, chan),
1853
SIGNAL(finished(Tp::PendingOperation*)),
1855
SLOT(onRemoveFinished(Tp::PendingOperation*)));
1858
void Channel::PendingLeave::onChanInvalidated(Tp::DBusProxy *proxy)
1864
debug() << "Finishing PendingLeave successfully as the channel was invalidated";
1869
void Channel::PendingLeave::onRemoveFinished(Tp::PendingOperation *op)
1875
ChannelPtr chan = ChannelPtr::staticCast(object());
1877
if (op->isValid()) {
1878
debug() << "We left the channel" << chan->objectPath();
1880
ContactPtr c = chan->groupSelfContact();
1882
if (chan->groupContacts().contains(c)
1883
|| chan->groupLocalPendingContacts().contains(c)
1884
|| chan->groupRemotePendingContacts().contains(c)) {
1885
debug() << "Waiting for self remove to be picked up";
1886
connect(chan.data(),
1887
SIGNAL(groupMembersChanged(Tp::Contacts,Tp::Contacts,Tp::Contacts,Tp::Contacts,
1888
Tp::Channel::GroupMemberChangeDetails)),
1890
SLOT(onMembersChanged(Tp::Contacts,Tp::Contacts,Tp::Contacts,Tp::Contacts)));
1898
debug() << "Leave RemoveMembersWithReason failed with " << op->errorName() << op->errorMessage()
1899
<< "- falling back to Close";
1901
// If the channel has been closed or otherwise invalidated already in this mainloop iteration,
1902
// the requestClose() operation will early-succeed
1903
connect(chan->requestClose(),
1904
SIGNAL(finished(Tp::PendingOperation*)),
1906
SLOT(onCloseFinished(Tp::PendingOperation*)));
1909
void Channel::PendingLeave::onMembersChanged(const Tp::Contacts &, const Tp::Contacts &,
1910
const Tp::Contacts &, const Tp::Contacts &removed)
1916
ChannelPtr chan = ChannelPtr::staticCast(object());
1917
ContactPtr c = chan->groupSelfContact();
1919
if (removed.contains(c)) {
1920
debug() << "Leave event picked up for" << chan->objectPath();
1925
void Channel::PendingLeave::onCloseFinished(Tp::PendingOperation *op)
1931
ChannelPtr chan = ChannelPtr::staticCast(object());
1933
if (op->isError()) {
1934
warning() << "Closing the channel" << chan->objectPath()
1935
<< "as a fallback for leaving it failed with"
1936
<< op->errorName() << op->errorMessage() << "- so didn't leave";
1937
setFinishedWithError(op->errorName(), op->errorMessage());
1939
debug() << "We left (by closing) the channel" << chan->objectPath();
1945
* Start an asynchronous request to leave this channel as gracefully as possible.
1947
* If leaving any more gracefully is not possible, this will revert to the same as requestClose().
1948
* In particular, this will be the case for channels with no group interface
1949
* (#TP_QT_IFACE_CHANNEL_INTERFACE_GROUP not in the list returned by interfaces()).
1951
* The returned PendingOperation object will signal the success or failure
1952
* of this request; under normal circumstances, it can be expected to
1955
* A message and a reason may be provided along with the request, which will be sent to the server
1956
* if supported, which is indicated by #ChannelGroupFlagMessageDepart and/or
1957
* #ChannelGroupFlagMessageReject.
1959
* Attempting to leave again when we have already left, either by our request or forcibly, will be a
1960
* no-op, with the returned PendingOperation immediately finishing successfully.
1962
* \param message The message, which can be blank if desired.
1963
* \param reason A reason for leaving.
1964
* \return A PendingOperation which will emit PendingOperation::finished
1965
* when the call has finished.
1967
PendingOperation *Channel::requestLeave(const QString &message, ChannelGroupChangeReason reason)
1969
// Leaving a channel does not make sense if it is already closed,
1970
// just silently Return.
1972
return new PendingSuccess(ChannelPtr(this));
1975
if (!isReady(Channel::FeatureCore)) {
1976
return new PendingFailure(TP_QT_ERROR_NOT_AVAILABLE,
1977
QLatin1String("Channel::FeatureCore must be ready to leave a channel"),
1981
if (!interfaces().contains(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP)) {
1982
return requestClose();
1985
ContactPtr self = groupSelfContact();
1987
if (!groupContacts().contains(self) && !groupLocalPendingContacts().contains(self)
1988
&& !groupRemotePendingContacts().contains(self)) {
1989
debug() << "Channel::requestLeave() called for " << objectPath() <<
1990
"which we aren't a member of";
1991
return new PendingSuccess(ChannelPtr(this));
1994
return new PendingLeave(ChannelPtr(this), message, reason);
1998
* \name Group interface
2000
* Cached access to state of the group interface on the associated remote
2001
* object, if the interface is present.
2003
* Some methods can be used when targetHandleType() == #HandleTypeContact, such
2004
* as groupFlags(), groupCanAddContacts(), groupCanRemoveContacts(),
2005
* groupSelfContact() and groupContacts().
2007
* As the group interface state can change freely during the lifetime of the
2008
* channel due to events like new contacts joining the group, the cached state
2009
* is automatically kept in sync with the remote object's state by hooking
2010
* to the change notification signals present in the D-Bus interface.
2012
* As the cached value changes, change notification signals are emitted.
2014
* Signals such as groupMembersChanged(), groupSelfContactChanged(), etc., are emitted to
2015
* indicate that properties have changed.
2017
* Check the individual signals' descriptions for details.
2023
* Return a set of flags indicating the capabilities and behaviour of the
2024
* group on this channel.
2026
* Change notification is via the groupFlagsChanged() signal.
2028
* This method requires Channel::FeatureCore to be ready.
2030
* \return The bitfield combination of flags as #ChannelGroupFlags.
2031
* \sa groupFlagsChanged()
2033
ChannelGroupFlags Channel::groupFlags() const
2035
if (!isReady(Channel::FeatureCore)) {
2036
warning() << "Channel::groupFlags() used channel not ready";
2039
return (ChannelGroupFlags) mPriv->groupFlags;
2043
* Return whether contacts can be added or invited to this channel.
2045
* Change notification is via the groupCanAddContactsChanged() signal.
2047
* This method requires Channel::FeatureCore to be ready.
2049
* \return \c true if contacts can be added or invited to the channel,
2050
* \c false otherwise.
2051
* \sa groupFlags(), groupAddContacts()
2053
bool Channel::groupCanAddContacts() const
2055
if (!isReady(Channel::FeatureCore)) {
2056
warning() << "Channel::groupCanAddContacts() used channel not ready";
2059
return mPriv->groupFlags & ChannelGroupFlagCanAdd;
2063
* Return whether a message is expected when adding/inviting contacts, who
2064
* are not already members, to this channel.
2066
* This method requires Channel::FeatureCore to be ready.
2068
* \return \c true if a message is expected, \c false otherwise.
2069
* \sa groupFlags(), groupAddContacts()
2071
bool Channel::groupCanAddContactsWithMessage() const
2073
if (!isReady(Channel::FeatureCore)) {
2074
warning() << "Channel::groupCanAddContactsWithMessage() used when channel not ready";
2077
return mPriv->groupFlags & ChannelGroupFlagMessageAdd;
2081
* Return whether a message is expected when accepting contacts' requests to
2082
* join this channel.
2084
* This method requires Channel::FeatureCore to be ready.
2086
* \return \c true if a message is expected, \c false otherwise.
2087
* \sa groupFlags(), groupAddContacts()
2089
bool Channel::groupCanAcceptContactsWithMessage() const
2091
if (!isReady(Channel::FeatureCore)) {
2092
warning() << "Channel::groupCanAcceptContactsWithMessage() used when channel not ready";
2095
return mPriv->groupFlags & ChannelGroupFlagMessageAccept;
2099
* Add contacts to this channel.
2101
* Contacts on the local pending list (those waiting for permission to join
2102
* the channel) can always be added. If groupCanAcceptContactsWithMessage()
2103
* returns \c true, an optional message is expected when doing this; if not,
2104
* the message parameter is likely to be ignored (so the user should not be
2105
* asked for a message, and the message parameter should be left empty).
2107
* Other contacts can only be added if groupCanAddContacts() returns \c true.
2108
* If groupCanAddContactsWithMessage() returns \c true, an optional message is
2109
* expected when doing this, and if not, the message parameter is likely to be
2112
* This method requires Channel::FeatureCore to be ready.
2114
* \param contacts Contacts to be added.
2115
* \param message A string message, which can be blank if desired.
2116
* \return A PendingOperation which will emit PendingOperation::finished
2117
* when the call has finished.
2118
* \sa groupCanAddContacts(), groupCanAddContactsWithMessage(), groupCanAcceptContactsWithMessage()
2120
PendingOperation *Channel::groupAddContacts(const QList<ContactPtr> &contacts,
2121
const QString &message)
2123
if (!isReady(Channel::FeatureCore)) {
2124
warning() << "Channel::groupAddContacts() used channel not ready";
2125
return new PendingFailure(TP_QT_ERROR_NOT_AVAILABLE,
2126
QLatin1String("Channel not ready"),
2128
} else if (contacts.isEmpty()) {
2129
warning() << "Channel::groupAddContacts() used with empty contacts param";
2130
return new PendingFailure(TP_QT_ERROR_INVALID_ARGUMENT,
2131
QLatin1String("contacts cannot be an empty list"),
2135
foreach (const ContactPtr &contact, contacts) {
2137
warning() << "Channel::groupAddContacts() used but contacts param contains "
2139
return new PendingFailure(TP_QT_ERROR_INVALID_ARGUMENT,
2140
QLatin1String("Unable to add invalid contacts"),
2145
if (!interfaces().contains(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP)) {
2146
warning() << "Channel::groupAddContacts() used with no group interface";
2147
return new PendingFailure(TP_QT_ERROR_NOT_IMPLEMENTED,
2148
QLatin1String("Channel does not support group interface"),
2153
foreach (const ContactPtr &contact, contacts) {
2154
handles << contact->handle()[0];
2156
return new PendingVoid(mPriv->group->AddMembers(handles, message), ChannelPtr(this));
2160
* Return whether contacts in groupRemotePendingContacts() can be removed from
2161
* this channel (i.e. whether an invitation can be rescinded).
2163
* Change notification is via the groupCanRescindContactsChanged() signal.
2165
* This method requires Channel::FeatureCore to be ready.
2167
* \return \c true if contacts can be removed, \c false otherwise.
2168
* \sa groupFlags(), groupRemoveContacts()
2170
bool Channel::groupCanRescindContacts() const
2172
if (!isReady(Channel::FeatureCore)) {
2173
warning() << "Channel::groupCanRescindContacts() used channel not ready";
2176
return mPriv->groupFlags & ChannelGroupFlagCanRescind;
2180
* Return whether a message is expected when removing contacts who are in
2181
* groupRemotePendingContacts() from this channel (i.e. rescinding an
2184
* This method requires Channel::FeatureCore to be ready.
2186
* \return \c true if a message is expected, \c false otherwise.
2187
* \sa groupFlags(), groupRemoveContacts()
2189
bool Channel::groupCanRescindContactsWithMessage() const
2191
if (!isReady(Channel::FeatureCore)) {
2192
warning() << "Channel::groupCanRescindContactsWithMessage() used when channel not ready";
2195
return mPriv->groupFlags & ChannelGroupFlagMessageRescind;
2199
* Return if contacts in groupContacts() can be removed from this channel.
2201
* Note that contacts in local pending lists, and the groupSelfContact(), can
2202
* always be removed from the channel.
2204
* Change notification is via the groupCanRemoveContactsChanged() signal.
2206
* This method requires Channel::FeatureCore to be ready.
2208
* \return \c true if contacts can be removed, \c false otherwise.
2209
* \sa groupFlags(), groupRemoveContacts()
2211
bool Channel::groupCanRemoveContacts() const
2213
if (!isReady(Channel::FeatureCore)) {
2214
warning() << "Channel::groupCanRemoveContacts() used channel not ready";
2217
return mPriv->groupFlags & ChannelGroupFlagCanRemove;
2221
* Return whether a message is expected when removing contacts who are in
2222
* groupContacts() from this channel.
2224
* This method requires Channel::FeatureCore to be ready.
2226
* \return \c true if a message is expected, \c false otherwise.
2227
* \sa groupFlags(), groupRemoveContacts()
2229
bool Channel::groupCanRemoveContactsWithMessage() const
2231
if (!isReady(Channel::FeatureCore)) {
2232
warning() << "Channel::groupCanRemoveContactsWithMessage() used when channel not ready";
2235
return mPriv->groupFlags & ChannelGroupFlagMessageRemove;
2239
* Return whether a message is expected when removing contacts who are in
2240
* groupLocalPendingContacts() from this channel (i.e. rejecting a request to
2243
* This method requires Channel::FeatureCore to be ready.
2245
* \return \c true if a message is expected, \c false otherwise.
2246
* \sa groupFlags(), groupRemoveContacts()
2248
bool Channel::groupCanRejectContactsWithMessage() const
2250
if (!isReady(Channel::FeatureCore)) {
2251
warning() << "Channel::groupCanRejectContactsWithMessage() used when channel not ready";
2254
return mPriv->groupFlags & ChannelGroupFlagMessageReject;
2258
* Return whether a message is expected when removing the groupSelfContact()
2259
* from this channel (i.e. departing from the channel).
2261
* \return \c true if a message is expected, \c false otherwise.
2262
* \sa groupFlags(), groupRemoveContacts()
2264
bool Channel::groupCanDepartWithMessage() const
2266
if (!isReady(Channel::FeatureCore)) {
2267
warning() << "Channel::groupCanDepartWithMessage() used when channel not ready";
2270
return mPriv->groupFlags & ChannelGroupFlagMessageDepart;
2274
* Remove contacts from this channel.
2276
* Contacts on the local pending list (those waiting for permission to join
2277
* the channel) can always be removed. If groupCanRejectContactsWithMessage()
2278
* returns \c true, an optional message is expected when doing this; if not,
2279
* the message parameter is likely to be ignored (so the user should not be
2280
* asked for a message, and the message parameter should be left empty).
2282
* The groupSelfContact() can also always be removed, as a way to leave the
2283
* group with an optional departure message and/or departure reason indication.
2284
* If groupCanDepartWithMessage() returns \c true, an optional message is
2285
* expected when doing this, and if not, the message parameter is likely to
2288
* Contacts in the group can only be removed (e.g. kicked) if
2289
* groupCanRemoveContacts() returns \c true. If
2290
* groupCanRemoveContactsWithMessage() returns \c true, an optional message is
2291
* expected when doing this, and if not, the message parameter is likely to be
2294
* Contacts in the remote pending list (those who have been invited to the
2295
* channel) can only be removed (have their invitations rescinded) if
2296
* groupCanRescindContacts() returns \c true. If
2297
* groupCanRescindContactsWithMessage() returns \c true, an optional message is
2298
* expected when doing this, and if not, the message parameter is likely to be
2301
* This method requires Channel::FeatureCore to be ready.
2303
* \param contacts Contacts to be removed.
2304
* \param message A string message, which can be blank if desired.
2305
* \param reason Reason of the change, as specified in
2306
* #ChannelGroupChangeReason
2307
* \return A PendingOperation which will emit PendingOperation::finished
2308
* when the call has finished.
2309
* \sa groupCanRemoveContacts(), groupCanRemoveContactsWithMessage(),
2310
* groupCanRejectContactsWithMessage(), groupCanRescindContacts(),
2311
* groupCanRescindContacts(), groupCanRescindContactsWithMessage(),
2312
* groupCanDepartWithMessage()
2314
PendingOperation *Channel::groupRemoveContacts(const QList<ContactPtr> &contacts,
2315
const QString &message, ChannelGroupChangeReason reason)
2317
if (!isReady(Channel::FeatureCore)) {
2318
warning() << "Channel::groupRemoveContacts() used channel not ready";
2319
return new PendingFailure(TP_QT_ERROR_NOT_AVAILABLE,
2320
QLatin1String("Channel not ready"),
2324
if (contacts.isEmpty()) {
2325
warning() << "Channel::groupRemoveContacts() used with empty contacts param";
2326
return new PendingFailure(TP_QT_ERROR_INVALID_ARGUMENT,
2327
QLatin1String("contacts param cannot be an empty list"),
2331
foreach (const ContactPtr &contact, contacts) {
2333
warning() << "Channel::groupRemoveContacts() used but contacts param contains "
2335
return new PendingFailure(TP_QT_ERROR_INVALID_ARGUMENT,
2336
QLatin1String("Unable to remove invalid contacts"),
2341
if (!interfaces().contains(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP)) {
2342
warning() << "Channel::groupRemoveContacts() used with no group interface";
2343
return new PendingFailure(TP_QT_ERROR_NOT_IMPLEMENTED,
2344
QLatin1String("Channel does not support group interface"),
2349
foreach (const ContactPtr &contact, contacts) {
2350
handles << contact->handle()[0];
2352
return new PendingVoid(
2353
mPriv->group->RemoveMembersWithReason(handles, message, reason),
2358
* Return the current contacts of the group.
2360
* It is possible to omit the contact representing the local user, even if
2361
* the contact is in the set, by passing \c false as the parameter \a
2362
* includeSelfContact.
2364
* Change notification is via the groupMembersChanged() signal.
2366
* This method requires Channel::FeatureCore to be ready.
2368
* \param includeSelfContact Whether to include the self contact in the returned set.
2369
* \return A set of pointers to the Contact objects.
2370
* \sa groupLocalPendingContacts(), groupRemotePendingContacts()
2372
Contacts Channel::groupContacts(bool includeSelfContact) const
2374
if (!isReady(Channel::FeatureCore)) {
2375
warning() << "Channel::groupMembers() used channel not ready";
2378
Contacts ret = mPriv->groupContacts.values().toSet();
2379
if (!includeSelfContact) {
2380
ret.remove(groupSelfContact());
2386
* Return the contacts currently waiting for local approval to join the
2389
* It is possible to omit the contact representing the local user, even if
2390
* the contact is in the set, by passing \c false as the parameter \a
2391
* includeSelfContact.
2393
* Change notification is via the groupMembersChanged() signal.
2395
* This method requires Channel::FeatureCore to be ready.
2397
* \param includeSelfContact Whether to include the self contact in the returned set.
2398
* \return A set of pointers to the Contact objects.
2399
* \sa groupContacts(), groupRemotePendingContacts()
2401
Contacts Channel::groupLocalPendingContacts(bool includeSelfContact) const
2403
if (!isReady(Channel::FeatureCore)) {
2404
warning() << "Channel::groupLocalPendingContacts() used channel not ready";
2405
} else if (!interfaces().contains(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP)) {
2406
warning() << "Channel::groupLocalPendingContacts() used with no group interface";
2409
Contacts ret = mPriv->groupLocalPendingContacts.values().toSet();
2410
if (!includeSelfContact) {
2411
ret.remove(groupSelfContact());
2417
* Return the contacts currently waiting for remote approval to join the
2420
* It is possible to omit the contact representing the local user, even if
2421
* the contact is in the set, by passing \c false as the parameter \a
2422
* includeSelfContact.
2424
* Change notification is via the groupMembersChanged() signal.
2426
* This method requires Channel::FeatureCore to be ready.
2428
* \param includeSelfContact Whether to include the self contact in the returned set.
2429
* \return A set of pointers to the Contact objects.
2430
* \sa groupContacts(), groupLocalPendingContacts()
2432
Contacts Channel::groupRemotePendingContacts(bool includeSelfContact) const
2434
if (!isReady(Channel::FeatureCore)) {
2435
warning() << "Channel::groupRemotePendingContacts() used channel not ready";
2436
} else if (!interfaces().contains(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP)) {
2437
warning() << "Channel::groupRemotePendingContacts() used with no "
2441
Contacts ret = mPriv->groupRemotePendingContacts.values().toSet();
2442
if (!includeSelfContact) {
2443
ret.remove(groupSelfContact());
2449
* Return information of a local pending contact change. If
2450
* no information is available, an object for which
2451
* GroupMemberChangeDetails::isValid() returns <code>false</code> is returned.
2453
* This method requires Channel::FeatureCore to be ready.
2455
* \param contact A Contact object that is on the local pending contacts list.
2456
* \return The change info as a GroupMemberChangeDetails object.
2458
Channel::GroupMemberChangeDetails Channel::groupLocalPendingContactChangeInfo(
2459
const ContactPtr &contact) const
2461
if (!isReady(Channel::FeatureCore)) {
2462
warning() << "Channel::groupLocalPendingContactChangeInfo() used channel not ready";
2463
} else if (!interfaces().contains(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP)) {
2464
warning() << "Channel::groupLocalPendingContactChangeInfo() used with no group interface";
2465
} else if (!contact) {
2466
warning() << "Channel::groupLocalPendingContactChangeInfo() used with null contact param";
2467
return GroupMemberChangeDetails();
2470
uint handle = contact->handle()[0];
2471
return mPriv->groupLocalPendingContactsChangeInfo.value(handle);
2475
* Return information on the removal of the local user from the group. If
2476
* the user hasn't been removed from the group, an object for which
2477
* GroupMemberChangeDetails::isValid() returns <code>false</code> is returned.
2479
* This method should be called only after you've left the channel.
2480
* This is useful for getting the remove information after missing the
2481
* corresponding groupMembersChanged() signal, as the local user being
2482
* removed usually causes the channel to be closed.
2484
* The returned information is not guaranteed to be correct if
2485
* groupIsSelfHandleTracked() returns false and a self handle change has
2486
* occurred on the remote object.
2488
* This method requires Channel::FeatureCore to be ready.
2490
* \return The remove info as a GroupMemberChangeDetails object.
2492
Channel::GroupMemberChangeDetails Channel::groupSelfContactRemoveInfo() const
2494
// Oftentimes, the channel will be closed as a result from being left - so checking a channel's
2495
// self remove info when it has been closed and hence invalidated is valid
2496
if (isValid() && !isReady(Channel::FeatureCore)) {
2497
warning() << "Channel::groupSelfContactRemoveInfo() used before Channel::FeatureCore is ready";
2498
} else if (!interfaces().contains(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP)) {
2499
warning() << "Channel::groupSelfContactRemoveInfo() used with "
2500
"no group interface";
2503
return mPriv->groupSelfContactRemoveInfo;
2507
* Return whether globally valid handles can be looked up using the
2508
* channel-specific handle on this channel using this object.
2510
* Handle owner lookup is only available if:
2512
* <li>The object is ready
2513
* <li>The list returned by interfaces() contains #TP_QT_IFACE_CHANNEL_INTERFACE_GROUP</li>
2514
* <li>The set of flags returned by groupFlags() contains
2515
* #GroupFlagProperties and #GroupFlagChannelSpecificHandles</li>
2518
* If this function returns \c false, the return value of
2519
* groupHandleOwners() is undefined and groupHandleOwnersChanged() will
2522
* The value returned by this function will stay fixed for the entire time
2523
* the object is ready, so no change notification is provided.
2525
* This method requires Channel::FeatureCore to be ready.
2527
* \return \c true if handle owner lookup functionality is available, \c false otherwise.
2529
bool Channel::groupAreHandleOwnersAvailable() const
2531
if (!isReady(Channel::FeatureCore)) {
2532
warning() << "Channel::groupAreHandleOwnersAvailable() used channel not ready";
2533
} else if (!interfaces().contains(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP)) {
2534
warning() << "Channel::groupAreHandleOwnersAvailable() used with "
2535
"no group interface";
2538
return mPriv->groupAreHandleOwnersAvailable;
2542
* Return a mapping of handles specific to this channel to globally valid
2545
* The mapping includes at least all of the channel-specific handles in this
2546
* channel's members, local-pending and remote-pending sets as keys. Any
2547
* handle not in the keys of this mapping is not channel-specific in this
2548
* channel. Handles which are channel-specific, but for which the owner is
2549
* unknown, appear in this mapping with 0 as owner.
2551
* Change notification is via the groupHandleOwnersChanged() signal.
2553
* This method requires Channel::FeatureCore to be ready.
2555
* \return A mapping from group-specific handles to globally valid handles.
2557
HandleOwnerMap Channel::groupHandleOwners() const
2559
if (!isReady(Channel::FeatureCore)) {
2560
warning() << "Channel::groupHandleOwners() used channel not ready";
2561
} else if (!interfaces().contains(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP)) {
2562
warning() << "Channel::groupAreHandleOwnersAvailable() used with no "
2565
else if (!groupAreHandleOwnersAvailable()) {
2566
warning() << "Channel::groupAreHandleOwnersAvailable() used, but handle "
2567
"owners not available";
2570
return mPriv->groupHandleOwners;
2574
* Return whether the value returned by groupSelfContact() is guaranteed to
2575
* accurately represent the local user even after nickname changes, etc.
2577
* This should always be \c true for new services implementing the group interface.
2579
* Older services not providing group properties don't necessarily
2580
* emit the SelfHandleChanged signal either, so self contact changes can't be
2583
* This method requires Channel::FeatureCore to be ready.
2585
* \return \c true if changes to the self contact are tracked, \c false otherwise.
2587
bool Channel::groupIsSelfContactTracked() const
2589
if (!isReady(Channel::FeatureCore)) {
2590
warning() << "Channel::groupIsSelfHandleTracked() used channel not ready";
2591
} else if (!interfaces().contains(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP)) {
2592
warning() << "Channel::groupIsSelfHandleTracked() used with "
2593
"no group interface";
2596
return mPriv->groupIsSelfHandleTracked;
2600
* Return a Contact object representing the user in the group if at all possible, otherwise a
2601
* Contact object representing the user globally.
2603
* Change notification is via the groupSelfContactChanged() signal.
2605
* This method requires Channel::FeatureCore to be ready.
2607
* \return A pointer to the Contact object.
2609
ContactPtr Channel::groupSelfContact() const
2611
if (!isReady(Channel::FeatureCore)) {
2612
warning() << "Channel::groupSelfContact() used channel not ready";
2615
return mPriv->groupSelfContact;
2619
* Return whether the local user is in the "local pending" state. This
2620
* indicates that the local user needs to take action to accept an invitation,
2621
* an incoming call, etc.
2623
* This method requires Channel::FeatureCore to be ready.
2625
* \return \c true if local user is in the channel's local-pending set, \c false otherwise.
2627
bool Channel::groupSelfHandleIsLocalPending() const
2629
if (!isReady(Channel::FeatureCore)) {
2630
warning() << "Channel::groupSelfHandleIsLocalPending() used when "
2631
"channel not ready";
2635
return mPriv->groupLocalPendingContacts.contains(mPriv->groupSelfHandle);
2639
* Attempt to add the local user to this channel. In some channel types,
2640
* such as Text and StreamedMedia, this is used to accept an invitation or an
2643
* This method requires Channel::FeatureCore to be ready.
2645
* \return A PendingOperation which will emit PendingOperation::finished
2646
* when the call has finished.
2648
PendingOperation *Channel::groupAddSelfHandle()
2650
if (!isReady(Channel::FeatureCore)) {
2651
warning() << "Channel::groupAddSelfHandle() used when channel not "
2653
return new PendingFailure(TP_QT_ERROR_INVALID_ARGUMENT,
2654
QLatin1String("Channel object not ready"),
2660
if (mPriv->groupSelfHandle == 0) {
2661
handles << mPriv->connection->selfHandle();
2663
handles << mPriv->groupSelfHandle;
2666
return new PendingVoid(
2667
mPriv->group->AddMembers(handles, QLatin1String("")),
2674
* Return whether this channel implements the conference interface
2675
* (#TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE is in the list returned by interfaces()).
2677
* This method requires Channel::FeatureCore to be ready.
2679
* \return \c true if the conference interface is supported, \c false otherwise.
2681
bool Channel::isConference() const
2683
return hasInterface(TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE);
2687
* Return a list of contacts invited to this conference when it was created.
2689
* This method requires Channel::FeatureConferenceInitialInviteeContacts to be ready.
2691
* \return A set of pointers to the Contact objects.
2693
Contacts Channel::conferenceInitialInviteeContacts() const
2695
return mPriv->conferenceInitialInviteeContacts;
2699
* Return the individual channels that are part of this conference.
2701
* Change notification is via the conferenceChannelMerged() and
2702
* conferenceChannelRemoved() signals.
2704
* Note that the returned channels are not guaranteed to be ready. Calling
2705
* Channel::becomeReady() may be needed.
2707
* This method requires Channel::FeatureCore to be ready.
2709
* \return A list of pointers to Channel objects containing all channels in the conference.
2710
* \sa conferenceInitialChannels(), conferenceOriginalChannels()
2712
QList<ChannelPtr> Channel::conferenceChannels() const
2714
return mPriv->conferenceChannels.values();
2718
* Return the initial value of conferenceChannels().
2720
* Note that the returned channels are not guaranteed to be ready. Calling
2721
* Channel::becomeReady() may be needed.
2723
* This method requires Channel::FeatureCore to be ready.
2725
* \return A list of pointers to Channel objects containing all channels that were initially
2726
* part of the conference.
2727
* \sa conferenceChannels(), conferenceOriginalChannels()
2729
QList<ChannelPtr> Channel::conferenceInitialChannels() const
2731
return mPriv->conferenceInitialChannels.values();
2735
* Return a map between channel specific handles and the corresponding channels of this conference.
2737
* This method is only relevant on GSM conference calls where it is possible to have the same phone
2738
* number in a conference twice; for instance, it could be the number of a corporate switchboard.
2739
* This is represented using channel-specific handles; whether or not a channel uses
2740
* channel-specific handles is reported in groupFlags(). The groupHandleOwners() specifies the
2741
* mapping from opaque channel-specific handles to actual numbers; this property specifies the
2742
* original 1-1 channel corresponding to each channel-specific handle in the conference.
2744
* In protocols where this situation cannot arise, such as XMPP, this method will return an empty
2747
* Example, consider this situation:
2748
* 1. Place a call (with path /call/to/simon) to the contact +441234567890 (which is assigned the
2749
* handle h, say), and ask to be put through to Simon McVittie;
2750
* 2. Put that call on hold;
2751
* 3. Place another call (with path /call/to/jonny) to +441234567890, and ask to be put through to
2753
* 4. Request a new conference channel with initial channels: ['/call/to/simon', '/call/to/jonny'].
2755
* The new channel will have the following properties, for some handles s and j:
2758
* groupFlags(): ChannelGroupFlagChannelSpecificHandles | (other flags),
2759
* groupMembers(): [self handle, s, j],
2760
* groupHandleOwners(): { s: h, j: h },
2761
* conferenceInitialChannels(): ['/call/to/simon', '/call/to/jonny'],
2762
* conferenceChannels(): ['/call/to/simon', '/call/to/jonny'],
2763
* conferenceOriginalChannels(): { s: '/call/to/simon', j: '/call/to/jonny' },
2767
* Note that the returned channels are not guaranteed to be ready. Calling
2768
* Channel::becomeReady() may be needed.
2770
* This method requires Channel::FeatureCore to be ready.
2772
* \return A map of channel specific handles to pointers to Channel objects.
2773
* \sa conferenceChannels(), conferenceInitialChannels()
2775
QHash<uint, ChannelPtr> Channel::conferenceOriginalChannels() const
2777
return mPriv->conferenceOriginalChannels;
2781
* Return whether this channel supports conference merging using conferenceMergeChannel().
2783
* This method requires Channel::FeatureCore to be ready.
2785
* \return \c true if the interface is supported, \c false otherwise.
2786
* \sa conferenceMergeChannel()
2788
bool Channel::supportsConferenceMerging() const
2790
return interfaces().contains(TP_QT_FUTURE_IFACE_CHANNEL_INTERFACE_MERGEABLE_CONFERENCE);
2794
* Request that the given channel be incorporated into this channel.
2796
* This method requires Channel::FeatureCore to be ready.
2798
* \return A PendingOperation which will emit PendingOperation::finished
2799
* when the call has finished.
2800
* \sa supportsConferenceMerging()
2802
PendingOperation *Channel::conferenceMergeChannel(const ChannelPtr &channel)
2804
if (!supportsConferenceMerging()) {
2805
return new PendingFailure(TP_QT_ERROR_NOT_IMPLEMENTED,
2806
QLatin1String("Channel does not support MergeableConference interface"),
2810
return new PendingVoid(mPriv->mergeableConferenceInterface()->Merge(
2811
QDBusObjectPath(channel->objectPath())),
2816
* Return whether this channel supports splitting using conferenceSplitChannel().
2818
* This method requires Channel::FeatureCore to be ready.
2820
* \return \c true if the interface is supported, \c false otherwise.
2821
* \sa conferenceSplitChannel()
2823
bool Channel::supportsConferenceSplitting() const
2825
return interfaces().contains(TP_QT_FUTURE_IFACE_CHANNEL_INTERFACE_SPLITTABLE);
2829
* Request that this channel is removed from any conference of which it is
2832
* This method requires Channel::FeatureCore to be ready.
2834
* \return A PendingOperation which will emit PendingOperation::finished
2835
* when the call has finished.
2836
* \sa supportsConferenceSplitting()
2838
PendingOperation *Channel::conferenceSplitChannel()
2840
if (!supportsConferenceSplitting()) {
2841
return new PendingFailure(TP_QT_ERROR_NOT_IMPLEMENTED,
2842
QLatin1String("Channel does not support Splittable interface"),
2846
return new PendingVoid(mPriv->splittableInterface()->Split(), ChannelPtr(this));
2850
* Return the Client::ChannelInterface interface proxy object for this channel.
2851
* This method is protected since the convenience methods provided by this
2852
* class should generally be used instead of calling D-Bus methods
2855
* \return A pointer to the existing Client::ChannelInterface object for this
2858
Client::ChannelInterface *Channel::baseInterface() const
2860
return mPriv->baseInterface;
2863
void Channel::gotMainProperties(QDBusPendingCallWatcher *watcher)
2865
QDBusPendingReply<QVariantMap> reply = *watcher;
2868
if (!reply.isError()) {
2869
debug() << "Got reply to Properties::GetAll(Channel)";
2870
props = reply.value();
2872
warning().nospace() << "Properties::GetAll(Channel) failed with " <<
2873
reply.error().name() << ": " << reply.error().message();
2876
mPriv->extractMainProps(props);
2878
mPriv->continueIntrospection();
2881
void Channel::gotChannelType(QDBusPendingCallWatcher *watcher)
2883
QDBusPendingReply<QString> reply = *watcher;
2885
if (reply.isError()) {
2886
warning().nospace() << "Channel::GetChannelType() failed with " <<
2887
reply.error().name() << ": " << reply.error().message() <<
2888
", Channel officially dead";
2889
invalidate(reply.error());
2893
debug() << "Got reply to fallback Channel::GetChannelType()";
2894
mPriv->channelType = reply.value();
2895
mPriv->continueIntrospection();
2898
void Channel::gotHandle(QDBusPendingCallWatcher *watcher)
2900
QDBusPendingReply<uint, uint> reply = *watcher;
2902
if (reply.isError()) {
2903
warning().nospace() << "Channel::GetHandle() failed with " <<
2904
reply.error().name() << ": " << reply.error().message() <<
2905
", Channel officially dead";
2906
invalidate(reply.error());
2910
debug() << "Got reply to fallback Channel::GetHandle()";
2911
mPriv->targetHandleType = reply.argumentAt<0>();
2912
mPriv->targetHandle = reply.argumentAt<1>();
2913
mPriv->continueIntrospection();
2916
void Channel::gotInterfaces(QDBusPendingCallWatcher *watcher)
2918
QDBusPendingReply<QStringList> reply = *watcher;
2920
if (reply.isError()) {
2921
warning().nospace() << "Channel::GetInterfaces() failed with " <<
2922
reply.error().name() << ": " << reply.error().message() <<
2923
", Channel officially dead";
2924
invalidate(reply.error());
2928
debug() << "Got reply to fallback Channel::GetInterfaces()";
2929
setInterfaces(reply.value());
2930
mPriv->readinessHelper->setInterfaces(interfaces());
2931
mPriv->nowHaveInterfaces();
2933
mPriv->fakeGroupInterfaceIfNeeded();
2935
mPriv->continueIntrospection();
2938
void Channel::onClosed()
2940
debug() << "Got Channel::Closed";
2944
if (mPriv->groupSelfContactRemoveInfo.isValid() &&
2945
mPriv->groupSelfContactRemoveInfo.hasReason()) {
2946
error = mPriv->groupMemberChangeDetailsTelepathyError(
2947
mPriv->groupSelfContactRemoveInfo);
2948
message = mPriv->groupSelfContactRemoveInfo.message();
2950
error = TP_QT_ERROR_CANCELLED;
2951
message = QLatin1String("channel closed");
2954
invalidate(error, message);
2957
void Channel::onConnectionReady(PendingOperation *op)
2959
if (op->isError()) {
2960
invalidate(op->errorName(), op->errorMessage());
2964
// FIXME: should connect to selfHandleChanged and act accordingly, but that is a PITA for
2965
// keeping the Contacts built and even if we don't do it, the new code is better than the
2966
// old one anyway because earlier on we just wouldn't have had a self contact.
2968
// besides, the only thing which breaks without connecting in the world likely is if you're
2969
// using idle and decide to change your nick, which I don't think we necessarily even have API
2970
// to do from tp-qt anyway (or did I make idle change the nick when setting your alias? can't
2973
// Simply put, I just don't care ATM.
2975
// Will be overwritten by the group self handle, if we can discover any.
2976
Q_ASSERT(!mPriv->groupSelfHandle);
2977
mPriv->groupSelfHandle = mPriv->connection->selfHandle();
2979
mPriv->introspectMainProperties();
2982
void Channel::onConnectionInvalidated()
2984
debug() << "Owning connection died leaving an orphan Channel, "
2985
"changing to closed";
2986
invalidate(TP_QT_ERROR_ORPHANED,
2987
QLatin1String("Connection given as the owner of this channel was invalidated"));
2990
void Channel::gotGroupProperties(QDBusPendingCallWatcher *watcher)
2992
QDBusPendingReply<QVariantMap> reply = *watcher;
2995
if (!reply.isError()) {
2996
debug() << "Got reply to Properties::GetAll(Channel.Interface.Group)";
2997
props = reply.value();
3000
warning().nospace() << "Properties::GetAll(Channel.Interface.Group) "
3001
"failed with " << reply.error().name() << ": " <<
3002
reply.error().message();
3005
mPriv->extract0176GroupProps(props);
3006
// Add extraction (and possible fallbacks) in similar functions, called from here
3008
mPriv->continueIntrospection();
3011
void Channel::gotGroupFlags(QDBusPendingCallWatcher *watcher)
3013
QDBusPendingReply<uint> reply = *watcher;
3015
if (reply.isError()) {
3016
warning().nospace() << "Channel.Interface.Group::GetGroupFlags() failed with " <<
3017
reply.error().name() << ": " << reply.error().message();
3020
debug() << "Got reply to fallback Channel.Interface.Group::GetGroupFlags()";
3021
mPriv->setGroupFlags(reply.value());
3023
if (mPriv->groupFlags & ChannelGroupFlagProperties) {
3024
warning() << " Reply included ChannelGroupFlagProperties, even "
3025
"though properties specified in 0.17.7 didn't work! - unsetting";
3026
mPriv->groupFlags &= ~ChannelGroupFlagProperties;
3030
mPriv->continueIntrospection();
3033
void Channel::gotAllMembers(QDBusPendingCallWatcher *watcher)
3035
QDBusPendingReply<UIntList, UIntList, UIntList> reply = *watcher;
3037
if (reply.isError()) {
3038
warning().nospace() << "Channel.Interface.Group::GetAllMembers() failed with " <<
3039
reply.error().name() << ": " << reply.error().message();
3041
debug() << "Got reply to fallback Channel.Interface.Group::GetAllMembers()";
3043
mPriv->groupInitialMembers = reply.argumentAt<0>();
3044
mPriv->groupInitialRP = reply.argumentAt<2>();
3046
foreach (uint handle, reply.argumentAt<1>()) {
3047
LocalPendingInfo info = {handle, 0, ChannelGroupChangeReasonNone,
3049
mPriv->groupInitialLP.push_back(info);
3053
mPriv->continueIntrospection();
3056
void Channel::gotLocalPendingMembersWithInfo(QDBusPendingCallWatcher *watcher)
3058
QDBusPendingReply<LocalPendingInfoList> reply = *watcher;
3060
if (reply.isError()) {
3061
warning().nospace() << "Channel.Interface.Group::GetLocalPendingMembersWithInfo() "
3062
"failed with " << reply.error().name() << ": " << reply.error().message();
3063
warning() << " Falling back to what GetAllMembers returned with no extended info";
3066
debug() << "Got reply to fallback "
3067
"Channel.Interface.Group::GetLocalPendingMembersWithInfo()";
3068
// Overrides the previous vague list provided by gotAllMembers
3069
mPriv->groupInitialLP = reply.value();
3072
mPriv->continueIntrospection();
3075
void Channel::gotSelfHandle(QDBusPendingCallWatcher *watcher)
3077
QDBusPendingReply<uint> reply = *watcher;
3079
if (reply.isError()) {
3080
warning().nospace() << "Channel.Interface.Group::GetSelfHandle() failed with " <<
3081
reply.error().name() << ": " << reply.error().message();
3083
debug() << "Got reply to fallback Channel.Interface.Group::GetSelfHandle()";
3084
// Don't overwrite the self handle we got from the connection with 0
3085
if (reply.value()) {
3086
mPriv->groupSelfHandle = reply.value();
3090
mPriv->nowHaveInitialMembers();
3092
mPriv->continueIntrospection();
3095
void Channel::gotContacts(PendingOperation *op)
3097
PendingContacts *pending = qobject_cast<PendingContacts *>(op);
3099
mPriv->buildingContacts = false;
3101
QList<ContactPtr> contacts;
3102
if (pending->isValid()) {
3103
contacts = pending->contacts();
3105
if (!pending->invalidHandles().isEmpty()) {
3106
warning() << "Unable to construct Contact objects for handles:" <<
3107
pending->invalidHandles();
3109
if (mPriv->groupSelfHandle &&
3110
pending->invalidHandles().contains(mPriv->groupSelfHandle)) {
3111
warning() << "Unable to retrieve self contact";
3112
mPriv->groupSelfContact.reset();
3113
emit groupSelfContactChanged();
3117
warning().nospace() << "Getting contacts failed with " <<
3118
pending->errorName() << ":" << pending->errorMessage();
3121
mPriv->updateContacts(contacts);
3124
void Channel::onGroupFlagsChanged(uint added, uint removed)
3126
debug().nospace() << "Got Channel.Interface.Group::GroupFlagsChanged(" <<
3127
hex << added << ", " << removed << ")";
3129
added &= ~(mPriv->groupFlags);
3130
removed &= mPriv->groupFlags;
3132
debug().nospace() << "Arguments after filtering (" << hex << added <<
3133
", " << removed << ")";
3135
uint groupFlags = mPriv->groupFlags;
3136
groupFlags |= added;
3137
groupFlags &= ~removed;
3138
// just emit groupFlagsChanged and related signals if the flags really
3139
// changed and we are ready
3140
if (mPriv->setGroupFlags(groupFlags) && isReady(Channel::FeatureCore)) {
3141
debug() << "Emitting groupFlagsChanged with" << mPriv->groupFlags <<
3142
"value" << added << "added" << removed << "removed";
3143
emit groupFlagsChanged((ChannelGroupFlags) mPriv->groupFlags,
3144
(ChannelGroupFlags) added, (ChannelGroupFlags) removed);
3146
if (added & ChannelGroupFlagCanAdd ||
3147
removed & ChannelGroupFlagCanAdd) {
3148
debug() << "Emitting groupCanAddContactsChanged";
3149
emit groupCanAddContactsChanged(groupCanAddContacts());
3152
if (added & ChannelGroupFlagCanRemove ||
3153
removed & ChannelGroupFlagCanRemove) {
3154
debug() << "Emitting groupCanRemoveContactsChanged";
3155
emit groupCanRemoveContactsChanged(groupCanRemoveContacts());
3158
if (added & ChannelGroupFlagCanRescind ||
3159
removed & ChannelGroupFlagCanRescind) {
3160
debug() << "Emitting groupCanRescindContactsChanged";
3161
emit groupCanRescindContactsChanged(groupCanRescindContacts());
3166
void Channel::onMembersChanged(const QString &message,
3167
const UIntList &added, const UIntList &removed,
3168
const UIntList &localPending, const UIntList &remotePending,
3169
uint actor, uint reason)
3171
// Ignore the signal if we're using the MCD signal to not duplicate events
3172
if (mPriv->usingMembersChangedDetailed) {
3176
debug() << "Got Channel.Interface.Group::MembersChanged with" << added.size() <<
3177
"added," << removed.size() << "removed," << localPending.size() <<
3178
"moved to LP," << remotePending.size() << "moved to RP," << actor <<
3179
"being the actor," << reason << "the reason and" << message << "the message";
3180
debug() << " synthesizing a corresponding MembersChangedDetailed signal";
3182
QVariantMap details;
3184
if (!message.isEmpty()) {
3185
details.insert(QLatin1String("message"), message);
3189
details.insert(QLatin1String("actor"), actor);
3192
details.insert(QLatin1String("change-reason"), reason);
3194
mPriv->doMembersChangedDetailed(added, removed, localPending, remotePending, details);
3197
void Channel::onMembersChangedDetailed(
3198
const UIntList &added, const UIntList &removed,
3199
const UIntList &localPending, const UIntList &remotePending,
3200
const QVariantMap &details)
3202
// Ignore the signal if we aren't (yet) using MCD to not duplicate events
3203
if (!mPriv->usingMembersChangedDetailed) {
3207
debug() << "Got Channel.Interface.Group::MembersChangedDetailed with" << added.size() <<
3208
"added," << removed.size() << "removed," << localPending.size() <<
3209
"moved to LP," << remotePending.size() << "moved to RP and with" << details.size() <<
3212
mPriv->doMembersChangedDetailed(added, removed, localPending, remotePending, details);
3215
void Channel::Private::doMembersChangedDetailed(
3216
const UIntList &added, const UIntList &removed,
3217
const UIntList &localPending, const UIntList &remotePending,
3218
const QVariantMap &details)
3220
if (!groupHaveMembers) {
3221
debug() << "Still waiting for initial group members, "
3222
"so ignoring delta signal...";
3226
if (added.isEmpty() && removed.isEmpty() &&
3227
localPending.isEmpty() && remotePending.isEmpty()) {
3228
debug() << "Nothing really changed, so skipping membersChanged";
3232
// let's store groupSelfContactRemoveInfo here as we may not have time
3233
// to build the contacts in case self contact is removed,
3234
// as Closed will be emitted right after
3235
if (removed.contains(groupSelfHandle)) {
3236
if (qdbus_cast<uint>(details.value(QLatin1String("change-reason"))) ==
3237
ChannelGroupChangeReasonRenamed) {
3238
if (removed.size() != 1 ||
3239
(added.size() + localPending.size() + remotePending.size()) != 1) {
3240
// spec-incompliant CM, ignoring members changed
3241
warning() << "Received MembersChangedDetailed with reason "
3242
"Renamed and removed.size != 1 or added.size + "
3243
"localPending.size + remotePending.size != 1. Ignoring";
3247
if (!added.isEmpty()) {
3248
newHandle = added.first();
3249
} else if (!localPending.isEmpty()) {
3250
newHandle = localPending.first();
3251
} else if (!remotePending.isEmpty()) {
3252
newHandle = remotePending.first();
3254
parent->onSelfHandleChanged(newHandle);
3258
// let's try to get the actor contact from contact manager if available
3259
groupSelfContactRemoveInfo = GroupMemberChangeDetails(
3260
connection->contactManager()->lookupContactByHandle(
3261
qdbus_cast<uint>(details.value(QLatin1String("actor")))),
3265
HandleIdentifierMap contactIds = qdbus_cast<HandleIdentifierMap>(
3266
details.value(GroupMembersChangedInfo::keyContactIds));
3267
connection->lowlevel()->injectContactIds(contactIds);
3269
groupMembersChangedQueue.enqueue(
3270
new Private::GroupMembersChangedInfo(
3272
localPending, remotePending,
3275
if (!buildingContacts) {
3276
// if we are building contacts, we should wait it to finish so we don't
3277
// present the user with wrong information
3278
processMembersChanged();
3282
void Channel::onHandleOwnersChanged(const HandleOwnerMap &added,
3283
const UIntList &removed)
3285
debug() << "Got Channel.Interface.Group::HandleOwnersChanged with" <<
3286
added.size() << "added," << removed.size() << "removed";
3288
if (!mPriv->groupAreHandleOwnersAvailable) {
3289
debug() << "Still waiting for initial handle owners, so ignoring "
3295
UIntList emitRemoved;
3297
for (HandleOwnerMap::const_iterator i = added.begin();
3300
uint handle = i.key();
3301
uint global = i.value();
3303
if (!mPriv->groupHandleOwners.contains(handle)
3304
|| mPriv->groupHandleOwners[handle] != global) {
3305
debug() << " +++/changed" << handle << "->" << global;
3306
mPriv->groupHandleOwners[handle] = global;
3307
emitAdded.append(handle);
3311
foreach (uint handle, removed) {
3312
if (mPriv->groupHandleOwners.contains(handle)) {
3313
debug() << " ---" << handle;
3314
mPriv->groupHandleOwners.remove(handle);
3315
emitRemoved.append(handle);
3319
// just emit groupHandleOwnersChanged if it really changed and
3321
if ((emitAdded.size() || emitRemoved.size()) && isReady(Channel::FeatureCore)) {
3322
debug() << "Emitting groupHandleOwnersChanged with" << emitAdded.size() <<
3323
"added" << emitRemoved.size() << "removed";
3324
emit groupHandleOwnersChanged(mPriv->groupHandleOwners,
3325
emitAdded, emitRemoved);
3329
void Channel::onSelfHandleChanged(uint selfHandle)
3331
debug().nospace() << "Got Channel.Interface.Group::SelfHandleChanged";
3333
if (selfHandle != mPriv->groupSelfHandle) {
3334
mPriv->groupSelfHandle = selfHandle;
3335
debug() << " Emitting groupSelfHandleChanged with new self handle" <<
3338
// FIXME: fix self contact building with no group
3339
mPriv->pendingRetrieveGroupSelfContact = true;
3343
void Channel::gotConferenceProperties(QDBusPendingCallWatcher *watcher)
3345
QDBusPendingReply<QVariantMap> reply = *watcher;
3348
mPriv->introspectingConference = false;
3350
if (!reply.isError()) {
3351
debug() << "Got reply to Properties::GetAll(Channel.Interface.Conference)";
3352
props = reply.value();
3354
ConnectionPtr conn = connection();
3355
ChannelFactoryConstPtr chanFactory = conn->channelFactory();
3357
ObjectPathList channels =
3358
qdbus_cast<ObjectPathList>(props[QLatin1String("Channels")]);
3359
foreach (const QDBusObjectPath &channelPath, channels) {
3360
if (mPriv->conferenceChannels.contains(channelPath.path())) {
3364
PendingReady *readyOp = chanFactory->proxy(conn,
3365
channelPath.path(), QVariantMap());
3366
ChannelPtr channel(ChannelPtr::qObjectCast(readyOp->proxy()));
3367
Q_ASSERT(!channel.isNull());
3369
mPriv->conferenceChannels.insert(channelPath.path(), channel);
3372
ObjectPathList initialChannels =
3373
qdbus_cast<ObjectPathList>(props[QLatin1String("InitialChannels")]);
3374
foreach (const QDBusObjectPath &channelPath, initialChannels) {
3375
if (mPriv->conferenceInitialChannels.contains(channelPath.path())) {
3379
PendingReady *readyOp = chanFactory->proxy(conn,
3380
channelPath.path(), QVariantMap());
3381
ChannelPtr channel(ChannelPtr::qObjectCast(readyOp->proxy()));
3382
Q_ASSERT(!channel.isNull());
3384
mPriv->conferenceInitialChannels.insert(channelPath.path(), channel);
3387
mPriv->conferenceInitialInviteeHandles =
3388
qdbus_cast<UIntList>(props[QLatin1String("InitialInviteeHandles")]);
3389
QStringList conferenceInitialInviteeIds =
3390
qdbus_cast<QStringList>(props[QLatin1String("InitialInviteeIDs")]);
3391
if (mPriv->conferenceInitialInviteeHandles.size() == conferenceInitialInviteeIds.size()) {
3392
HandleIdentifierMap contactIds;
3394
foreach (uint handle, mPriv->conferenceInitialInviteeHandles) {
3395
contactIds.insert(handle, conferenceInitialInviteeIds.at(i++));
3397
mPriv->connection->lowlevel()->injectContactIds(contactIds);
3400
mPriv->conferenceInvitationMessage =
3401
qdbus_cast<QString>(props[QLatin1String("InvitationMessage")]);
3403
ChannelOriginatorMap originalChannels = qdbus_cast<ChannelOriginatorMap>(
3404
props[QLatin1String("OriginalChannels")]);
3405
for (ChannelOriginatorMap::const_iterator i = originalChannels.constBegin();
3406
i != originalChannels.constEnd(); ++i) {
3407
PendingReady *readyOp = chanFactory->proxy(conn,
3408
i.value().path(), QVariantMap());
3409
ChannelPtr channel(ChannelPtr::qObjectCast(readyOp->proxy()));
3410
Q_ASSERT(!channel.isNull());
3412
mPriv->conferenceOriginalChannels.insert(i.key(), channel);
3415
warning().nospace() << "Properties::GetAll(Channel.Interface.Conference) "
3416
"failed with " << reply.error().name() << ": " <<
3417
reply.error().message();
3420
mPriv->continueIntrospection();
3423
void Channel::gotConferenceInitialInviteeContacts(PendingOperation *op)
3425
PendingContacts *pending = qobject_cast<PendingContacts *>(op);
3427
if (pending->isValid()) {
3428
mPriv->conferenceInitialInviteeContacts = pending->contacts().toSet();
3430
warning().nospace() << "Getting conference initial invitee contacts "
3431
"failed with " << pending->errorName() << ":" <<
3432
pending->errorMessage();
3435
mPriv->readinessHelper->setIntrospectCompleted(
3436
FeatureConferenceInitialInviteeContacts, true);
3439
void Channel::onConferenceChannelMerged(const QDBusObjectPath &channelPath,
3440
uint channelSpecificHandle, const QVariantMap &properties)
3442
if (mPriv->conferenceChannels.contains(channelPath.path())) {
3446
ConnectionPtr conn = connection();
3447
ChannelFactoryConstPtr chanFactory = conn->channelFactory();
3448
PendingReady *readyOp = chanFactory->proxy(conn,
3449
channelPath.path(), properties);
3450
ChannelPtr channel(ChannelPtr::qObjectCast(readyOp->proxy()));
3451
Q_ASSERT(!channel.isNull());
3453
mPriv->conferenceChannels.insert(channelPath.path(), channel);
3454
emit conferenceChannelMerged(channel);
3456
if (channelSpecificHandle != 0) {
3457
mPriv->conferenceOriginalChannels.insert(channelSpecificHandle, channel);
3461
void Channel::onConferenceChannelMerged(const QDBusObjectPath &channelPath)
3463
onConferenceChannelMerged(channelPath, 0, QVariantMap());
3466
void Channel::onConferenceChannelRemoved(const QDBusObjectPath &channelPath,
3467
const QVariantMap &details)
3469
if (!mPriv->conferenceChannels.contains(channelPath.path())) {
3473
HandleIdentifierMap contactIds = qdbus_cast<HandleIdentifierMap>(
3474
details.value(Private::GroupMembersChangedInfo::keyContactIds));
3475
mPriv->connection->lowlevel()->injectContactIds(contactIds);
3477
mPriv->conferenceChannelRemovedQueue.enqueue(
3478
new Private::ConferenceChannelRemovedInfo(channelPath, details));
3479
mPriv->processConferenceChannelRemoved();
3482
void Channel::onConferenceChannelRemoved(const QDBusObjectPath &channelPath)
3484
onConferenceChannelRemoved(channelPath, QVariantMap());
3487
void Channel::gotConferenceChannelRemovedActorContact(PendingOperation *op)
3489
ContactPtr actorContact;
3492
PendingContacts *pc = qobject_cast<PendingContacts *>(op);
3494
if (pc->isValid()) {
3495
Q_ASSERT(pc->contacts().size() == 1);
3496
actorContact = pc->contacts().first();
3498
warning().nospace() << "Getting conference channel removed actor "
3499
"failed with " << pc->errorName() << ":" <<
3504
Private::ConferenceChannelRemovedInfo *info = mPriv->conferenceChannelRemovedQueue.dequeue();
3506
ChannelPtr channel = mPriv->conferenceChannels[info->channelPath.path()];
3507
mPriv->conferenceChannels.remove(info->channelPath.path());
3508
emit conferenceChannelRemoved(channel, GroupMemberChangeDetails(actorContact,
3511
for (QHash<uint, ChannelPtr>::iterator i = mPriv->conferenceOriginalChannels.begin();
3512
i != mPriv->conferenceOriginalChannels.end();) {
3513
if (i.value() == channel) {
3514
i = mPriv->conferenceOriginalChannels.erase(i);
3522
mPriv->buildingConferenceChannelRemovedActorContact = false;
3523
mPriv->processConferenceChannelRemoved();
3527
* \fn void Channel::groupFlagsChanged(uint flags, uint added, uint removed)
3529
* Emitted when the value of groupFlags() changes.
3531
* \param flags The value which would now be returned by groupFlags().
3532
* \param added Flags added compared to the previous value.
3533
* \param removed Flags removed compared to the previous value.
3537
* \fn void Channel::groupCanAddContactsChanged(bool canAddContacts)
3539
* Emitted when the value of groupCanAddContacts() changes.
3541
* \param canAddContacts Whether a contact can be added to this channel.
3542
* \sa groupCanAddContacts()
3546
* \fn void Channel::groupCanRemoveContactsChanged(bool canRemoveContacts)
3548
* Emitted when the value of groupCanRemoveContacts() changes.
3550
* \param canRemoveContacts Whether a contact can be removed from this channel.
3551
* \sa groupCanRemoveContacts()
3555
* \fn void Channel::groupCanRescindContactsChanged(bool canRescindContacts)
3557
* Emitted when the value of groupCanRescindContacts() changes.
3559
* \param canRescindContacts Whether contact invitations can be rescinded.
3560
* \sa groupCanRescindContacts()
3564
* \fn void Channel::groupMembersChanged(
3565
* const Tp::Contacts &groupMembersAdded,
3566
* const Tp::Contacts &groupLocalPendingMembersAdded,
3567
* const Tp::Contacts &groupRemotePendingMembersAdded,
3568
* const Tp::Contacts &groupMembersRemoved,
3569
* const Channel::GroupMemberChangeDetails &details)
3571
* Emitted when the value returned by groupContacts(), groupLocalPendingContacts() or
3572
* groupRemotePendingContacts() changes.
3574
* \param groupMembersAdded The contacts that were added to this channel.
3575
* \param groupLocalPendingMembersAdded The local pending contacts that were
3576
* added to this channel.
3577
* \param groupRemotePendingMembersAdded The remote pending contacts that were
3578
* added to this channel.
3579
* \param groupMembersRemoved The contacts removed from this channel.
3580
* \param details Additional details such as the contact requesting or causing
3585
* \fn void Channel::groupHandleOwnersChanged(const HandleOwnerMap &owners,
3586
* const Tp::UIntList &added, const Tp::UIntList &removed)
3588
* Emitted when the value returned by groupHandleOwners() changes.
3590
* \param owners The value which would now be returned by
3591
* groupHandleOwners().
3592
* \param added Handles which have been added to the mapping as keys, or
3593
* existing handle keys for which the mapped-to value has changed.
3594
* \param removed Handles which have been removed from the mapping.
3598
* \fn void Channel::groupSelfContactChanged()
3600
* Emitted when the value returned by groupSelfContact() changes.
3604
* \fn void Channel::conferenceChannelMerged(const Tp::ChannelPtr &channel)
3606
* Emitted when a new channel is added to the value of conferenceChannels().
3608
* \param channel The channel that was added to conferenceChannels().
3612
* \fn void Channel::conferenceChannelRemoved(const Tp::ChannelPtr &channel,
3613
* const Tp::Channel::GroupMemberChangeDetails &details)
3615
* Emitted when a new channel is removed from the value of conferenceChannels().
3617
* \param channel The channel that was removed from conferenceChannels().
3618
* \param details The change details.