2
* Copyright (C) 2013 Canonical, Ltd.
4
* This program is free software: you can redistribute it and/or modify it under
5
* the terms of the GNU General Public License version 3, as published by
6
* the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful, but WITHOUT
9
* ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10
* SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
* General Public License for more details.
13
* You should have received a copy of the GNU General Public License
14
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16
* Authors: Tiago Salem Herrmann <tiago.herrmann@canonical.com>
17
* Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
22
#include <TelepathyQt/Constants>
23
#include <TelepathyQt/BaseChannel>
24
#include <TelepathyQt/DBusObject>
27
#include "connection.h"
28
#include "phoneutils.h"
31
#include "mockconnectiondbus.h"
33
MockConnection::MockConnection(const QDBusConnection &dbusConnection,
34
const QString &cmName,
35
const QString &protocolName,
36
const QVariantMap ¶meters) :
37
Tp::BaseConnection(dbusConnection, cmName, protocolName, parameters),
40
setSelfHandle(newHandle("<SelfHandle>"));
42
setConnectCallback(Tp::memFun(this,&MockConnection::connect));
43
setInspectHandlesCallback(Tp::memFun(this,&MockConnection::inspectHandles));
44
setRequestHandlesCallback(Tp::memFun(this,&MockConnection::requestHandles));
45
setCreateChannelCallback(Tp::memFun(this,&MockConnection::createChannel));
47
// initialise requests interface (Connection.Interface.Requests)
48
requestsIface = Tp::BaseConnectionRequestsInterface::create(this);
50
// set requestable text channel properties
51
Tp::RequestableChannelClass text;
52
text.fixedProperties[TP_QT_IFACE_CHANNEL+".ChannelType"] = TP_QT_IFACE_CHANNEL_TYPE_TEXT;
53
text.fixedProperties[TP_QT_IFACE_CHANNEL+".TargetHandleType"] = Tp::HandleTypeContact;
54
text.allowedProperties.append(TP_QT_IFACE_CHANNEL+".TargetHandle");
55
text.allowedProperties.append(TP_QT_IFACE_CHANNEL+".TargetID");
57
// set requestable call channel properties
58
Tp::RequestableChannelClass call;
59
call.fixedProperties[TP_QT_IFACE_CHANNEL+".ChannelType"] = TP_QT_IFACE_CHANNEL_TYPE_CALL;
60
call.fixedProperties[TP_QT_IFACE_CHANNEL+".TargetHandleType"] = Tp::HandleTypeContact;
61
call.fixedProperties[TP_QT_IFACE_CHANNEL_TYPE_CALL+".InitialAudio"] = true;
62
call.allowedProperties.append(TP_QT_IFACE_CHANNEL+".TargetHandle");
63
call.allowedProperties.append(TP_QT_IFACE_CHANNEL+".TargetID");
64
call.allowedProperties.append(TP_QT_IFACE_CHANNEL_TYPE_CALL+".InitialAudio");
65
call.allowedProperties.append(TP_QT_IFACE_CHANNEL_TYPE_CALL+".InitialVideo");
66
call.allowedProperties.append(TP_QT_IFACE_CHANNEL_TYPE_CALL+".InitialAudioName");
67
call.allowedProperties.append(TP_QT_IFACE_CHANNEL_TYPE_CALL+".InitialVideoName");
68
call.allowedProperties.append(TP_QT_IFACE_CHANNEL_TYPE_CALL+".InitialTransport");
69
call.allowedProperties.append(TP_QT_IFACE_CHANNEL_TYPE_CALL+".HardwareStreaming");
71
requestsIface->requestableChannelClasses << text << call;
73
plugInterface(Tp::AbstractConnectionInterfacePtr::dynamicCast(requestsIface));
75
// init presence interface
76
simplePresenceIface = Tp::BaseConnectionSimplePresenceInterface::create();
77
simplePresenceIface->setSetPresenceCallback(Tp::memFun(this,&MockConnection::setPresence));
78
plugInterface(Tp::AbstractConnectionInterfacePtr::dynamicCast(simplePresenceIface));
81
Tp::SimpleStatusSpec presenceOnline;
82
presenceOnline.type = Tp::ConnectionPresenceTypeAvailable;
83
presenceOnline.maySetOnSelf = true;
84
presenceOnline.canHaveMessage = false;
86
Tp::SimpleStatusSpec presenceOffline;
87
presenceOffline.type = Tp::ConnectionPresenceTypeOffline;
88
presenceOffline.maySetOnSelf = false;
89
presenceOffline.canHaveMessage = false;
91
Tp::SimpleStatusSpecMap statuses;
92
statuses.insert(QLatin1String("available"), presenceOnline);
93
statuses.insert(QLatin1String("offline"), presenceOffline);
95
simplePresenceIface->setStatuses(statuses);
96
mSelfPresence.type = Tp::ConnectionPresenceTypeOffline;
97
mRequestedSelfPresence.type = Tp::ConnectionPresenceTypeOffline;
99
contactsIface = Tp::BaseConnectionContactsInterface::create();
100
contactsIface->setGetContactAttributesCallback(Tp::memFun(this,&MockConnection::getContactAttributes));
101
contactsIface->setContactAttributeInterfaces(QStringList()
102
<< TP_QT_IFACE_CONNECTION
103
<< TP_QT_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE);
104
plugInterface(Tp::AbstractConnectionInterfacePtr::dynamicCast(contactsIface));
106
mDBus = new MockConnectionDBus(this);
111
void MockConnection::addMMSToService(const QString &path, const QVariantMap &properties, const QString &servicePath)
113
qDebug() << "addMMSToService " << path << properties << servicePath;
116
MMSDMessage *msg = new MMSDMessage(path, properties);
117
QObject::connect(msg, SIGNAL(propertyChanged(QString,QVariant)), SLOT(onMMSPropertyChanged(QString,QVariant)));
118
mServiceMMSList[servicePath].append(msg);
119
if (properties["Status"] == "received") {
120
const QString normalizedNumber = PhoneUtils::normalizePhoneNumber(properties["Sender"].toString());
121
// check if there is an open channel for this number and use it
122
Q_FOREACH(const QString &phoneNumber, mTextChannels.keys()) {
123
if (PhoneUtils::comparePhoneNumbers(normalizedNumber, phoneNumber)) {
124
qDebug() << "existing channel" << mTextChannels[phoneNumber];
125
mTextChannels[phoneNumber]->mmsReceived(path, properties);
132
qDebug() << "new handle" << normalizedNumber;
133
uint handle = newHandle(normalizedNumber);
134
ensureChannel(TP_QT_IFACE_CHANNEL_TYPE_TEXT,Tp::HandleTypeContact, handle, yours, handle, false, &error);
135
if(error.isValid()) {
136
qCritical() << "Error creating channel for incoming message " << error.name() << error.message();
139
mTextChannels[normalizedNumber]->mmsReceived(path, properties);
144
MockConnection::~MockConnection()
148
uint MockConnection::setPresence(const QString& status, const QString& statusMessage, Tp::DBusError *error)
150
qDebug() << "setPresence" << status;
151
if (status == "available") {
152
mRequestedSelfPresence.type = Tp::ConnectionPresenceTypeAvailable;
157
Tp::ContactAttributesMap MockConnection::getContactAttributes(const Tp::UIntList &handles, const QStringList &ifaces, Tp::DBusError *error)
159
qDebug() << "getContactAttributes" << handles << ifaces;
160
Tp::ContactAttributesMap attributesMap;
161
QVariantMap attributes;
162
Q_FOREACH(uint handle, handles) {
163
attributes[TP_QT_IFACE_CONNECTION+"/contact-id"] = inspectHandles(Tp::HandleTypeContact, Tp::UIntList() << handle, error).at(0);
164
if (ifaces.contains(TP_QT_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE)) {
165
attributes[TP_QT_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE+"/presence"] = QVariant::fromValue(mSelfPresence);
167
attributesMap[handle] = attributes;
169
return attributesMap;
172
void MockConnection::setOnline(bool online)
174
qDebug() << "setOnline" << online;
175
Tp::SimpleContactPresences presences;
177
mSelfPresence.status = "available";
178
mSelfPresence.statusMessage = "";
179
mSelfPresence.type = Tp::ConnectionPresenceTypeAvailable;
181
mSelfPresence.status = "offline";
182
mSelfPresence.statusMessage = "";
183
mSelfPresence.type = Tp::ConnectionPresenceTypeOffline;
185
presences[selfHandle()] = mSelfPresence;
186
simplePresenceIface->setPresences(presences);
189
uint MockConnection::newHandle(const QString &identifier)
191
mHandles[++mHandleCount] = identifier;
195
QStringList MockConnection::inspectHandles(uint handleType, const Tp::UIntList& handles, Tp::DBusError *error)
197
QStringList identifiers;
199
if( handleType != Tp::HandleTypeContact ) {
200
error->set(TP_QT_ERROR_INVALID_ARGUMENT,"Not supported");
201
return QStringList();
204
qDebug() << "MockConnection::inspectHandles " << handles;
205
Q_FOREACH( uint handle, handles) {
206
if (mHandles.keys().contains(handle)) {
207
identifiers.append(mHandles.value(handle));
209
error->set(TP_QT_ERROR_INVALID_HANDLE, "Handle not found");
210
return QStringList();
213
qDebug() << "MockConnection::inspectHandles " << identifiers;
217
void MockConnection::connect(Tp::DBusError *error) {
218
qDebug() << "MockConnection::connect";
219
setStatus(Tp::ConnectionStatusConnected, Tp::ConnectionStatusReasonRequested);
222
Tp::UIntList MockConnection::requestHandles(uint handleType, const QStringList& identifiers, Tp::DBusError* error)
224
qDebug() << "requestHandles";
225
Tp::UIntList handles;
227
if( handleType != Tp::HandleTypeContact ) {
228
error->set(TP_QT_ERROR_INVALID_ARGUMENT, "Not supported");
229
return Tp::UIntList();
232
Q_FOREACH( const QString& identifier, identifiers) {
233
if (mHandles.values().contains(identifier)) {
234
handles.append(mHandles.key(identifier));
236
handles.append(newHandle(identifier));
239
qDebug() << "requestHandles" << handles;
243
Tp::BaseChannelPtr MockConnection::createTextChannel(uint targetHandleType,
244
uint targetHandle, Tp::DBusError *error)
246
Q_UNUSED(targetHandleType);
249
QString requestedId = mHandles.value(targetHandle);
251
if (mTextChannels.contains(requestedId)) {
252
return mTextChannels[requestedId]->baseChannel();
255
MockTextChannel *channel = new MockTextChannel(this, requestedId, targetHandle);
256
QObject::connect(channel, SIGNAL(messageRead(QString)), SLOT(onMessageRead(QString)));
257
QObject::connect(channel, SIGNAL(destroyed()), SLOT(onTextChannelClosed()));
258
QObject::connect(channel, SIGNAL(messageSent(QString,QVariantMap)), SIGNAL(messageSent(QString,QVariantMap)));
260
mTextChannels[requestedId] = channel;
261
return channel->baseChannel();
264
void MockConnection::onMessageRead(const QString &id)
269
Tp::BaseChannelPtr MockConnection::createCallChannel(uint targetHandleType,
270
uint targetHandle, Tp::DBusError *error)
272
Q_UNUSED(targetHandleType);
275
QString requestedId = mHandles.value(targetHandle);
277
Q_FOREACH(const QString &id, mCallChannels.keys()) {
278
if (id == requestedId) {
279
return mCallChannels[id]->baseChannel();
283
QString state = "dialing";
284
if (mInitialCallStatus.contains(requestedId)) {
285
state = mInitialCallStatus.take(requestedId);
288
mCallChannels[requestedId] = new MockCallChannel(this, requestedId, state, targetHandle);
289
QObject::connect(mCallChannels[requestedId], SIGNAL(destroyed()), SLOT(onCallChannelClosed()));
290
QObject::connect(mCallChannels[requestedId], SIGNAL(callStateChanged(MockCallChannel*,QString)), SLOT(onCallStateChanged(MockCallChannel*,QString)));
291
qDebug() << mCallChannels[requestedId];
293
if (!mIncomingCalls.contains(requestedId)) {
294
Q_EMIT callReceived(requestedId);
296
return mCallChannels[requestedId]->baseChannel();
299
Tp::BaseChannelPtr MockConnection::createChannel(const QString& channelType, uint targetHandleType,
300
uint targetHandle, Tp::DBusError *error)
302
qDebug() << "MockConnection::createChannel" << targetHandle;
303
if( (targetHandleType != Tp::HandleTypeContact) || targetHandle == 0 || !mHandles.keys().contains(targetHandle)) {
304
error->set(TP_QT_ERROR_INVALID_HANDLE, "Handle not found");
305
return Tp::BaseChannelPtr();
308
if (mSelfPresence.type != Tp::ConnectionPresenceTypeAvailable) {
309
error->set(TP_QT_ERROR_NETWORK_ERROR, "No network available");
310
return Tp::BaseChannelPtr();
313
if (channelType == TP_QT_IFACE_CHANNEL_TYPE_TEXT) {
314
return createTextChannel(targetHandleType, targetHandle, error);
315
} else if (channelType == TP_QT_IFACE_CHANNEL_TYPE_CALL) {
316
return createCallChannel(targetHandleType, targetHandle, error);
318
error->set(TP_QT_ERROR_NOT_IMPLEMENTED, "Channel type not available");
321
return Tp::BaseChannelPtr();
324
void MockConnection::placeIncomingMessage(const QString &message, const QVariantMap &info)
326
const QString sender = info["Sender"].toString();
327
// check if there is an open channel for this sender and use it
328
Q_FOREACH(const QString &id, mTextChannels.keys()) {
330
mTextChannels[id]->messageReceived(message, info);
337
uint handle = newHandle(sender);
338
ensureChannel(TP_QT_IFACE_CHANNEL_TYPE_TEXT,Tp::HandleTypeContact, handle, yours, handle, false, &error);
339
if(error.isValid()) {
340
qWarning() << "Error creating channel for incoming message" << error.name() << error.message();
343
mTextChannels[sender]->messageReceived(message, info);
346
void MockConnection::onTextChannelClosed()
348
MockTextChannel *channel = static_cast<MockTextChannel*>(sender());
350
QString key = mTextChannels.key(channel);
351
qDebug() << "text channel closed for number " << key;
352
mTextChannels.remove(key);
356
void MockConnection::onCallChannelClosed()
358
qDebug() << "onCallChannelClosed()";
359
MockCallChannel *channel = static_cast<MockCallChannel*>(sender());
361
QString key = mCallChannels.key(channel);
362
qDebug() << "call channel closed for number " << key;
363
mCallChannels.remove(key);
364
mIncomingCalls.removeAll(key);
365
Q_EMIT callEnded(key);
369
void MockConnection::onCallStateChanged(MockCallChannel *channel, const QString &state)
371
const QString key = mCallChannels.key(channel);
376
Q_EMIT callStateChanged(key, channel->objectPath(), state);
379
uint MockConnection::ensureHandle(const QString &id)
381
if (mHandles.values().contains(id)) {
382
return mHandles.key(id);
384
return newHandle(id);
387
void MockConnection::placeCall(const QVariantMap &properties)
389
qDebug() << "new call" << properties;
393
QString callerId = properties["Caller"].toString();
394
QString state = properties["State"].toString();
396
if (mCallChannels.contains(callerId)) {
400
uint handle = ensureHandle(callerId);
401
uint initiatorHandle = 0;
402
if (state == "incoming" || state == "waiting") {
403
initiatorHandle = handle;
405
initiatorHandle = selfHandle();
408
qDebug() << "initiatorHandle " <<initiatorHandle;
409
qDebug() << "handle" << handle;
411
mInitialCallStatus[callerId] = state;
412
mIncomingCalls.append(callerId);
414
Tp::BaseChannelPtr channel = ensureChannel(TP_QT_IFACE_CHANNEL_TYPE_CALL, Tp::HandleTypeContact, handle, yours, initiatorHandle, false, &error);
415
if (error.isValid() || channel.isNull()) {
416
qWarning() << "error creating the channel " << error.name() << error.message();
421
void MockConnection::hangupCall(const QString &callerId)
423
if (!mCallChannels.contains(callerId)) {
427
mCallChannels[callerId]->setCallState("disconnected");
428
mIncomingCalls.removeAll(callerId);
431
void MockConnection::setCallState(const QString &phoneNumber, const QString &state)
433
if (!mCallChannels.contains(phoneNumber)) {
437
mCallChannels[phoneNumber]->setCallState(state);