Merge lp:~phablet-team/telepathy-ofono/mms_group_as_room into lp:telepathy-ofono/staging
- mms_group_as_room
- Merge into staging
Proposed by
Gustavo Pichorim Boiko
Status: | Merged |
---|---|
Approved by: | Tiago Salem Herrmann |
Approved revision: | 180 |
Merged at revision: | 178 |
Proposed branch: | lp:~phablet-team/telepathy-ofono/mms_group_as_room |
Merge into: | lp:telepathy-ofono/staging |
Prerequisite: | lp:~phablet-team/telepathy-ofono/add-targetids-initialinviteeids |
Diff against target: |
933 lines (+471/-82) 9 files modified
CMakeLists.txt (+3/-1) connection.cpp (+149/-27) connection.h (+8/-2) mmsgroupcache.cpp (+146/-0) mmsgroupcache.h (+44/-0) ofonotextchannel.cpp (+98/-47) ofonotextchannel.h (+10/-5) schema/CMakeLists.txt (+3/-0) schema/v2.sql (+10/-0) |
To merge this branch: | bzr merge lp:~phablet-team/telepathy-ofono/mms_group_as_room |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tiago Salem Herrmann (community) | Approve | ||
Gustavo Pichorim Boiko (community) | Approve | ||
Review via email: mp+308993@code.launchpad.net |
This proposal supersedes a proposal from 2016-10-08.
Commit message
Implement MMS groups as Room channels.
Description of the change
Implement MMS groups as Room channels.
To post a comment you must log in.
Revision history for this message
Tiago Salem Herrmann (tiagosh) wrote : | # |
looks good to me too.
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2015-07-20 19:38:54 +0000 |
3 | +++ CMakeLists.txt 2016-10-21 08:03:20 +0000 |
4 | @@ -33,6 +33,7 @@ |
5 | find_package(LibPhoneNumber REQUIRED) |
6 | find_package(Qt5Core) |
7 | find_package(Qt5DBus) |
8 | +find_package(Qt5Network) |
9 | add_definitions(-DQT_NO_KEYWORDS) |
10 | |
11 | find_package(PkgConfig REQUIRED) |
12 | @@ -93,6 +94,7 @@ |
13 | mmsdmanager.cpp |
14 | mmsdservice.cpp |
15 | mmsdmessage.cpp |
16 | + mmsgroupcache.cpp |
17 | pendingmessagesmanager.cpp |
18 | phoneutils.cpp |
19 | powerdaudiomodemediator.cpp |
20 | @@ -111,7 +113,7 @@ |
21 | |
22 | enable_testing() |
23 | |
24 | -target_link_libraries(${TELEPATHY_OFONO} ${Qt5Core_LIBRARIES} ${Qt5DBus_LIBRARIES} ${WAUDIO_LIBRARIES} -ltelepathy-qt5 ${TELEPATHY_QT5_SERVICE_LIBRARIES} ${OFONO_QT_LIBRARIES} ${PULSEAUDIO_LIBRARIES} ${SQLITE3_LIBRARIES} ${LibPhoneNumber_LIBRARIES}) |
25 | +target_link_libraries(${TELEPATHY_OFONO} ${Qt5Core_LIBRARIES} ${Qt5DBus_LIBRARIES} ${WAUDIO_LIBRARIES} -ltelepathy-qt5 ${TELEPATHY_QT5_SERVICE_LIBRARIES} ${Qt5Network_LIBRARIES} ${OFONO_QT_LIBRARIES} ${PULSEAUDIO_LIBRARIES} ${SQLITE3_LIBRARIES} ${LibPhoneNumber_LIBRARIES}) |
26 | install(TARGETS ${TELEPATHY_OFONO} DESTINATION ${DAEMON_DIR}) |
27 | |
28 | configure_file(ofono.service.in org.freedesktop.Telepathy.ConnectionManager.ofono.service) |
29 | |
30 | === modified file 'connection.cpp' |
31 | --- connection.cpp 2016-10-21 08:03:20 +0000 |
32 | +++ connection.cpp 2016-10-21 08:03:20 +0000 |
33 | @@ -1,5 +1,5 @@ |
34 | /** |
35 | - * Copyright (C) 2013 Canonical, Ltd. |
36 | + * Copyright (C) 2013-2016 Canonical, Ltd. |
37 | * |
38 | * This program is free software: you can redistribute it and/or modify it under |
39 | * the terms of the GNU Lesser General Public License version 3, as published by |
40 | @@ -32,6 +32,7 @@ |
41 | |
42 | #include "mmsdmessage.h" |
43 | #include "mmsdservice.h" |
44 | +#include "mmsgroupcache.h" |
45 | |
46 | #ifdef USE_PULSEAUDIO |
47 | #include "qpulseaudioengine.h" |
48 | @@ -83,6 +84,7 @@ |
49 | Tp::BaseConnection(dbusConnection, cmName, protocolName, parameters), |
50 | mOfonoModemManager(new OfonoModemManager(this)), |
51 | mHandleCount(0), |
52 | + mGroupHandleCount(0), |
53 | mMmsdManager(new MMSDManager(this)), |
54 | mConferenceCall(NULL) |
55 | { |
56 | @@ -125,6 +127,19 @@ |
57 | text.allowedProperties.append(TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE + QLatin1String(".InitialInviteeHandles")); |
58 | text.allowedProperties.append(TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE + QLatin1String(".InitialInviteeIDs")); |
59 | |
60 | + Tp::RequestableChannelClass existingGroupChat; |
61 | + existingGroupChat.fixedProperties[TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType")] = TP_QT_IFACE_CHANNEL_TYPE_TEXT; |
62 | + existingGroupChat.fixedProperties[TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandleType")] = Tp::HandleTypeRoom; |
63 | + existingGroupChat.allowedProperties.append(TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandle")); |
64 | + existingGroupChat.allowedProperties.append(TP_QT_IFACE_CHANNEL + QLatin1String(".TargetID")); |
65 | + |
66 | + Tp::RequestableChannelClass newGroupChat; |
67 | + newGroupChat.fixedProperties[TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType")] = TP_QT_IFACE_CHANNEL_TYPE_TEXT; |
68 | + newGroupChat.fixedProperties[TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandleType")] = Tp::HandleTypeNone; |
69 | + newGroupChat.allowedProperties.append(TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE + QLatin1String(".InitialInviteeHandles")); |
70 | + newGroupChat.allowedProperties.append(TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE + QLatin1String(".InitialInviteeIDs")); |
71 | + newGroupChat.allowedProperties.append(TP_QT_IFACE_CHANNEL_INTERFACE_ROOM + QLatin1String(".RoomName")); |
72 | + |
73 | // set requestable call channel properties |
74 | Tp::RequestableChannelClass call; |
75 | call.fixedProperties[TP_QT_IFACE_CHANNEL+".ChannelType"] = TP_QT_IFACE_CHANNEL_TYPE_CALL; |
76 | @@ -140,7 +155,7 @@ |
77 | call.allowedProperties.append(TP_QT_IFACE_CHANNEL_TYPE_CALL+".HardwareStreaming"); |
78 | call.allowedProperties.append(TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE + QLatin1String(".InitialChannels")); |
79 | |
80 | - requestsIface->requestableChannelClasses << text << call; |
81 | + requestsIface->requestableChannelClasses << text << call << existingGroupChat << newGroupChat; |
82 | |
83 | plugInterface(Tp::AbstractConnectionInterfacePtr::dynamicCast(requestsIface)); |
84 | |
85 | @@ -368,6 +383,16 @@ |
86 | qDebug() << "oFonoConnection::onMMSServiceRemoved" << path; |
87 | } |
88 | |
89 | +oFonoTextChannel* oFonoConnection::textChannelForId(const QString &id) |
90 | +{ |
91 | + Q_FOREACH(oFonoTextChannel* channel, mTextChannels) { |
92 | + if (channel->baseChannel()->targetID() == id) { |
93 | + return channel; |
94 | + } |
95 | + } |
96 | + return NULL; |
97 | +} |
98 | + |
99 | oFonoTextChannel* oFonoConnection::textChannelForMembers(const QStringList &members) |
100 | { |
101 | Q_FOREACH(oFonoTextChannel* channel, mTextChannels) { |
102 | @@ -398,6 +423,7 @@ |
103 | void oFonoConnection::addMMSToService(const QString &path, const QVariantMap &properties, const QString &servicePath) |
104 | { |
105 | qDebug() << "addMMSToService " << path << properties << servicePath; |
106 | + bool isRoom = false; |
107 | MMSDMessage *msg = new MMSDMessage(path, properties); |
108 | mServiceMMSList[servicePath].append(msg); |
109 | if (properties["Status"] == "received") { |
110 | @@ -409,6 +435,7 @@ |
111 | // remove empty strings if any |
112 | recipientList.removeAll(""); |
113 | if (recipientList.size() > 1) { |
114 | + isRoom = true; |
115 | // remove ourselves from the recipient list |
116 | Q_FOREACH(const QString &myNumber, mOfonoSimManager->subscriberNumbers()) { |
117 | Q_FOREACH(const QString &remoteNumber, recipientList) { |
118 | @@ -428,8 +455,19 @@ |
119 | senderNormalizedNumber = "x-ofono-unknown"; |
120 | } |
121 | |
122 | - // check if there is an open channel for this number and use it |
123 | - oFonoTextChannel *channel = textChannelForMembers(QStringList() << senderNormalizedNumber << recipients.toList()); |
124 | + oFonoTextChannel *channel = NULL; |
125 | + MMSGroup group; |
126 | + if (isRoom) { |
127 | + group = MMSGroupCache::existingGroup(QStringList() << senderNormalizedNumber << recipients.toList()); |
128 | + if (!group.groupId.isEmpty()) { |
129 | + // check if there is an open channel for this group and use it |
130 | + channel = textChannelForId(group.groupId); |
131 | + } |
132 | + } else { |
133 | + // check if there is an open channel for these numbers and use it (1-1 chat here) |
134 | + channel = textChannelForMembers(QStringList() << senderNormalizedNumber << recipients.toList()); |
135 | + } |
136 | + |
137 | if (channel) { |
138 | channel->mmsReceived(path, ensureHandle(senderNormalizedNumber), properties); |
139 | return; |
140 | @@ -445,8 +483,13 @@ |
141 | request[TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType")] = TP_QT_IFACE_CHANNEL_TYPE_TEXT; |
142 | request[TP_QT_IFACE_CHANNEL + QLatin1String(".InitiatorHandle")] = handle; |
143 | |
144 | - if (initialInviteeHandles.size() > 0) { |
145 | + if (isRoom) { |
146 | initialInviteeHandles << handle; |
147 | + request[TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandleType")] = Tp::HandleTypeRoom; |
148 | + // if the group exists, fill the targetId with the existing id |
149 | + if (!group.groupId.isEmpty()) { |
150 | + request[TP_QT_IFACE_CHANNEL + QLatin1String(".TargetID")] = group.groupId; |
151 | + } |
152 | request[TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE + QLatin1String(".InitialInviteeHandles")] = QVariant::fromValue(initialInviteeHandles); |
153 | ensureChannel(request, yours, false, &error); |
154 | } else { |
155 | @@ -455,13 +498,19 @@ |
156 | ensureChannel(request, yours, false, &error); |
157 | } |
158 | |
159 | - if(error.isValid()) { |
160 | + if (error.isValid()) { |
161 | qCritical() << "Error creating channel for incoming message " << error.name() << error.message(); |
162 | return; |
163 | } |
164 | - channel = textChannelForMembers(QStringList() << senderNormalizedNumber << recipients.toList()); |
165 | + if (isRoom) { |
166 | + channel = textChannelForId(group.groupId); |
167 | + } else { |
168 | + channel = textChannelForMembers(QStringList() << senderNormalizedNumber << recipients.toList()); |
169 | + } |
170 | if (channel) { |
171 | channel->mmsReceived(path, handle, properties); |
172 | + } else { |
173 | + qCritical() << "Failed to create channel for incoming mms" << "isRoom" << isRoom << "groupId" << group.groupId; |
174 | } |
175 | } |
176 | } |
177 | @@ -623,23 +672,42 @@ |
178 | return mHandleCount; |
179 | } |
180 | |
181 | +uint oFonoConnection::newGroupHandle(const QString &identifier) |
182 | +{ |
183 | + mGroupHandles[++mGroupHandleCount] = identifier; |
184 | + return mGroupHandleCount; |
185 | +} |
186 | + |
187 | QStringList oFonoConnection::inspectHandles(uint handleType, const Tp::UIntList& handles, Tp::DBusError *error) |
188 | { |
189 | QStringList identifiers; |
190 | |
191 | - if( handleType != Tp::HandleTypeContact ) { |
192 | + switch (handleType) { |
193 | + case Tp::HandleTypeContact: |
194 | + qDebug() << "oFonoConnection::inspectHandles contact" << handles; |
195 | + Q_FOREACH(uint handle, handles) { |
196 | + if (mHandles.keys().contains(handle)) { |
197 | + identifiers.append(mHandles.value(handle)); |
198 | + } else { |
199 | + error->set(TP_QT_ERROR_INVALID_HANDLE, "Contact handle not found"); |
200 | + return QStringList(); |
201 | + } |
202 | + } |
203 | + break; |
204 | + case Tp::HandleTypeRoom: |
205 | + qDebug() << "oFonoConnection::inspectHandles group" << handles; |
206 | + Q_FOREACH(uint handle, handles) { |
207 | + if (mGroupHandles.keys().contains(handle)) { |
208 | + identifiers.append(mGroupHandles.value(handle)); |
209 | + } else { |
210 | + error->set(TP_QT_ERROR_INVALID_HANDLE, "Group handle not found"); |
211 | + return QStringList(); |
212 | + } |
213 | + } |
214 | + break; |
215 | + default: |
216 | error->set(TP_QT_ERROR_INVALID_ARGUMENT,"Not supported"); |
217 | - return QStringList(); |
218 | - } |
219 | - |
220 | - qDebug() << "oFonoConnection::inspectHandles " << handles; |
221 | - Q_FOREACH( uint handle, handles) { |
222 | - if (mHandles.keys().contains(handle)) { |
223 | - identifiers.append(mHandles.value(handle)); |
224 | - } else { |
225 | - error->set(TP_QT_ERROR_INVALID_HANDLE, "Handle not found"); |
226 | - return QStringList(); |
227 | - } |
228 | + break; |
229 | } |
230 | qDebug() << "oFonoConnection::inspectHandles " << identifiers; |
231 | return identifiers; |
232 | @@ -676,18 +744,16 @@ |
233 | |
234 | Tp::BaseChannelPtr oFonoConnection::createTextChannel(const QVariantMap &request, Tp::DBusError *error) |
235 | { |
236 | + uint targetHandleType = request.value(TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandleType")).toUInt(); |
237 | uint targetHandle = request.value(TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandle")).toUInt(); |
238 | - const QString targetId = request.value(TP_QT_IFACE_CHANNEL + QLatin1String(".TargetID")).toString(); |
239 | + QString targetId = request.value(TP_QT_IFACE_CHANNEL + QLatin1String(".TargetID")).toString(); |
240 | + bool isRoom = request.contains(TP_QT_IFACE_CHANNEL_INTERFACE_ROOM + ".RoomName"); |
241 | |
242 | if (mSelfPresence.type != Tp::ConnectionPresenceTypeAvailable) { |
243 | error->set(TP_QT_ERROR_NETWORK_ERROR, "No network available"); |
244 | return Tp::BaseChannelPtr(); |
245 | } |
246 | |
247 | - if (!targetId.isEmpty()) { |
248 | - targetHandle = ensureHandle(targetId); |
249 | - } |
250 | - |
251 | QStringList phoneNumbers; |
252 | bool flash = false; |
253 | if (request.contains(TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE + QLatin1String(".InitialInviteeIDs"))) { |
254 | @@ -699,15 +765,63 @@ |
255 | phoneNumbers << inspectHandles(Tp::HandleTypeContact, handles, error); |
256 | } else if (request.contains(TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE + QLatin1String(".InitialInviteeHandles"))) { |
257 | phoneNumbers << inspectHandles(Tp::HandleTypeContact, qdbus_cast<Tp::UIntList>(request[TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE + QLatin1String(".InitialInviteeHandles")]), error); |
258 | - } else { |
259 | - phoneNumbers << mHandles.value(targetHandle); |
260 | + }; |
261 | + |
262 | + // if the handle type is none and RoomName is present, we should try to find an existing MMS group |
263 | + if (targetHandleType == Tp::HandleTypeNone && isRoom) { |
264 | + MMSGroup group = MMSGroupCache::existingGroup(phoneNumbers); |
265 | + if (!group.groupId.isEmpty()) { |
266 | + targetId = group.groupId; |
267 | + targetHandleType = Tp::HandleTypeRoom; |
268 | + } |
269 | + } else if (targetHandleType == Tp::HandleTypeRoom) { |
270 | + if (targetId.isEmpty()) { |
271 | + targetId = mGroupHandles.value(targetHandle); |
272 | + } |
273 | + // we got the groupId, now lookup the members and subject in the cache |
274 | + MMSGroup group = MMSGroupCache::existingGroup(targetId); |
275 | + if (group.groupId.isEmpty()) { |
276 | + error->set(TP_QT_ERROR_INVALID_HANDLE, "MMS Group not found in cache."); |
277 | + return Tp::BaseChannelPtr(); |
278 | + } |
279 | + phoneNumbers = group.members; |
280 | + isRoom = true; |
281 | + // FIXME(MMSGroup): add support for MMS group subject |
282 | + } else if (targetHandleType == Tp::HandleTypeContact && targetHandle != 0) { |
283 | + targetId = mHandles.value(targetHandle); |
284 | + } |
285 | + |
286 | + // now get the appropriate handle |
287 | + if (!targetId.isEmpty()) { |
288 | + switch (targetHandleType) { |
289 | + case Tp::HandleTypeRoom: |
290 | + targetHandle = ensureGroupHandle(targetId); |
291 | + break; |
292 | + case Tp::HandleTypeContact: |
293 | + default: |
294 | + targetHandle = ensureHandle(targetId); |
295 | + phoneNumbers << mHandles.value(targetHandle); |
296 | + break; |
297 | + } |
298 | } |
299 | |
300 | if (request.contains(TP_QT_IFACE_CHANNEL_INTERFACE_SMS + QLatin1String(".Flash"))) { |
301 | flash = request[TP_QT_IFACE_CHANNEL_INTERFACE_SMS + QLatin1String(".Flash")].toBool(); |
302 | } |
303 | |
304 | - oFonoTextChannel *channel = new oFonoTextChannel(this, phoneNumbers, flash); |
305 | + oFonoTextChannel *channel = 0; |
306 | + if (isRoom) { |
307 | + // FIXME(MMSGroup): if targetId is empty, this means this is a new group, so we need to |
308 | + // generate the group ID, probably something like "mms:<hash of member IDs>". |
309 | + // Also, we need to call MMSGroupCache::saveGroup() to save the group in the cache |
310 | + MMSGroup group; |
311 | + group.groupId = MMSGroupCache::generateId(phoneNumbers); |
312 | + group.members = phoneNumbers; |
313 | + MMSGroupCache::saveGroup(group); |
314 | + channel = new oFonoTextChannel(this, targetId, phoneNumbers); |
315 | + } else { |
316 | + channel = new oFonoTextChannel(this, QString(), phoneNumbers, flash); |
317 | + } |
318 | mTextChannels << channel; |
319 | QObject::connect(channel, SIGNAL(messageRead(QString)), SLOT(onMessageRead(QString))); |
320 | QObject::connect(channel, SIGNAL(destroyed()), SLOT(onTextChannelClosed())); |
321 | @@ -1002,6 +1116,14 @@ |
322 | return newHandle(normalizedNumber); |
323 | } |
324 | |
325 | +uint oFonoConnection::ensureGroupHandle(const QString &groupId) |
326 | +{ |
327 | + if (mGroupHandles.values().contains(groupId)) { |
328 | + return mGroupHandles.key(groupId); |
329 | + } |
330 | + return newGroupHandle(groupId); |
331 | +} |
332 | + |
333 | bool oFonoConnection::matchChannel(const Tp::BaseChannelPtr &channel, const QVariantMap &request, Tp::DBusError *error) |
334 | { |
335 | QString channelType = request[TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType")].toString(); |
336 | |
337 | === modified file 'connection.h' |
338 | --- connection.h 2015-08-10 21:06:36 +0000 |
339 | +++ connection.h 2016-10-21 08:03:20 +0000 |
340 | @@ -1,5 +1,5 @@ |
341 | /** |
342 | - * Copyright (C) 2013 Canonical, Ltd. |
343 | + * Copyright (C) 2013-2016 Canonical, Ltd. |
344 | * |
345 | * This program is free software: you can redistribute it and/or modify it under |
346 | * the terms of the GNU Lesser General Public License version 3, as published by |
347 | @@ -14,6 +14,7 @@ |
348 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
349 | * |
350 | * Authors: Tiago Salem Herrmann <tiago.herrmann@canonical.com> |
351 | + * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
352 | */ |
353 | |
354 | #ifndef OFONOCONNECTION_H |
355 | @@ -94,6 +95,7 @@ |
356 | BaseConnectionVoicemailInterfacePtr voicemailIface; |
357 | BaseConnectionUSSDInterfacePtr supplementaryServicesIface; |
358 | uint newHandle(const QString &identifier); |
359 | + uint newGroupHandle(const QString &identifier); |
360 | |
361 | OfonoMessageManager *messageManager(); |
362 | OfonoVoiceCallManager *voiceCallManager(); |
363 | @@ -101,6 +103,8 @@ |
364 | QMap<QString, oFonoCallChannel*> callChannels(); |
365 | |
366 | uint ensureHandle(const QString &phoneNumber); |
367 | + uint ensureGroupHandle(const QString &groupId); |
368 | + oFonoTextChannel* textChannelForId(const QString &id); |
369 | oFonoTextChannel* textChannelForMembers(const QStringList &members); |
370 | Tp::BaseChannelPtr createTextChannel(const QVariantMap &request, Tp::DBusError *error); |
371 | Tp::BaseChannelPtr createCallChannel(const QVariantMap &request, Tp::DBusError *error); |
372 | @@ -159,6 +163,9 @@ |
373 | void addMMSToService(const QString &path, const QVariantMap &properties, const QString &servicePath); |
374 | void ensureTextChannel(const QString &message, const QVariantMap &info, bool flash); |
375 | QMap<uint, QString> mHandles; |
376 | + uint mHandleCount; |
377 | + QMap<uint, QString> mGroupHandles; |
378 | + uint mGroupHandleCount; |
379 | |
380 | #ifdef USE_PULSEAUDIO |
381 | bool mHasPulseAudio; |
382 | @@ -177,7 +184,6 @@ |
383 | OfonoSupplementaryServices *mOfonoSupplementaryServices; |
384 | OfonoSimManager *mOfonoSimManager; |
385 | OfonoModem *mOfonoModem; |
386 | - uint mHandleCount; |
387 | Tp::SimplePresence mSelfPresence; |
388 | MMSDManager *mMmsdManager; |
389 | QMap<QString, MMSDService*> mMmsdServices; |
390 | |
391 | === added file 'mmsgroupcache.cpp' |
392 | --- mmsgroupcache.cpp 1970-01-01 00:00:00 +0000 |
393 | +++ mmsgroupcache.cpp 2016-10-21 08:03:20 +0000 |
394 | @@ -0,0 +1,146 @@ |
395 | +/** |
396 | + * Copyright (C) 2016 Canonical, Ltd. |
397 | + * |
398 | + * This program is free software: you can redistribute it and/or modify it under |
399 | + * the terms of the GNU Lesser General Public License version 3, as published by |
400 | + * the Free Software Foundation. |
401 | + * |
402 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
403 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
404 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
405 | + * Lesser General Public License for more details. |
406 | + * |
407 | + * You should have received a copy of the GNU Lesser General Public License |
408 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
409 | + * |
410 | + * Authors: Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
411 | + */ |
412 | + |
413 | +#include "mmsgroupcache.h" |
414 | +#include "phoneutils_p.h" |
415 | +#include "sqlitedatabase.h" |
416 | +#include <QSqlQuery> |
417 | +#include <QVariant> |
418 | +#include <QCryptographicHash> |
419 | + |
420 | +MMSGroupCache::MMSGroupCache(QObject *parent) : QObject(parent) |
421 | +{ |
422 | +} |
423 | + |
424 | +MMSGroup MMSGroupCache::existingGroup(const QStringList &members) |
425 | +{ |
426 | + MMSGroup group; |
427 | + if (members.isEmpty()) { |
428 | + return group; |
429 | + } |
430 | + |
431 | + // try to find all the threads which at least the first member is part of |
432 | + QString firstMember = members.first(); |
433 | + QSqlQuery query(SQLiteDatabase::instance()->database()); |
434 | + query.prepare("SELECT groupId FROM mms_group_members WHERE comparePhoneNumbers(memberId, :memberId)"); |
435 | + query.bindValue(":memberId", firstMember); |
436 | + if (!query.exec()) { |
437 | + return group; |
438 | + } |
439 | + |
440 | + QStringList groupIds; |
441 | + while (query.next()) { |
442 | + groupIds << query.value(0).toString(); |
443 | + } |
444 | + |
445 | + // now get all the groups and see if any matches all the members |
446 | + for (auto groupId : groupIds) { |
447 | + query.prepare("SELECT memberId FROM mms_group_members WHERE groupId=:groupId"); |
448 | + query.bindValue(":groupId", groupId); |
449 | + if (!query.exec()) { |
450 | + return group; |
451 | + } |
452 | + QStringList groupMembers; |
453 | + while (query.next()) { |
454 | + groupMembers << query.value(0).toString(); |
455 | + } |
456 | + |
457 | + // if the groups have a different number of members, they are certainly not the same |
458 | + if (groupMembers.count() != members.count()) { |
459 | + continue; |
460 | + } |
461 | + |
462 | + // compare the members to see if they match |
463 | + int match = 0; |
464 | + for (auto groupMember : groupMembers) { |
465 | + for (auto member : members) { |
466 | + if (PhoneUtils::comparePhoneNumbers(groupMember, member)) { |
467 | + match++; |
468 | + continue; |
469 | + } |
470 | + } |
471 | + } |
472 | + if (match == members.count()) { |
473 | + query.prepare("SELECT subject FROM mms_groups WHERE groupId=:groupId"); |
474 | + query.bindValue(":groupId", groupId); |
475 | + if (query.exec()) { |
476 | + query.next(); |
477 | + group.subject = query.value(0).toString(); |
478 | + } |
479 | + group.groupId = groupId; |
480 | + group.members = groupMembers; |
481 | + break; |
482 | + } |
483 | + } |
484 | + return group; |
485 | +} |
486 | + |
487 | +MMSGroup MMSGroupCache::existingGroup(const QString &groupId) |
488 | +{ |
489 | + MMSGroup group; |
490 | + |
491 | + // select the group to make sure it exists |
492 | + QSqlQuery query(SQLiteDatabase::instance()->database()); |
493 | + query.prepare("SELECT subject FROM mms_groups WHERE groupId=:groupId"); |
494 | + query.bindValue(":groupId", groupId); |
495 | + if (query.exec() && query.next()) { |
496 | + group.groupId = groupId; |
497 | + group.subject = query.value(0).toString(); |
498 | + query.prepare("SELECT memberId FROM mms_group_members WHERE groupId=:groupId"); |
499 | + query.bindValue(":groupId", groupId); |
500 | + if (!query.exec()) { |
501 | + return group; |
502 | + } |
503 | + |
504 | + while (query.next()) { |
505 | + group.members << query.value(0).toString(); |
506 | + } |
507 | + } |
508 | + |
509 | + return group; |
510 | +} |
511 | + |
512 | +bool MMSGroupCache::saveGroup(const MMSGroup &group) |
513 | +{ |
514 | + SQLiteDatabase::instance()->beginTransation(); |
515 | + |
516 | + QSqlQuery query(SQLiteDatabase::instance()->database()); |
517 | + query.prepare("INSERT INTO mms_groups(groupId, subject) VALUES (:groupId, :subject)"); |
518 | + query.bindValue(":groupId", group.groupId); |
519 | + query.bindValue(":subject", group.subject); |
520 | + if (!query.exec()) { |
521 | + SQLiteDatabase::instance()->rollbackTransaction(); |
522 | + return false; |
523 | + } |
524 | + for (auto member : group.members) { |
525 | + query.prepare("INSERT INTO mms_group_members(groupId, memberId) VALUES(:groupId, :memberId)"); |
526 | + query.bindValue(":groupId", group.groupId); |
527 | + query.bindValue(":memberId", member); |
528 | + if (!query.exec()) { |
529 | + SQLiteDatabase::instance()->rollbackTransaction(); |
530 | + return false; |
531 | + } |
532 | + } |
533 | + SQLiteDatabase::instance()->finishTransaction(); |
534 | + return true; |
535 | +} |
536 | + |
537 | +QString MMSGroupCache::generateId(const QStringList &phoneNumbers) |
538 | +{ |
539 | + return QString("mms:%1").arg(QString(QCryptographicHash::hash(phoneNumbers.join(";").toLocal8Bit(),QCryptographicHash::Md5).toHex())); |
540 | +} |
541 | |
542 | === added file 'mmsgroupcache.h' |
543 | --- mmsgroupcache.h 1970-01-01 00:00:00 +0000 |
544 | +++ mmsgroupcache.h 2016-10-21 08:03:20 +0000 |
545 | @@ -0,0 +1,44 @@ |
546 | +/** |
547 | + * Copyright (C) 2016 Canonical, Ltd. |
548 | + * |
549 | + * This program is free software: you can redistribute it and/or modify it under |
550 | + * the terms of the GNU Lesser General Public License version 3, as published by |
551 | + * the Free Software Foundation. |
552 | + * |
553 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
554 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
555 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
556 | + * Lesser General Public License for more details. |
557 | + * |
558 | + * You should have received a copy of the GNU Lesser General Public License |
559 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
560 | + * |
561 | + * Authors: Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
562 | + */ |
563 | + |
564 | +#ifndef MMSGROUPCACHE_H |
565 | +#define MMSGROUPCACHE_H |
566 | + |
567 | +#include <QObject> |
568 | +#include <QStringList> |
569 | + |
570 | +typedef struct { |
571 | + QString groupId; |
572 | + QString subject; |
573 | + QStringList members; |
574 | +} MMSGroup; |
575 | + |
576 | +class MMSGroupCache : public QObject |
577 | +{ |
578 | + Q_OBJECT |
579 | +public: |
580 | + static MMSGroup existingGroup(const QStringList &members); |
581 | + static MMSGroup existingGroup(const QString &groupId); |
582 | + static bool saveGroup(const MMSGroup &group); |
583 | + static QString generateId(const QStringList &phoneNumbers); |
584 | + |
585 | +private: |
586 | + explicit MMSGroupCache(QObject *parent = 0); |
587 | +}; |
588 | + |
589 | +#endif // MMSGROUPCACHE_H |
590 | |
591 | === modified file 'ofonotextchannel.cpp' |
592 | --- ofonotextchannel.cpp 2015-08-11 14:01:52 +0000 |
593 | +++ ofonotextchannel.cpp 2016-10-21 08:03:20 +0000 |
594 | @@ -1,5 +1,5 @@ |
595 | /** |
596 | - * Copyright (C) 2013 Canonical, Ltd. |
597 | + * Copyright (C) 2013-2016 Canonical, Ltd. |
598 | * |
599 | * This program is free software: you can redistribute it and/or modify it under |
600 | * the terms of the GNU Lesser General Public License version 3, as published by |
601 | @@ -14,6 +14,7 @@ |
602 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
603 | * |
604 | * Authors: Tiago Salem Herrmann <tiago.herrmann@canonical.com> |
605 | + * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
606 | */ |
607 | |
608 | // ofono-qt |
609 | @@ -40,7 +41,7 @@ |
610 | } |
611 | |
612 | |
613 | -oFonoTextChannel::oFonoTextChannel(oFonoConnection *conn, QStringList phoneNumbers, bool flash, QObject *parent): |
614 | +oFonoTextChannel::oFonoTextChannel(oFonoConnection *conn, const QString &targetId, QStringList phoneNumbers, bool flash, QObject *parent): |
615 | QObject(parent), |
616 | mConnection(conn), |
617 | mPhoneNumbers(phoneNumbers), |
618 | @@ -49,14 +50,21 @@ |
619 | { |
620 | qDBusRegisterMetaType<IncomingAttachmentStruct>(); |
621 | qDBusRegisterMetaType<IncomingAttachmentList>(); |
622 | + bool mmsGroupChat = !targetId.isEmpty(); |
623 | |
624 | Tp::BaseChannelPtr baseChannel; |
625 | - if (phoneNumbers.size() == 1) { |
626 | + if (mmsGroupChat) { |
627 | + baseChannel = Tp::BaseChannel::create(mConnection, |
628 | + TP_QT_IFACE_CHANNEL_TYPE_TEXT, |
629 | + Tp::HandleTypeRoom, |
630 | + mConnection->ensureGroupHandle(targetId)); |
631 | + } else if (phoneNumbers.size() == 1) { |
632 | baseChannel = Tp::BaseChannel::create(mConnection, |
633 | TP_QT_IFACE_CHANNEL_TYPE_TEXT, |
634 | Tp::HandleTypeContact, |
635 | mConnection->ensureHandle(mPhoneNumbers[0])); |
636 | } else { |
637 | + // sms broadcast |
638 | baseChannel = Tp::BaseChannel::create(mConnection, |
639 | TP_QT_IFACE_CHANNEL_TYPE_TEXT); |
640 | } |
641 | @@ -80,59 +88,54 @@ |
642 | |
643 | baseChannel->plugInterface(Tp::AbstractChannelInterfacePtr::dynamicCast(mMessagesIface)); |
644 | |
645 | - mGroupIface = Tp::BaseChannelGroupInterface::create(Tp::ChannelGroupFlagCanAdd, conn->selfHandle()); |
646 | - mGroupIface->setAddMembersCallback(Tp::memFun(this,&oFonoTextChannel::onAddMembers)); |
647 | - mGroupIface->setRemoveMembersCallback(Tp::memFun(this,&oFonoTextChannel::onRemoveMembers)); |
648 | + Tp::ChannelGroupFlags groupFlags = Tp::ChannelGroupFlagHandleOwnersNotAvailable | |
649 | + Tp::ChannelGroupFlagMembersChangedDetailed | |
650 | + Tp::ChannelGroupFlagProperties; |
651 | + |
652 | + mGroupIface = Tp::BaseChannelGroupInterface::create(groupFlags, mConnection->selfHandle()); |
653 | |
654 | baseChannel->plugInterface(Tp::AbstractChannelInterfacePtr::dynamicCast(mGroupIface)); |
655 | - addMembers(phoneNumbers); |
656 | + |
657 | + Q_FOREACH(const QString &phoneNumber, phoneNumbers) { |
658 | + uint handle = mConnection->ensureHandle(phoneNumber); |
659 | + if (!mMembers.contains(handle)) { |
660 | + mMembers << handle; |
661 | + } |
662 | + } |
663 | + |
664 | + mGroupIface->addMembers(mMembers, phoneNumbers); |
665 | |
666 | mSMSIface = Tp::BaseChannelSMSInterface::create(flash, true); |
667 | baseChannel->plugInterface(Tp::AbstractChannelInterfacePtr::dynamicCast(mSMSIface)); |
668 | |
669 | + // FIXME(MMSGroup): create and plug the Room interface, and maybe the subject or the |
670 | + // roomconfig interface for the subject |
671 | + |
672 | mBaseChannel = baseChannel; |
673 | mTextChannel = Tp::BaseChannelTextTypePtr::dynamicCast(mBaseChannel->interface(TP_QT_IFACE_CHANNEL_TYPE_TEXT)); |
674 | mTextChannel->setMessageAcknowledgedCallback(Tp::memFun(this,&oFonoTextChannel::messageAcknowledged)); |
675 | QObject::connect(mBaseChannel.data(), SIGNAL(closed()), this, SLOT(deleteLater())); |
676 | } |
677 | |
678 | -void oFonoTextChannel::onAddMembers(const Tp::UIntList& handles, const QString& message, Tp::DBusError* error) |
679 | -{ |
680 | - addMembers(mConnection->inspectHandles(Tp::HandleTypeContact, handles, error)); |
681 | -} |
682 | - |
683 | -void oFonoTextChannel::onRemoveMembers(const Tp::UIntList& handles, const QString& message, Tp::DBusError* error) |
684 | -{ |
685 | - Q_FOREACH(uint handle, handles) { |
686 | - Q_FOREACH(const QString &phoneNumber, mConnection->inspectHandles(Tp::HandleTypeContact, Tp::UIntList() << handle, error)) { |
687 | - mPhoneNumbers.removeAll(phoneNumber); |
688 | - } |
689 | - mMembers.removeAll(handle); |
690 | - } |
691 | - mGroupIface->removeMembers(handles); |
692 | -} |
693 | - |
694 | -void oFonoTextChannel::addMembers(QStringList phoneNumbers) |
695 | -{ |
696 | - Tp::UIntList handles; |
697 | - Q_FOREACH(const QString &phoneNumber, phoneNumbers) { |
698 | - uint handle = mConnection->ensureHandle(phoneNumber); |
699 | - handles << handle; |
700 | - if (!mPhoneNumbers.contains(phoneNumber)) { |
701 | - mPhoneNumbers << phoneNumber; |
702 | - } |
703 | - if (!mMembers.contains(handle)) { |
704 | - mMembers << handle; |
705 | - } |
706 | - } |
707 | - mGroupIface->addMembers(handles, phoneNumbers); |
708 | -} |
709 | - |
710 | Tp::UIntList oFonoTextChannel::members() |
711 | { |
712 | return mMembers; |
713 | } |
714 | |
715 | + |
716 | +bool oFonoTextChannel::isMultiPartMessage(const Tp::MessagePartList &message) const |
717 | +{ |
718 | + // multi-part messages happen in two cases: |
719 | + // - if it has more than two parts |
720 | + // - if the second part (the first is the header) is not text |
721 | + if (message.size() > 2 || |
722 | + (message.size() == 2 && !message[1]["content-type"].variant().toString().startsWith("text/"))) { |
723 | + return true; |
724 | + } |
725 | + |
726 | + return false; |
727 | +} |
728 | + |
729 | oFonoTextChannel::~oFonoTextChannel() |
730 | { |
731 | Q_FOREACH(const QStringList &fileList, mFilesToRemove) { |
732 | @@ -177,13 +180,14 @@ |
733 | Tp::MessagePart body = message.at(1); |
734 | QString objpath; |
735 | |
736 | - bool mms = header["x-canonical-mms"].variant().toBool(); |
737 | + bool isRoom = baseChannel()->targetHandleType() == Tp::HandleTypeRoom; |
738 | + bool isMMS = isRoom || isMultiPartMessage(message); |
739 | |
740 | - if (mms) { |
741 | + // any mms, either 1-1, group or broadcast |
742 | + if (isMMS || isRoom) { |
743 | // pop header out |
744 | message.removeFirst(); |
745 | OutgoingAttachmentList attachments; |
746 | - // FIXME group chat |
747 | QString phoneNumber = mPhoneNumbers[0]; |
748 | uint handle = mConnection->ensureHandle(phoneNumber); |
749 | QStringList temporaryFiles; |
750 | @@ -218,6 +222,23 @@ |
751 | attachment.filePath = file.fileName(); |
752 | attachments << attachment; |
753 | } |
754 | + // if this is a broadcast, send multiple mms |
755 | + if (!isRoom) { |
756 | + // generate an id to this broadcast operation and its delivery reports |
757 | + objpath = QDateTime::currentDateTimeUtc().toString(Qt::ISODate) + "-" + QString::number(mMessageCounter++); |
758 | + Q_FOREACH(const QString &phoneNumber, mPhoneNumbers) { |
759 | + QString realObjpath = mConnection->sendMMS(QStringList() << phoneNumber, attachments).path(); |
760 | + MMSDMessage *msg = new MMSDMessage(realObjpath, QVariantMap(), this); |
761 | + QObject::connect(msg, SIGNAL(propertyChanged(QString,QVariant)), SLOT(onMMSPropertyChanged(QString,QVariant))); |
762 | + mPendingBroadcastMMS[realObjpath] = objpath; |
763 | + mPendingDeliveryReportUnknown[objpath] = handle; |
764 | + QTimer::singleShot(0, this, SLOT(onProcessPendingDeliveryReport())); |
765 | + } |
766 | + if (temporaryFiles.size() > 0 && !mFilesToRemove.contains(objpath)) { |
767 | + mFilesToRemove[objpath] = temporaryFiles; |
768 | + } |
769 | + return objpath; |
770 | + } |
771 | objpath = mConnection->sendMMS(mPhoneNumbers, attachments).path(); |
772 | if (objpath.isEmpty()) { |
773 | Q_FOREACH(const QString& file, temporaryFiles) { |
774 | @@ -241,6 +262,7 @@ |
775 | return objpath; |
776 | } |
777 | |
778 | + // 1-1 sms |
779 | if (mPhoneNumbers.size() == 1) { |
780 | QString phoneNumber = mPhoneNumbers[0]; |
781 | uint handle = mConnection->ensureHandle(phoneNumber); |
782 | @@ -270,13 +292,13 @@ |
783 | QObject::connect(msg, SIGNAL(stateChanged(QString)), SLOT(onOfonoMessageStateChanged(QString))); |
784 | return objpath; |
785 | } else { |
786 | + // Broadcast sms |
787 | bool someMessageSent = false; |
788 | QString lastPhoneNumber; |
789 | Q_FOREACH(const QString &phoneNumber, mPhoneNumbers) { |
790 | - uint handle = mConnection->ensureHandle(mPhoneNumbers[0]); |
791 | objpath = mConnection->messageManager()->sendMessage(phoneNumber, body["content"].variant().toString(), success).path(); |
792 | lastPhoneNumber = phoneNumber; |
793 | - // dont fail if this is a group chat as we cannot track individual messages |
794 | + // dont fail if this is a broadcast chat as we cannot track individual messages |
795 | if (objpath.isEmpty() || !success) { |
796 | if (!success) { |
797 | qWarning() << mConnection->messageManager()->errorName() << mConnection->messageManager()->errorMessage(); |
798 | @@ -313,12 +335,14 @@ |
799 | void oFonoTextChannel::onMMSPropertyChanged(QString property, QVariant value) |
800 | { |
801 | qDebug() << "oFonoTextChannel::onMMSPropertyChanged" << property << value; |
802 | + bool canRemoveFiles = true; |
803 | MMSDMessage *msg = qobject_cast<MMSDMessage*>(sender()); |
804 | // FIXME - mms groupchat |
805 | uint handle = mConnection->ensureHandle(mPhoneNumbers[0]); |
806 | if (!msg) { |
807 | return; |
808 | } |
809 | + QString objectPath = msg->path(); |
810 | if (property == "Status") { |
811 | Tp::DeliveryStatus status = Tp::DeliveryStatusUnknown; |
812 | if (value == "Sent") { |
813 | @@ -331,11 +355,38 @@ |
814 | // while it is draft we dont actually send a delivery report |
815 | return; |
816 | } |
817 | - Q_FOREACH(const QString& file, mFilesToRemove[msg->path()]) { |
818 | + if (mPendingBroadcastMMS.contains(objectPath)) { |
819 | + // if this is the last outstanding mms, we can now remove the files |
820 | + objectPath = mPendingBroadcastMMS.take(objectPath); |
821 | + QStringList originalObjPaths = mPendingBroadcastMMS.keys(objectPath); |
822 | + canRemoveFiles = originalObjPaths.size() == 0; |
823 | + |
824 | + if (status == Tp::DeliveryStatusAccepted) { |
825 | + // if we get at least one sucess, we notify sucess no matter if the others fail |
826 | + mPendingBroadcastFinalResult[objectPath] = true; |
827 | + } |
828 | + |
829 | + if (canRemoveFiles) { |
830 | + if (mPendingBroadcastFinalResult[objectPath]) { |
831 | + status = Tp::DeliveryStatusAccepted; |
832 | + } else { |
833 | + status = Tp::DeliveryStatusPermanentlyFailed; |
834 | + } |
835 | + Q_FOREACH(const QString& file, mFilesToRemove[objectPath]) { |
836 | + QFile::remove(file); |
837 | + } |
838 | + mFilesToRemove.remove(objectPath); |
839 | + sendDeliveryReport(objectPath, handle, status); |
840 | + mPendingBroadcastFinalResult.remove(objectPath); |
841 | + } |
842 | + return; |
843 | + } |
844 | + |
845 | + Q_FOREACH(const QString& file, mFilesToRemove[objectPath]) { |
846 | QFile::remove(file); |
847 | } |
848 | - mFilesToRemove.remove(msg->path()); |
849 | - sendDeliveryReport(msg->path(), handle, status); |
850 | + mFilesToRemove.remove(objectPath); |
851 | + sendDeliveryReport(objectPath, handle, status); |
852 | } |
853 | } |
854 | |
855 | |
856 | === modified file 'ofonotextchannel.h' |
857 | --- ofonotextchannel.h 2014-07-24 20:05:08 +0000 |
858 | +++ ofonotextchannel.h 2016-10-21 08:03:20 +0000 |
859 | @@ -1,5 +1,5 @@ |
860 | /** |
861 | - * Copyright (C) 2013 Canonical, Ltd. |
862 | + * Copyright (C) 2013-2016 Canonical, Ltd. |
863 | * |
864 | * This program is free software: you can redistribute it and/or modify it under |
865 | * the terms of the GNU Lesser General Public License version 3, as published by |
866 | @@ -14,6 +14,7 @@ |
867 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
868 | * |
869 | * Authors: Tiago Salem Herrmann <tiago.herrmann@canonical.com> |
870 | + * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
871 | */ |
872 | |
873 | #ifndef OFONOTEXTCHANNEL_H |
874 | @@ -34,7 +35,9 @@ |
875 | { |
876 | Q_OBJECT |
877 | public: |
878 | - oFonoTextChannel(oFonoConnection *conn, QStringList phoneNumbers, bool flash = false, QObject *parent = 0); |
879 | + /** @brief Constructs a text channel to be used in 1-1 or SMS broadcast conversations */ |
880 | + oFonoTextChannel(oFonoConnection *conn, const QString &targetId, QStringList phoneNumbers, bool flash = false, QObject *parent = 0); |
881 | + |
882 | QString sendMessage(Tp::MessagePartList message, uint flags, Tp::DBusError* error); |
883 | void messageReceived(const QString & message, uint handle, const QVariantMap &info); |
884 | Tp::BaseChannelPtr baseChannel(); |
885 | @@ -42,10 +45,10 @@ |
886 | void mmsReceived(const QString &id, uint handle, const QVariantMap &properties); |
887 | void deliveryReportReceived(const QString& messageId, uint handle, bool success); |
888 | void sendDeliveryReport(const QString &messageId, uint handle, Tp::DeliveryStatus status); |
889 | - void addMembers(QStringList phoneNumbers); |
890 | Tp::UIntList members(); |
891 | - void onAddMembers(const Tp::UIntList& handles, const QString& message, Tp::DBusError* error); |
892 | - void onRemoveMembers(const Tp::UIntList& handles, const QString& message, Tp::DBusError* error); |
893 | + |
894 | +protected: |
895 | + bool isMultiPartMessage(const Tp::MessagePartList &message) const; |
896 | |
897 | private Q_SLOTS: |
898 | void onMMSPropertyChanged(QString property, QVariant value); |
899 | @@ -70,6 +73,8 @@ |
900 | QMap<QString, uint> mPendingDeliveryReportAccepted; |
901 | QMap<QString, uint> mPendingDeliveryReportDelivered; |
902 | QMap<QString, uint> mPendingDeliveryReportUnknown; |
903 | + QMap<QString, QString> mPendingBroadcastMMS; |
904 | + QMap<QString, bool> mPendingBroadcastFinalResult; |
905 | Tp::UIntList mMembers; |
906 | QMap<QString, QStringList> mFilesToRemove; |
907 | bool mFlash; |
908 | |
909 | === modified file 'schema/CMakeLists.txt' |
910 | --- schema/CMakeLists.txt 2013-12-11 14:52:03 +0000 |
911 | +++ schema/CMakeLists.txt 2016-10-21 08:03:20 +0000 |
912 | @@ -11,3 +11,6 @@ |
913 | ) |
914 | |
915 | add_custom_target(schema_update DEPENDS ${SCHEMA_FILE} ${VERSION_FILE}) |
916 | + |
917 | +# just to get the v*sql files to show on QtCreator |
918 | +add_custom_target(schema_files_target ALL SOURCES ${SCHEMA_FILES}) |
919 | |
920 | === added file 'schema/v2.sql' |
921 | --- schema/v2.sql 1970-01-01 00:00:00 +0000 |
922 | +++ schema/v2.sql 2016-10-21 08:03:20 +0000 |
923 | @@ -0,0 +1,10 @@ |
924 | +CREATE TABLE mms_groups ( |
925 | + groupId varchar(255) PRIMARY KEY, |
926 | + subject varchar(512) |
927 | +); |
928 | + |
929 | +CREATE TABLE mms_group_members ( |
930 | + groupId varchar(255), |
931 | + memberId varchar(255), |
932 | + FOREIGN KEY(groupId) REFERENCES mms_groups(groupId) |
933 | +); |
Looks good!