Merge lp:~phablet-team/telephony-service/async_send_message into lp:telephony-service

Proposed by Gustavo Pichorim Boiko
Status: Superseded
Proposed branch: lp:~phablet-team/telephony-service/async_send_message
Merge into: lp:telephony-service
Prerequisite: lp:~phablet-team/telephony-service/group-chat
Diff against target: 3029 lines (+1526/-851)
26 files modified
.bzrignore (+2/-0)
Ubuntu/Telephony/components.cpp (+1/-1)
handler/CMakeLists.txt (+5/-0)
handler/ChatStartingJob.xml (+31/-0)
handler/Handler.xml (+2/-1)
handler/MessageSendingJob.xml (+36/-0)
handler/chatstartingjob.cpp (+171/-0)
handler/chatstartingjob.h (+69/-0)
handler/handlerdbus.cpp (+22/-2)
handler/handlerdbus.h (+7/-2)
handler/main.cpp (+1/-3)
handler/messagejob.cpp (+96/-0)
handler/messagejob.h (+75/-0)
handler/messagesendingjob.cpp (+390/-0)
handler/messagesendingjob.h (+90/-0)
handler/texthandler.cpp (+11/-438)
handler/texthandler.h (+4/-16)
libtelephonyservice/chatentry.cpp (+253/-78)
libtelephonyservice/chatentry.h (+51/-17)
libtelephonyservice/chatmanager.cpp (+132/-198)
libtelephonyservice/chatmanager.h (+9/-22)
libtelephonyservice/telepathyhelper.cpp (+6/-3)
libtelephonyservice/telepathyhelper.h (+2/-2)
tests/handler/HandlerTest.cpp (+40/-17)
tests/libtelephonyservice/ChatEntryTest.cpp (+2/-0)
tests/libtelephonyservice/ChatManagerTest.cpp (+18/-51)
To merge this branch: bzr merge lp:~phablet-team/telephony-service/async_send_message
Reviewer Review Type Date Requested Status
system-apps-ci-bot continuous-integration Approve
PS Jenkins bot continuous-integration Pending
Ubuntu Phablet Team Pending
Review via email: mp+295529@code.launchpad.net

This proposal supersedes a proposal from 2016-05-24.

This proposal has been superseded by a proposal from 2016-10-13.

Commit message

Make it possible to send messages asynchronously and still report the results.

Description of the change

Make it possible to send messages asynchronously and still report the results.

To post a comment you must log in.
Revision history for this message
system-apps-ci-bot (system-apps-ci-bot) wrote :

FAILED: Continuous integration, rev:1217
https://jenkins.canonical.com/system-apps/job/lp-telephony-service-ci/2/
Executed test runs:
    FAILURE: https://jenkins.canonical.com/system-apps/job/build/521/console
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-0-fetch/521
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-1-sourcepkg/release=vivid+overlay/514
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-1-sourcepkg/release=xenial/514
    FAILURE: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=amd64,release=vivid+overlay/509/console
    FAILURE: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=amd64,release=xenial/509/console
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=armhf,release=vivid+overlay/509
        deb: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=armhf,release=vivid+overlay/509/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=armhf,release=xenial/509
        deb: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=armhf,release=xenial/509/artifact/output/*zip*/output.zip
    FAILURE: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=i386,release=vivid+overlay/509/console
    FAILURE: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=i386,release=xenial/509/console

Click here to trigger a rebuild:
https://jenkins.canonical.com/system-apps/job/lp-telephony-service-ci/2/rebuild

review: Needs Fixing (continuous-integration)
1218. By Gustavo Pichorim Boiko

Set also the threadId in properties so that the channel gets
created correctly.

Revision history for this message
system-apps-ci-bot (system-apps-ci-bot) wrote :

FAILED: Continuous integration, rev:1218
https://jenkins.canonical.com/system-apps/job/lp-telephony-service-ci/9/
Executed test runs:
    FAILURE: https://jenkins.canonical.com/system-apps/job/build/532/console
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-0-fetch/532
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-1-sourcepkg/release=vivid+overlay/523
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-1-sourcepkg/release=xenial/523
    FAILURE: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=amd64,release=vivid+overlay/519/console
    FAILURE: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=amd64,release=xenial/519/console
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=armhf,release=vivid+overlay/519
        deb: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=armhf,release=vivid+overlay/519/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=armhf,release=xenial/519
        deb: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=armhf,release=xenial/519/artifact/output/*zip*/output.zip
    FAILURE: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=i386,release=vivid+overlay/519/console
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=i386,release=xenial/519
        deb: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=i386,release=xenial/519/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/system-apps/job/lp-telephony-service-ci/9/rebuild

review: Needs Fixing (continuous-integration)
1219. By Gustavo Pichorim Boiko

If no accountId is given, match channels from all accounts.

1220. By Gustavo Pichorim Boiko

Only set the accountId in properties if we are trying to match a chatroom

Revision history for this message
system-apps-ci-bot (system-apps-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
1221. By Gustavo Pichorim Boiko

We don't need to verify accounts here, it is done in a later stage if needed

Revision history for this message
system-apps-ci-bot (system-apps-ci-bot) wrote :

FAILED: Continuous integration, rev:1221
https://jenkins.canonical.com/system-apps/job/lp-telephony-service-ci/18/
Executed test runs:
    FAILURE: https://jenkins.canonical.com/system-apps/job/build/560/console
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-0-fetch/560
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-1-sourcepkg/release=vivid+overlay/548
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-1-sourcepkg/release=xenial/548
    FAILURE: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=amd64,release=vivid+overlay/543/console
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=amd64,release=xenial/543
        deb: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=amd64,release=xenial/543/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=armhf,release=vivid+overlay/543
        deb: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=armhf,release=vivid+overlay/543/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=armhf,release=xenial/543
        deb: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=armhf,release=xenial/543/artifact/output/*zip*/output.zip
    FAILURE: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=i386,release=vivid+overlay/543/console
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=i386,release=xenial/543
        deb: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=i386,release=xenial/543/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/system-apps/job/lp-telephony-service-ci/18/rebuild

review: Needs Fixing (continuous-integration)
1222. By Gustavo Pichorim Boiko

merge parent

Revision history for this message
system-apps-ci-bot (system-apps-ci-bot) wrote :

FAILED: Continuous integration, rev:1222
https://jenkins.canonical.com/system-apps/job/lp-telephony-service-ci/22/
Executed test runs:
    FAILURE: https://jenkins.canonical.com/system-apps/job/build/611/console
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-0-fetch/611
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-1-sourcepkg/release=vivid+overlay/581
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-1-sourcepkg/release=xenial/581
    FAILURE: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=amd64,release=vivid+overlay/574/console
    FAILURE: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=amd64,release=xenial/574/console
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=armhf,release=vivid+overlay/574
        deb: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=armhf,release=vivid+overlay/574/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=armhf,release=xenial/574
        deb: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=armhf,release=xenial/574/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=i386,release=vivid+overlay/574
        deb: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=i386,release=vivid+overlay/574/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=i386,release=xenial/574
        deb: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=i386,release=xenial/574/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/system-apps/job/lp-telephony-service-ci/22/rebuild

review: Needs Fixing (continuous-integration)
1223. By Gustavo Pichorim Boiko

Do not delete the interface classes. They are destroyed together with the channel itself.

Revision history for this message
system-apps-ci-bot (system-apps-ci-bot) wrote :

PASSED: Continuous integration, rev:1223
https://jenkins.canonical.com/system-apps/job/lp-telephony-service-ci/25/
Executed test runs:
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build/620
    SUCCESS: https://jenkins.canonical.com/system-apps/job/test-0-autopkgtest/label=phone-armhf,release=vivid+overlay,testname=default/80
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-0-fetch/620
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-1-sourcepkg/release=vivid+overlay/590
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-1-sourcepkg/release=xenial/590
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=amd64,release=vivid+overlay/583
        deb: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=amd64,release=vivid+overlay/583/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=amd64,release=xenial/583
        deb: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=amd64,release=xenial/583/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=armhf,release=vivid+overlay/583
        deb: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=armhf,release=vivid+overlay/583/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=armhf,release=xenial/583
        deb: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=armhf,release=xenial/583/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=i386,release=vivid+overlay/583
        deb: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=i386,release=vivid+overlay/583/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=i386,release=xenial/583
        deb: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=i386,release=xenial/583/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/system-apps/job/lp-telephony-service-ci/25/rebuild

review: Approve (continuous-integration)
1224. By Gustavo Pichorim Boiko

Update copyright headers.

1225. By Gustavo Pichorim Boiko

Merge parent

1226. By Gustavo Pichorim Boiko

Expose chatstarting to dbus

1227. By Gustavo Pichorim Boiko

Make startChat return the job's objectPath

1228. By Gustavo Pichorim Boiko

Remove wrongly committed changelog.

1229. By Tiago Salem Herrmann

merge parent branch

1230. By Tiago Salem Herrmann

merge parent branch

1231. By Tiago Salem Herrmann

merge parent branch

1232. By Gustavo Pichorim Boiko

Handle channel invalidation.

1233. By Tiago Salem Herrmann

merge parent branch

1234. By Tiago Salem Herrmann

merge parent branch

1235. By Tiago Salem Herrmann

merge parent branch

1236. By Tiago Salem Herrmann

merge parent branch

1237. By Tiago Salem Herrmann

fix handler test

1238. By Tiago Salem Herrmann

merge parent branch

1239. By Tiago Salem Herrmann

merge parent branch

1240. By Gustavo Pichorim Boiko

Add a FIXME on a disabled test

1241. By Gustavo Pichorim Boiko

Merge parent

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2015-07-02 03:35:20 +0000
3+++ .bzrignore 2016-10-06 19:34:37 +0000
4@@ -30,6 +30,7 @@
5 approver/*.service
6 approver/approveradaptor.*
7 handler/handleradaptor.*
8+handler/messagesendingjobadaptor.*
9 indicator/*.desktop
10 indicator/*.service
11 indicator/NotificationsInterface.*
12@@ -57,3 +58,4 @@
13 tests/libtelephonyservice/GreeterContactsTestExe
14 tests/libtelephonyservice/GreeterContactsTestServerExe
15 tests/libtelephonyservice/*Mock
16+tests/indicator/NotificationsInterface.*
17
18=== modified file 'Ubuntu/Telephony/components.cpp'
19--- Ubuntu/Telephony/components.cpp 2015-11-19 12:59:31 +0000
20+++ Ubuntu/Telephony/components.cpp 2016-10-06 19:34:37 +0000
21@@ -75,12 +75,12 @@
22 // @uri Telephony
23 qmlRegisterUncreatableType<TelepathyHelper>(uri, 0, 1, "TelepathyHelper", "This is a singleton helper class");
24 qmlRegisterUncreatableType<CallEntry>(uri, 0, 1, "CallEntry", "Objects of this type are created in CallManager and made available to QML for usage");
25- qmlRegisterUncreatableType<ChatEntry>(uri, 0, 1, "ChatEntry", "Objects of this type are created in ChatManager and made available to QML for usage");
26 qmlRegisterUncreatableType<ContactChatState>(uri, 0, 1, "ContactChatState", "Objects of this type are created in ChatEntry and made available to QML");
27 qmlRegisterUncreatableType<AudioOutput>(uri, 0, 1, "AudioOutput", "Objects of this type are created in CallEntry and made available to QML for usage");
28 qmlRegisterUncreatableType<AccountEntry>(uri, 0, 1, "AccountEntry", "Objects of this type are created in TelepathyHelper and made available to QML");
29 qmlRegisterUncreatableType<USSDManager>(uri, 0, 1, "USSDManager", "Objects of this type are created in AccountEntry and made available to QML");
30 qmlRegisterUncreatableType<Protocol>(uri, 0, 1, "ProtocolManager", "Objects of this type are created in ProtocolManager and made available to QML");
31+ qmlRegisterType<ChatEntry>(uri, 0, 1, "ChatEntry");
32 qmlRegisterType<ContactWatcher>(uri, 0, 1, "ContactWatcher");
33 qmlRegisterType<PresenceRequest>(uri, 0, 1, "PresenceRequest");
34 qmlRegisterType<PhoneUtils>(uri, 0, 1, "PhoneUtils");
35
36=== modified file 'handler/CMakeLists.txt'
37--- handler/CMakeLists.txt 2016-03-18 19:02:50 +0000
38+++ handler/CMakeLists.txt 2016-10-06 19:34:37 +0000
39@@ -1,13 +1,18 @@
40
41 set(qt_SRCS
42 callhandler.cpp
43+ chatstartingjob.cpp
44 handler.cpp
45 handlerdbus.cpp
46+ messagejob.cpp
47+ messagesendingjob.cpp
48 texthandler.cpp
49 )
50
51 set(handler_SRCS main.cpp ${qt_SRCS})
52 qt5_add_dbus_adaptor(handler_SRCS Handler.xml handler/handlerdbus.h HandlerDBus)
53+qt5_add_dbus_adaptor(handler_SRCS ChatStartingJob.xml handler/chatstartingjob.h ChatStartingJob)
54+qt5_add_dbus_adaptor(handler_SRCS MessageSendingJob.xml handler/messagesendingjob.h MessageSendingJob)
55
56 include_directories(
57 ${TP_QT5_INCLUDE_DIRS}
58
59=== added file 'handler/ChatStartingJob.xml'
60--- handler/ChatStartingJob.xml 1970-01-01 00:00:00 +0000
61+++ handler/ChatStartingJob.xml 2016-10-06 19:34:37 +0000
62@@ -0,0 +1,31 @@
63+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
64+<node xmlns:dox="http://www.ayatana.org/dbus/dox.dtd">
65+ <dox:d><![CDATA[
66+ @mainpage
67+
68+ An interface to the asynchronous chat creation job
69+ ]]></dox:d>
70+ <interface name="com.canonical.TelephonyServiceHandler.ChatStartingJob" xmlns:dox="http://www.ayatana.org/dbus/dox.dtd">
71+ <dox:d>
72+ An interface to the phone handler helper application.
73+ </dox:d>
74+ <property name="accountId" type="s" access="read"/>
75+ <property name="channelObjectPath" type="s" access="read"/>
76+ <property name="objectPath" type="s" access="read"/>
77+ <property name="properties" type="a{sv}" access="read">
78+ <annotation name="org.qtproject.QtDBus.QtTypeName" value="QVariantMap"/>
79+ </property>
80+ <property name="status" type="i" access="read"/>
81+ <property name="isFinished" type="b" access="read"/>
82+ <signal name="channelObjectPathChanged">
83+ </signal>
84+ <signal name="statusChanged">
85+ </signal>
86+ <signal name="isFinishedChanged">
87+ </signal>
88+ <signal name="finished">
89+ </signal>
90+ <method name="startJob">
91+ </method>
92+ </interface>
93+</node>
94
95=== modified file 'handler/Handler.xml'
96--- handler/Handler.xml 2016-10-06 19:34:36 +0000
97+++ handler/Handler.xml 2016-10-06 19:34:37 +0000
98@@ -18,10 +18,10 @@
99 <arg name="accountId" type="s" direction="in"/>
100 <arg name="message" type="s" direction="in"/>
101 <arg name="attachments" type="a(sss)" direction="in"/>
102- <arg name="accountIdOut" type="s" direction="out"/>
103 <annotation name="org.qtproject.QtDBus.QtTypeName.In2" value="AttachmentList"/>
104 <arg name="properties" type="a{sv}" direction="in"/>
105 <annotation name="org.qtproject.QtDBus.QtTypeName.In3" value="QVariantMap"/>
106+ <arg name="objectPath" type="s" direction="out"/>
107 </method>
108 <method name="AcknowledgeMessages">
109 <dox:d><![CDATA[
110@@ -37,6 +37,7 @@
111 <arg name="accountId" type="s" direction="in"/>
112 <arg name="properties" type="a{sv}" direction="in"/>
113 <annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="QVariantMap"/>
114+ <arg name="objectPath" type="s" direction="out"/>
115 </method>
116 <method name="AcknowledgeAllMessages">
117 <dox:d><![CDATA[
118
119=== added file 'handler/MessageSendingJob.xml'
120--- handler/MessageSendingJob.xml 1970-01-01 00:00:00 +0000
121+++ handler/MessageSendingJob.xml 2016-10-06 19:34:37 +0000
122@@ -0,0 +1,36 @@
123+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
124+<node xmlns:dox="http://www.ayatana.org/dbus/dox.dtd">
125+ <dox:d><![CDATA[
126+ @mainpage
127+
128+ An interface to the asynchronous message sending job
129+ ]]></dox:d>
130+ <interface name="com.canonical.TelephonyServiceHandler.MessageSendingJob" xmlns:dox="http://www.ayatana.org/dbus/dox.dtd">
131+ <dox:d>
132+ An interface to the phone handler helper application.
133+ </dox:d>
134+ <property name="accountId" type="s" access="read"/>
135+ <property name="messageId" type="s" access="read"/>
136+ <property name="channelObjectPath" type="s" access="read"/>
137+ <property name="objectPath" type="s" access="read"/>
138+ <property name="properties" type="a{sv}" access="read">
139+ <annotation name="org.qtproject.QtDBus.QtTypeName" value="QVariantMap"/>
140+ </property>
141+ <property name="status" type="i" access="read"/>
142+ <property name="isFinished" type="b" access="read"/>
143+ <signal name="accountIdChanged">
144+ </signal>
145+ <signal name="messageIdChanged">
146+ </signal>
147+ <signal name="channelObjectPathChanged">
148+ </signal>
149+ <signal name="statusChanged">
150+ </signal>
151+ <signal name="isFinishedChanged">
152+ </signal>
153+ <signal name="finished">
154+ </signal>
155+ <method name="startJob">
156+ </method>
157+ </interface>
158+</node>
159
160=== added file 'handler/chatstartingjob.cpp'
161--- handler/chatstartingjob.cpp 1970-01-01 00:00:00 +0000
162+++ handler/chatstartingjob.cpp 2016-10-06 19:34:37 +0000
163@@ -0,0 +1,171 @@
164+/*
165+ * Copyright (C) 2016 Canonical, Ltd.
166+ *
167+ * Authors:
168+ * Tiago Salem Herrmann <tiago.herrmann@canonical.com>
169+ * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
170+ *
171+ * This file is part of telephony-service.
172+ *
173+ * telephony-service is free software; you can redistribute it and/or modify
174+ * it under the terms of the GNU General Public License as published by
175+ * the Free Software Foundation; version 3.
176+ *
177+ * telephony-service is distributed in the hope that it will be useful,
178+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
179+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
180+ * GNU General Public License for more details.
181+ *
182+ * You should have received a copy of the GNU General Public License
183+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
184+ */
185+
186+#include "chatstartingjob.h"
187+#include "chatstartingjobadaptor.h"
188+#include "telepathyhelper.h"
189+#include "texthandler.h"
190+#include <TelepathyQt/PendingChannelRequest>
191+
192+ChatStartingJob::ChatStartingJob(TextHandler *textHandler, const QString &accountId, const QVariantMap &properties)
193+: MessageJob(new ChatStartingJobAdaptor(this), textHandler), mTextHandler(textHandler), mAccountId(accountId), mProperties(properties)
194+{
195+ qDebug() << __PRETTY_FUNCTION__;
196+ connect(this, &ChatStartingJob::textChannelChanged, &ChatStartingJob::channelObjectPathChanged);
197+}
198+
199+QString ChatStartingJob::accountId()
200+{
201+ return mAccountId;
202+}
203+
204+void ChatStartingJob::startJob()
205+{
206+ qDebug() << __PRETTY_FUNCTION__;
207+ setStatus(Running);
208+
209+ // Request the contact to start chatting to
210+ // FIXME: make it possible to select which account to use, for now, pick the first one
211+ AccountEntry *account = TelepathyHelper::instance()->accountForId(mAccountId);
212+ if (!account || !account->connected()) {
213+ qCritical() << "The selected account does not have a connection. AccountId:" << mAccountId;
214+ setStatus(Failed);
215+ scheduleDeletion();
216+ return;
217+ }
218+
219+ switch(mProperties["chatType"].toUInt()) {
220+ case Tp::HandleTypeNone:
221+ case Tp::HandleTypeContact:
222+ startTextChat(account->account(), mProperties);
223+ break;
224+ case Tp::HandleTypeRoom:
225+ startTextChatRoom(account->account(), mProperties);
226+ break;
227+ default:
228+ qCritical() << "Chat type not supported";
229+ }
230+}
231+
232+void ChatStartingJob::startTextChat(const Tp::AccountPtr &account, const QVariantMap &properties)
233+{
234+ qDebug() << __PRETTY_FUNCTION__;
235+ Tp::PendingChannelRequest *op = NULL;
236+ QStringList participants = properties["participantIds"].toStringList();
237+ switch(participants.size()) {
238+ case 0:
239+ qCritical() << "Error: No participant list provided";
240+ break;
241+ case 1:
242+ op = account->ensureTextChat(participants[0], QDateTime::currentDateTime(), TP_QT_IFACE_CLIENT + ".TelephonyServiceHandler");
243+ break;
244+ default:
245+ op = account->createConferenceTextChat(QList<Tp::ChannelPtr>(), participants, QDateTime::currentDateTime(), TP_QT_IFACE_CLIENT + ".TelephonyServiceHandler");
246+ }
247+
248+ if (!op) {
249+ setStatus(Failed);
250+ scheduleDeletion();
251+ return;
252+ }
253+
254+ connect(op, &Tp::PendingOperation::finished,
255+ this, &ChatStartingJob::onChannelRequestFinished);
256+}
257+
258+void ChatStartingJob::startTextChatRoom(const Tp::AccountPtr &account, const QVariantMap &properties)
259+{
260+ qDebug() << __PRETTY_FUNCTION__;
261+ QString roomName = properties["threadId"].toString();
262+
263+ // these properties are still not used
264+ //QString server = properties["Server"].toString();
265+ //QString creator = properties["Creator"].toString();
266+
267+ QVariantMap request;
268+ Tp::PendingChannelRequest *op = NULL;
269+ if (roomName.isEmpty()) {
270+ request.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType"), TP_QT_IFACE_CHANNEL_TYPE_TEXT);
271+ request.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandleType"), (uint) Tp::HandleTypeNone);
272+ QStringList initialInviteeIDs = properties["participantIds"].toStringList();
273+ if (!initialInviteeIDs.isEmpty()) {
274+ request.insert(TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE + QLatin1String(".InitialInviteeIDs"), initialInviteeIDs);
275+ }
276+ // the presence of RoomName indicates the returned channel must be of type Room
277+ request.insert(TP_QT_IFACE_CHANNEL_INTERFACE_ROOM + QLatin1String(".RoomName"), QString());
278+
279+ // TODO use the instance returned by createChanne() to track when the channel creation is finished
280+ op = account->createChannel(request, QDateTime::currentDateTime(), TP_QT_IFACE_CLIENT + ".TelephonyServiceHandler");
281+ } else {
282+ op = account->ensureTextChatroom(roomName, QDateTime::currentDateTime(), TP_QT_IFACE_CLIENT + ".TelephonyServiceHandler", request);
283+ }
284+
285+ if (!op) {
286+ setStatus(Failed);
287+ scheduleDeletion();
288+ return;
289+ }
290+ connect(op, &Tp::PendingOperation::finished,
291+ this, &ChatStartingJob::onChannelRequestFinished);
292+}
293+
294+Tp::TextChannelPtr ChatStartingJob::textChannel() const
295+{
296+ qDebug() << __PRETTY_FUNCTION__;
297+ return mTextChannel;
298+}
299+
300+QString ChatStartingJob::channelObjectPath() const
301+{
302+ if (mTextChannel.isNull()) {
303+ return QString::null;
304+ }
305+ return mTextChannel->objectPath();
306+}
307+
308+void ChatStartingJob::setTextChannel(Tp::TextChannelPtr channel)
309+{
310+ qDebug() << __PRETTY_FUNCTION__;
311+ mTextChannel = channel;
312+ Q_EMIT textChannelChanged();
313+}
314+
315+void ChatStartingJob::onChannelRequestFinished(Tp::PendingOperation *op)
316+{
317+ qDebug() << __PRETTY_FUNCTION__;
318+ Status status;
319+ if (op->isError()) {
320+ status = Failed;
321+ } else {
322+ Tp::PendingChannelRequest *channelRequest = qobject_cast<Tp::PendingChannelRequest*>(op);
323+ if (!channelRequest) {
324+ status = Failed;
325+ } else {
326+ setTextChannel(Tp::TextChannelPtr::dynamicCast(channelRequest->channelRequest()->channel()));
327+ status = Finished;
328+ }
329+ }
330+
331+ setStatus(status);
332+ scheduleDeletion();
333+}
334+
335
336=== added file 'handler/chatstartingjob.h'
337--- handler/chatstartingjob.h 1970-01-01 00:00:00 +0000
338+++ handler/chatstartingjob.h 2016-10-06 19:34:37 +0000
339@@ -0,0 +1,69 @@
340+/*
341+ * Copyright (C) 2016 Canonical, Ltd.
342+ *
343+ * Authors:
344+ * Tiago Salem Herrmann <tiago.herrmann@canonical.com>
345+ * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
346+ *
347+ * This file is part of telephony-service.
348+ *
349+ * telephony-service is free software; you can redistribute it and/or modify
350+ * it under the terms of the GNU General Public License as published by
351+ * the Free Software Foundation; version 3.
352+ *
353+ * telephony-service is distributed in the hope that it will be useful,
354+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
355+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
356+ * GNU General Public License for more details.
357+ *
358+ * You should have received a copy of the GNU General Public License
359+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
360+ */
361+
362+#ifndef CHATSTARTINGJOB_H
363+#define CHATSTARTINGJOB_H
364+
365+#include <QObject>
366+#include "messagejob.h"
367+#include <TelepathyQt/Types>
368+#include <TelepathyQt/PendingOperation>
369+
370+class TextHandler;
371+
372+class ChatStartingJob : public MessageJob
373+{
374+ Q_OBJECT
375+ Q_PROPERTY(QString accountId READ accountId CONSTANT)
376+ Q_PROPERTY(Tp::TextChannelPtr textChannel READ textChannel NOTIFY textChannelChanged)
377+ Q_PROPERTY(QString channelObjectPath READ channelObjectPath NOTIFY channelObjectPathChanged)
378+public:
379+ ChatStartingJob(TextHandler *textHandler, const QString &accountId, const QVariantMap &properties);
380+
381+ QString accountId();
382+ Tp::TextChannelPtr textChannel() const;
383+ QString channelObjectPath() const;
384+
385+public Q_SLOTS:
386+ virtual void startJob();
387+
388+Q_SIGNALS:
389+ void textChannelChanged();
390+ void channelObjectPathChanged();
391+
392+
393+protected Q_SLOTS:
394+ void startTextChat(const Tp::AccountPtr &account, const QVariantMap &properties);
395+ void startTextChatRoom(const Tp::AccountPtr &account, const QVariantMap &properties);
396+ void setTextChannel(Tp::TextChannelPtr channel);
397+
398+ void onChannelRequestFinished(Tp::PendingOperation *op);
399+
400+private:
401+ TextHandler *mTextHandler;
402+ QString mAccountId;
403+ QVariantMap mProperties;
404+ Tp::TextChannelPtr mTextChannel;
405+
406+};
407+
408+#endif // CHATSTARTINGJOB_H
409
410=== modified file 'handler/handlerdbus.cpp'
411--- handler/handlerdbus.cpp 2016-10-06 19:34:36 +0000
412+++ handler/handlerdbus.cpp 2016-10-06 19:34:37 +0000
413@@ -81,6 +81,26 @@
414 Q_EMIT CallIndicatorVisibleChanged(visible);
415 }
416
417+QString HandlerDBus::registerObject(QObject *object, const QString &path)
418+{
419+ QString fullPath = QString("%1/%2").arg(DBUS_OBJECT_PATH, path);
420+ if (QDBusConnection::sessionBus().registerObject(fullPath, object)) {
421+ return fullPath;
422+ }
423+ return QString::null;
424+}
425+
426+void HandlerDBus::unregisterObject(const QString &path)
427+{
428+ QDBusConnection::sessionBus().unregisterObject(path);
429+}
430+
431+HandlerDBus *HandlerDBus::instance()
432+{
433+ static HandlerDBus *self = new HandlerDBus;
434+ return self;
435+}
436+
437 bool HandlerDBus::connectToBus()
438 {
439 bool ok = QDBusConnection::sessionBus().registerService(DBUS_SERVICE);
440@@ -103,9 +123,9 @@
441 TextHandler::instance()->acknowledgeMessages(messages);
442 }
443
444-void HandlerDBus::StartChat(const QString &accountId, const QVariantMap &properties)
445+QString HandlerDBus::StartChat(const QString &accountId, const QVariantMap &properties)
446 {
447- TextHandler::instance()->startChat(accountId, properties);
448+ return TextHandler::instance()->startChat(accountId, properties);
449 }
450
451 void HandlerDBus::AcknowledgeAllMessages(const QVariantMap &properties)
452
453=== modified file 'handler/handlerdbus.h'
454--- handler/handlerdbus.h 2016-10-06 19:34:36 +0000
455+++ handler/handlerdbus.h 2016-10-06 19:34:37 +0000
456@@ -30,7 +30,7 @@
457 #include "dbustypes.h"
458
459 /**
460- * DBus interface for the phone approver
461+ * DBus interface for the phone handler
462 */
463 class HandlerDBus : public QObject, protected QDBusContext
464 {
465@@ -51,13 +51,18 @@
466 bool callIndicatorVisible() const;
467 void setCallIndicatorVisible(bool visible);
468
469+ QString registerObject(QObject *object, const QString &path);
470+ void unregisterObject(const QString &path);
471+
472+ static HandlerDBus *instance();
473+
474 public Q_SLOTS:
475 bool connectToBus();
476
477 // messages related
478 QString SendMessage(const QString &accountId, const QString &message, const AttachmentList &attachments, const QVariantMap &properties);
479 Q_NOREPLY void AcknowledgeMessages(const QVariantList &messages);
480- Q_NOREPLY void StartChat(const QString &accountId, const QVariantMap &properties);
481+ QString StartChat(const QString &accountId, const QVariantMap &properties);
482 Q_NOREPLY void AcknowledgeAllMessages(const QVariantMap &properties);
483
484 // call related
485
486=== modified file 'handler/main.cpp'
487--- handler/main.cpp 2016-03-18 19:02:50 +0000
488+++ handler/main.cpp 2016-10-06 19:34:37 +0000
489@@ -54,10 +54,8 @@
490 QObject::connect(handler, SIGNAL(textChannelAvailable(Tp::TextChannelPtr)),
491 TextHandler::instance(), SLOT(onTextChannelAvailable(Tp::TextChannelPtr)));
492
493- HandlerDBus dbus;
494-
495 QObject::connect(TelepathyHelper::instance(), SIGNAL(setupReady()),
496- &dbus, SLOT(connectToBus()));
497+ HandlerDBus::instance(), SLOT(connectToBus()));
498
499 return app.exec();
500 }
501
502=== added file 'handler/messagejob.cpp'
503--- handler/messagejob.cpp 1970-01-01 00:00:00 +0000
504+++ handler/messagejob.cpp 2016-10-06 19:34:37 +0000
505@@ -0,0 +1,96 @@
506+/*
507+ * Copyright (C) 2016 Canonical, Ltd.
508+ *
509+ * Authors:
510+ * Tiago Salem Herrmann <tiago.herrmann@canonical.com>
511+ * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
512+ *
513+ * This file is part of telephony-service.
514+ *
515+ * telephony-service is free software; you can redistribute it and/or modify
516+ * it under the terms of the GNU General Public License as published by
517+ * the Free Software Foundation; version 3.
518+ *
519+ * telephony-service is distributed in the hope that it will be useful,
520+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
521+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
522+ * GNU General Public License for more details.
523+ *
524+ * You should have received a copy of the GNU General Public License
525+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
526+ */
527+
528+#include "messagejob.h"
529+#include "handlerdbus.h"
530+#include <QCoreApplication>
531+#include <QTime>
532+#include <QTimer>
533+#include <QDebug>
534+
535+MessageJob::MessageJob(QDBusAbstractAdaptor *adaptor, QObject *parent)
536+: QObject(parent), mStatus(Pending), mFinished(false), mAdaptor(adaptor)
537+{
538+ static ulong count = 0;
539+ // just to avoid overflowing
540+ if (count == ULONG_MAX) {
541+ count = 0;
542+ }
543+ mObjectPath = HandlerDBus::instance()->registerObject(this, QString("messagejob%1").arg(count++));
544+}
545+
546+MessageJob::~MessageJob()
547+{
548+ HandlerDBus::instance()->unregisterObject(mObjectPath);
549+}
550+
551+MessageJob::Status MessageJob::status() const
552+{
553+ return mStatus;
554+}
555+
556+bool MessageJob::isFinished() const
557+{
558+ return mFinished;
559+}
560+
561+QString MessageJob::objectPath() const
562+{
563+ return mObjectPath;
564+}
565+
566+void MessageJob::waitForFinished(int timeout)
567+{
568+ QTime time;
569+ time.start();
570+ while (!mFinished && time.elapsed() < timeout) {
571+ QCoreApplication::processEvents();
572+ }
573+}
574+
575+void MessageJob::startJob()
576+{
577+ // the default implementation just sets the status to Finished
578+ setStatus(Finished);
579+}
580+
581+void MessageJob::setStatus(MessageJob::Status status)
582+{
583+ mStatus = status;
584+ Q_EMIT statusChanged();
585+
586+ // update the isFinished property too
587+ bool wasFinished = mFinished;
588+ mFinished = mStatus == Finished || mStatus == Failed;
589+ if (wasFinished != mFinished) {
590+ Q_EMIT isFinishedChanged();
591+ }
592+ if (mFinished) {
593+ Q_EMIT finished();
594+ }
595+}
596+
597+void MessageJob::scheduleDeletion(int timeout)
598+{
599+ QTimer::singleShot(timeout, this, &QObject::deleteLater);
600+}
601+
602
603=== added file 'handler/messagejob.h'
604--- handler/messagejob.h 1970-01-01 00:00:00 +0000
605+++ handler/messagejob.h 2016-10-06 19:34:37 +0000
606@@ -0,0 +1,75 @@
607+/*
608+ * Copyright (C) 2016 Canonical, Ltd.
609+ *
610+ * Authors:
611+ * Tiago Salem Herrmann <tiago.herrmann@canonical.com>
612+ * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
613+ *
614+ * This file is part of telephony-service.
615+ *
616+ * telephony-service is free software; you can redistribute it and/or modify
617+ * it under the terms of the GNU General Public License as published by
618+ * the Free Software Foundation; version 3.
619+ *
620+ * telephony-service is distributed in the hope that it will be useful,
621+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
622+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
623+ * GNU General Public License for more details.
624+ *
625+ * You should have received a copy of the GNU General Public License
626+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
627+ */
628+
629+#ifndef MESSAGEJOB_H
630+#define MESSAGEJOB_H
631+
632+#include <QObject>
633+#include <QDBusAbstractAdaptor>
634+#include <QDBusContext>
635+
636+class MessageJob : public QObject, protected QDBusContext
637+{
638+ Q_OBJECT
639+ Q_PROPERTY(int status READ status NOTIFY statusChanged)
640+ Q_PROPERTY(bool isFinished READ isFinished NOTIFY isFinishedChanged)
641+ Q_PROPERTY(QString objectPath READ objectPath CONSTANT)
642+ Q_ENUMS(Status)
643+public:
644+ enum Status {
645+ Pending,
646+ Initialising,
647+ Running,
648+ Finished,
649+ Failed
650+ };
651+
652+ explicit MessageJob(QDBusAbstractAdaptor *adaptor, QObject *parent = 0);
653+ virtual ~MessageJob();
654+
655+ Status status() const;
656+ bool isFinished() const;
657+
658+ QString objectPath() const;
659+
660+ void waitForFinished(int timeout = 10000);
661+
662+Q_SIGNALS:
663+ void statusChanged();
664+ void isFinishedChanged();
665+ void finished();
666+
667+public Q_SLOTS:
668+ virtual void startJob();
669+
670+protected:
671+ void setStatus(Status status);
672+ void scheduleDeletion(int timeout = 60000);
673+
674+private:
675+ Status mStatus;
676+ bool mFinished;
677+ QString mObjectPath;
678+ QDBusAbstractAdaptor *mAdaptor;
679+};
680+
681+#endif // MESSAGEJOB_H
682
683=== added file 'handler/messagesendingjob.cpp'
684--- handler/messagesendingjob.cpp 1970-01-01 00:00:00 +0000
685+++ handler/messagesendingjob.cpp 2016-10-06 19:34:37 +0000
686@@ -0,0 +1,390 @@
687+/*
688+ * Copyright (C) 2016 Canonical, Ltd.
689+ *
690+ * Authors:
691+ * Tiago Salem Herrmann <tiago.herrmann@canonical.com>
692+ * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
693+ *
694+ * This file is part of telephony-service.
695+ *
696+ * telephony-service is free software; you can redistribute it and/or modify
697+ * it under the terms of the GNU General Public License as published by
698+ * the Free Software Foundation; version 3.
699+ *
700+ * telephony-service is distributed in the hope that it will be useful,
701+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
702+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
703+ * GNU General Public License for more details.
704+ *
705+ * You should have received a copy of the GNU General Public License
706+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
707+ */
708+
709+#include "accountentry.h"
710+#include "chatstartingjob.h"
711+#include "messagesendingjob.h"
712+#include "messagesendingjobadaptor.h"
713+#include "telepathyhelper.h"
714+#include "texthandler.h"
715+#include <TelepathyQt/ContactManager>
716+#include <TelepathyQt/PendingContacts>
717+#include <QImage>
718+
719+#define SMIL_TEXT_REGION "<region id=\"Text\" width=\"100%\" height=\"100%\" fit=\"scroll\" />"
720+#define SMIL_IMAGE_REGION "<region id=\"Image\" width=\"100%\" height=\"100%\" fit=\"meet\" />"
721+#define SMIL_VIDEO_REGION "<region id=\"Video\" width=\"100%\" height=\"100%\" fit=\"meet\" />"
722+#define SMIL_AUDIO_REGION "<region id=\"Audio\" width=\"100%\" height=\"100%\" fit=\"meet\" />"
723+#define SMIL_TEXT_PART "<par dur=\"3s\">\
724+ <text src=\"cid:%1\" region=\"Text\" />\
725+ </par>"
726+#define SMIL_IMAGE_PART "<par dur=\"5000ms\">\
727+ <img src=\"cid:%1\" region=\"Image\" />\
728+ </par>"
729+#define SMIL_VIDEO_PART "<par>\
730+ <video src=\"cid:%1\" region=\"Video\" />\
731+ </par>"
732+#define SMIL_AUDIO_PART "<par>\
733+ <audio src=\"cid:%1\" region=\"Audio\" />\
734+ </par>"
735+
736+#define SMIL_FILE "<smil>\
737+ <head>\
738+ <layout>\
739+ %1\
740+ </layout>\
741+ </head>\
742+ <body>\
743+ %2\
744+ </body>\
745+ </smil>"
746+
747+MessageSendingJob::MessageSendingJob(TextHandler *textHandler, PendingMessage message)
748+: MessageJob(new MessageSendingJobAdaptor(this), textHandler), mTextHandler(textHandler), mMessage(message), mFinished(false)
749+{
750+}
751+
752+MessageSendingJob::~MessageSendingJob()
753+{
754+ qDebug() << __PRETTY_FUNCTION__;
755+}
756+
757+QString MessageSendingJob::accountId() const
758+{
759+ qDebug() << __PRETTY_FUNCTION__;
760+ return mAccountId;
761+}
762+
763+QString MessageSendingJob::messageId() const
764+{
765+ return mMessageId;
766+}
767+
768+QString MessageSendingJob::channelObjectPath() const
769+{
770+ qDebug() << __PRETTY_FUNCTION__;
771+ return mChannelObjectPath;
772+}
773+
774+QVariantMap MessageSendingJob::properties() const
775+{
776+ return mMessage.properties;
777+}
778+
779+void MessageSendingJob::startJob()
780+{
781+ qDebug() << __PRETTY_FUNCTION__;
782+ qDebug() << "Getting account for id:" << mMessage.accountId;
783+ AccountEntry *account = TelepathyHelper::instance()->accountForId(mMessage.accountId);
784+ if (!account) {
785+ setStatus(Failed);
786+ scheduleDeletion();
787+ }
788+
789+ setStatus(Running);
790+
791+ // check if the message should be sent via multimedia account
792+ // we just use fallback to 1-1 chats
793+ if (account->type() == AccountEntry::PhoneAccount) {
794+ Q_FOREACH(AccountEntry *newAccount, TelepathyHelper::instance()->accounts()) {
795+ // TODO: we have to find the multimedia account that matches the same phone number,
796+ // but for now we just pick any multimedia connected account
797+ if (newAccount->type() != AccountEntry::MultimediaAccount) {
798+ continue;
799+ }
800+ // FIXME: the fallback implementation needs to be changed to use protocol info and create a map of
801+ // accounts. Also, it needs to check connection capabilities to determine if we can send message
802+ // to offline contacts.
803+ bool shouldFallback = true;
804+ // if the account is offline, dont fallback to this account
805+ if (!newAccount->connected()) {
806+ continue;
807+ }
808+ QList<Tp::TextChannelPtr> channels = mTextHandler->existingChannels(newAccount->accountId(), mMessage.properties);
809+ // check if we have a channel for this contact already and get the contact pointer from there,
810+ // this way we avoid doing the while(op->isFinished()) all the time
811+ if (!channels.isEmpty()) {
812+ // FIXME: we need to re-evaluate the rules to fallback
813+ // if the contact is known, force fallback to this account
814+ Q_FOREACH(const Tp::ContactPtr &contact, channels.first()->groupContacts(false)) {
815+ Tp::Presence presence = contact->presence();
816+ shouldFallback = (presence.type() == Tp::ConnectionPresenceTypeAvailable ||
817+ presence.type() == Tp::ConnectionPresenceTypeOffline);
818+ if (!shouldFallback) {
819+ break;
820+ }
821+ }
822+ } else {
823+ QStringList participantIds = mMessage.properties["participantIds"].toStringList();
824+ Tp::PendingContacts *op = newAccount->account()->connection()->contactManager()->contactsForIdentifiers(participantIds);
825+ while (!op->isFinished()) {
826+ qApp->processEvents();
827+ }
828+ Q_FOREACH(const Tp::ContactPtr &contact, op->contacts()) {
829+ Tp::Presence presence = contact->presence();
830+ shouldFallback = (presence.type() == Tp::ConnectionPresenceTypeAvailable ||
831+ presence.type() == Tp::ConnectionPresenceTypeOffline);
832+ if (!shouldFallback) {
833+ break;
834+ }
835+ }
836+ }
837+ if (shouldFallback) {
838+ account = newAccount;
839+ break;
840+ }
841+ }
842+ }
843+
844+ // save the account
845+ mAccount = account;
846+ setAccountId(mAccount->accountId());
847+
848+ if (!account->connected()) {
849+ connect(account, &AccountEntry::connectedChanged, [this, account]() {
850+ if (account->connected()) {
851+ findOrCreateChannel();
852+ }
853+ });
854+ return;
855+ }
856+
857+ findOrCreateChannel();
858+}
859+
860+void MessageSendingJob::findOrCreateChannel()
861+{
862+ qDebug() << __PRETTY_FUNCTION__;
863+ // now that we know what account to use, find existing channels or request a new one
864+ QList<Tp::TextChannelPtr> channels = mTextHandler->existingChannels(mAccount->accountId(), mMessage.properties);
865+ if (channels.isEmpty()) {
866+ ChatStartingJob *job = new ChatStartingJob(mTextHandler, mAccount->accountId(), mMessage.properties);
867+ connect(job, &MessageJob::finished, [this, job]() {
868+ if (job->status() == MessageJob::Failed) {
869+ setStatus(Failed);
870+ scheduleDeletion();
871+ return;
872+ }
873+
874+ mTextChannel = job->textChannel();
875+ sendMessage();
876+ });
877+ job->startJob();
878+ return;
879+ }
880+
881+ mTextChannel = channels.last();
882+ sendMessage();
883+}
884+
885+void MessageSendingJob::sendMessage()
886+{
887+ qDebug() << __PRETTY_FUNCTION__;
888+ Tp::PendingSendMessage *op = mTextChannel->send(buildMessage(mMessage));
889+ connect(op, &Tp::PendingOperation::finished, [this, op]() {
890+ if (op->isError()) {
891+ setStatus(Failed);
892+ scheduleDeletion();
893+ return;
894+ }
895+
896+ setChannelObjectPath(mTextChannel->objectPath());
897+ setMessageId(op->sentMessageToken());
898+ setStatus(Finished);
899+ scheduleDeletion();
900+ });
901+}
902+
903+void MessageSendingJob::setAccountId(const QString &accountId)
904+{
905+ qDebug() << __PRETTY_FUNCTION__;
906+ mAccountId = accountId;
907+ Q_EMIT accountIdChanged();
908+}
909+
910+void MessageSendingJob::setChannelObjectPath(const QString &objectPath)
911+{
912+ qDebug() << __PRETTY_FUNCTION__;
913+ mChannelObjectPath = objectPath;
914+ Q_EMIT channelObjectPathChanged();
915+}
916+
917+void MessageSendingJob::setMessageId(const QString &id)
918+{
919+ mMessageId = id;
920+ Q_EMIT messageIdChanged();
921+}
922+
923+Tp::MessagePartList MessageSendingJob::buildMessage(const PendingMessage &pendingMessage)
924+{
925+ qDebug() << __PRETTY_FUNCTION__;
926+ Tp::MessagePartList message;
927+ Tp::MessagePart header;
928+ QString smil, regions, parts;
929+ bool hasImage = false, hasText = false, hasVideo = false, hasAudio = false, isMMS = false;
930+
931+ AccountEntry *account = TelepathyHelper::instance()->accountForId(pendingMessage.accountId);
932+ if (!account) {
933+ // account does not exist
934+ return Tp::MessagePartList();
935+ }
936+
937+ bool temporaryFiles = (pendingMessage.properties.contains("x-canonical-tmp-files") &&
938+ pendingMessage.properties["x-canonical-tmp-files"].toBool());
939+
940+ // add the remaining properties to the message header
941+ QVariantMap::const_iterator it = pendingMessage.properties.begin();
942+ for (; it != pendingMessage.properties.end(); ++it) {
943+ header[it.key()] = QDBusVariant(it.value());
944+ }
945+
946+ // check if this message should be sent as an MMS
947+ if (account->type() == AccountEntry::PhoneAccount) {
948+ isMMS = (pendingMessage.attachments.size() > 0 ||
949+ (header.contains("x-canonical-mms") && header["x-canonical-mms"].variant().toBool()) ||
950+ (pendingMessage.properties["participantIds"].toStringList().size() > 1 && TelepathyHelper::instance()->mmsGroupChat()));
951+ if (isMMS) {
952+ header["x-canonical-mms"] = QDBusVariant(true);
953+ }
954+ }
955+
956+ // this flag should not be in the message header, it's only useful for the handler
957+ header.remove("x-canonical-tmp-files");
958+ header.remove("chatType");
959+ header.remove("threadId");
960+ header.remove("participantIds");
961+
962+ header["message-type"] = QDBusVariant(0);
963+ message << header;
964+
965+ // convert AttachmentList struct into telepathy Message parts
966+ Q_FOREACH(const AttachmentStruct &attachment, pendingMessage.attachments) {
967+ QByteArray fileData;
968+ QString newFilePath = QString(attachment.filePath).replace("file://", "");
969+ QFile attachmentFile(newFilePath);
970+ if (!attachmentFile.open(QIODevice::ReadOnly)) {
971+ qWarning() << "fail to load attachment" << attachmentFile.errorString() << attachment.filePath;
972+ continue;
973+ }
974+ if (attachment.contentType.startsWith("image/")) {
975+ if (isMMS) {
976+ hasImage = true;
977+ parts += QString(SMIL_IMAGE_PART).arg(attachment.id);
978+ // check if we need to reduce de image size in case it's bigger than 300k
979+ // this check is only valid for MMS
980+ if (attachmentFile.size() > 307200) {
981+ QImage scaledImage(newFilePath);
982+ if (!scaledImage.isNull()) {
983+ QBuffer buffer(&fileData);
984+ buffer.open(QIODevice::WriteOnly);
985+ scaledImage.scaled(640, 640, Qt::KeepAspectRatio, Qt::SmoothTransformation).save(&buffer, "jpg");
986+ }
987+ } else {
988+ fileData = attachmentFile.readAll();
989+ }
990+ }
991+ } else if (attachment.contentType.startsWith("video/")) {
992+ if (isMMS) {
993+ hasVideo = true;
994+ parts += QString(SMIL_VIDEO_PART).arg(attachment.id);
995+ }
996+ } else if (attachment.contentType.startsWith("audio/")) {
997+ if (isMMS) {
998+ hasAudio = true;
999+ parts += QString(SMIL_AUDIO_PART).arg(attachment.id);
1000+ }
1001+ } else if (attachment.contentType.startsWith("text/plain")) {
1002+ if (isMMS) {
1003+ hasText = true;
1004+ parts += QString(SMIL_TEXT_PART).arg(attachment.id);
1005+ }
1006+ } else if (attachment.contentType.startsWith("text/vcard") ||
1007+ attachment.contentType.startsWith("text/x-vcard")) {
1008+ } else if (isMMS) {
1009+ // for MMS we just support the contentTypes above
1010+ if (temporaryFiles) {
1011+ attachmentFile.remove();
1012+ }
1013+ continue;
1014+ }
1015+
1016+ if (fileData.isEmpty()) {
1017+ fileData = attachmentFile.readAll();
1018+ }
1019+
1020+ if (temporaryFiles) {
1021+ attachmentFile.remove();
1022+ }
1023+
1024+ if (hasVideo) {
1025+ regions += QString(SMIL_VIDEO_REGION);
1026+ }
1027+
1028+ if (hasAudio) {
1029+ regions += QString(SMIL_AUDIO_REGION);
1030+ }
1031+
1032+ if (hasText) {
1033+ regions += QString(SMIL_TEXT_REGION);
1034+ }
1035+ if (hasImage) {
1036+ regions += QString(SMIL_IMAGE_REGION);
1037+ }
1038+
1039+ Tp::MessagePart part;
1040+ part["content-type"] = QDBusVariant(attachment.contentType);
1041+ part["identifier"] = QDBusVariant(attachment.id);
1042+ part["content"] = QDBusVariant(fileData);
1043+ part["size"] = QDBusVariant(fileData.size());
1044+
1045+ message << part;
1046+ }
1047+
1048+ if (!pendingMessage.message.isEmpty()) {
1049+ Tp::MessagePart part;
1050+ QString tmpTextId("text_0.txt");
1051+ part["content-type"] = QDBusVariant(QString("text/plain"));
1052+ part["identifier"] = QDBusVariant(tmpTextId);
1053+ part["content"] = QDBusVariant(pendingMessage.message);
1054+ part["size"] = QDBusVariant(pendingMessage.message.size());
1055+ if (isMMS) {
1056+ parts += QString(SMIL_TEXT_PART).arg(tmpTextId);
1057+ regions += QString(SMIL_TEXT_REGION);
1058+ }
1059+ message << part;
1060+ }
1061+
1062+ if (isMMS) {
1063+ Tp::MessagePart smilPart;
1064+ smil = QString(SMIL_FILE).arg(regions).arg(parts);
1065+ smilPart["content-type"] = QDBusVariant(QString("application/smil"));
1066+ smilPart["identifier"] = QDBusVariant(QString("smil.xml"));
1067+ smilPart["content"] = QDBusVariant(smil);
1068+ smilPart["size"] = QDBusVariant(smil.size());
1069+
1070+ message << smilPart;
1071+ }
1072+
1073+ return message;
1074+}
1075+
1076+
1077
1078=== added file 'handler/messagesendingjob.h'
1079--- handler/messagesendingjob.h 1970-01-01 00:00:00 +0000
1080+++ handler/messagesendingjob.h 2016-10-06 19:34:37 +0000
1081@@ -0,0 +1,90 @@
1082+/*
1083+ * Copyright (C) 2016 Canonical, Ltd.
1084+ *
1085+ * Authors:
1086+ * Tiago Salem Herrmann <tiago.herrmann@canonical.com>
1087+ * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
1088+ *
1089+ * This file is part of telephony-service.
1090+ *
1091+ * telephony-service is free software; you can redistribute it and/or modify
1092+ * it under the terms of the GNU General Public License as published by
1093+ * the Free Software Foundation; version 3.
1094+ *
1095+ * telephony-service is distributed in the hope that it will be useful,
1096+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1097+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1098+ * GNU General Public License for more details.
1099+ *
1100+ * You should have received a copy of the GNU General Public License
1101+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1102+ */
1103+
1104+#ifndef MESSAGESENDINGJOB_H
1105+#define MESSAGESENDINGJOB_H
1106+
1107+#include <QObject>
1108+#include <TelepathyQt/Types>
1109+#include "dbustypes.h"
1110+#include "messagejob.h"
1111+
1112+class AccountEntry;
1113+class TextHandler;
1114+class MessageSendingJobAdaptor;
1115+
1116+struct PendingMessage {
1117+ QString accountId;
1118+ QString message;
1119+ AttachmentList attachments;
1120+ QVariantMap properties;
1121+};
1122+Q_DECLARE_METATYPE(PendingMessage)
1123+
1124+class MessageSendingJob : public MessageJob
1125+{
1126+ Q_OBJECT
1127+ Q_PROPERTY(QString accountId READ accountId NOTIFY accountIdChanged)
1128+ Q_PROPERTY(QString messageId READ messageId NOTIFY messageIdChanged)
1129+ Q_PROPERTY(QString channelObjectPath READ channelObjectPath NOTIFY channelObjectPathChanged)
1130+ Q_PROPERTY(QVariantMap properties READ properties CONSTANT)
1131+
1132+public:
1133+ explicit MessageSendingJob(TextHandler *textHandler, PendingMessage message);
1134+ ~MessageSendingJob();
1135+
1136+ QString accountId() const;
1137+ QString messageId() const;
1138+ QString channelObjectPath() const;
1139+ QVariantMap properties() const;
1140+
1141+Q_SIGNALS:
1142+ void accountIdChanged();
1143+ void messageIdChanged();
1144+ void channelObjectPathChanged();
1145+
1146+public Q_SLOTS:
1147+ void startJob();
1148+
1149+protected Q_SLOTS:
1150+ void findOrCreateChannel();
1151+ void sendMessage();
1152+
1153+ void setAccountId(const QString &accountId);
1154+ void setChannelObjectPath(const QString &objectPath);
1155+ void setMessageId(const QString &id);
1156+
1157+private:
1158+ TextHandler *mTextHandler;
1159+ PendingMessage mMessage;
1160+ QString mAccountId;
1161+ QString mMessageId;
1162+ AccountEntry *mAccount;
1163+ QString mChannelObjectPath;
1164+ Tp::TextChannelPtr mTextChannel;
1165+ bool mFinished;
1166+
1167+ Tp::MessagePartList buildMessage(const PendingMessage &pendingMessage);
1168+
1169+};
1170+
1171+#endif // MESSAGESENDINGJOB_H
1172
1173=== modified file 'handler/texthandler.cpp'
1174--- handler/texthandler.cpp 2016-10-06 19:34:36 +0000
1175+++ handler/texthandler.cpp 2016-10-06 19:34:37 +0000
1176@@ -26,87 +26,19 @@
1177 #include "config.h"
1178 #include "dbustypes.h"
1179 #include "accountentry.h"
1180+#include "chatstartingjob.h"
1181
1182 #include <QImage>
1183 #include <TelepathyQt/ContactManager>
1184 #include <TelepathyQt/PendingContacts>
1185 #include <TelepathyQt/PendingChannelRequest>
1186
1187-#define SMIL_TEXT_REGION "<region id=\"Text\" width=\"100%\" height=\"100%\" fit=\"scroll\" />"
1188-#define SMIL_IMAGE_REGION "<region id=\"Image\" width=\"100%\" height=\"100%\" fit=\"meet\" />"
1189-#define SMIL_VIDEO_REGION "<region id=\"Video\" width=\"100%\" height=\"100%\" fit=\"meet\" />"
1190-#define SMIL_AUDIO_REGION "<region id=\"Audio\" width=\"100%\" height=\"100%\" fit=\"meet\" />"
1191-#define SMIL_TEXT_PART "<par dur=\"3s\">\
1192- <text src=\"cid:%1\" region=\"Text\" />\
1193- </par>"
1194-#define SMIL_IMAGE_PART "<par dur=\"5000ms\">\
1195- <img src=\"cid:%1\" region=\"Image\" />\
1196- </par>"
1197-#define SMIL_VIDEO_PART "<par>\
1198- <video src=\"cid:%1\" region=\"Video\" />\
1199- </par>"
1200-#define SMIL_AUDIO_PART "<par>\
1201- <audio src=\"cid:%1\" region=\"Audio\" />\
1202- </par>"
1203-
1204-#define SMIL_FILE "<smil>\
1205- <head>\
1206- <layout>\
1207- %1\
1208- </layout>\
1209- </head>\
1210- <body>\
1211- %2\
1212- </body>\
1213- </smil>"
1214-
1215 TextHandler::TextHandler(QObject *parent)
1216 : QObject(parent)
1217 {
1218 qDBusRegisterMetaType<AttachmentStruct>();
1219 qDBusRegisterMetaType<AttachmentList>();
1220 qRegisterMetaType<PendingMessage>();
1221-
1222- // track when the account becomes available
1223- connect(TelepathyHelper::instance(),
1224- SIGNAL(setupReady()),
1225- SLOT(onConnectedChanged()));
1226-}
1227-
1228-void TextHandler::onConnectedChanged()
1229-{
1230- if (!TelepathyHelper::instance()->ready()) {
1231- return;
1232- }
1233-
1234- // now check which accounts are connected
1235- Q_FOREACH(AccountEntry *account, TelepathyHelper::instance()->accounts()) {
1236- QString accountId = account->accountId();
1237- if (!account->connected()) {
1238- continue;
1239- }
1240-
1241- // create text channels to send the pending messages
1242- QList<QStringList> recipientsList;
1243- Q_FOREACH(const PendingMessage &pendingMessage, mPendingMessages) {
1244- if (accountId != pendingMessage.accountId) {
1245- continue;
1246- }
1247- bool found = false;
1248- // avoid adding twice the same list of participants
1249-/* Q_FOREACH(const QStringList &recipients, recipientsList) {
1250- if (recipients == pendingMessage.recipients) {
1251- found = true;
1252- break;
1253- }
1254- }
1255- if (!found) {
1256- recipientsList << pendingMessage.recipients;
1257- }*/
1258- // TODO AVOID CALLING TWICE FOR SAME CHANNEL
1259- startChat(accountId, pendingMessage.properties);
1260- }
1261- }
1262 }
1263
1264 TextHandler *TextHandler::instance()
1265@@ -115,344 +47,20 @@
1266 return handler;
1267 }
1268
1269-void TextHandler::startChat(const QString &accountId, const QVariantMap &properties)
1270-{
1271- // Request the contact to start chatting to
1272- // FIXME: make it possible to select which account to use, for now, pick the first one
1273- AccountEntry *account = TelepathyHelper::instance()->accountForId(accountId);
1274- if (!account || !account->connected()) {
1275- qCritical() << "The selected account does not have a connection. AccountId:" << accountId;
1276- return;
1277- }
1278-
1279- switch(properties["chatType"].toUInt()) {
1280- case Tp::HandleTypeNone:
1281- case Tp::HandleTypeContact:
1282- startTextChat(account->account(), properties);
1283- break;
1284- case Tp::HandleTypeRoom:
1285- startTextChatroom(account->account(), properties);
1286- break;
1287- default:
1288- qCritical() << "Chat type not supported";
1289- }
1290-}
1291-
1292-void TextHandler::startTextChat(const Tp::AccountPtr &account, const QVariantMap &properties)
1293-{
1294- QStringList participants = properties["participantIds"].toStringList();
1295- switch(participants.size()) {
1296- case 0:
1297- qCritical() << "Error: No participant list provided";
1298- break;
1299- case 1:
1300- account->ensureTextChat(participants[0], QDateTime::currentDateTime(), TP_QT_IFACE_CLIENT + ".TelephonyServiceHandler");
1301- break;
1302- default:
1303- account->createConferenceTextChat(QList<Tp::ChannelPtr>(), participants, QDateTime::currentDateTime(), TP_QT_IFACE_CLIENT + ".TelephonyServiceHandler");
1304- }
1305-}
1306-
1307-Tp::TextChannelPtr TextHandler::startTextChatroom(const Tp::AccountPtr &account, const QVariantMap &properties)
1308-{
1309- QString roomName = properties["threadId"].toString();
1310-
1311- // these properties are still not used
1312- //QString server = properties["Server"].toString();
1313- //QString creator = properties["Creator"].toString();
1314-
1315- QVariantMap request;
1316- Tp::PendingChannelRequest *op = NULL;
1317- if (roomName.isEmpty()) {
1318- request.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType"), TP_QT_IFACE_CHANNEL_TYPE_TEXT);
1319- request.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandleType"), (uint) Tp::HandleTypeNone);
1320- QStringList initialInviteeIDs = properties["participantIds"].toStringList();
1321- if (!initialInviteeIDs.isEmpty()) {
1322- request.insert(TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE + QLatin1String(".InitialInviteeIDs"), initialInviteeIDs);
1323- }
1324- // the presence of RoomName indicates the returned channel must be of type Room
1325- request.insert(TP_QT_IFACE_CHANNEL_INTERFACE_ROOM + QLatin1String(".RoomName"), QString());
1326-
1327- // TODO use the instance returned by createChanne() to track when the channel creation is finished
1328- op = account->createChannel(request, QDateTime::currentDateTime(), TP_QT_IFACE_CLIENT + ".TelephonyServiceHandler");
1329- } else {
1330-
1331- op = account->ensureTextChatroom(roomName, QDateTime::currentDateTime(), TP_QT_IFACE_CLIENT + ".TelephonyServiceHandler", request);
1332- }
1333-
1334- if (!op) {
1335- return Tp::TextChannelPtr();
1336- }
1337- while (!op->isFinished()) {
1338- qApp->processEvents();
1339- }
1340- Tp::TextChannelPtr textChannel(qobject_cast<Tp::TextChannel*>(op->channelRequest()->channel().data()));
1341- return textChannel;
1342-}
1343-
1344-Tp::MessagePartList TextHandler::buildMessage(const PendingMessage &pendingMessage)
1345-{
1346- Tp::MessagePartList message;
1347- Tp::MessagePart header;
1348- QString smil, regions, parts;
1349- bool hasImage = false, hasText = false, hasVideo = false, hasAudio = false, isMMS = false;
1350-
1351- AccountEntry *account = TelepathyHelper::instance()->accountForId(pendingMessage.accountId);
1352- if (!account) {
1353- // account does not exist
1354- return Tp::MessagePartList();
1355- }
1356-
1357- bool temporaryFiles = (pendingMessage.properties.contains("x-canonical-tmp-files") &&
1358- pendingMessage.properties["x-canonical-tmp-files"].toBool());
1359-
1360- // add the remaining properties to the message header
1361- QVariantMap::const_iterator it = pendingMessage.properties.begin();
1362- for (; it != pendingMessage.properties.end(); ++it) {
1363- header[it.key()] = QDBusVariant(it.value());
1364- }
1365-
1366- // check if this message should be sent as an MMS
1367- if (account->type() == AccountEntry::PhoneAccount) {
1368- isMMS = (pendingMessage.attachments.size() > 0 ||
1369- (header.contains("x-canonical-mms") && header["x-canonical-mms"].variant().toBool()) ||
1370- (pendingMessage.properties["participantIds"].toStringList().size() > 1 && TelepathyHelper::instance()->mmsGroupChat()));
1371- if (isMMS) {
1372- header["x-canonical-mms"] = QDBusVariant(true);
1373- }
1374- }
1375-
1376- // this flag should not be in the message header, it's only useful for the handler
1377- header.remove("x-canonical-tmp-files");
1378- header.remove("chatType");
1379- header.remove("threadId");
1380- header.remove("participantIds");
1381-
1382- header["message-type"] = QDBusVariant(0);
1383- message << header;
1384-
1385- // convert AttachmentList struct into telepathy Message parts
1386- Q_FOREACH(const AttachmentStruct &attachment, pendingMessage.attachments) {
1387- QByteArray fileData;
1388- QString newFilePath = QString(attachment.filePath).replace("file://", "");
1389- QFile attachmentFile(newFilePath);
1390- if (!attachmentFile.open(QIODevice::ReadOnly)) {
1391- qWarning() << "fail to load attachment" << attachmentFile.errorString() << attachment.filePath;
1392- continue;
1393- }
1394- if (attachment.contentType.startsWith("image/")) {
1395- if (isMMS) {
1396- hasImage = true;
1397- parts += QString(SMIL_IMAGE_PART).arg(attachment.id);
1398- // check if we need to reduce de image size in case it's bigger than 300k
1399- // this check is only valid for MMS
1400- if (attachmentFile.size() > 307200) {
1401- QImage scaledImage(newFilePath);
1402- if (!scaledImage.isNull()) {
1403- QBuffer buffer(&fileData);
1404- buffer.open(QIODevice::WriteOnly);
1405- scaledImage.scaled(640, 640, Qt::KeepAspectRatio, Qt::SmoothTransformation).save(&buffer, "jpg");
1406- }
1407- } else {
1408- fileData = attachmentFile.readAll();
1409- }
1410- }
1411- } else if (attachment.contentType.startsWith("video/")) {
1412- if (isMMS) {
1413- hasVideo = true;
1414- parts += QString(SMIL_VIDEO_PART).arg(attachment.id);
1415- }
1416- } else if (attachment.contentType.startsWith("audio/")) {
1417- if (isMMS) {
1418- hasAudio = true;
1419- parts += QString(SMIL_AUDIO_PART).arg(attachment.id);
1420- }
1421- } else if (attachment.contentType.startsWith("text/plain")) {
1422- if (isMMS) {
1423- hasText = true;
1424- parts += QString(SMIL_TEXT_PART).arg(attachment.id);
1425- }
1426- } else if (attachment.contentType.startsWith("text/vcard") ||
1427- attachment.contentType.startsWith("text/x-vcard")) {
1428- } else if (isMMS) {
1429- // for MMS we just support the contentTypes above
1430- if (temporaryFiles) {
1431- attachmentFile.remove();
1432- }
1433- continue;
1434- }
1435-
1436- if (fileData.isEmpty()) {
1437- fileData = attachmentFile.readAll();
1438- }
1439-
1440- if (temporaryFiles) {
1441- attachmentFile.remove();
1442- }
1443-
1444- if (hasVideo) {
1445- regions += QString(SMIL_VIDEO_REGION);
1446- }
1447-
1448- if (hasAudio) {
1449- regions += QString(SMIL_AUDIO_REGION);
1450- }
1451-
1452- if (hasText) {
1453- regions += QString(SMIL_TEXT_REGION);
1454- }
1455- if (hasImage) {
1456- regions += QString(SMIL_IMAGE_REGION);
1457- }
1458-
1459- Tp::MessagePart part;
1460- part["content-type"] = QDBusVariant(attachment.contentType);
1461- part["identifier"] = QDBusVariant(attachment.id);
1462- part["content"] = QDBusVariant(fileData);
1463- part["size"] = QDBusVariant(fileData.size());
1464-
1465- message << part;
1466- }
1467-
1468- if (!pendingMessage.message.isEmpty()) {
1469- Tp::MessagePart part;
1470- QString tmpTextId("text_0.txt");
1471- part["content-type"] = QDBusVariant(QString("text/plain"));
1472- part["identifier"] = QDBusVariant(tmpTextId);
1473- part["content"] = QDBusVariant(pendingMessage.message);
1474- part["size"] = QDBusVariant(pendingMessage.message.size());
1475- if (isMMS) {
1476- parts += QString(SMIL_TEXT_PART).arg(tmpTextId);
1477- regions += QString(SMIL_TEXT_REGION);
1478- }
1479- message << part;
1480- }
1481-
1482- if (isMMS) {
1483- Tp::MessagePart smilPart;
1484- smil = QString(SMIL_FILE).arg(regions).arg(parts);
1485- smilPart["content-type"] = QDBusVariant(QString("application/smil"));
1486- smilPart["identifier"] = QDBusVariant(QString("smil.xml"));
1487- smilPart["content"] = QDBusVariant(smil);
1488- smilPart["size"] = QDBusVariant(smil.size());
1489-
1490- message << smilPart;
1491- }
1492-
1493- return message;
1494+QString TextHandler::startChat(const QString &accountId, const QVariantMap &properties)
1495+{
1496+ ChatStartingJob *job = new ChatStartingJob(this, accountId, properties);
1497+ job->startJob();
1498+ return job->objectPath();
1499 }
1500
1501 QString TextHandler::sendMessage(const QString &accountId, const QString &message, const AttachmentList &attachments, const QVariantMap &properties)
1502 {
1503- AccountEntry *account = TelepathyHelper::instance()->accountForId(accountId);
1504- if (!account) {
1505- // account does not exist
1506- return QString();
1507- }
1508-
1509- // check if the message should be sent via multimedia account
1510- // we just use fallback to 1-1 chats
1511- if (account->type() == AccountEntry::PhoneAccount) {
1512- Q_FOREACH(AccountEntry *newAccount, TelepathyHelper::instance()->accounts()) {
1513- // TODO: we have to find the multimedia account that matches the same phone number,
1514- // but for now we just pick any multimedia connected account
1515- if (newAccount->type() != AccountEntry::MultimediaAccount) {
1516- continue;
1517- }
1518- // FIXME: the fallback implementation needs to be changed to use protocol info and create a map of
1519- // accounts. Also, it needs to check connection capabilities to determine if we can send message
1520- // to offline contacts.
1521- bool shouldFallback = true;
1522- // if the account is offline, dont fallback to this account
1523- if (!newAccount->connected()) {
1524- continue;
1525- }
1526- QList<Tp::TextChannelPtr> channels = existingChannels(newAccount->accountId(), properties);
1527- // check if we have a channel for this contact already and get the contact pointer from there,
1528- // this way we avoid doing the while(op->isFinished()) all the time
1529- if (!channels.isEmpty()) {
1530- // if the contact is known, force fallback to this account
1531- Q_FOREACH(const Tp::ContactPtr &contact, channels.first()->groupContacts(false)) {
1532- Tp::Presence presence = contact->presence();
1533- shouldFallback = (presence.type() == Tp::ConnectionPresenceTypeAvailable ||
1534- presence.type() == Tp::ConnectionPresenceTypeOffline);
1535- if (!shouldFallback) {
1536- break;
1537- }
1538- }
1539- } else {
1540- QStringList participants = properties["participantIds"].toStringList();
1541- Tp::PendingOperation *op = newAccount->account()->connection()->contactManager()->contactsForIdentifiers(participants);
1542- while (!op->isFinished()) {
1543- qApp->processEvents();
1544- }
1545- Tp::PendingContacts *pc = qobject_cast<Tp::PendingContacts*>(op);
1546- if (pc) {
1547- Q_FOREACH(const Tp::ContactPtr &contact, pc->contacts()) {
1548- Tp::Presence presence = contact->presence();
1549- shouldFallback = (presence.type() == Tp::ConnectionPresenceTypeAvailable ||
1550- presence.type() == Tp::ConnectionPresenceTypeOffline);
1551- if (!shouldFallback) {
1552- break;
1553- }
1554- }
1555- }
1556- }
1557- if (shouldFallback) {
1558- account = newAccount;
1559- break;
1560- }
1561- }
1562- }
1563-
1564- // keep recipient list always sorted to be able to compare
1565- PendingMessage pendingMessage = {account->accountId(), message, attachments, properties};
1566-
1567- if (!account->connected()) {
1568- mPendingMessages.append(pendingMessage);
1569- return account->accountId();
1570- }
1571-
1572- QList<Tp::TextChannelPtr> channels = existingChannels(account->accountId(), properties);
1573- if (channels.isEmpty()) {
1574- // temporary
1575- switch(properties["chatType"].toUInt()) {
1576- case Tp::HandleTypeNone:
1577- case Tp::HandleTypeContact:
1578- mPendingMessages.append(pendingMessage);
1579- startTextChat(account->account(), pendingMessage.properties);
1580- return account->accountId();
1581- case Tp::HandleTypeRoom: {
1582- channels << startTextChatroom(account->account(), pendingMessage.properties);
1583- qDebug() << "channel returned" << channels.last();
1584-
1585- // multimedia fails if we send the message right away
1586- QTimer *timer = new QTimer(this);
1587- timer->setInterval(3000);
1588- timer->setSingleShot(true);
1589- QObject::connect(timer, &QTimer::timeout, [=]() {
1590- qDebug() << "sending message" << channels.last();
1591- connect(channels.last()->send(buildMessage(pendingMessage)),
1592- SIGNAL(finished(Tp::PendingOperation*)),
1593- SLOT(onMessageSent(Tp::PendingOperation*)));
1594-
1595- timer->deleteLater();
1596- });
1597- timer->start();
1598- return account->accountId();
1599- break;
1600- }
1601- }
1602-
1603- //startChat(account->accountId(), pendingMessage.properties);
1604- //return account->accountId();
1605- }
1606-
1607- connect(channels.last()->send(buildMessage(pendingMessage)),
1608- SIGNAL(finished(Tp::PendingOperation*)),
1609- SLOT(onMessageSent(Tp::PendingOperation*)));
1610-
1611- return account->accountId();
1612+ PendingMessage pendingMessage = {accountId, message, attachments, properties};
1613+ MessageSendingJob *job = new MessageSendingJob(this, pendingMessage);
1614+ job->startJob();
1615+
1616+ return job->objectPath();
1617 }
1618
1619 void TextHandler::acknowledgeMessages(const QVariantList &messages)
1620@@ -508,41 +116,6 @@
1621
1622 QString accountId = account->accountId();
1623 mChannels.append(channel);
1624-
1625- // check for pending messages for this channel
1626- if (mPendingMessages.isEmpty()) {
1627- return;
1628- }
1629-
1630- QList<PendingMessage>::iterator it = mPendingMessages.begin();
1631- while (it != mPendingMessages.end()) {
1632- bool found = false;
1633- Q_FOREACH(const Tp::TextChannelPtr &existingChannel, existingChannels(it->accountId, it->properties)) {
1634- if (existingChannel == channel) {
1635- sendMessage(it->accountId, it->message, it->attachments, it->properties);
1636- it = mPendingMessages.erase(it);
1637- found = true;
1638- break;
1639- }
1640- }
1641- if (!found) {
1642- ++it;
1643- }
1644- }
1645-}
1646-
1647-void TextHandler::onMessageSent(Tp::PendingOperation *op)
1648-{
1649- Tp::PendingSendMessage *psm = qobject_cast<Tp::PendingSendMessage*>(op);
1650- if(!psm) {
1651- qWarning() << "The pending object was not a pending operation:" << op;
1652- return;
1653- }
1654-
1655- if (psm->isError()) {
1656- qWarning() << "Error sending message:" << psm->errorName() << psm->errorMessage();
1657- return;
1658- }
1659 }
1660
1661 QList<Tp::TextChannelPtr> TextHandler::existingChannels(const QString &accountId, const QVariantMap &properties)
1662
1663=== modified file 'handler/texthandler.h'
1664--- handler/texthandler.h 2016-10-06 19:34:36 +0000
1665+++ handler/texthandler.h 2016-10-06 19:34:37 +0000
1666@@ -27,23 +27,16 @@
1667 #include <TelepathyQt/TextChannel>
1668 #include <TelepathyQt/ReceivedMessage>
1669 #include "dbustypes.h"
1670-
1671-struct PendingMessage {
1672- QString accountId;
1673- QString message;
1674- AttachmentList attachments;
1675- QVariantMap properties;
1676-};
1677-Q_DECLARE_METATYPE(PendingMessage)
1678+#include "messagesendingjob.h"
1679
1680 class TextHandler : public QObject
1681 {
1682 Q_OBJECT
1683 public:
1684 static TextHandler *instance();
1685- void startChat(const QString &accountId, const QVariantMap &properties);
1686- void startTextChat(const Tp::AccountPtr &account, const QVariantMap &properties);
1687- Tp::TextChannelPtr startTextChatroom(const Tp::AccountPtr &account, const QVariantMap &properties);
1688+ QString startChat(const QString &accountId, const QVariantMap &properties);
1689+
1690+ friend class MessageSendingJob;
1691
1692 public Q_SLOTS:
1693 QString sendMessage(const QString &accountId, const QString &message, const AttachmentList &attachments, const QVariantMap &properties);
1694@@ -53,18 +46,13 @@
1695 protected Q_SLOTS:
1696 void onTextChannelAvailable(Tp::TextChannelPtr channel);
1697 void onTextChannelInvalidated();
1698- void onMessageSent(Tp::PendingOperation *op);
1699- void onConnectedChanged();
1700
1701 protected:
1702 QList<Tp::TextChannelPtr> existingChannels(const QString &accountId, const QVariantMap &properties);
1703
1704 private:
1705 explicit TextHandler(QObject *parent = 0);
1706- Tp::MessagePartList buildMessage(const PendingMessage &pendingMessage);
1707-
1708 QList<Tp::TextChannelPtr> mChannels;
1709- QList<PendingMessage> mPendingMessages;
1710 };
1711
1712 #endif // TEXTHANDLER_H
1713
1714=== modified file 'libtelephonyservice/chatentry.cpp'
1715--- libtelephonyservice/chatentry.cpp 2016-10-06 19:34:36 +0000
1716+++ libtelephonyservice/chatentry.cpp 2016-10-06 19:34:37 +0000
1717@@ -1,8 +1,9 @@
1718 /*
1719- * Copyright (C) 2015 Canonical, Ltd.
1720+ * Copyright (C) 2015-2016 Canonical, Ltd.
1721 *
1722 * Authors:
1723 * Tiago Salem Herrmann <tiago.herrmann@canonical.com>
1724+ * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
1725 *
1726 * This file is part of telephony-service.
1727 *
1728@@ -22,6 +23,10 @@
1729 #include "telepathyhelper.h"
1730 #include "accountentry.h"
1731 #include "chatentry.h"
1732+#include "chatmanager.h"
1733+
1734+// FIXME: move this class to libtelephonyservice
1735+#include "handler/messagejob.h"
1736
1737 #include <TelepathyQt/Contact>
1738 #include <TelepathyQt/PendingReady>
1739@@ -29,51 +34,20 @@
1740
1741 Q_DECLARE_METATYPE(ContactChatStates)
1742
1743-ChatEntry::ChatEntry(const Tp::TextChannelPtr &channel, QObject *parent) :
1744+ChatEntry::ChatEntry(QObject *parent) :
1745 QObject(parent),
1746- mChannel(channel),
1747+ mChatType(ChatTypeNone),
1748 roomInterface(NULL),
1749 roomConfigInterface(NULL),
1750 subjectInterface(NULL)
1751 {
1752 qRegisterMetaType<ContactChatStates>();
1753- mAccount = TelepathyHelper::instance()->accountForConnection(mChannel->connection());
1754- Q_FOREACH (Tp::ContactPtr contact, mChannel->groupContacts(false)) {
1755- ContactChatState *state = new ContactChatState(contact->id(), mChannel->chatState(contact));
1756- mChatStates[contact->id()] = state;
1757- }
1758-
1759- roomInterface = channel->optionalInterface<Tp::Client::ChannelInterfaceRoomInterface>();
1760- roomConfigInterface = channel->optionalInterface<Tp::Client::ChannelInterfaceRoomConfigInterface>();
1761- subjectInterface = channel->optionalInterface<Tp::Client::ChannelInterfaceSubjectInterface>();
1762-
1763- if (roomInterface) {
1764- roomInterface->setMonitorProperties(true);
1765- connect(roomInterface, SIGNAL(propertiesChanged(const QVariantMap &,const QStringList &)),
1766- SLOT(onRoomPropertiesChanged(const QVariantMap &,const QStringList &)));
1767- }
1768- if (roomConfigInterface) {
1769- roomConfigInterface->setMonitorProperties(true);
1770- connect(roomConfigInterface, SIGNAL(propertiesChanged(const QVariantMap &,const QStringList &)),
1771- SLOT(onRoomPropertiesChanged(const QVariantMap &,const QStringList &)));
1772- }
1773- if (subjectInterface) {
1774- subjectInterface->setMonitorProperties(true);
1775- connect(subjectInterface, SIGNAL(propertiesChanged(const QVariantMap &,const QStringList &)),
1776- SLOT(onRoomPropertiesChanged(const QVariantMap &,const QStringList &)));
1777- }
1778-
1779- connect(channel.data(), SIGNAL(chatStateChanged(const Tp::ContactPtr &, Tp::ChannelChatState)),
1780- this, SLOT(onChatStateChanged(const Tp::ContactPtr &,Tp::ChannelChatState)));
1781- connect(channel.data(), SIGNAL(groupMembersChanged(const Tp::Contacts &, const Tp::Contacts &, const Tp::Contacts &,
1782- const Tp::Contacts &, const Tp::Channel::GroupMemberChangeDetails &)), this, SIGNAL(participantsChanged()));
1783 }
1784
1785 void ChatEntry::onRoomPropertiesChanged(const QVariantMap &changed,const QStringList &invalidated)
1786 {
1787 if (changed.contains("RoomName")) {
1788- mRoomName = changed["RoomName"].toString();
1789- Q_EMIT roomNameChanged();
1790+ setRoomName(changed["RoomName"].toString());
1791 }
1792 if (changed.contains("Title")) {
1793 mTitle = changed["Title"].toString();
1794@@ -81,12 +55,52 @@
1795 }
1796 }
1797
1798-QString ChatEntry::roomName()
1799+void ChatEntry::onSendingMessageFinished()
1800+{
1801+ QDBusInterface *job = qobject_cast<QDBusInterface*>(sender());
1802+ if (!job) {
1803+ return;
1804+ }
1805+
1806+ QString accountId = job->property("accountId").toString();
1807+ QString messageId = job->property("messageId").toString();
1808+ QString channelObjectPath = job->property("channelObjectPath").toString();
1809+ QVariantMap properties = job->property("properties").toMap();
1810+ qDebug() << accountId << messageId << channelObjectPath << properties;
1811+ Tp::TextChannelPtr channel = ChatManager::instance()->channelForObjectPath(channelObjectPath);
1812+
1813+ if (channel.isNull()) {
1814+ Q_EMIT messageSendingFailed(accountId, messageId, properties);
1815+ job->deleteLater();
1816+ return;
1817+ }
1818+
1819+ // even if sending the message fails, we can use the channel if available
1820+ addChannel(channel);
1821+
1822+ if (job->property("status").toInt() == MessageJob::Failed || channel.isNull()) {
1823+ Q_EMIT messageSendingFailed(accountId, messageId, properties);
1824+ job->deleteLater();
1825+ return;
1826+ }
1827+
1828+ Q_EMIT messageSent(accountId, messageId, properties);
1829+ job->deleteLater();
1830+}
1831+
1832+QString ChatEntry::roomName() const
1833 {
1834 return mRoomName;
1835 }
1836
1837-QString ChatEntry::title()
1838+void ChatEntry::setRoomName(const QString &name)
1839+{
1840+ mRoomName = name;
1841+ Q_EMIT roomNameChanged();
1842+ // FIXME: we need to invalidate the existing channels & data and start fresh
1843+}
1844+
1845+QString ChatEntry::title() const
1846 {
1847 return mTitle;
1848 }
1849@@ -101,25 +115,32 @@
1850 it.next();
1851 delete it.value();
1852 }
1853-
1854- if (roomInterface) {
1855- roomInterface->deleteLater();
1856- }
1857- if (roomConfigInterface) {
1858- roomConfigInterface->deleteLater();
1859- }
1860- if (subjectInterface) {
1861- subjectInterface->deleteLater();
1862- }
1863-}
1864-
1865-QString ChatEntry::chatId()
1866-{
1867- if (mChannel) {
1868- return mChannel->targetId();
1869- }
1870- return QString();
1871-}
1872+}
1873+
1874+QString ChatEntry::chatId() const
1875+{
1876+ return mChatId;
1877+}
1878+
1879+void ChatEntry::setChatId(const QString &id)
1880+{
1881+ mChatId = id;
1882+ Q_EMIT chatIdChanged();
1883+ // FIXME: we need to invalidate the existing channels & data and start fresh
1884+}
1885+
1886+QString ChatEntry::accountId() const
1887+{
1888+ return mAccountId;
1889+}
1890+
1891+void ChatEntry::setAccountId(const QString &id)
1892+{
1893+ mAccountId = id;
1894+ Q_EMIT accountIdChanged();
1895+ // FIXME: we need to invalidate the existing channels & data and start fresh
1896+}
1897+
1898
1899 void ChatEntry::onChatStateChanged(const Tp::ContactPtr &contact, Tp::ChannelChatState state)
1900 {
1901@@ -133,28 +154,28 @@
1902 Q_EMIT chatStatesChanged();
1903 }
1904
1905-ChatEntry::ChatType ChatEntry::chatType()
1906-{
1907- return (ChatType)mChannel->targetHandleType();
1908-}
1909-
1910-Tp::TextChannelPtr ChatEntry::channel()
1911-{
1912- return mChannel;
1913-}
1914-
1915-QStringList ChatEntry::participants()
1916-{
1917- QStringList participantList;
1918- Q_FOREACH (Tp::ContactPtr contact, mChannel->groupContacts(false)) {
1919- participantList << contact->id();
1920- }
1921- return participantList;
1922-}
1923-
1924-AccountEntry *ChatEntry::account()
1925-{
1926- return mAccount;
1927+ChatEntry::ChatType ChatEntry::chatType() const
1928+{
1929+ return mChatType;
1930+}
1931+
1932+void ChatEntry::setChatType(ChatEntry::ChatType type)
1933+{
1934+ mChatType = type;
1935+ Q_EMIT chatTypeChanged();
1936+}
1937+
1938+QStringList ChatEntry::participants() const
1939+{
1940+ return mParticipants;
1941+}
1942+
1943+void ChatEntry::setParticipants(const QStringList &participants)
1944+{
1945+ mParticipants = participants;
1946+ Q_EMIT participantsChanged();
1947+
1948+ // FIXME: we need to invalidate the existing channels & data and start fresh
1949 }
1950
1951 QQmlListProperty<ContactChatState> ChatEntry::chatStates()
1952@@ -179,3 +200,157 @@
1953 }
1954 return entry->mChatStates.values()[index];
1955 }
1956+
1957+void ChatEntry::sendMessage(const QString &accountId, const QString &message, const QVariant &attachments, const QVariantMap &properties)
1958+{
1959+ QString objPath = ChatManager::instance()->sendMessage(accountId, message, attachments, properties);
1960+ QDBusInterface *sendingJob = new QDBusInterface(TelepathyHelper::instance()->handlerInterface()->service(), objPath,
1961+ "com.canonical.TelephonyServiceHandler.MessageSendingJob");
1962+ qDebug() << sendingJob->isValid();
1963+ sendingJob->dumpObjectInfo();
1964+ connect(sendingJob, SIGNAL(finished()), SLOT(onSendingMessageFinished()));
1965+ QDBusReply<QString> reply = sendingJob->call("Introspect");
1966+ qDebug() << reply.value();
1967+}
1968+
1969+void ChatEntry::classBegin()
1970+{
1971+ // nothing to do here
1972+}
1973+
1974+void ChatEntry::componentComplete()
1975+{
1976+ QVariantMap properties = generateProperties();
1977+ QList<Tp::TextChannelPtr> channels = ChatManager::instance()->channelForProperties(properties);
1978+ QList<AccountEntry*> accounts;
1979+
1980+ if (!channels.isEmpty()) {
1981+ setChannels(channels);
1982+ }
1983+
1984+ // now filter out the Phone accounts from the accounts list
1985+ Q_FOREACH(AccountEntry *account, TelepathyHelper::instance()->activeAccounts(true)) {
1986+ if (account->type() != AccountEntry::PhoneAccount) {
1987+ accounts << account;
1988+ }
1989+ }
1990+
1991+ // now check that we have channels for all !Phone accounts
1992+ // we need channels to be able to show typing notifications
1993+ Q_FOREACH(const Tp::TextChannelPtr &channel, channels) {
1994+ AccountEntry *account = TelepathyHelper::instance()->accountForConnection(channel->connection());
1995+ accounts.removeAll(account);
1996+ }
1997+
1998+ // if there is any remaining account, request to start chatting using the account
1999+ Q_FOREACH(AccountEntry *account, accounts) {
2000+ ChatManager::instance()->startChat(account->accountId(), properties);
2001+ }
2002+
2003+ connect(ChatManager::instance(), &ChatManager::textChannelAvailable,
2004+ this, &ChatEntry::onTextChannelAvailable);
2005+}
2006+
2007+void ChatEntry::setChannels(const QList<Tp::TextChannelPtr> &channels)
2008+{
2009+ Q_FOREACH(const Tp::TextChannelPtr &channel, channels) {
2010+ addChannel(channel);
2011+ }
2012+}
2013+
2014+void ChatEntry::addChannel(const Tp::TextChannelPtr &channel)
2015+{
2016+ qDebug() << "adding channel" << channel->objectPath();
2017+ if (mChannels.contains(channel)) {
2018+ return;
2019+ }
2020+
2021+ roomInterface = channel->optionalInterface<Tp::Client::ChannelInterfaceRoomInterface>();
2022+ roomConfigInterface = channel->optionalInterface<Tp::Client::ChannelInterfaceRoomConfigInterface>();
2023+ subjectInterface = channel->optionalInterface<Tp::Client::ChannelInterfaceSubjectInterface>();
2024+
2025+ if (roomInterface) {
2026+ roomInterface->setProperty("channel", QVariant::fromValue(channel.data()));
2027+ roomInterface->setMonitorProperties(true);
2028+ connect(roomInterface, SIGNAL(propertiesChanged(const QVariantMap &,const QStringList &)),
2029+ SLOT(onRoomPropertiesChanged(const QVariantMap &,const QStringList &)));
2030+ }
2031+ if (roomConfigInterface) {
2032+ roomConfigInterface->setProperty("channel", QVariant::fromValue(channel.data()));
2033+ roomConfigInterface->setMonitorProperties(true);
2034+ connect(roomConfigInterface, SIGNAL(propertiesChanged(const QVariantMap &,const QStringList &)),
2035+ SLOT(onRoomPropertiesChanged(const QVariantMap &,const QStringList &)));
2036+ }
2037+ if (subjectInterface) {
2038+ subjectInterface->setProperty("channel", QVariant::fromValue(channel.data()));
2039+ subjectInterface->setMonitorProperties(true);
2040+ connect(subjectInterface, SIGNAL(propertiesChanged(const QVariantMap &,const QStringList &)),
2041+ SLOT(onRoomPropertiesChanged(const QVariantMap &,const QStringList &)));
2042+ }
2043+
2044+ connect(channel.data(), SIGNAL(chatStateChanged(const Tp::ContactPtr &, Tp::ChannelChatState)),
2045+ this, SLOT(onChatStateChanged(const Tp::ContactPtr &,Tp::ChannelChatState)));
2046+ connect(channel.data(), SIGNAL(groupMembersChanged(const Tp::Contacts &, const Tp::Contacts &, const Tp::Contacts &,
2047+ const Tp::Contacts &, const Tp::Channel::GroupMemberChangeDetails &)), this, SIGNAL(participantsChanged()));
2048+ connect(channel.data(), SIGNAL(invalidated(Tp::DBusProxy*,const QString&, const QString&)),
2049+ this, SLOT(onChannelInvalidated()));
2050+
2051+ Q_FOREACH (Tp::ContactPtr contact, channel->groupContacts(false)) {
2052+ // FIXME: we should not create new chat states for contacts already found in previous channels
2053+ ContactChatState *state = new ContactChatState(contact->id(), channel->chatState(contact));
2054+ mChatStates[contact->id()] = state;
2055+ }
2056+
2057+ // now fill the properties with the data from the channel
2058+ if (chatType() != (ChatType)channel->targetHandleType()) {
2059+ setChatType((ChatType)channel->targetHandleType());
2060+ }
2061+ if (chatType() == ChatTypeRoom && mChatId != channel->targetId()) {
2062+ setChatId(channel->targetId());
2063+ }
2064+
2065+ mChannels << channel;
2066+}
2067+
2068+QVariantMap ChatEntry::generateProperties() const
2069+{
2070+ QVariantMap properties;
2071+
2072+ properties["participantIds"] = participants();
2073+ properties["chatType"] = (int)chatType();
2074+ properties["chatId"] = chatId();
2075+ properties["threadId"] = chatId();
2076+
2077+ if (chatType() == ChatEntry::ChatTypeRoom) {
2078+ properties["accountId"] = accountId();
2079+ }
2080+
2081+ return properties;
2082+}
2083+
2084+void ChatEntry::onTextChannelAvailable(const Tp::TextChannelPtr &channel)
2085+{
2086+ if (ChatManager::channelMatchProperties(channel, generateProperties())) {
2087+ addChannel(channel);
2088+ }
2089+}
2090+
2091+void ChatEntry::onChannelInvalidated()
2092+{
2093+ qDebug() << __PRETTY_FUNCTION__;
2094+ Tp::TextChannelPtr channel(qobject_cast<Tp::TextChannel*>(sender()));
2095+ mChannels.removeAll(channel);
2096+
2097+ if (roomInterface && roomInterface->property("channel").value<Tp::TextChannel*>() == channel.data()) {
2098+ roomInterface->disconnect(this);
2099+ roomInterface = 0;
2100+ }
2101+ if (roomConfigInterface && roomConfigInterface->property("channel").value<Tp::TextChannel*>() == channel.data()) {
2102+ roomConfigInterface->disconnect(this);
2103+ roomConfigInterface = 0;
2104+ }
2105+ if (subjectInterface && subjectInterface->property("channel").value<Tp::TextChannel*>() == channel.data()) {
2106+ subjectInterface->disconnect(this);
2107+ subjectInterface = 0;
2108+ }
2109+}
2110
2111=== modified file 'libtelephonyservice/chatentry.h'
2112--- libtelephonyservice/chatentry.h 2016-10-06 19:34:36 +0000
2113+++ libtelephonyservice/chatentry.h 2016-10-06 19:34:37 +0000
2114@@ -1,8 +1,9 @@
2115 /*
2116- * Copyright (C) 2015 Canonical, Ltd.
2117+ * Copyright (C) 2015-2016 Canonical, Ltd.
2118 *
2119 * Authors:
2120 * Tiago Salem Herrmann <tiago.herrmann@canonical.com>
2121+ * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
2122 *
2123 * This file is part of telephony-service.
2124 *
2125@@ -23,6 +24,7 @@
2126 #define CHATENTRY_H
2127
2128 #include <QObject>
2129+#include <QQmlParserStatus>
2130 #include <TelepathyQt/TextChannel>
2131
2132 class AccountEntry;
2133@@ -42,6 +44,7 @@
2134 }
2135 Q_SIGNALS:
2136 void stateChanged();
2137+
2138 private:
2139 QString mContactId;
2140 int mState;
2141@@ -49,14 +52,15 @@
2142
2143 typedef QList<ContactChatState* > ContactChatStates;
2144
2145-class ChatEntry : public QObject
2146+class ChatEntry : public QObject, public QQmlParserStatus
2147 {
2148 Q_OBJECT
2149- Q_PROPERTY(AccountEntry* account READ account CONSTANT)
2150- Q_PROPERTY(ChatType chatType READ chatType CONSTANT)
2151- Q_PROPERTY(QStringList participants READ participants NOTIFY participantsChanged)
2152- Q_PROPERTY(QString roomName READ roomName NOTIFY roomNameChanged)
2153- Q_PROPERTY(QString chatId READ chatId CONSTANT)
2154+ Q_INTERFACES(QQmlParserStatus)
2155+ Q_PROPERTY(ChatType chatType READ chatType WRITE setChatType NOTIFY chatTypeChanged)
2156+ Q_PROPERTY(QStringList participants READ participants WRITE setParticipants NOTIFY participantsChanged)
2157+ Q_PROPERTY(QString roomName READ roomName WRITE setRoomName NOTIFY roomNameChanged)
2158+ Q_PROPERTY(QString chatId READ chatId WRITE setChatId NOTIFY chatIdChanged)
2159+ Q_PROPERTY(QString accountId READ accountId WRITE setAccountId NOTIFY accountIdChanged)
2160 Q_PROPERTY(QString title READ title NOTIFY titleChanged)
2161 Q_PROPERTY(QQmlListProperty<ContactChatState> chatStates
2162 READ chatStates
2163@@ -79,35 +83,65 @@
2164 ChannelChatStateComposing = Tp::ChannelChatStateComposing
2165 };
2166
2167- explicit ChatEntry(const Tp::TextChannelPtr &channel, QObject *parent = 0);
2168+ explicit ChatEntry(QObject *parent = 0);
2169 ~ChatEntry();
2170- Tp::TextChannelPtr channel();
2171- AccountEntry *account();
2172 QQmlListProperty<ContactChatState> chatStates();
2173- QStringList participants();
2174- ChatType chatType();
2175- QString chatId();
2176- QString roomName();
2177- QString title();
2178+ QStringList participants() const;
2179+ void setParticipants(const QStringList &participants);
2180+ ChatType chatType() const;
2181+ void setChatType(ChatType type);
2182+ QString chatId() const;
2183+ void setChatId(const QString &id);
2184+ QString accountId() const;
2185+ void setAccountId(const QString &id);
2186+ QString roomName() const;
2187+ void setRoomName(const QString &name);
2188+ QString title() const;
2189 static int chatStatesCount(QQmlListProperty<ContactChatState> *p);
2190 static ContactChatState *chatStatesAt(QQmlListProperty<ContactChatState> *p, int index);
2191
2192+ // QML parser status
2193+ void classBegin();
2194+ void componentComplete();
2195+
2196+public Q_SLOTS:
2197+ // FIXME: void or return something?
2198+ void sendMessage(const QString &accountId, const QString &message, const QVariant &attachments = QVariant(), const QVariantMap &properties = QVariantMap());
2199+
2200+protected:
2201+ void setChannels(const QList<Tp::TextChannelPtr> &channels);
2202+ void addChannel(const Tp::TextChannelPtr &channel);
2203+
2204+ QVariantMap generateProperties() const;
2205+
2206 private Q_SLOTS:
2207+ void onTextChannelAvailable(const Tp::TextChannelPtr &channel);
2208+ void onChannelInvalidated();
2209 void onChatStateChanged(const Tp::ContactPtr &contact, Tp::ChannelChatState state);
2210 void onRoomPropertiesChanged(const QVariantMap &changed,const QStringList &invalidated);
2211+ void onSendingMessageFinished();
2212
2213 Q_SIGNALS:
2214+ void chatTypeChanged();
2215+ void chatIdChanged();
2216+ void accountIdChanged();
2217 void chatStatesChanged();
2218 void participantsChanged();
2219 void roomNameChanged();
2220 void titleChanged();
2221
2222+ void messageSent(const QString &accountId, const QString &messageId, const QVariantMap &properties);
2223+ void messageSendingFailed(const QString &accountId, const QString &messageId, const QVariantMap &properties);
2224+
2225 private:
2226- AccountEntry *mAccount;
2227- Tp::TextChannelPtr mChannel;
2228+ QList<Tp::TextChannelPtr> mChannels;
2229+ QStringList mParticipants;
2230 QMap<QString, ContactChatState*> mChatStates;
2231 QString mRoomName;
2232 QString mTitle;
2233+ QString mChatId;
2234+ QString mAccountId;
2235+ ChatType mChatType;
2236 Tp::Client::ChannelInterfaceRoomInterface *roomInterface;
2237 Tp::Client::ChannelInterfaceRoomConfigInterface *roomConfigInterface;
2238 Tp::Client::ChannelInterfaceSubjectInterface *subjectInterface;
2239
2240=== modified file 'libtelephonyservice/chatmanager.cpp'
2241--- libtelephonyservice/chatmanager.cpp 2016-10-06 19:34:36 +0000
2242+++ libtelephonyservice/chatmanager.cpp 2016-10-06 19:34:37 +0000
2243@@ -1,5 +1,5 @@
2244 /*
2245- * Copyright (C) 2012-2013 Canonical, Ltd.
2246+ * Copyright (C) 2012-2016 Canonical, Ltd.
2247 *
2248 * Authors:
2249 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
2250@@ -20,6 +20,7 @@
2251 */
2252
2253 #include "chatmanager.h"
2254+#include "chatentry.h"
2255 #include "telepathyhelper.h"
2256 #include "phoneutils.h"
2257 #include "config.h"
2258@@ -61,8 +62,7 @@
2259 }
2260
2261 ChatManager::ChatManager(QObject *parent)
2262-: QObject(parent),
2263- mReady(TelepathyHelper::instance()->ready())
2264+: QObject(parent)
2265 {
2266 qDBusRegisterMetaType<AttachmentList>();
2267 qDBusRegisterMetaType<AttachmentStruct>();
2268@@ -70,29 +70,13 @@
2269 mMessagesAckTimer.setInterval(25);
2270 mMessagesAckTimer.setSingleShot(true);
2271 connect(TelepathyHelper::instance(), SIGNAL(channelObserverUnregistered()), SLOT(onChannelObserverUnregistered()));
2272- connect(TelepathyHelper::instance(), SIGNAL(setupReady()), SLOT(onTelepathyReady()));
2273 connect(&mMessagesAckTimer, SIGNAL(timeout()), SLOT(onAckTimerTriggered()));
2274 connect(TelepathyHelper::instance(), SIGNAL(setupReady()), SLOT(onConnectedChanged()));
2275 }
2276
2277-void ChatManager::onTelepathyReady()
2278-{
2279- mReady = true;
2280- Q_FOREACH(const Tp::TextChannelPtr &channel, mPendingChannels) {
2281- onTextChannelAvailable(channel);
2282- }
2283- mPendingChannels.clear();
2284-}
2285-
2286 void ChatManager::onChannelObserverUnregistered()
2287 {
2288- QList<ChatEntry*> tmp = mChatEntries;
2289- mChatEntries.clear();
2290- Q_EMIT chatsChanged();
2291- Q_FOREACH(ChatEntry *entry, tmp) {
2292- // for some reason deleteLater is not working
2293- delete entry;
2294- }
2295+ mTextChannels.clear();
2296 }
2297
2298 void ChatManager::onConnectedChanged()
2299@@ -108,6 +92,13 @@
2300 return manager;
2301 }
2302
2303+void ChatManager::startChat(const QString &accountId, const QVariantMap &properties)
2304+{
2305+ QVariantMap propMap = convertPropertiesForDBus(properties);
2306+ QDBusInterface *phoneAppHandler = TelepathyHelper::instance()->handlerInterface();
2307+ phoneAppHandler->asyncCall("StartChat", accountId, propMap);
2308+}
2309+
2310 QString ChatManager::sendMessage(const QString &accountId, const QString &message, const QVariant &attachments, const QVariantMap &properties)
2311 {
2312 AccountEntry *account = TelepathyHelper::instance()->accountForId(accountId);
2313@@ -164,86 +155,138 @@
2314 return QString();
2315 }
2316
2317+QList<Tp::TextChannelPtr> ChatManager::channelForProperties(const QVariantMap &properties)
2318+{
2319+ // FIXME: remove before releasing
2320+ qDebug() << __PRETTY_FUNCTION__ << properties;
2321+ QList<Tp::TextChannelPtr> channels;
2322+
2323+
2324+ Q_FOREACH (Tp::TextChannelPtr channel, mTextChannels) {
2325+ if (channelMatchProperties(channel, properties)) {
2326+ channels << channel;
2327+ }
2328+ }
2329+
2330+ return channels;
2331+}
2332+
2333+Tp::TextChannelPtr ChatManager::channelForObjectPath(const QString &objectPath)
2334+{
2335+ Q_FOREACH(Tp::TextChannelPtr channel, mTextChannels) {
2336+ if (channel->objectPath() == objectPath) {
2337+ return channel;
2338+ }
2339+ }
2340+ return Tp::TextChannelPtr();
2341+}
2342+
2343+bool ChatManager::channelMatchProperties(const Tp::TextChannelPtr &channel, const QVariantMap &properties)
2344+{
2345+ QVariantMap propMap = properties;
2346+ ChatEntry::ChatType chatType = ChatEntry::ChatTypeNone;
2347+
2348+ QStringList participants;
2349+ // participants coming from qml are variants
2350+ if (properties.contains("participantIds")) {
2351+ participants = properties["participantIds"].toStringList();
2352+ if (!participants.isEmpty()) {
2353+ propMap["participantIds"] = participants;
2354+ }
2355+ }
2356+
2357+ if (participants.isEmpty() && propMap.contains("participants")) {
2358+ // try to generate list of participants from "participants"
2359+ Q_FOREACH(const QVariant &participantMap, propMap["participants"].toList()) {
2360+ if (participantMap.toMap().contains("identifier")) {
2361+ participants << participantMap.toMap()["identifier"].toString();
2362+ }
2363+ }
2364+ if (!participants.isEmpty()) {
2365+ propMap["participantIds"] = participants;
2366+ }
2367+ }
2368+
2369+ if (properties.contains("chatType")) {
2370+ chatType = (ChatEntry::ChatType)properties["chatType"].toInt();
2371+ } else {
2372+ if (participants.length() == 1) {
2373+ chatType = ChatEntry::ChatTypeContact;
2374+ }
2375+ }
2376+
2377+ QString accountId;
2378+ if (propMap.contains("accountId")) {
2379+ accountId = propMap["accountId"].toString();
2380+ }
2381+
2382+ if (participants.count() == 0 && chatType == ChatEntry::ChatTypeContact) {
2383+ return false;
2384+ }
2385+
2386+ AccountEntry *account = TelepathyHelper::instance()->accountForConnection(channel->connection());
2387+ if (!account) {
2388+ return false;
2389+ }
2390+
2391+ // only channels of the correct type should be returned
2392+ if ((ChatEntry::ChatType)channel->targetHandleType() != chatType) {
2393+ return false;
2394+ }
2395+
2396+ if (chatType == ChatEntry::ChatTypeRoom) {
2397+ QString chatId = propMap["threadId"].toString();
2398+ if (!chatId.isEmpty() && channel->targetId() == chatId) {
2399+ // if we are filtering by one specific accountId, make sure it matches
2400+ if (!accountId.isEmpty() && accountId != account->accountId()) {
2401+ return false;
2402+ }
2403+
2404+ return true;
2405+ }
2406+ return false;
2407+ }
2408+
2409+ Tp::Contacts contacts = channel->groupContacts(false);
2410+ if (participants.count() != contacts.count()) {
2411+ return false;
2412+ }
2413+ int participantCount = 0;
2414+ // iterate over participants
2415+ Q_FOREACH (const Tp::ContactPtr &contact, contacts) {
2416+ if (account->type() == AccountEntry::PhoneAccount || account->type() == AccountEntry::MultimediaAccount) {
2417+ Q_FOREACH(const QString &participant, participants) {
2418+ if (PhoneUtils::comparePhoneNumbers(participant, contact->id()) > PhoneUtils::NO_MATCH) {
2419+ participantCount++;
2420+ break;
2421+ }
2422+ }
2423+ continue;
2424+ }
2425+ if (participants.contains(contact->id())) {
2426+ participantCount++;
2427+ } else {
2428+ break;
2429+ }
2430+ }
2431+ return (participantCount == participants.count());
2432+}
2433+
2434 void ChatManager::onTextChannelAvailable(Tp::TextChannelPtr channel)
2435 {
2436- if (!mReady) {
2437- mPendingChannels.append(channel);
2438- return;
2439- }
2440- ChatEntry *chatEntry = new ChatEntry(channel, this);
2441- mChatEntries.append(chatEntry);
2442-
2443- connect(channel.data(),
2444- SIGNAL(messageReceived(Tp::ReceivedMessage)),
2445- SLOT(onMessageReceived(Tp::ReceivedMessage)));
2446- connect(channel.data(),
2447- SIGNAL(messageSent(Tp::Message,Tp::MessageSendingFlags,QString)),
2448- SLOT(onMessageSent(Tp::Message,Tp::MessageSendingFlags,QString)));
2449+ mTextChannels << channel;
2450 connect(channel.data(),
2451 SIGNAL(invalidated(Tp::DBusProxy*,const QString&, const QString&)),
2452 SLOT(onChannelInvalidated()));
2453
2454- Q_FOREACH(const Tp::ReceivedMessage &message, channel->messageQueue()) {
2455- onMessageReceived(message);
2456- }
2457-
2458- Q_EMIT chatsChanged();
2459- Q_EMIT chatEntryCreated(chatEntry->account()->accountId(), chatEntry->participants(), chatEntry);
2460+ Q_EMIT textChannelAvailable(channel);
2461 }
2462
2463 void ChatManager::onChannelInvalidated()
2464 {
2465 Tp::TextChannelPtr channel(qobject_cast<Tp::TextChannel*>(sender()));
2466- ChatEntry *chatEntry = chatEntryForChannel(channel);
2467- if (chatEntry) {
2468- mChatEntries.removeAll(chatEntry);
2469- // for some reason deleteLater is not working
2470- delete chatEntry;
2471- Q_EMIT chatsChanged();
2472- }
2473-}
2474-
2475-ChatEntry *ChatManager::chatEntryForChannel(const Tp::TextChannelPtr &channel)
2476-{
2477- Q_FOREACH (ChatEntry *chatEntry, mChatEntries) {
2478- if (channel == chatEntry->channel()) {
2479- return chatEntry;
2480- }
2481- }
2482- return NULL;
2483-}
2484-
2485-void ChatManager::onMessageReceived(const Tp::ReceivedMessage &message)
2486-{
2487- // ignore delivery reports for now
2488- // FIXME: we need to handle errors on sending messages at some point
2489- if (message.isDeliveryReport()) {
2490- return;
2491- }
2492-
2493- if (!message.sender()) {
2494- return;
2495- }
2496-
2497- Q_EMIT messageReceived(message.sender()->id(), message.text(), message.received(), message.messageToken(), true);
2498-}
2499-
2500-void ChatManager::onMessageSent(const Tp::Message &sentMessage, const Tp::MessageSendingFlags flags, const QString &message)
2501-{
2502- Q_UNUSED(message)
2503- Q_UNUSED(flags)
2504-
2505- Tp::TextChannel *channel = qobject_cast<Tp::TextChannel*>(sender());
2506- if (!channel) {
2507- return;
2508- }
2509-
2510- QStringList recipients;
2511- Q_FOREACH(const Tp::ContactPtr &contact, channel->groupContacts(false)) {
2512- recipients << contact->id();
2513- }
2514-
2515- Q_EMIT messageSent(recipients, sentMessage.text());
2516+ mTextChannels.removeAll(channel);
2517+ Q_EMIT textChannelInvalidated(channel);
2518 }
2519
2520 void ChatManager::acknowledgeMessage(const QVariantMap &properties)
2521@@ -267,112 +310,3 @@
2522
2523 mMessagesToAck.clear();
2524 }
2525-
2526-QList<ChatEntry*> ChatManager::chatEntries() const
2527-{
2528- return mChatEntries;
2529-}
2530-
2531-ChatEntry *ChatManager::chatEntryForProperties(const QString &accountId, const QVariantMap &properties, bool create)
2532-{
2533- QVariantMap propMap = properties;
2534- int chatType = 0;
2535-
2536- QStringList participants;
2537- // participants coming from qml are variants
2538- if (properties.contains("participantIds")) {
2539- participants = properties["participantIds"].toStringList();
2540- if (!participants.isEmpty()) {
2541- propMap["participantIds"] = participants;
2542- }
2543- }
2544-
2545- if (participants.isEmpty() && propMap.contains("participants")) {
2546- // try to generate list of participants from "participants"
2547- Q_FOREACH(const QVariant &participantMap, propMap["participants"].toList()) {
2548- if (participantMap.toMap().contains("identifier")) {
2549- participants << participantMap.toMap()["identifier"].toString();
2550- }
2551- }
2552- if (!participants.isEmpty()) {
2553- propMap["participantIds"] = participants;
2554- }
2555- }
2556-
2557- if (properties.contains("chatType")) {
2558- chatType = properties["chatType"].toInt();
2559- } else {
2560- if (participants.length() == 1) {
2561- chatType = 1;
2562- }
2563- }
2564-
2565- if ((participants.count() == 0 && chatType == 1) || accountId.isEmpty()) {
2566- return NULL;
2567- }
2568-
2569- AccountEntry *account = TelepathyHelper::instance()->accountForId(accountId);
2570-
2571- if (!account) {
2572- return NULL;
2573- }
2574-
2575- Q_FOREACH (ChatEntry *chatEntry, mChatEntries) {
2576- int participantCount = 0;
2577-
2578- if (chatType == 2) {
2579- QString roomId = propMap["threadId"].toString();
2580- if (!roomId.isEmpty() && chatEntry->chatType() == 2 && roomId == chatEntry->chatId()) {
2581- return chatEntry;
2582- }
2583- continue;
2584- }
2585-
2586- Tp::Contacts contacts = chatEntry->channel()->groupContacts(false);
2587- if (participants.count() != contacts.count()) {
2588- continue;
2589- }
2590- // iterate over participants
2591- Q_FOREACH (const Tp::ContactPtr &contact, contacts) {
2592- if (account->type() == AccountEntry::PhoneAccount || account->type() == AccountEntry::MultimediaAccount) {
2593- Q_FOREACH(const QString &participant, participants) {
2594- if (PhoneUtils::comparePhoneNumbers(participant, contact->id()) > PhoneUtils::NO_MATCH) {
2595- participantCount++;
2596- break;
2597- }
2598- }
2599- continue;
2600- }
2601- if (participants.contains(contact->id())) {
2602- participantCount++;
2603- } else {
2604- break;
2605- }
2606- }
2607- if (participantCount == participants.count()) {
2608- return chatEntry;
2609- }
2610- }
2611-
2612- if (create) {
2613- QDBusInterface *phoneAppHandler = TelepathyHelper::instance()->handlerInterface();
2614- phoneAppHandler->call("StartChat", accountId, propMap);
2615- }
2616- return NULL;
2617-}
2618-
2619-QQmlListProperty<ChatEntry> ChatManager::chats()
2620-{
2621- return QQmlListProperty<ChatEntry>(this, 0, chatCount, chatAt);
2622-}
2623-
2624-int ChatManager::chatCount(QQmlListProperty<ChatEntry> *p)
2625-{
2626- return ChatManager::instance()->chatEntries().count();
2627-}
2628-
2629-ChatEntry *ChatManager::chatAt(QQmlListProperty<ChatEntry> *p, int index)
2630-{
2631- return ChatManager::instance()->chatEntries()[index];
2632-}
2633-
2634
2635=== modified file 'libtelephonyservice/chatmanager.h'
2636--- libtelephonyservice/chatmanager.h 2016-10-06 19:34:36 +0000
2637+++ libtelephonyservice/chatmanager.h 2016-10-06 19:34:37 +0000
2638@@ -1,5 +1,5 @@
2639 /*
2640- * Copyright (C) 2012 Canonical, Ltd.
2641+ * Copyright (C) 2012-2016 Canonical, Ltd.
2642 *
2643 * Authors:
2644 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
2645@@ -28,57 +28,44 @@
2646 #include <TelepathyQt/TextChannel>
2647 #include <TelepathyQt/ReceivedMessage>
2648 #include "dbustypes.h"
2649-#include "chatentry.h"
2650
2651 class ChatManager : public QObject
2652 {
2653 Q_OBJECT
2654- Q_PROPERTY(QQmlListProperty<ChatEntry> chats
2655- READ chats
2656- NOTIFY chatsChanged)
2657 public:
2658 static ChatManager *instance();
2659
2660- Q_INVOKABLE QString sendMessage(const QString &accountId, const QString &message, const QVariant &attachments = QVariant(), const QVariantMap &properties = QVariantMap());
2661- Q_INVOKABLE ChatEntry *chatEntryForProperties(const QString &accountId, const QVariantMap &properties, bool create = false);
2662+ void startChat(const QString &accountId, const QVariantMap &properties);
2663+ QString sendMessage(const QString &accountId, const QString &message, const QVariant &attachments = QVariant(), const QVariantMap &properties = QVariantMap());
2664+ QList<Tp::TextChannelPtr> channelForProperties(const QVariantMap &properties);
2665+ Tp::TextChannelPtr channelForObjectPath(const QString &objectPath);
2666
2667- QQmlListProperty<ChatEntry> chats();
2668- static int chatCount(QQmlListProperty<ChatEntry> *p);
2669- static ChatEntry* chatAt(QQmlListProperty<ChatEntry> *p, int index);
2670+ static bool channelMatchProperties(const Tp::TextChannelPtr &channel, const QVariantMap &properties);
2671
2672 Q_SIGNALS:
2673- void messageReceived(const QString &sender, const QString &message, const QDateTime &timestamp, const QString &messageId, bool unread);
2674- void messageSent(const QStringList &recipients, const QString &message);
2675- void chatsChanged();
2676- void chatEntryCreated(QString accountId, QStringList participants, ChatEntry *chatEntry);
2677+ void textChannelAvailable(Tp::TextChannelPtr);
2678+ void textChannelInvalidated(Tp::TextChannelPtr);
2679
2680 public Q_SLOTS:
2681 void onTextChannelAvailable(Tp::TextChannelPtr channel);
2682 void onChannelInvalidated();
2683 void onConnectedChanged();
2684- void onMessageReceived(const Tp::ReceivedMessage &message);
2685- void onMessageSent(const Tp::Message &sentMessage, const Tp::MessageSendingFlags flags, const QString &message);
2686
2687 void acknowledgeMessage(const QVariantMap &properties);
2688 void acknowledgeAllMessages(const QVariantMap &properties);
2689
2690 private Q_SLOTS:
2691 void onChannelObserverUnregistered();
2692- void onTelepathyReady();
2693
2694 protected Q_SLOTS:
2695 void onAckTimerTriggered();
2696
2697 private:
2698 explicit ChatManager(QObject *parent = 0);
2699- ChatEntry *chatEntryForChannel(const Tp::TextChannelPtr &channel);
2700- QList<ChatEntry*> chatEntries() const;
2701
2702- mutable QList<ChatEntry*> mChatEntries;
2703 QVariantList mMessagesToAck;
2704- QList<Tp::TextChannelPtr> mPendingChannels;
2705+ QList<Tp::TextChannelPtr> mTextChannels;
2706 QTimer mMessagesAckTimer;
2707- bool mReady;
2708 };
2709
2710 #endif // CHATMANAGER_H
2711
2712=== modified file 'libtelephonyservice/telepathyhelper.cpp'
2713--- libtelephonyservice/telepathyhelper.cpp 2016-10-06 19:34:36 +0000
2714+++ libtelephonyservice/telepathyhelper.cpp 2016-10-06 19:34:37 +0000
2715@@ -1,5 +1,5 @@
2716 /*
2717- * Copyright (C) 2012-2015 Canonical, Ltd.
2718+ * Copyright (C) 2012-2016 Canonical, Ltd.
2719 *
2720 * Authors:
2721 * Tiago Salem Herrmann <tiago.herrmann@canonical.com>
2722@@ -149,11 +149,14 @@
2723 return mAccounts;
2724 }
2725
2726-QList<AccountEntry*> TelepathyHelper::activeAccounts() const
2727+QList<AccountEntry*> TelepathyHelper::activeAccounts(bool includeMultimedia) const
2728 {
2729 QList<AccountEntry*> activeAccountList;
2730 Q_FOREACH(AccountEntry *account, mAccounts) {
2731- if (account->active() && account->type() != AccountEntry::MultimediaAccount) {
2732+ if (account->active()) {
2733+ if (account->type() == AccountEntry::MultimediaAccount && !includeMultimedia) {
2734+ continue;
2735+ }
2736 activeAccountList << account;
2737 }
2738 }
2739
2740=== modified file 'libtelephonyservice/telepathyhelper.h'
2741--- libtelephonyservice/telepathyhelper.h 2016-10-06 19:34:36 +0000
2742+++ libtelephonyservice/telepathyhelper.h 2016-10-06 19:34:37 +0000
2743@@ -1,5 +1,5 @@
2744 /*
2745- * Copyright (C) 2012 Canonical, Ltd.
2746+ * Copyright (C) 2012-2016 Canonical, Ltd.
2747 *
2748 * Authors:
2749 * Tiago Salem Herrmann <tiago.herrmann@canonical.com>
2750@@ -75,7 +75,7 @@
2751 static TelepathyHelper *instance();
2752 QList<AccountEntry*> accounts() const;
2753 QList<AccountEntry*> phoneAccounts() const;
2754- QList<AccountEntry*> activeAccounts() const;
2755+ QList<AccountEntry*> activeAccounts(bool includeMultimedia=false) const;
2756 QQmlListProperty<AccountEntry> qmlAccounts();
2757 QQmlListProperty<AccountEntry> qmlPhoneAccounts();
2758 QQmlListProperty<AccountEntry> qmlActiveAccounts();
2759
2760=== modified file 'tests/handler/HandlerTest.cpp'
2761--- tests/handler/HandlerTest.cpp 2016-10-06 19:34:36 +0000
2762+++ tests/handler/HandlerTest.cpp 2016-10-06 19:34:37 +0000
2763@@ -18,6 +18,7 @@
2764
2765 #include <QtCore/QObject>
2766 #include <QtTest/QtTest>
2767+#include <QDBusInterface>
2768 #include "telepathytest.h"
2769 #include "chatmanager.h"
2770 #include "handlercontroller.h"
2771@@ -27,6 +28,8 @@
2772 #include "accountentryfactory.h"
2773 #include "telepathyhelper.h"
2774
2775+Q_DECLARE_METATYPE(Tp::TextChannelPtr)
2776+
2777 class HandlerTest : public TelepathyTest
2778 {
2779 Q_OBJECT
2780@@ -70,6 +73,8 @@
2781 QSignalSpy setupReadySpy(TelepathyHelper::instance(), SIGNAL(setupReady()));
2782 TRY_COMPARE(setupReadySpy.count(), 1);
2783
2784+ qRegisterMetaType<Tp::TextChannelPtr>();
2785+
2786 registerApprover();
2787 }
2788
2789@@ -354,18 +359,24 @@
2790
2791 void HandlerTest::testAcknowledgeMessage()
2792 {
2793+ QString recipient("84376666");
2794+ QString recipient2("+554184376666");
2795+ QString message("Hello, world!");
2796+
2797+ QSignalSpy messageSentSpy(mMockController, SIGNAL(MessageSent(QString,QVariantList,QVariantMap)));
2798+ // first send a message to a certain number so the handler request one channel
2799+ QString objectPath = HandlerController::instance()->sendMessage(mTpAccount->uniqueIdentifier(), QStringList() << recipient, message);
2800+ QDBusInterface *sendingJob = new QDBusInterface(TelepathyHelper::instance()->handlerInterface()->service(), objectPath,
2801+ "com.canonical.TelephonyServiceHandler.MessageSendingJob");
2802+ QSignalSpy handlerHasChannel(sendingJob, SIGNAL(finished()));
2803+
2804+ TRY_COMPARE(messageSentSpy.count(), 1);
2805+ TRY_COMPARE(handlerHasChannel.count(), 1);
2806+
2807 // if we register the observer before this test, other tests fail
2808- TelepathyHelper::instance()->registerChannelObserver();
2809- QString recipient("84376666");
2810- QString recipient2("+554184376666");
2811- QString message("Hello, world!");
2812- QSignalSpy messageSentSpy(mMockController, SIGNAL(MessageSent(QString,QVariantList,QVariantMap)));
2813-
2814- // first send a message to a certain number so the handler request one channel
2815- HandlerController::instance()->sendMessage(mTpAccount->uniqueIdentifier(), QStringList() << recipient, message);
2816- TRY_COMPARE(messageSentSpy.count(), 1);
2817-
2818- QSignalSpy messageReceivedSpy(ChatManager::instance(), SIGNAL(messageReceived(QString,QString,QDateTime,QString,bool)));
2819+ TelepathyHelper::instance()->registerChannelObserver("TelephonyServiceTests");
2820+ TRY_VERIFY(QDBusConnection::sessionBus().interface()->isServiceRegistered(TP_QT_IFACE_CLIENT + ".TelephonyServiceTests"));
2821+ QSignalSpy textChannelAvailableSpy(ChatManager::instance(), SIGNAL(textChannelAvailable(Tp::TextChannelPtr)));
2822
2823 // now receive a message from a very similar number so CM creates another
2824 // channel and the handler needs to deal with both
2825@@ -374,8 +385,11 @@
2826 properties["Recipients"] = (QStringList() << recipient2);
2827 mMockController->PlaceIncomingMessage(message, properties);
2828
2829- TRY_COMPARE(messageReceivedSpy.count(), 1);
2830- QString receivedMessageId = messageReceivedSpy.first()[3].toString();
2831+ TRY_COMPARE(textChannelAvailableSpy.count(), 2);
2832+ Tp::TextChannelPtr channel = textChannelAvailableSpy[1].first().value<Tp::TextChannelPtr>();
2833+ QVERIFY(!channel.isNull());
2834+ TRY_COMPARE(channel->messageQueue().count(), 1);
2835+ QString receivedMessageId = channel->messageQueue().first().messageToken();
2836
2837 // then acknowledge the message that arrived in the second channel and make sure handler
2838 // does the right thing
2839@@ -404,7 +418,7 @@
2840 HandlerController::instance()->sendMessage(mTpAccount->uniqueIdentifier(), QStringList() << recipient, message);
2841 TRY_COMPARE(messageSentSpy.count(), 1);
2842
2843- QSignalSpy messageReceivedSpy(ChatManager::instance(), SIGNAL(messageReceived(QString,QString,QDateTime,QString,bool)));
2844+ QSignalSpy textChannelAvailableSpy(ChatManager::instance(), SIGNAL(textChannelAvailable(Tp::TextChannelPtr)));
2845
2846 // now receive some messages from a very similar number so CM creates another
2847 // channel and the handler needs to deal with both
2848@@ -415,7 +429,11 @@
2849 mMockController->PlaceIncomingMessage(message.arg(QString::number(i)), properties);
2850 }
2851
2852- TRY_COMPARE(messageReceivedSpy.count(), messageCount);
2853+ TRY_COMPARE(textChannelAvailableSpy.count(), 1);
2854+ Tp::TextChannelPtr channel = textChannelAvailableSpy.first().first().value<Tp::TextChannelPtr>();
2855+ QVERIFY(!channel.isNull());
2856+ TRY_COMPARE(channel->messageQueue().count(), messageCount);
2857+ QString receivedMessageId = channel->messageQueue().first().messageToken();
2858
2859 // then acknowledge the messages that arrived in the second channel and make sure handler
2860 // does the right thing
2861@@ -491,8 +509,13 @@
2862 QSignalSpy messageSentOfonoSpy(mOfonoMockController, SIGNAL(MessageSent(QString,QVariantList,QVariantMap)));
2863 QSignalSpy messageSentMultimediaSpy(mMultimediaMockController, SIGNAL(MessageSent(QString,QVariantList,QVariantMap)));
2864
2865- QString accountId = HandlerController::instance()->sendMessage(mOfonoTpAccount->uniqueIdentifier(), QStringList() << recipient, message);
2866- QCOMPARE(accountId, mMultimediaTpAccount->uniqueIdentifier());
2867+ QString jobObjectPath = HandlerController::instance()->sendMessage(mOfonoTpAccount->uniqueIdentifier(), QStringList() << recipient, message);
2868+
2869+ QDBusInterface jobInterface(TelepathyHelper::instance()->handlerInterface()->service(),
2870+ jobObjectPath);
2871+ QSignalSpy finishedSpy(&jobInterface, SIGNAL(finished()));
2872+ TRY_COMPARE(finishedSpy.count(), 1);
2873+ QCOMPARE(jobInterface.property("accountId").toString(), mMultimediaTpAccount->uniqueIdentifier());
2874 TRY_COMPARE(messageSentMultimediaSpy.count(), 1);
2875 QCOMPARE(messageSentOfonoSpy.count(), 0);
2876 QString sentMessage = messageSentMultimediaSpy.first().first().toString();
2877
2878=== modified file 'tests/libtelephonyservice/ChatEntryTest.cpp'
2879--- tests/libtelephonyservice/ChatEntryTest.cpp 2016-10-06 19:34:36 +0000
2880+++ tests/libtelephonyservice/ChatEntryTest.cpp 2016-10-06 19:34:37 +0000
2881@@ -93,6 +93,7 @@
2882 QFETCH(QString, accountId);
2883 QFETCH(QStringList, participants);
2884
2885+ /*
2886 MockController *mockController = accountId.startsWith("mock/mock") ? mGenericMockController : mMultimediaMockController;
2887
2888 QSignalSpy chatEntryCreatedSpy(ChatManager::instance(), SIGNAL(chatEntryCreated(QString, QStringList,ChatEntry *)));
2889@@ -137,6 +138,7 @@
2890 QTRY_COMPARE(chatStateChangedSpy2.count(), 1);
2891 QCOMPARE(contactChatState2->state(), (int)ChatEntry::ChannelChatStatePaused);
2892 }
2893+ */
2894 }
2895
2896 QTEST_MAIN(ChatEntryTest)
2897
2898=== modified file 'tests/libtelephonyservice/ChatManagerTest.cpp'
2899--- tests/libtelephonyservice/ChatManagerTest.cpp 2016-10-06 19:34:36 +0000
2900+++ tests/libtelephonyservice/ChatManagerTest.cpp 2016-10-06 19:34:37 +0000
2901@@ -24,6 +24,8 @@
2902 #include "telepathyhelper.h"
2903 #include "mockcontroller.h"
2904
2905+Q_DECLARE_METATYPE(Tp::TextChannelPtr)
2906+
2907 class ChatManagerTest : public TelepathyTest
2908 {
2909 Q_OBJECT
2910@@ -36,9 +38,7 @@
2911 void testSendMessage();
2912 void testSendMessageWithAttachments_data();
2913 void testSendMessageWithAttachments();
2914- void testMessageReceived();
2915 void testAcknowledgeMessages();
2916- void testChatEntry();
2917
2918 private:
2919 Tp::AccountPtr mGenericTpAccount;
2920@@ -54,6 +54,8 @@
2921 TelepathyHelper::instance()->setMmsGroupChat(false);
2922 TelepathyHelper::instance()->registerChannelObserver();
2923
2924+ qRegisterMetaType<Tp::TextChannelPtr>();
2925+
2926 // just give telepathy some time to register the observer
2927 QTest::qWait(1000);
2928 }
2929@@ -102,11 +104,15 @@
2930
2931 MockController *controller = accountId.startsWith("mock/mock") ? mGenericMockController : mPhoneMockController;
2932 QSignalSpy controllerMessageSentSpy(controller, SIGNAL(MessageSent(QString,QVariantList,QVariantMap)));
2933- QSignalSpy messageSentSpy(ChatManager::instance(), SIGNAL(messageSent(QStringList,QString)));
2934
2935 QVariantMap properties;
2936 properties["participantIds"] = recipients;
2937- ChatManager::instance()->sendMessage(accountId, message, QVariantMap(), properties);
2938+ QString jobObjectPath = ChatManager::instance()->sendMessage(accountId, message, QVariantMap(), properties);
2939+
2940+ QDBusInterface iface(TelepathyHelper::instance()->handlerInterface()->service(),
2941+ jobObjectPath);
2942+
2943+ QSignalSpy finishedSpy(&iface, SIGNAL(finished()));
2944
2945 TRY_COMPARE(controllerMessageSentSpy.count(), 1);
2946 QString messageText = controllerMessageSentSpy.first()[0].toString();
2947@@ -116,34 +122,13 @@
2948 QCOMPARE(messageText, message);
2949 QCOMPARE(messageRecipients, recipients);
2950
2951- TRY_COMPARE(messageSentSpy.count(), 1);
2952- messageRecipients = messageSentSpy.first()[0].toStringList();
2953- qSort(messageRecipients);
2954- messageText = messageSentSpy.first()[1].toString();
2955- QCOMPARE(messageText, message);
2956- QCOMPARE(messageRecipients, recipients);
2957-}
2958-
2959-void ChatManagerTest::testMessageReceived()
2960-{
2961- QSignalSpy messageReceivedSpy(ChatManager::instance(), SIGNAL(messageReceived(QString,QString,QDateTime,QString,bool)));
2962-
2963- QVariantMap properties;
2964- properties["Sender"] = "12345";
2965- properties["Recipients"] = (QStringList() << "12345");
2966- QString message("Hi there");
2967- mGenericMockController->PlaceIncomingMessage(message, properties);
2968-
2969- TRY_COMPARE(messageReceivedSpy.count(), 1);
2970- QString sender = messageReceivedSpy.first()[0].toString();
2971- QString receivedMessage = messageReceivedSpy.first()[1].toString();
2972- QCOMPARE(sender, properties["Sender"].toString());
2973- QCOMPARE(receivedMessage, message);
2974+ // the rest of the properties are tested in the MessageSendingJob tests.
2975+ TRY_COMPARE(finishedSpy.count(), 1);
2976 }
2977
2978 void ChatManagerTest::testAcknowledgeMessages()
2979 {
2980- QSignalSpy messageReceivedSpy(ChatManager::instance(), SIGNAL(messageReceived(QString,QString,QDateTime,QString,bool)));
2981+ QSignalSpy textChannelAvailableSpy(ChatManager::instance(), SIGNAL(textChannelAvailable(Tp::TextChannelPtr)));
2982
2983 QVariantMap properties;
2984 properties["Sender"] = "12345";
2985@@ -155,12 +140,14 @@
2986 // the wait shouldn't be needed, but just in case
2987 QTest::qWait(50);
2988 }
2989- TRY_COMPARE(messageReceivedSpy.count(), messages.count());
2990+ TRY_COMPARE(textChannelAvailableSpy.count(), 1);
2991+ Tp::TextChannelPtr channel = textChannelAvailableSpy.first().first().value<Tp::TextChannelPtr>();
2992+ QVERIFY(!channel.isNull());
2993
2994+ TRY_COMPARE(channel->messageQueue().count(), messages.count());
2995 QStringList messageIds;
2996 for (int i = 0; i < messages.count(); ++i) {
2997- QString messageId = messageReceivedSpy[i][3].toString();
2998- messageIds << messageId;
2999+ messageIds << channel->messageQueue()[i].messageToken();
3000 }
3001
3002 QSignalSpy messageReadSpy(mGenericMockController, SIGNAL(MessageRead(QString)));
3003@@ -183,26 +170,6 @@
3004 QCOMPARE(receivedIds, messageIds);
3005 }
3006
3007-void ChatManagerTest::testChatEntry()
3008-{
3009- QStringList recipients;
3010- recipients << "user@domain.com" << "user2@domain.com";
3011- QSignalSpy chatEntryCreatedSpy(ChatManager::instance(), SIGNAL(chatEntryCreated(QString, QStringList,ChatEntry *)));
3012- QVariantMap properties;
3013- properties["participantIds"] = recipients;
3014-
3015- ChatEntry *entry = ChatManager::instance()->chatEntryForProperties("mock/mock/account0", properties, true);
3016- QVERIFY(entry == NULL);
3017- QTRY_COMPARE(chatEntryCreatedSpy.count(), 1);
3018-
3019- entry = ChatManager::instance()->chatEntryForProperties("mock/mock/account0", properties, false);
3020- QVERIFY(entry != NULL);
3021- QList<QVariant> arguments = chatEntryCreatedSpy.takeFirst();
3022- QCOMPARE(QString("mock/mock/account0"), arguments.at(0).toString());
3023- QCOMPARE(recipients.toSet(), arguments.at(1).toStringList().toSet());
3024- QCOMPARE(entry, arguments.at(2).value<ChatEntry*>());
3025-}
3026-
3027 void ChatManagerTest::testSendMessageWithAttachments_data()
3028 {
3029 QTest::addColumn<QStringList>("recipients");

Subscribers

People subscribed via source and target branches

to all changes: