Merge lp:telephony-service/staging into lp:telephony-service

Proposed by Gustavo Pichorim Boiko
Status: Approved
Approved by: Gustavo Pichorim Boiko
Approved revision: 1252
Proposed branch: lp:telephony-service/staging
Merge into: lp:telephony-service
Diff against target: 7142 lines (+4636/-297)
100 files modified
CMakeLists.txt (+10/-10)
TODO (+6/-0)
Ubuntu/Telephony/CMakeLists.txt (+1/-0)
Ubuntu/Telephony/components.cpp (+2/-0)
Ubuntu/Telephony/participantsmodel.cpp (+235/-0)
Ubuntu/Telephony/participantsmodel.h (+92/-0)
accounts/CMakeLists.txt (+2/-0)
accounts/common/DynamicField.qml (+172/-0)
accounts/common/Main.qml (+106/-0)
accounts/common/NewAccountInterface.qml (+314/-0)
accounts/irc/CMakeLists.txt (+2/-0)
accounts/irc/data/CMakeLists.txt (+5/-0)
accounts/irc/data/telephony-irc-im.service (+22/-0)
accounts/irc/data/telephony-irc.provider (+5/-0)
accounts/irc/qml/CMakeLists.txt (+13/-0)
accounts/irc/qml/NewAccount.qml (+63/-0)
accounts/sip/CMakeLists.txt (+2/-0)
accounts/sip/data/CMakeLists.txt (+5/-0)
accounts/sip/data/telephony-sip-im.service (+22/-0)
accounts/sip/data/telephony-sip.provider (+5/-0)
accounts/sip/qml/CMakeLists.txt (+13/-0)
accounts/sip/qml/NewAccount.qml (+48/-0)
approver/approver.cpp (+23/-20)
cmake/modules/GenerateTest.cmake (+1/-0)
debian/account-plugin-irc-unity8.install (+3/-0)
debian/account-plugin-sip-unity8.install (+3/-0)
debian/control (+24/-1)
handler/CMakeLists.txt (+21/-0)
handler/Handler.xml (+64/-9)
handler/TelephonyServiceHandler.client (+9/-0)
handler/accountproperties.cpp (+71/-0)
handler/accountproperties.h (+47/-0)
handler/audioroutemanager.cpp (+231/-0)
handler/audioroutemanager.h (+75/-0)
handler/callagent.cpp (+118/-0)
handler/callagent.h (+52/-0)
handler/callhandler.cpp (+202/-39)
handler/callhandler.h (+10/-2)
handler/farstreamchannel.cpp (+326/-0)
handler/farstreamchannel.h (+80/-0)
handler/handler.cpp (+20/-5)
handler/handler.h (+2/-1)
handler/handlerdbus.cpp (+45/-6)
handler/handlerdbus.h (+21/-2)
handler/main.cpp (+16/-3)
handler/powerd.h (+34/-0)
handler/powerdaudiomodemediator.cpp (+63/-0)
handler/powerdaudiomodemediator.h (+46/-0)
handler/powerddbus.cpp (+43/-0)
handler/powerddbus.h (+38/-0)
handler/qpulseaudioengine.cpp (+853/-0)
handler/qpulseaudioengine.h (+126/-0)
handler/texthandler.cpp (+47/-0)
handler/texthandler.h (+6/-0)
indicator/callchannelobserver.cpp (+19/-3)
indicator/callchannelobserver.h (+2/-1)
indicator/displaynamesettings.cpp (+2/-2)
indicator/messagingmenu.cpp (+14/-14)
indicator/messagingmenu.h (+2/-2)
indicator/textchannelobserver.cpp (+6/-0)
libtelephonyservice/CMakeLists.txt (+4/-8)
libtelephonyservice/accountentry.cpp (+79/-6)
libtelephonyservice/accountentry.h (+24/-1)
libtelephonyservice/accountlist.cpp (+11/-2)
libtelephonyservice/accountlist.h (+2/-0)
libtelephonyservice/applicationutils.cpp (+11/-13)
libtelephonyservice/audiooutput.cpp (+17/-0)
libtelephonyservice/audiooutput.h (+4/-0)
libtelephonyservice/callentry.cpp (+54/-54)
libtelephonyservice/callentry.h (+3/-3)
libtelephonyservice/chatentry.cpp (+46/-23)
libtelephonyservice/chatentry.h (+9/-1)
libtelephonyservice/chatmanager.cpp (+16/-0)
libtelephonyservice/chatmanager.h (+3/-1)
libtelephonyservice/contactwatcher.cpp (+67/-24)
libtelephonyservice/contactwatcher.h (+5/-1)
libtelephonyservice/dbustypes.h (+7/-0)
libtelephonyservice/greetercontacts.cpp (+17/-1)
libtelephonyservice/greetercontacts.h (+3/-1)
libtelephonyservice/ofonoaccountentry.cpp (+7/-1)
libtelephonyservice/ofonoaccountentry.h (+3/-1)
libtelephonyservice/participant.cpp (+13/-2)
libtelephonyservice/participant.h (+11/-1)
libtelephonyservice/phoneutils.cpp (+43/-1)
libtelephonyservice/phoneutils.h (+4/-1)
libtelephonyservice/protocol.cpp (+83/-3)
libtelephonyservice/protocol.h (+42/-0)
libtelephonyservice/protocolmanager.cpp (+2/-2)
libtelephonyservice/telepathyhelper.cpp (+37/-8)
libtelephonyservice/telepathyhelper.h (+5/-2)
libtelephonyservice/tonegenerator.cpp (+12/-1)
libtelephonyservice/tonegenerator.h (+6/-2)
protocols/README.protocols (+8/-0)
protocols/ofono.protocol (+5/-0)
protocols/sip.protocol (+7/-0)
tests/Ubuntu.Telephony/ContactWatcherTest.cpp (+13/-5)
tests/libtelephonyservice/OfonoAccountEntryTest.cpp (+5/-5)
tests/libtelephonyservice/ProtocolTest.cpp (+30/-2)
tests/libtelephonyservice/testProtocols/foo.protocol (+7/-0)
upstart/telephony-service-indicator.conf (+1/-1)
To merge this branch: bzr merge lp:telephony-service/staging
Reviewer Review Type Date Requested Status
Gustavo Pichorim Boiko (community) Approve
system-apps-ci-bot continuous-integration Needs Fixing
Review via email: mp+320715@code.launchpad.net

Commit message

- Add new property to .protocol files to allow join existing channels.
- Add VOIP support to telephony-service.
- Create unity8 sip and irc account packages.
- Add more properties to the .protocol files
- Expose method on dbus to leave all rooms from a certain account.
- Fix protocol filters
- Uses "ua_url_dispatcher_session_open" to launch external apps. That fixes some problems with "ua_url_dispatcher_session_open" not working on Desktop.
- Add method to leave channel by properties.
- Implement AudioRouteManager in telephony-service-handler
- Multiple performance improvements on Roles Interfaces and ContactWatcher
- Changed upstart job to also launch telephony-service-indicator on desktop
- Add ParticipantsModel
- Expose the account parameters to QML
- Export startChat to QML.
- Implemented contact match by online accounts (IRC).
- Monitor app and disconnect/connect accounts when appropriate.

Description of the change

- Add new property to .protocol files to allow join existing channels.
- Add VOIP support to telephony-service.
- Create unity8 sip and irc account packages.
- Add more properties to the .protocol files
- Expose method on dbus to leave all rooms from a certain account.
- Fix protocol filters
- Uses "ua_url_dispatcher_session_open" to launch external apps. That fixes some problems with "ua_url_dispatcher_session_open" not working on Desktop.
- Add method to leave channel by properties.
- Implement AudioRouteManager in telephony-service-handler
- Multiple performance improvements on Roles Interfaces and ContactWatcher
- Changed upstart job to also launch telephony-service-indicator on desktop
- Add ParticipantsModel
- Expose the account parameters to QML
- Export startChat to QML.
- Implemented contact match by online accounts (IRC).
- Monitor app and disconnect/connect accounts when appropriate.

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:1251
https://jenkins.canonical.com/system-apps/job/lp-telephony-service-ci/1/
Executed test runs:
    FAILURE: https://jenkins.canonical.com/system-apps/job/build/2343/console
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-0-fetch/2343
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=amd64,release=xenial+overlay/2161
        deb: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=amd64,release=xenial+overlay/2161/artifact/output/*zip*/output.zip
    FAILURE: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=amd64,release=zesty/2161/console
    FAILURE: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=armhf,release=xenial+overlay/2161/console
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=armhf,release=zesty/2161
        deb: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=armhf,release=zesty/2161/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=i386,release=xenial+overlay/2161
        deb: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=i386,release=xenial+overlay/2161/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=i386,release=zesty/2161
        deb: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=i386,release=zesty/2161/artifact/output/*zip*/output.zip

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

review: Needs Fixing (continuous-integration)
lp:telephony-service/staging updated
1252. By Gustavo Pichorim Boiko

Disable PA on tests.

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

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

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

review: Needs Fixing (continuous-integration)
Revision history for this message
Gustavo Pichorim Boiko (boiko) wrote :

The changes were reviewed individually and tested from this branch, so approving.

review: Approve
lp:telephony-service/staging updated
1253. By Tiago Salem Herrmann

- Initialize pointer to avoid crashes.
- Connect new accounts in case messaging-app is already active

1254. By Tiago Salem Herrmann

- Use alsasrc as a workaround to be able to record from the microphone.
pulsesrc does not work on some environments.

1255. By Tiago Salem Herrmann

Add requires to telepathy-rakia and mfw-plugin-irc

1256. By Gustavo Pichorim Boiko

Fix watching the connection status changes and properly notify the changes to displayed accounts

Unmerged revisions

1256. By Gustavo Pichorim Boiko

Fix watching the connection status changes and properly notify the changes to displayed accounts

1255. By Tiago Salem Herrmann

Add requires to telepathy-rakia and mfw-plugin-irc

1254. By Tiago Salem Herrmann

- Use alsasrc as a workaround to be able to record from the microphone.
pulsesrc does not work on some environments.

1253. By Tiago Salem Herrmann

- Initialize pointer to avoid crashes.
- Connect new accounts in case messaging-app is already active

1252. By Gustavo Pichorim Boiko

Disable PA on tests.

1251. By Gustavo Pichorim Boiko

Monitor app and disconnect/connect accounts when appropriate.

1250. By Gustavo Pichorim Boiko

Implemented contact match by online accounts (IRC).

1249. By Gustavo Pichorim Boiko

Export startChat to QML.

1248. By Gustavo Pichorim Boiko

Expose the account parameters to QML

1247. By Gustavo Pichorim Boiko

Add ParticipantsModel

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2016-10-06 19:41:33 +0000
+++ CMakeLists.txt 2017-04-05 14:05:16 +0000
@@ -15,13 +15,6 @@
15# just to make debug easier, print the system processor15# just to make debug easier, print the system processor
16message(STATUS "System processor: ${CMAKE_SYSTEM_PROCESSOR}")16message(STATUS "System processor: ${CMAKE_SYSTEM_PROCESSOR}")
1717
18# Check if should build using ubuntu platform api
19check_include_file_cxx("ubuntu/application/init.h" USE_UBUNTU_PLATFORM_API)
20
21if (USE_UBUNTU_PLATFORM_API)
22 add_definitions(-DUSE_UBUNTU_PLATFORM_API)
23endif (USE_UBUNTU_PLATFORM_API)
24
25set(TELEPHONY_SERVICE_DIR ${CMAKE_INSTALL_DATADIR}/telephony-service)18set(TELEPHONY_SERVICE_DIR ${CMAKE_INSTALL_DATADIR}/telephony-service)
2619
27configure_file(config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h @ONLY)20configure_file(config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h @ONLY)
@@ -35,10 +28,8 @@
3528
36find_package(Qt5Contacts)29find_package(Qt5Contacts)
37find_package(Qt5DBus)30find_package(Qt5DBus)
38#find_package(Qt5Gui)
39find_package(Qt5Multimedia)31find_package(Qt5Multimedia)
40find_package(Qt5Qml)32find_package(Qt5Qml)
41#find_package(Qt5Quick)
42find_package(Qt5Test)33find_package(Qt5Test)
43find_package(Qt5Feedback)34find_package(Qt5Feedback)
44find_package(Qt5Network)35find_package(Qt5Network)
@@ -48,7 +39,7 @@
4839
49option(SKIP_QML_TESTS "Skip QML tests" OFF)40option(SKIP_QML_TESTS "Skip QML tests" OFF)
5041
51if(NOT CMAKE_CROSSCOMPILING)42if(CMAKE_CROSSCOMPILING)
52 find_program(QMAKE_EXECUTABLE qmake)43 find_program(QMAKE_EXECUTABLE qmake)
53 if(QMAKE_EXECUTABLE STREQUAL "QMAKE_EXECUTABLE-NOTFOUND")44 if(QMAKE_EXECUTABLE STREQUAL "QMAKE_EXECUTABLE-NOTFOUND")
54 message(FATAL_ERROR "qmake not found")45 message(FATAL_ERROR "qmake not found")
@@ -71,10 +62,18 @@
7162
72find_package(PkgConfig REQUIRED)63find_package(PkgConfig REQUIRED)
73pkg_check_modules(TP_QT5 REQUIRED TelepathyQt5)64pkg_check_modules(TP_QT5 REQUIRED TelepathyQt5)
65pkg_check_modules(TP_QT5_FS REQUIRED TelepathyQt5Farstream)
74pkg_check_modules(NOTIFY REQUIRED libnotify)66pkg_check_modules(NOTIFY REQUIRED libnotify)
75pkg_check_modules(MESSAGING_MENU REQUIRED messaging-menu)67pkg_check_modules(MESSAGING_MENU REQUIRED messaging-menu)
76pkg_check_modules(UserMetrics REQUIRED libusermetricsinput-1)68pkg_check_modules(UserMetrics REQUIRED libusermetricsinput-1)
77pkg_check_modules(HISTORY REQUIRED history-service)69pkg_check_modules(HISTORY REQUIRED history-service)
70pkg_check_modules(TPFS REQUIRED telepathy-farstream)
71pkg_check_modules(GST REQUIRED gstreamer-1.0)
72pkg_check_modules(FS REQUIRED farstream-0.2)
73pkg_check_modules(UBUNTU_PLATFORM_API ubuntu-platform-api)
74pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
75pkg_check_modules(PULSEAUDIO libpulse)
76pkg_check_modules(URL_DISPATCHER REQUIRED url-dispatcher-1)
7877
79add_definitions(-DQT_NO_KEYWORDS)78add_definitions(-DQT_NO_KEYWORDS)
8079
@@ -110,6 +109,7 @@
110add_subdirectory(po)109add_subdirectory(po)
111add_subdirectory(tests)110add_subdirectory(tests)
112add_subdirectory(protocols)111add_subdirectory(protocols)
112add_subdirectory(accounts)
113113
114include(EnableCoverageReport)114include(EnableCoverageReport)
115#####################################################################115#####################################################################
116116
=== modified file 'TODO'
--- TODO 2012-11-27 12:21:47 +0000
+++ TODO 2017-04-05 14:05:16 +0000
@@ -1,6 +1,9 @@
1General items1General items
2- Formatting of phone numbers2- Formatting of phone numbers
3- Check how to import/export potfiles from the ts one3- Check how to import/export potfiles from the ts one
4- Add an automated test to check if features are getting correctly exported
5 by AccountEntry
6- Implement unit test for launching external app from indicator.
47
5Contact integration items:8Contact integration items:
6- Fix contact search when names contain accentuated characters9- Fix contact search when names contain accentuated characters
@@ -16,3 +19,6 @@
16 places19 places
17- Change the ChannelObserver and the ChannelApprover code to allow using from20- Change the ChannelObserver and the ChannelApprover code to allow using from
18 both places.21 both places.
22
23Handler
24- Move app monitoring to from TextHandler to ApplicationUtils
1925
=== modified file 'Ubuntu/Telephony/CMakeLists.txt'
--- Ubuntu/Telephony/CMakeLists.txt 2016-07-05 03:34:11 +0000
+++ Ubuntu/Telephony/CMakeLists.txt 2017-04-05 14:05:16 +0000
@@ -2,6 +2,7 @@
22
3set(plugin_SRCS3set(plugin_SRCS
4 presencerequest.cpp4 presencerequest.cpp
5 participantsmodel.cpp
5 components.cpp6 components.cpp
6 )7 )
78
89
=== modified file 'Ubuntu/Telephony/components.cpp'
--- Ubuntu/Telephony/components.cpp 2016-11-23 19:28:18 +0000
+++ Ubuntu/Telephony/components.cpp 2017-04-05 14:05:16 +0000
@@ -39,6 +39,7 @@
39#include "accountentry.h"39#include "accountentry.h"
40#include "accountlist.h"40#include "accountlist.h"
41#include "audiooutput.h"41#include "audiooutput.h"
42#include "participantsmodel.h"
4243
43#include <QQmlEngine>44#include <QQmlEngine>
44#include <qqml.h>45#include <qqml.h>
@@ -87,5 +88,6 @@
87 qmlRegisterType<ContactWatcher>(uri, 0, 1, "ContactWatcher");88 qmlRegisterType<ContactWatcher>(uri, 0, 1, "ContactWatcher");
88 qmlRegisterType<Participant>(uri, 0, 1, "Participant");89 qmlRegisterType<Participant>(uri, 0, 1, "Participant");
89 qmlRegisterType<PresenceRequest>(uri, 0, 1, "PresenceRequest");90 qmlRegisterType<PresenceRequest>(uri, 0, 1, "PresenceRequest");
91 qmlRegisterType<ParticipantsModel>(uri, 0, 1, "ParticipantsModel");
90 qmlRegisterType<PhoneUtils>(uri, 0, 1, "PhoneUtils");92 qmlRegisterType<PhoneUtils>(uri, 0, 1, "PhoneUtils");
91}93}
9294
=== added file 'Ubuntu/Telephony/participantsmodel.cpp'
--- Ubuntu/Telephony/participantsmodel.cpp 1970-01-01 00:00:00 +0000
+++ Ubuntu/Telephony/participantsmodel.cpp 2017-04-05 14:05:16 +0000
@@ -0,0 +1,235 @@
1/*
2 * Copyright (C) 2013-2017 Canonical, Ltd.
3 *
4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
6 * Tiago Salem Herrmann <tiago.herrmann@canonical.com>
7 *
8 * This file is part of telephony-service.
9 *
10 * telephony-service is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; version 3.
13 *
14 * telephony-service is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23#include "participantsmodel.h"
24#include <participant.h>
25#include <QDebug>
26
27Q_DECLARE_METATYPE(Participant)
28
29ParticipantsModel::ParticipantsModel(QObject *parent) :
30 QAbstractListModel(parent), mWaitingForQml(false), mCanFetchMore(true), mChatEntry(NULL)
31{
32 qRegisterMetaType<Participant>();
33 mRoles[AliasRole] = "alias";
34 mRoles[IdentifierRole] = "identifier";
35 mRoles[RolesRole] = "roles";
36 mRoles[StateRole] = "state";
37
38 connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SIGNAL(countChanged()));
39 connect(this, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SIGNAL(countChanged()));
40 connect(this, SIGNAL(modelReset()), this, SIGNAL(countChanged()));
41}
42
43bool ParticipantsModel::canFetchMore(const QModelIndex &parent) const
44{
45 return !mParticipantsCache.isEmpty();
46}
47
48ParticipantsModel::~ParticipantsModel()
49{
50}
51
52void ParticipantsModel::fetchMore(const QModelIndex &parent)
53{
54 if (parent.isValid() ) {
55 return;
56 }
57
58 int max = 14;
59 while (max >= 0 && !mParticipantsCache.isEmpty()) {
60 addParticipant(mParticipantsCache.takeFirst());
61 max--;
62 }
63
64 if (mParticipantsCache.isEmpty()) {
65 mCanFetchMore = false;
66 Q_EMIT canFetchMoreChanged();
67 }
68}
69
70int ParticipantsModel::rowCount(const QModelIndex &parent) const
71{
72 if (parent.isValid()) {
73 return 0;
74 }
75
76 return mParticipants.count();
77}
78
79QHash<int, QByteArray> ParticipantsModel::roleNames() const
80{
81 return mRoles;
82}
83
84void ParticipantsModel::addParticipantCache(Participant *participant)
85{
86 int pos = positionForItem(participant->identifier(), true);
87 mParticipantsCache.insert(pos, participant);
88}
89
90void ParticipantsModel::addParticipant(Participant *participant)
91{
92 int pos = positionForItem(participant->identifier());
93 beginInsertRows(QModelIndex(), pos, pos);
94 mParticipants.insert(pos, participant);
95 endInsertRows();
96}
97
98void ParticipantsModel::removeParticipant(Participant *participant)
99{
100 int pos = mParticipants.indexOf(participant);
101 if (pos >= 0) {
102 beginRemoveRows(QModelIndex(), pos, pos);
103 mParticipants.removeAt(pos);
104 endRemoveRows();
105 }
106 pos = mParticipantsCache.indexOf(participant);
107 if (pos >= 0) {
108 mParticipantsCache.removeAt(pos);
109 }
110}
111
112QVariant ParticipantsModel::data(const QModelIndex &index, int role) const
113{
114 if (!index.isValid() || index.row() < 0 || index.row() >= rowCount()) {
115 return QVariant();
116 }
117 switch (role) {
118 case IdentifierRole:
119 return mParticipants[index.row()]->identifier();
120 break;
121 case AliasRole:
122 return mParticipants[index.row()]->alias();
123 break;
124 case StateRole:
125 return 0;
126 break;
127 case RolesRole:
128 return mParticipants[index.row()]->roles();
129 break;
130 }
131 return QVariant();
132}
133
134bool ParticipantsModel::lessThan(const QString &left, const QString &right) const
135{
136 // this method will push participant with names starting with non-letter
137 // characters to the end of the list
138 if (left.isEmpty() || right.isEmpty()) {
139 return false;
140 }
141 if (left.at(0).isLetter() && right.at(0).isLetter()) {
142 return left.localeAwareCompare(right) < 0;
143 }
144 if (!left.at(0).isLetter() && right.at(0).isLetter()) {
145 return false;
146 }
147 if (left.at(0).isLetter() && !right.at(0).isLetter()) {
148 return true;
149 }
150
151 return false;
152}
153
154int ParticipantsModel::positionForItem(const QString &item, bool cache) const
155{
156 // do a binary search for the item position on the list
157 int lowerBound = 0;
158 int upperBound = cache ? mParticipantsCache.count() - 1 : rowCount() - 1;
159 if (upperBound < 0) {
160 return 0;
161 }
162
163 while (true) {
164 int pos = (upperBound + lowerBound) / 2;
165 const QString posItem = cache ? mParticipantsCache[pos]->identifier() : index(pos).data(IdentifierRole).toString();
166 if (lowerBound == pos) {
167 if (lessThan(item, posItem)) {
168 return pos;
169 }
170 }
171 if (lessThan(posItem, item)) {
172 lowerBound = pos + 1; // its in the upper
173 if (lowerBound > upperBound) {
174 return pos += 1;
175 }
176 } else if (lowerBound > upperBound) {
177 return pos;
178 } else {
179 upperBound = pos - 1; // its in the lower
180 }
181 }
182}
183
184void ParticipantsModel::classBegin()
185{
186 mWaitingForQml = true;
187}
188
189void ParticipantsModel::componentComplete()
190{
191 mWaitingForQml = false;
192}
193
194QVariant ParticipantsModel::get(int row) const
195{
196 QVariantMap data;
197 QModelIndex idx = index(row, 0);
198 if (idx.isValid()) {
199 QHash<int, QByteArray> roles = roleNames();
200 Q_FOREACH(int role, roles.keys()) {
201 data.insert(roles[role], idx.data(role));
202 }
203 }
204
205 return data;
206}
207
208ChatEntry* ParticipantsModel::chatEntry() const
209{
210 return mChatEntry;
211}
212
213void ParticipantsModel::setChatEntry(ChatEntry *entry)
214{
215 if (mChatEntry == entry) {
216 return;
217 }
218 ChatEntry *previousChatEntry = mChatEntry;
219 mChatEntry = entry;
220 if (!entry) {
221 return;
222 }
223 if (previousChatEntry) {
224 previousChatEntry->disconnect(this);
225 }
226 connect(mChatEntry, SIGNAL(participantAdded(Participant *)), SLOT(addParticipant(Participant *)));
227 connect(mChatEntry, SIGNAL(participantRemoved(Participant *)), SLOT(removeParticipant(Participant *)));
228 Q_FOREACH(Participant *participant, mChatEntry->allParticipants()) {
229 addParticipantCache(participant);
230 }
231 fetchMore();
232 mCanFetchMore = !mParticipantsCache.isEmpty();
233 Q_EMIT canFetchMoreChanged();
234 Q_EMIT chatEntryChanged();
235}
0236
=== added file 'Ubuntu/Telephony/participantsmodel.h'
--- Ubuntu/Telephony/participantsmodel.h 1970-01-01 00:00:00 +0000
+++ Ubuntu/Telephony/participantsmodel.h 2017-04-05 14:05:16 +0000
@@ -0,0 +1,92 @@
1/*
2 * Copyright (C) 2013-2017 Canonical, Ltd.
3 *
4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
6 * Tiago Salem Herrmann <tiago.herrmann@canonical.com>
7 *
8 * This file is part of telephony-service.
9 *
10 * telephony-service is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; version 3.
13 *
14 * telephony-service is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23#ifndef PARTICIPANTSMODEL_H
24#define PARTICIPANTSMODEL_H
25
26#include "chatentry.h"
27#include <QAbstractListModel>
28#include <QStringList>
29#include <QQmlParserStatus>
30#include <QQmlListProperty>
31
32class Participant;
33
34class ParticipantsModel : public QAbstractListModel, public QQmlParserStatus
35{
36 Q_OBJECT
37 Q_INTERFACES(QQmlParserStatus)
38 Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
39 Q_PROPERTY(bool canFetchMore READ canFetchMore NOTIFY canFetchMoreChanged)
40 Q_PROPERTY(ChatEntry* chatEntry READ chatEntry WRITE setChatEntry NOTIFY chatEntryChanged)
41 Q_ENUMS(Role)
42
43public:
44 enum Role {
45 IdentifierRole = Qt::UserRole,
46 AliasRole,
47 RolesRole,
48 StateRole
49 };
50
51 explicit ParticipantsModel(QObject *parent = 0);
52 ~ParticipantsModel();
53
54 Q_INVOKABLE virtual bool canFetchMore(const QModelIndex &parent = QModelIndex()) const;
55 Q_INVOKABLE virtual void fetchMore(const QModelIndex &parent = QModelIndex());
56 virtual QHash<int, QByteArray> roleNames() const;
57 virtual QVariant data(const QModelIndex &index, int role) const;
58 int rowCount(const QModelIndex &parent = QModelIndex()) const;
59
60 Q_INVOKABLE virtual QVariant get(int row) const;
61
62 Q_INVOKABLE void setChatEntry(ChatEntry *entry);
63 ChatEntry* chatEntry() const;
64
65 void addParticipantCache(Participant *participant);
66
67 void classBegin();
68 void componentComplete();
69
70private Q_SLOTS:
71 void addParticipant(Participant *participant);
72 void removeParticipant(Participant *participant);
73
74Q_SIGNALS:
75 void countChanged();
76 void canFetchMoreChanged();
77 void chatEntryChanged();
78
79protected:
80 bool lessThan(const QString &left, const QString &right) const;
81 int positionForItem(const QString &item, bool cache = false) const;
82
83private:
84 QHash<int, QByteArray> mRoles;
85 QList<Participant*> mParticipants;
86 bool mWaitingForQml;
87 bool mCanFetchMore;
88 ChatEntry *mChatEntry;
89 QList<Participant*> mParticipantsCache;
90};
91
92#endif // PARTICIPANTSMODEL_H
093
=== added directory 'accounts'
=== added file 'accounts/CMakeLists.txt'
--- accounts/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ accounts/CMakeLists.txt 2017-04-05 14:05:16 +0000
@@ -0,0 +1,2 @@
1add_subdirectory(sip)
2add_subdirectory(irc)
03
=== added directory 'accounts/common'
=== added file 'accounts/common/DynamicField.qml'
--- accounts/common/DynamicField.qml 1970-01-01 00:00:00 +0000
+++ accounts/common/DynamicField.qml 2017-04-05 14:05:16 +0000
@@ -0,0 +1,172 @@
1/*
2 * Copyright (C) 2017 Canonical, Ltd.
3 *
4 * Authors:
5 * Renato Araujo Oliveira Filho <renato.filho@canonical.com>
6 *
7 * This file is part of telephony-service.
8 *
9 * telephony-service is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 3.
12 *
13 * telephony-service is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22import QtQuick 2.0
23import Ubuntu.Components 1.3
24
25Loader {
26 id: root
27
28 property var model
29 readonly property string value: status === Loader.Ready ? item.value : ""
30 readonly property bool isEmpty: status === Loader.Ready ? item.isEmpty : true
31
32 Component {
33 id: stringField
34
35 TextField {
36 id: field
37
38 property alias label: field.placeholderText
39 property string defaultValue
40
41 readonly property alias value: field.text
42 readonly property bool isEmpty: value === "" || (defaultValue && value === model.defaultValue)
43
44 text: defaultValue ? defaultValue : ""
45 }
46 }
47
48 Component {
49 id: booleanField
50
51 Item {
52 property alias label: fieldLabel.text
53 property string defaultValue
54
55 readonly property string value: fieldValue.checked ? "true" : "false"
56 readonly property bool isEmpty: (defaultValue && value === model.defaultValue)
57
58 height: fieldValue.height
59
60 Label {
61 id: fieldLabel
62
63 anchors {
64 left: parent.left
65 right: fieldValue.left
66 verticalCenter: parent.verticalCenter
67 }
68 }
69 Switch {
70 id: fieldValue
71
72 anchors {
73 right: parent.right
74 verticalCenter: parent.verticalCenter
75 }
76 checked: (defaultValue && (defaultValue === 'true'))
77 }
78 }
79 }
80
81 Component {
82 id: numericField
83
84 TextField {
85 id: field
86
87 property alias label: field.placeholderText
88 property string defaultValue
89
90 readonly property alias value: field.text
91 readonly property bool isEmpty: value === "" || (defaultValue && (value === defaultValue))
92
93 inputMethodHints: Qt.ImhDigitsOnly
94 validator: IntValidator {}
95 }
96 }
97
98 Component{
99 id: passwordField
100
101 Item {
102 property alias label: field.placeholderText
103 readonly property alias value: field.text
104 readonly property bool isEmpty: value === ""
105
106 height: field.height + showPasswordCheck.height
107 TextField {
108 id: field
109
110 echoMode: showPasswordCheck.checked ? TextInput.Normal : TextInput.Password
111 anchors {
112 left: parent.left
113 right: parent.right
114 }
115 onTextChanged: root.changed()
116 }
117 CheckBox {
118 id: showPasswordCheck
119 anchors {
120 left: field.left
121 top: field.bottom
122 topMargin: units.gu(1)
123 }
124 }
125 Label {
126 text: i18n.tr("Show Password")
127 anchors {
128 top: showPasswordCheck.top
129 left: showPasswordCheck.right
130 leftMargin: units.gu(1)
131 right: field.right
132 }
133 }
134 }
135 }
136
137
138 sourceComponent: {
139 if (!model)
140 return null
141
142 if (!model.inputType) {
143 console.warn("Model does not contain 'inputType'")
144 return null
145 }
146
147 switch (model.inputType) {
148 case 'string':
149 return stringField
150 case 'boolean':
151 return booleanField
152 case 'numeric':
153 return numericField
154 case 'password':
155 return passwordField
156 }
157 }
158
159 Binding {
160 target: root.item
161 property: "label"
162 value: model.label
163 when: root.status == Loader.Ready
164 }
165
166 Binding {
167 target: root.item
168 property: "defaultValue"
169 value: model.defaultValue
170 when: model.hasOwnProperty('defaultValue') && root.status == Loader.Ready
171 }
172}
0173
=== added file 'accounts/common/Main.qml'
--- accounts/common/Main.qml 1970-01-01 00:00:00 +0000
+++ accounts/common/Main.qml 2017-04-05 14:05:16 +0000
@@ -0,0 +1,106 @@
1/*
2 * Copyright (C) 2017 Canonical, Ltd.
3 *
4 * Authors:
5 * Renato Araujo Oliveira Filho <renato.filho@canonical.com>
6 *
7 * This file is part of telephony-service.
8 *
9 * telephony-service is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 3.
12 *
13 * telephony-service is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22import QtQuick 2.0
23import Ubuntu.Components 1.3
24import Ubuntu.OnlineAccounts.Plugin 1.0
25
26Item {
27 id: rootFlickable
28
29 property int keyboardSize: Qt.inputMethod.visible ? Qt.inputMethod.keyboardRectangle.height : 0
30
31 signal finished
32
33 anchors.fill: parent
34
35 Flickable {
36 anchors {
37 left: parent.left
38 right: parent.right
39 top: parent.top
40 bottom: btnConfirm.top
41 }
42 contentWidth: parent.width
43 contentHeight: editPageLoader.item.height + keyboardSize
44 clip: true
45
46 Loader {
47 id: editPageLoader
48 sourceComponent: account.accountId != 0 ? existingAccountComponent : newAccountComponent
49 anchors {
50 left: parent.left
51 right: parent.right
52 }
53
54 Connections {
55 target: editPageLoader.item
56 onFinished: rootFlickable.finished()
57 }
58 }
59
60 Component {
61 id: newAccountComponent
62 NewAccount {}
63 }
64
65 Component {
66 id: existingAccountComponent
67 Options {}
68 }
69 }
70
71 Button {
72 id: btnConfirm
73 text: i18n.tr("Continue")
74 color: UbuntuColors.orange
75 anchors {
76 left: parent.left
77 right: parent.right
78 bottom: btnCancel.top
79 margins: units.gu(2)
80 bottomMargin: units.gu(1)
81 }
82 enabled: editPageLoader.item && editPageLoader.item.isValid
83 onClicked: {
84 if (editPageLoader.item)
85 editPageLoader.item.confirm()
86 }
87
88 }
89
90 Button {
91 id: btnCancel
92
93 text: i18n.tr("Cancel")
94 anchors {
95 left: parent.left
96 right: parent.right
97 bottom: parent.bottom
98 margins: units.gu(2)
99 bottomMargin: units.gu(1)
100 }
101 onClicked: {
102 if (editPageLoader.item)
103 editPageLoader.item.cancel()
104 }
105 }
106}
0107
=== added file 'accounts/common/NewAccountInterface.qml'
--- accounts/common/NewAccountInterface.qml 1970-01-01 00:00:00 +0000
+++ accounts/common/NewAccountInterface.qml 2017-04-05 14:05:16 +0000
@@ -0,0 +1,314 @@
1/*
2 * Copyright (C) 2017 Canonical, Ltd.
3 *
4 * Authors:
5 * Renato Araujo Oliveira Filho <renato.filho@canonical.com>
6 *
7 * This file is part of telephony-service.
8 *
9 * telephony-service is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 3.
12 *
13 * telephony-service is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22import QtQuick 2.0
23import Ubuntu.Components 1.3
24import Ubuntu.OnlineAccounts 0.1
25
26Item {
27 id: root
28
29 readonly property string keyPrefix: "telepathy/"
30 readonly property var accountObjectHandle: account ? account.objectHandle : undefined
31 readonly property alias isValid: paramsRepeater.fieldHasValues
32
33 property string manager
34 property string protocol
35 property string icon
36 property var params
37 property var advancedParams
38 property bool hasCrendentials: true
39
40
41 signal finished
42 height: fields.childrenRect.height +
43 units.gu(10)
44
45 function getAccountService() {
46 var service = serviceModel.get(0, "accountServiceHandle")
47 if (!service) {
48 console.warn("No service handle from model")
49 return null
50 }
51
52 return accountServiceComponent.createObject(null,
53 {"objectHandle": service})
54 }
55
56 // virual
57 function extendedSettings(inputFields)
58 {
59 return {}
60 //Helper class to be extended by derived class
61 }
62
63 // virtual
64 function formatDisplayName(inputFields)
65 {
66 return inputFields['account']
67 // Helper function that allow the derived class to format a different display name
68 }
69
70 function saveServiceSettings(serviceIM, creds) {
71 var settingsIM = serviceIM.settings
72 var inputFields = {}
73
74 settingsIM[root.keyPrefix + 'manager'] = root.manager
75 settingsIM[root.keyPrefix + 'protocol'] = root.protocol
76 settingsIM[root.keyPrefix + 'Icon'] = root.icon
77
78 // basic fields
79 for (var i=0; i < paramsRepeater.count; i++) {
80 var fieldData = root.params[i]
81 var field = paramsRepeater.itemAt(i)
82 var fieldParamName = root.keyPrefix + 'param-' + fieldData.name
83
84 if (field.isEmpty) {
85 delete settingsIM[fieldParamName]
86 } else {
87 inputFields[fieldData.name] = field.value
88 if (fieldData.store) {
89 settingsIM[fieldParamName] = field.value
90 }
91 }
92 }
93
94 // advanced fields
95 for (var i=0; i < advancedParamsRepeater.count; i++) {
96 var xFieldData = root.advancedParams[i]
97 var xField = advancedParamsRepeater.itemAt(i)
98 var xFieldParamName = root.keyPrefix + 'param-' + xFieldData.name
99
100 if (xField.isEmpty) {
101 delete settingsIM[xFieldParamName]
102 } else {
103 inputFields[xFieldData.name] = xField.value
104
105 if (xFieldData.store) {
106 settingsIM[xFieldParamName] = xField.value
107 }
108 }
109 }
110
111
112 var xSettings = extendedSettings(inputFields)
113 for (var key in xSettings) {
114 settingsIM[root.keyPrefix + key] = xSettings[key]
115 }
116
117 account.updateDisplayName(formatDisplayName(inputFields))
118
119 serviceIM.updateSettings(settingsIM)
120 //serviceIM.credentials = creds
121 //serviceIM.updateServiceEnabled(true)
122 }
123
124 function continueAccountSave(creds) {
125 var imService = root.getAccountService()
126 if (!imService) {
127 console.warn("Fail to retrieve account service")
128 return
129 }
130
131 root.saveServiceSettings(imService, creds)
132 if (creds)
133 globalAccountService.credentials = creds
134 globalAccountService.updateServiceEnabled(true)
135
136 account.synced.connect(root.finished)
137 account.sync()
138 }
139
140 function credentialsStored() {
141 if (creds.credentialsId === 0) {
142 console.warn("Credentials not stored correct")
143 return
144 }
145
146 var imService = root.getAccountService()
147 if (!imService) {
148 console.warn("Fail to retrieve account service")
149 return
150 }
151
152 continueAccountSave(creds)
153 }
154
155 function parseCrendentials() {
156 var credentials = {'userName': '', 'password': ''}
157
158 for (var i=0; i < paramsRepeater.count; i++) {
159 var fieldData = root.params[i]
160 var field = paramsRepeater.itemAt(i)
161
162 if (fieldData.name === 'account')
163 credentials['userName'] = field.value
164
165 if (fieldData.name === 'password')
166 credentials['password'] = field.value
167 }
168
169 return credentials
170 }
171
172 function cancel() {
173 account.removed.connect(root.finished)
174 account.remove(Account.RemoveCredentials)
175 }
176
177 function confirm() {
178 var info = root.parseCrendentials()
179 // save account
180 account.updateDisplayName(info.userName)
181 if (root.hasCrendentials) {
182 creds.userName = info.userName
183 creds.secret = info.password
184 creds.sync()
185 } else {
186 continueAccountSave(null)
187 }
188 }
189
190 Column {
191 id: fields
192
193 anchors {
194 top: parent.top
195 topMargin: units.gu(5)
196 left: parent.left
197 right: parent.right
198 }
199 height: childrenRect.height
200 spacing: units.gu(2)
201
202 Icon {
203 anchors.horizontalCenter: fields.horizontalCenter
204 name: root.icon
205 }
206
207 Repeater {
208 id: paramsRepeater
209
210 property bool fieldHasValues: false
211
212 function checkFieldsHasValues()
213 {
214 var hasEmptyField = false
215 for (var i = 0; i < paramsRepeater.count; i++) {
216
217 var child = paramsRepeater.itemAt(i)
218 if (child && child.isEmpty) {
219 hasEmptyField = true
220 break
221 }
222 }
223 fieldHasValues = !hasEmptyField
224 }
225
226 width: parent.width
227 model: root.params
228 DynamicField {
229 model: modelData
230 anchors{
231 left: parent.left
232 right: parent.right
233 margins: units.gu(4)
234 }
235 onValueChanged: paramsRepeater.checkFieldsHasValues()
236 }
237 }
238
239 Item {
240 id: div
241
242 anchors{
243 left: parent.left
244 right: parent.right
245 }
246 height: units.gu(3)
247 visible: root.advancedParams.length > 0
248 }
249
250 Label {
251 id: advancedParamsTitle
252
253 anchors{
254 left: parent.left
255 right: parent.right
256 margins: units.gu(4)
257 }
258 visible: root.advancedParams.length > 0
259 text: i18n.tr("Advanced Options")
260 textSize: Label.Medium
261 }
262
263 Repeater {
264 id: advancedParamsRepeater
265
266 width: parent.width
267 model: root.advancedParams
268 DynamicField {
269 model: modelData
270 anchors{
271 left: parent.left
272 right: parent.right
273 margins: units.gu(4)
274 }
275 }
276 }
277 }
278
279 AccountService {
280 id: globalAccountService
281
282 objectHandle: account.accountServiceHandle
283 autoSync: false
284 }
285
286 Credentials {
287 id: creds
288
289 caption: account.provider.id
290 acl: "*" // untill later
291 storeSecret: true
292 onCredentialsIdChanged: {
293 console.debug("Credetials id changed")
294 root.credentialsStored()
295 }
296 }
297
298 // necessary to store settings on the "IM" service
299 AccountServiceModel {
300 id: serviceModel
301
302 includeDisabled: true
303 account: root.accountObjectHandle
304 serviceType: "IM"
305 }
306
307 Component {
308 id: accountServiceComponent
309
310 AccountService {
311 autoSync: false
312 }
313 }
314}
0315
=== added directory 'accounts/irc'
=== added file 'accounts/irc/CMakeLists.txt'
--- accounts/irc/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ accounts/irc/CMakeLists.txt 2017-04-05 14:05:16 +0000
@@ -0,0 +1,2 @@
1add_subdirectory(qml)
2add_subdirectory(data)
03
=== added directory 'accounts/irc/data'
=== added file 'accounts/irc/data/CMakeLists.txt'
--- accounts/irc/data/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ accounts/irc/data/CMakeLists.txt 2017-04-05 14:05:16 +0000
@@ -0,0 +1,5 @@
1file(GLOB PROVIDER_FILES *.provider)
2install(FILES ${PROVIDER_FILES} DESTINATION share/accounts/providers/)
3
4file(GLOB SERVICE_FILES *.service)
5install(FILES ${SERVICE_FILES} DESTINATION share/accounts/services/)
06
=== added file 'accounts/irc/data/telephony-irc-im.service'
--- accounts/irc/data/telephony-irc-im.service 1970-01-01 00:00:00 +0000
+++ accounts/irc/data/telephony-irc-im.service 2017-04-05 14:05:16 +0000
@@ -0,0 +1,22 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<service id="telephony-irc-im">
3 <type>IM</type>
4 <name>IRC</name>
5 <icon>irc</icon>
6 <provider>telephony-irc</provider>
7
8 <!-- default settings (account settings have precedence over these) -->
9 <template>
10 <group name="telepathy">
11 <setting name="manager">irc</setting>
12 <setting name="protocol">irc</setting>
13 <setting name="ConnectAutomatically">false</setting>
14 <setting type="s" name="AutomaticPresence">1;offline;;</setting>
15 </group>
16 <group name="auth">
17 <setting name="method">password</setting>
18 <setting name="mechanism">password</setting>
19 </group>
20 </template>
21
22</service>
023
=== added file 'accounts/irc/data/telephony-irc.provider'
--- accounts/irc/data/telephony-irc.provider 1970-01-01 00:00:00 +0000
+++ accounts/irc/data/telephony-irc.provider 2017-04-05 14:05:16 +0000
@@ -0,0 +1,5 @@
1<?xml version="1.0" encoding="UTF-8" ?>
2<provider id="telephony-irc">
3 <name>IRC</name>
4 <icon>irc</icon>
5</provider>
06
=== added directory 'accounts/irc/qml'
=== added file 'accounts/irc/qml/AccountInfo.qml'
=== added file 'accounts/irc/qml/CMakeLists.txt'
--- accounts/irc/qml/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ accounts/irc/qml/CMakeLists.txt 2017-04-05 14:05:16 +0000
@@ -0,0 +1,13 @@
1file(GLOB QML_PLUGIN_FILES *.qml)
2
3# The path (including plug-in name) where the QML files are installed
4set(QML_PLUGIN_INSTALL_DIR share/accounts/qml-plugins/telephony-irc/)
5
6# Do not install symbolic links
7set (QML_PLUGIN_RESOLVED_FILES "")
8foreach (QML_PLUGIN_FILE ${QML_PLUGIN_FILES})
9 get_filename_component(resolvedFile "${QML_PLUGIN_FILE}" REALPATH)
10 list (APPEND QML_PLUGIN_RESOLVED_FILES "${resolvedFile}")
11endforeach()
12
13install(FILES ${QML_PLUGIN_RESOLVED_FILES} DESTINATION ${QML_PLUGIN_INSTALL_DIR})
014
=== added symlink 'accounts/irc/qml/DynamicField.qml'
=== target is u'../../common/DynamicField.qml'
=== added symlink 'accounts/irc/qml/Main.qml'
=== target is u'../../common/Main.qml'
=== added file 'accounts/irc/qml/NewAccount.qml'
--- accounts/irc/qml/NewAccount.qml 1970-01-01 00:00:00 +0000
+++ accounts/irc/qml/NewAccount.qml 2017-04-05 14:05:16 +0000
@@ -0,0 +1,63 @@
1/*
2 * Copyright (C) 2017 Canonical, Ltd.
3 *
4 * Authors:
5 * Renato Araujo Oliveira Filho <renato.filho@canonical.com>
6 *
7 * This file is part of telephony-service.
8 *
9 * telephony-service is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 3.
12 *
13 * telephony-service is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22import QtQuick 2.0
23
24NewAccountInterface {
25 id: root
26
27 anchors {
28 left: parent.left
29 right: parent.right
30 verticalCenter: parent.verticalCenter
31 }
32
33 manager: 'irc'
34 protocol: 'irc'
35 icon: 'irc'
36 hasCrendentials: false
37 params: [
38 {'name': 'server', 'inputType': 'string', 'label': i18n.tr('Network. (Eg: chat.freenode.net)'), 'store': true},
39 {'name': 'nickname', 'inputType': 'string', 'label': i18n.tr('Nickname'), 'store': true},
40 ]
41 advancedParams: [
42 {'name': 'port', 'inputType': 'numeric', 'label': i18n.tr('Port'), 'store': true},
43 {'name': 'use-ssl', 'inputType': 'boolean', 'label': i18n.tr('Use ssl'), 'store': true},
44 {'name': 'verify-ssl-cert', 'inputType': 'boolean', 'label': i18n.tr('Verify ssl certificate'), 'store': true},
45 {'name': 'username', 'inputType': 'string', 'label': i18n.tr('Username'), 'store': true},
46 {'name': 'password', 'inputType': 'password', 'label': i18n.tr('Password'), 'store': true},
47 {'name': 'fullname', 'inputType': 'string', 'label': i18n.tr('Real name'), 'store': true},
48 ]
49
50 function extendedSettings(inputFields)
51 {
52 var settings = {}
53 settings['param-account'] = inputFields['nickname'] + "@" + inputFields['server']
54 if (settings['param-port'] == "")
55 settings['param-port'] = "6667"
56 return settings
57 }
58
59 function formatDisplayName(inputFields)
60 {
61 return inputFields['nickname'] + "@" + inputFields['server']
62 }
63}
064
=== added symlink 'accounts/irc/qml/NewAccountInterface.qml'
=== target is u'../../common/NewAccountInterface.qml'
=== added directory 'accounts/sip'
=== added file 'accounts/sip/CMakeLists.txt'
--- accounts/sip/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ accounts/sip/CMakeLists.txt 2017-04-05 14:05:16 +0000
@@ -0,0 +1,2 @@
1add_subdirectory(qml)
2add_subdirectory(data)
03
=== added directory 'accounts/sip/data'
=== added file 'accounts/sip/data/CMakeLists.txt'
--- accounts/sip/data/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ accounts/sip/data/CMakeLists.txt 2017-04-05 14:05:16 +0000
@@ -0,0 +1,5 @@
1file(GLOB PROVIDER_FILES *.provider)
2install(FILES ${PROVIDER_FILES} DESTINATION share/accounts/providers/)
3
4file(GLOB SERVICE_FILES *.service)
5install(FILES ${SERVICE_FILES} DESTINATION share/accounts/services/)
06
=== added file 'accounts/sip/data/telephony-sip-im.service'
--- accounts/sip/data/telephony-sip-im.service 1970-01-01 00:00:00 +0000
+++ accounts/sip/data/telephony-sip-im.service 2017-04-05 14:05:16 +0000
@@ -0,0 +1,22 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<service id="telephony-sip-im">
3 <type>IM</type>
4 <name>SIP</name>
5 <icon>sip</icon>
6 <provider>telephony-sip</provider>
7
8 <!-- default settings (account settings have precedence over these) -->
9 <template>
10 <group name="telepathy">
11 <setting name="manager">sofiasip</setting>
12 <setting name="protocol">sip</setting>
13 <setting name="ConnectAutomatically">true</setting>
14 <setting type="s" name="AutomaticPresence">2;available;;</setting>
15 </group>
16 <group name="auth">
17 <setting name="method">password</setting>
18 <setting name="mechanism">password</setting>
19 </group>
20 </template>
21
22</service>
023
=== added file 'accounts/sip/data/telephony-sip.provider'
--- accounts/sip/data/telephony-sip.provider 1970-01-01 00:00:00 +0000
+++ accounts/sip/data/telephony-sip.provider 2017-04-05 14:05:16 +0000
@@ -0,0 +1,5 @@
1<?xml version="1.0" encoding="UTF-8" ?>
2<provider id="telephony-sip">
3 <name>SIP</name>
4 <icon>sip</icon>
5</provider>
06
=== added directory 'accounts/sip/qml'
=== added file 'accounts/sip/qml/AccountInfo.qml'
=== added file 'accounts/sip/qml/CMakeLists.txt'
--- accounts/sip/qml/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ accounts/sip/qml/CMakeLists.txt 2017-04-05 14:05:16 +0000
@@ -0,0 +1,13 @@
1file(GLOB QML_PLUGIN_FILES *.qml)
2
3# The path (including plug-in name) where the QML files are installed
4set(QML_PLUGIN_INSTALL_DIR share/accounts/qml-plugins/telephony-sip/)
5
6# Do not install symbolic links
7set (QML_PLUGIN_RESOLVED_FILES "")
8foreach (QML_PLUGIN_FILE ${QML_PLUGIN_FILES})
9 get_filename_component(resolvedFile "${QML_PLUGIN_FILE}" REALPATH)
10 list (APPEND QML_PLUGIN_RESOLVED_FILES "${resolvedFile}")
11endforeach()
12
13install(FILES ${QML_PLUGIN_RESOLVED_FILES} DESTINATION ${QML_PLUGIN_INSTALL_DIR})
014
=== added symlink 'accounts/sip/qml/DynamicField.qml'
=== target is u'../../common/DynamicField.qml'
=== added symlink 'accounts/sip/qml/Main.qml'
=== target is u'../../common/Main.qml'
=== added file 'accounts/sip/qml/NewAccount.qml'
--- accounts/sip/qml/NewAccount.qml 1970-01-01 00:00:00 +0000
+++ accounts/sip/qml/NewAccount.qml 2017-04-05 14:05:16 +0000
@@ -0,0 +1,48 @@
1/*
2 * Copyright (C) 2017 Canonical, Ltd.
3 *
4 * Authors:
5 * Renato Araujo Oliveira Filho <renato.filho@canonical.com>
6 *
7 * This file is part of telephony-service.
8 *
9 * telephony-service is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 3.
12 *
13 * telephony-service is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22import QtQuick 2.0
23
24NewAccountInterface {
25 id: root
26
27 manager: 'sofiasip'
28 protocol: 'sip'
29 icon: 'sip'
30 hasCrendentials: false
31 params: [
32 {'name': 'account', 'inputType': 'string', 'label': i18n.tr('Sip Id. (Eg: user@my.sip.net)'), 'store': true},
33 {'name': 'password', 'inputType': 'password', 'label': i18n.tr('Password'), 'store': true}
34 ]
35 advancedParams: [
36 {'name': 'discover-stun', 'inputType': 'boolean', 'label': i18n.tr('Discover the STUN server automatically'), 'store': true, 'defaultValue' : 'false' },
37 {'name': 'stun-server', 'inputType': 'string', 'label': i18n.tr('STUN server'), 'store': true},
38 {'name': 'stun-port', 'inputType': 'numeric', 'label': i18n.tr('STUN port'), 'store': true},
39 {'name': 'discover-binding', 'inputType': 'boolean', 'label': i18n.tr('Divscover Binding'), 'store': true, 'defaultValue': 'true'},
40 {'name': 'proxy-host', 'inputType': 'string', 'label': i18n.tr('Proxy server'), 'store': true},
41 {'name': 'port', 'inputType': 'numeric', 'label': i18n.tr('Proxy port'), 'store': true},
42 {'name': 'keepalive-mechanism', 'inputType': 'string', 'label': i18n.tr('Keep alive mechanism'), 'store': true, 'defaultValue': 'auto'},
43 {'name': 'keepalive-interval', 'inputType': 'numeric', 'label': i18n.tr('Keep alive interval'), 'store': true},
44 {'name': 'auth-user', 'inputType': 'string', 'label': i18n.tr('Authentication username'), 'store': true},
45 {'name': 'transport', 'inputType': 'string', 'label': i18n.tr('Transport'), 'store': true, 'defaultValue': 'auto'},
46 {'name': 'loose-routing', 'inputType': 'boolean', 'label': i18n.tr('Loose Routing'), 'store': true, 'defaultValue': 'false'}
47 ]
48}
049
=== added symlink 'accounts/sip/qml/NewAccountInterface.qml'
=== target is u'../../common/NewAccountInterface.qml'
=== modified file 'approver/approver.cpp'
--- approver/approver.cpp 2016-11-23 19:28:18 +0000
+++ approver/approver.cpp 2017-04-05 14:05:16 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2012-2016 Canonical, Ltd.2 * Copyright (C) 2012-2017 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Tiago Salem Herrmann <tiago.herrmann@canonical.com>5 * Tiago Salem Herrmann <tiago.herrmann@canonical.com>
@@ -27,10 +27,12 @@
27#include "chatmanager.h"27#include "chatmanager.h"
28#include "config.h"28#include "config.h"
29#include "contactutils.h"29#include "contactutils.h"
30#include "contactwatcher.h"
30#include "greetercontacts.h"31#include "greetercontacts.h"
31#include "ringtone.h"32#include "ringtone.h"
32#include "callmanager.h"33#include "callmanager.h"
33#include "callentry.h"34#include "callentry.h"
35#include "protocol.h"
34#include "protocolmanager.h"36#include "protocolmanager.h"
35#include "tonegenerator.h"37#include "tonegenerator.h"
36#include "telepathyhelper.h"38#include "telepathyhelper.h"
@@ -315,11 +317,13 @@
315317
316 mChannels.remove(pr);318 mChannels.remove(pr);
317319
320 QString id = ContactWatcher::normalizeIdentifier(contact->id(), true);
321
318 // and now set up the contact matching for either greeter mode or regular mode322 // and now set up the contact matching for either greeter mode or regular mode
319 if (GreeterContacts::isGreeterMode()) {323 if (GreeterContacts::isGreeterMode()) {
320 // show the snap decision right away because contact info might never arrive324 // show the snap decision right away because contact info might never arrive
321 showSnapDecision(dispatchOp, channel);325 showSnapDecision(dispatchOp, channel);
322 GreeterContacts::instance()->setContactFilter(QContactPhoneNumber::match(contact->id()));326 GreeterContacts::instance()->setContactFilter(QContactPhoneNumber::match(id));
323 } else {327 } else {
324 AccountEntry *account = TelepathyHelper::instance()->accountForConnection(callChannel->connection());328 AccountEntry *account = TelepathyHelper::instance()->accountForConnection(callChannel->connection());
325 if (!account) {329 if (!account) {
@@ -329,7 +333,7 @@
329333
330 // try to match the contact info334 // try to match the contact info
331 QContactFetchRequest *request = new QContactFetchRequest(this);335 QContactFetchRequest *request = new QContactFetchRequest(this);
332 request->setFilter(QContactPhoneNumber::match(contact->id()));336 request->setFilter(QContactPhoneNumber::match(id));
333337
334 // lambda function to update the notification338 // lambda function to update the notification
335 QObject::connect(request, &QContactAbstractRequest::stateChanged, [this, request, dispatchOp, channel](QContactAbstractRequest::State state) {339 QObject::connect(request, &QContactAbstractRequest::stateChanged, [this, request, dispatchOp, channel](QContactAbstractRequest::State state) {
@@ -350,14 +354,9 @@
350 showSnapDecision(dispatchOp, channel, contact);354 showSnapDecision(dispatchOp, channel, contact);
351 });355 });
352356
353 // FIXME: For accounts not based on phone numbers, don't try to match contacts for now357 // FIXME: For accounts not based on phone numbers, check what to do
354 if (account->type() == AccountEntry::PhoneAccount) {358 request->setManager(ContactUtils::sharedManager());
355 request->setManager(ContactUtils::sharedManager());359 request->start();
356 request->start();
357 } else {
358 // just emit the signal to pretend we did a contact search
359 Q_EMIT request->stateChanged(QContactAbstractRequest::FinishedState);
360 }
361 }360 }
362}361}
363362
@@ -440,42 +439,46 @@
440 data->channel = channel;439 data->channel = channel;
441 bool unknownNumber = false;440 bool unknownNumber = false;
442441
442 QString id = ContactWatcher::normalizeIdentifier(telepathyContact->id(), true);
443
443 AccountEntry *account = TelepathyHelper::instance()->accountForConnection(channel->connection());444 AccountEntry *account = TelepathyHelper::instance()->accountForConnection(channel->connection());
444 if (!account) {445 if (!account) {
445 qCritical() << "Call exists with no account for connection";446 qCritical() << "Call exists with no account for connection";
446 return false;447 return false;
447 }448 }
448449
450 bool supportsText = (account->protocolInfo()->features() & Protocol::TextChats);
451
449 mCachedBody = QString();452 mCachedBody = QString();
450453
451 if (account->type() == AccountEntry::PhoneAccount &&454 if (account->type() == AccountEntry::PhoneAccount &&
452 TelepathyHelper::instance()->multiplePhoneAccounts()) {455 TelepathyHelper::instance()->multiplePhoneAccounts()) {
453 mCachedBody = QString::fromUtf8(C::gettext("On [%1]")).arg(account->displayName());456 mCachedBody = QString::fromUtf8(C::gettext("On [%1]")).arg(account->displayName());
454 mCachedBody += "\n";457 mCachedBody += "\n";
455 if (!telepathyContact->id().isEmpty()) {458 if (!id.isEmpty()) {
456 if (telepathyContact->id().startsWith(OFONO_PRIVATE_NUMBER)) {459 if (id.startsWith(OFONO_PRIVATE_NUMBER)) {
457 mCachedBody += QString::fromUtf8(C::gettext("Private number"));460 mCachedBody += QString::fromUtf8(C::gettext("Private number"));
458 unknownNumber = true;461 unknownNumber = true;
459 } else if (telepathyContact->id().startsWith(OFONO_UNKNOWN_NUMBER)) {462 } else if (id.startsWith(OFONO_UNKNOWN_NUMBER)) {
460 mCachedBody += QString::fromUtf8(C::gettext("Unknown number"));463 mCachedBody += QString::fromUtf8(C::gettext("Unknown number"));
461 unknownNumber = true;464 unknownNumber = true;
462 } else {465 } else {
463 mCachedBody += telepathyContact->id();466 mCachedBody += id;
464 }467 }
465 } else {468 } else {
466 mCachedBody += C::gettext("Caller number is not available");469 mCachedBody += C::gettext("Caller number is not available");
467 unknownNumber = true;470 unknownNumber = true;
468 }471 }
469 } else {472 } else {
470 if (!telepathyContact->id().isEmpty()) {473 if (!id.isEmpty()) {
471 if (telepathyContact->id().startsWith(OFONO_PRIVATE_NUMBER)) {474 if (id.startsWith(OFONO_PRIVATE_NUMBER)) {
472 mCachedBody = QString::fromUtf8(C::gettext("Calling from private number"));475 mCachedBody = QString::fromUtf8(C::gettext("Calling from private number"));
473 unknownNumber = true;476 unknownNumber = true;
474 } else if (telepathyContact->id().startsWith(OFONO_UNKNOWN_NUMBER)) {477 } else if (id.startsWith(OFONO_UNKNOWN_NUMBER)) {
475 mCachedBody = QString::fromUtf8(C::gettext("Calling from unknown number"));478 mCachedBody = QString::fromUtf8(C::gettext("Calling from unknown number"));
476 unknownNumber = true;479 unknownNumber = true;
477 } else {480 } else {
478 mCachedBody = QString::fromUtf8(C::gettext("Calling from %1")).arg(telepathyContact->id());481 mCachedBody = QString::fromUtf8(C::gettext("Calling from %1")).arg(id);
479 }482 }
480 } else {483 } else {
481 mCachedBody = C::gettext("Caller number is not available");484 mCachedBody = C::gettext("Caller number is not available");
@@ -544,7 +547,7 @@
544 data,547 data,
545 delete_event_data);548 delete_event_data);
546549
547 if (!unknownNumber) {550 if (!unknownNumber && supportsText) {
548 notify_notification_add_action(notification,551 notify_notification_add_action(notification,
549 "action_decline_expansion",552 "action_decline_expansion",
550 C::gettext("Message & decline"),553 C::gettext("Message & decline"),
551554
=== modified file 'cmake/modules/GenerateTest.cmake'
--- cmake/modules/GenerateTest.cmake 2016-12-13 01:51:47 +0000
+++ cmake/modules/GenerateTest.cmake 2017-04-05 14:05:16 +0000
@@ -78,6 +78,7 @@
78 MC_ACCOUNT_DIR=${TMPDIR}78 MC_ACCOUNT_DIR=${TMPDIR}
79 MC_MANAGER_DIR=${TMPDIR}79 MC_MANAGER_DIR=${TMPDIR}
80 MC_CLIENTS_DIR=${TMPDIR}80 MC_CLIENTS_DIR=${TMPDIR}
81 PA_DISABLED=1
81 TELEPHONY_SERVICE_TEST=182 TELEPHONY_SERVICE_TEST=1
82 TELEPHONY_SERVICE_PROTOCOLS_DIR=${CMAKE_SOURCE_DIR}/tests/common/protocols83 TELEPHONY_SERVICE_PROTOCOLS_DIR=${CMAKE_SOURCE_DIR}/tests/common/protocols
83 TEST_DATA_DIR=${CMAKE_SOURCE_DIR}/tests/common/data)84 TEST_DATA_DIR=${CMAKE_SOURCE_DIR}/tests/common/data)
8485
=== added file 'debian/account-plugin-irc-unity8.install'
--- debian/account-plugin-irc-unity8.install 1970-01-01 00:00:00 +0000
+++ debian/account-plugin-irc-unity8.install 2017-04-05 14:05:16 +0000
@@ -0,0 +1,3 @@
1usr/share/accounts/qml-plugins/telephony-irc/*
2usr/share/accounts/providers/telephony-irc.provider
3usr/share/accounts/services/telephony-irc-im.service
04
=== added file 'debian/account-plugin-sip-unity8.install'
--- debian/account-plugin-sip-unity8.install 1970-01-01 00:00:00 +0000
+++ debian/account-plugin-sip-unity8.install 2017-04-05 14:05:16 +0000
@@ -0,0 +1,3 @@
1usr/share/accounts/qml-plugins/telephony-sip/*
2usr/share/accounts/providers/telephony-sip.provider
3usr/share/accounts/services/telephony-sip-im.service
04
=== modified file 'debian/control'
--- debian/control 2016-12-05 15:14:33 +0000
+++ debian/control 2017-04-05 14:05:16 +0000
@@ -13,10 +13,12 @@
13 libicu-dev,13 libicu-dev,
14 libmessaging-menu-dev,14 libmessaging-menu-dev,
15 libnotify-dev,15 libnotify-dev,
16 libgsettings-qt-dev,
16 libphonenumber-dev,17 libphonenumber-dev,
17 libtelepathy-qt5-dev,18 libtelepathy-qt5-dev,
18 libubuntu-application-api-dev [armhf],
19 libprotobuf-dev,19 libprotobuf-dev,
20 libpulse-dev,
21 liburl-dispatcher1-dev,
20 pkg-config,22 pkg-config,
21 python:any,23 python:any,
22 qml-module-qttest,24 qml-module-qttest,
@@ -92,3 +94,24 @@
92 This package contains the QML plugin providing the features from the telephony94 This package contains the QML plugin providing the features from the telephony
93 PhoneNumber to applications.95 PhoneNumber to applications.
9496
97Package: account-plugin-sip-unity8
98Architecture: all
99Pre-Depends: dpkg (>= 1.15.6~)
100Depends: mcp-account-manager-uoa-common,
101 telepathy-accounts-signon,
102 telepathy-rakia
103Description: Online account plugin for unity8
104 Online account plugin for unity8.
105 .
106 This package contains the online account plugin providing sip account.
107
108Package: account-plugin-irc-unity8
109Architecture: all
110Pre-Depends: dpkg (>= 1.15.6~)
111Depends: mcp-account-manager-uoa-common,
112 mfw-plugin-irc
113Description: Online account plugin for unity8
114 Online account plugin for unity8.
115 .
116 This package contains the online account plugin providing irc account.
117
95118
=== modified file 'handler/CMakeLists.txt'
--- handler/CMakeLists.txt 2016-12-06 16:45:01 +0000
+++ handler/CMakeLists.txt 2017-04-05 14:05:16 +0000
@@ -1,12 +1,24 @@
1if (PULSEAUDIO_FOUND)
2 add_definitions(-DUSE_PULSEAUDIO)
3 set(USE_PULSEAUDIO ON)
4 set(QPULSEAUDIOENGINE_CPP qpulseaudioengine.cpp)
5endif (PULSEAUDIO_FOUND)
16
2set(qt_SRCS7set(qt_SRCS
8 accountproperties.cpp
9 callagent.cpp
10 audioroutemanager.cpp
3 callhandler.cpp11 callhandler.cpp
4 chatstartingjob.cpp12 chatstartingjob.cpp
13 farstreamchannel.cpp
5 handler.cpp14 handler.cpp
6 handlerdbus.cpp15 handlerdbus.cpp
7 messagejob.cpp16 messagejob.cpp
8 messagesendingjob.cpp17 messagesendingjob.cpp
18 powerdaudiomodemediator.cpp
19 powerddbus.cpp
9 texthandler.cpp20 texthandler.cpp
21 ${QPULSEAUDIOENGINE_CPP}
10 )22 )
1123
12set(handler_SRCS main.cpp ${qt_SRCS})24set(handler_SRCS main.cpp ${qt_SRCS})
@@ -16,8 +28,13 @@
1628
17include_directories(29include_directories(
18 ${TP_QT5_INCLUDE_DIRS}30 ${TP_QT5_INCLUDE_DIRS}
31 ${TPFS_INCLUDE_DIRS}
32 ${FS_INCLUDE_DIRS}
33 ${GST_INCLUDE_DIRS}
19 ${CMAKE_SOURCE_DIR}/libtelephonyservice34 ${CMAKE_SOURCE_DIR}/libtelephonyservice
20 ${CMAKE_CURRENT_BINARY_DIR}35 ${CMAKE_CURRENT_BINARY_DIR}
36 ${GSETTINGS_QT_INCLUDE_DIRS}
37 ${PULSEAUDIO_INCLUDE_DIRS}
21 )38 )
2239
23add_executable(telephony-service-handler ${handler_SRCS} ${handler_HDRS})40add_executable(telephony-service-handler ${handler_SRCS} ${handler_HDRS})
@@ -25,7 +42,11 @@
2542
26target_link_libraries(telephony-service-handler43target_link_libraries(telephony-service-handler
27 ${TP_QT5_LIBRARIES}44 ${TP_QT5_LIBRARIES}
45 ${TP_QT5_FS_LIBRARIES}
46 ${TPFS_LIBRARIES}
47 ${FS_LIBRARIES}
28 telephonyservice48 telephonyservice
49 ${PULSEAUDIO_LIBRARIES}
29 )50 )
3051
31enable_coverage(telephony-service-handler)52enable_coverage(telephony-service-handler)
3253
=== modified file 'handler/Handler.xml'
--- handler/Handler.xml 2016-12-06 16:45:01 +0000
+++ handler/Handler.xml 2017-04-05 14:05:16 +0000
@@ -114,13 +114,6 @@
114 <arg name="objectPath" type="s" direction="in"/>114 <arg name="objectPath" type="s" direction="in"/>
115 <arg name="muted" type="b" direction="in"/>115 <arg name="muted" type="b" direction="in"/>
116 </method>116 </method>
117 <method name="SetActiveAudioOutput">
118 <dox:d><![CDATA[
119 Change the current active audio output
120 ]]></dox:d>
121 <arg name="objectPath" type="s" direction="in"/>
122 <arg name="id" type="s" direction="in"/>
123 </method>
124 <method name="SendDTMF">117 <method name="SendDTMF">
125 <dox:d><![CDATA[118 <dox:d><![CDATA[
126 Send a DTMF to the given channel119 Send a DTMF to the given channel
@@ -177,9 +170,48 @@
177 <dox:d><![CDATA[170 <dox:d><![CDATA[
178 Get the list of current available protocols171 Get the list of current available protocols
179 ]]></dox:d>172 ]]></dox:d>
180 <arg name="result" type="a(sussss)" direction="out"/>173 <arg name="result" type="a(susussbbssssbbbbbbb)" direction="out"/>
181 <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="ProtocolList"/>174 <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="ProtocolList"/>
182 </method>175 </method>
176 <method name="LeaveRooms">
177 <dox:d><![CDATA[
178 Close all rooms of a given account
179 ]]></dox:d>
180 <arg name="accountId" type="s" direction="in"/>
181 <arg name="message" type="s" direction="in"/>
182 </method>
183 <method name="GetAllAccountsProperties">
184 <dox:d><![CDATA[
185 Get the properties of all available accounts
186 ]]></dox:d>
187 <arg name="result" type="a{sa{sv}}" direction="out"/>
188 <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="AllAccountsProperties"/>
189 </method>
190 <method name="GetAccountProperties">
191 <dox:d><![CDATA[
192 Get the properties of the given accountId
193 ]]></dox:d>
194 <arg name="accoundId" type="s" direction="in"/>
195 <arg name="result" type="a{sv}" direction="out"/>
196 <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
197 </method>
198 <method name="SetAccountProperties">
199 <dox:d><![CDATA[
200 Set the properties of the given accountId
201 ]]></dox:d>
202 <arg name="accoundId" type="s" direction="in"/>
203 <arg name="properties" type="a{sv}" direction="in"/>
204 <annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="QVariantMap"/>
205 </method>
206 <signal name="AccountPropertiesChanged">
207 <dox:d><![CDATA[
208 The properties of a given account changed.
209 ]]></dox:d>
210 <arg name="accountId" type="s"/>
211 <arg name="properties" type="a{sv}"/>
212 <annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="QVariantMap"/>
213 <annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="QVariantMap"/>
214 </signal>
183 <signal name="CallPropertiesChanged">215 <signal name="CallPropertiesChanged">
184 <dox:d><![CDATA[216 <dox:d><![CDATA[
185 The properties of a given call changed.217 The properties of a given call changed.
@@ -213,9 +245,32 @@
213 <dox:d><![CDATA[245 <dox:d><![CDATA[
214 The protocols files in protocols dir have changed246 The protocols files in protocols dir have changed
215 ]]></dox:d>247 ]]></dox:d>
216 <arg name="protocols" type="a(sussss)"/>248 <arg name="protocols" type="a(susussbbssssbbbbbbb)"/>
217 <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="ProtocolList"/>249 <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="ProtocolList"/>
218 <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="ProtocolList"/>250 <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="ProtocolList"/>
219 </signal>251 </signal>
252 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
253 <property name="ActiveAudioOutput" type="s" access="readwrite"/>
254 <signal name="ActiveAudioOutputChanged">
255 <dox:d><![CDATA[
256 The active audio output has changed
257 ]]></dox:d>
258 <arg name="id" type="s"/>
259 </signal>
260 <method name="AudioOutputs">
261 <dox:d><![CDATA[
262 ]]></dox:d>
263 <arg name="outputs" type="a(sss)" direction="out"/>
264 <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="AudioOutputDBusList"/>
265 <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="AudioOutputDBusList"/>
266 </method>
267 <signal name="AudioOutputsChanged">
268 <dox:d><![CDATA[
269 The available audio outputs have changed
270 ]]></dox:d>
271 <arg name="outputs" type="a(sss)"/>
272 <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="AudioOutputDBusList"/>
273 <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="AudioOutputDBusList"/>
274 </signal>
220 </interface>275 </interface>
221</node>276</node>
222277
=== modified file 'handler/TelephonyServiceHandler.client'
--- handler/TelephonyServiceHandler.client 2016-04-15 22:25:51 +0000
+++ handler/TelephonyServiceHandler.client 2017-04-05 14:05:16 +0000
@@ -6,3 +6,12 @@
66
7[org.freedesktop.Telepathy.Client.Handler.HandlerChannelFilter 3]7[org.freedesktop.Telepathy.Client.Handler.HandlerChannelFilter 3]
8org.freedesktop.Telepathy.Channel.ChannelType s=org.freedesktop.Telepathy.Channel.Type.Call18org.freedesktop.Telepathy.Channel.ChannelType s=org.freedesktop.Telepathy.Channel.Type.Call1
9
10[org.freedesktop.Telepathy.Client.Handler.HandlerChannelFilter 1]
11org.freedesktop.Telepathy.Channel.ChannelType s=org.freedesktop.Telepathy.Channel.Type.Call1
12org.freedesktop.Telepathy.Channel.Type.Call1.InitialAudio b=true
13
14[org.freedesktop.Telepathy.Client.Handler.Capabilities]
15org.freedesktop.Telepathy.Channel.Type.Call1/audio=true
16org.freedesktop.Telepathy.Channel.Type.Call1/ice=true
17org.freedesktop.Telepathy.Channel.Type.Call1/gtalk-p2p=true
918
=== added file 'handler/accountproperties.cpp'
--- handler/accountproperties.cpp 1970-01-01 00:00:00 +0000
+++ handler/accountproperties.cpp 2017-04-05 14:05:16 +0000
@@ -0,0 +1,71 @@
1/*
2 * Copyright (C) 2017 Canonical, Ltd.
3 *
4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
6 *
7 * This file is part of telephony-service.
8 *
9 * telephony-service is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 3.
12 *
13 * telephony-service is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include "accountproperties.h"
23#include "telepathyhelper.h"
24#include <QSettings>
25
26#define SETTINGS_DOMAIN "com.canonical.TelephonyServiceHandler"
27
28AccountProperties *AccountProperties::instance()
29{
30 static AccountProperties *self = new AccountProperties();
31 return self;
32}
33
34QMap<QString, QVariantMap> AccountProperties::allProperties()
35{
36 QMap<QString,QVariantMap> props;
37 for (auto accountId : TelepathyHelper::instance()->accountIds()) {
38 props[accountId] = accountProperties(accountId);
39 }
40}
41
42QVariantMap AccountProperties::accountProperties(const QString &accountId)
43{
44 QVariantMap props;
45 mSettings->beginGroup(formatAccountId(accountId));
46 for (auto key : mSettings->allKeys()) {
47 props[key] = mSettings->value(key);
48 }
49 mSettings->endGroup();
50 return props;
51}
52
53void AccountProperties::setAccountProperties(const QString &accountId, const QVariantMap &properties)
54{
55 mSettings->beginGroup(formatAccountId(accountId));
56 for (auto key : properties.keys()) {
57 mSettings->setValue(key, properties[key]);
58 }
59 mSettings->endGroup();
60}
61
62QString AccountProperties::formatAccountId(const QString &accountId)
63{
64 return QUrl::toPercentEncoding(accountId);
65}
66
67AccountProperties::AccountProperties(QObject *parent)
68: QObject(parent),
69 mSettings(new QSettings(SETTINGS_DOMAIN, QString(), parent))
70{
71}
072
=== added file 'handler/accountproperties.h'
--- handler/accountproperties.h 1970-01-01 00:00:00 +0000
+++ handler/accountproperties.h 2017-04-05 14:05:16 +0000
@@ -0,0 +1,47 @@
1/*
2 * Copyright (C) 2017 Canonical, Ltd.
3 *
4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
6 *
7 * This file is part of telephony-service.
8 *
9 * telephony-service is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 3.
12 *
13 * telephony-service is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#ifndef ACCOUNTPROPERTIES_H
23#define ACCOUNTPROPERTIES_H
24
25#include <QObject>
26
27class QSettings;
28
29class AccountProperties : public QObject
30{
31 Q_OBJECT
32public:
33 static AccountProperties *instance();
34
35 QMap<QString,QVariantMap> allProperties();
36 QVariantMap accountProperties(const QString &accountId);
37 void setAccountProperties(const QString &accountId, const QVariantMap &properties);
38 QString formatAccountId(const QString &accountId);
39
40protected:
41 explicit AccountProperties(QObject *parent = 0);
42
43private:
44 QSettings *mSettings;
45};
46
47#endif // ACCOUNTPROPERTIES_H
048
=== added file 'handler/audioroutemanager.cpp'
--- handler/audioroutemanager.cpp 1970-01-01 00:00:00 +0000
+++ handler/audioroutemanager.cpp 2017-04-05 14:05:16 +0000
@@ -0,0 +1,231 @@
1/*
2 * Copyright (C) 2016 Canonical, Ltd.
3 *
4 * Authors:
5 * Tiago Salem Herrmann <tiago.herrmann@canonical.com>
6 *
7 * This file is part of telephony-service.
8 *
9 * telephony-service is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 3.
12 *
13 * telephony-service is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include "audioroutemanager.h"
23#include "telepathyhelper.h"
24#include "accountentry.h"
25#include <TelepathyQt/Contact>
26#include <TelepathyQt/Functors>
27
28
29static void enable_earpiece()
30{
31#ifdef USE_PULSEAUDIO
32 QPulseAudioEngine::instance()->setCallMode(CallActive, AudioModeBtOrWiredOrEarpiece);
33#endif
34}
35
36static void enable_normal()
37{
38#ifdef USE_PULSEAUDIO
39 QTimer* timer = new QTimer();
40 timer->setSingleShot(true);
41 QObject::connect(timer, &QTimer::timeout, [=](){
42 QPulseAudioEngine::instance()->setMicMute(false);
43 QPulseAudioEngine::instance()->setCallMode(CallEnded, AudioModeWiredOrSpeaker);
44 timer->deleteLater();
45 });
46 timer->start(2000);
47#endif
48}
49
50static void enable_speaker()
51{
52#ifdef USE_PULSEAUDIO
53 QPulseAudioEngine::instance()->setCallMode(CallActive, AudioModeSpeaker);
54#endif
55}
56
57static void enable_ringtone()
58{
59#ifdef USE_PULSEAUDIO
60 QPulseAudioEngine::instance()->setCallMode(CallRinging, AudioModeBtOrWiredOrSpeaker);
61#endif
62}
63
64AudioRouteManager *AudioRouteManager::instance()
65{
66 static AudioRouteManager *self = new AudioRouteManager();
67 return self;
68}
69
70AudioRouteManager::AudioRouteManager(QObject *parent) :
71 QObject(parent), mAudioModeMediator(mPowerDDBus)
72{
73 TelepathyHelper::instance()->registerChannelObserver("TelephonyServiceHandlerAudioRouteManager");
74
75 QObject::connect(TelepathyHelper::instance()->channelObserver(), SIGNAL(callChannelAvailable(Tp::CallChannelPtr)),
76 this, SLOT(onCallChannelAvailable(Tp::CallChannelPtr)));
77
78#ifdef USE_PULSEAUDIO
79 // update audio modes
80 QObject::connect(QPulseAudioEngine::instance(), SIGNAL(audioModeChanged(AudioMode)), SLOT(onAudioModeChanged(AudioMode)));
81 QObject::connect(QPulseAudioEngine::instance(), SIGNAL(availableAudioModesChanged(AudioModes)), SLOT(onAvailableAudioModesChanged(AudioModes)));
82
83 // check if we should indeed use pulseaudio
84 QByteArray pulseAudioDisabled = qgetenv("PA_DISABLED");
85 mHasPulseAudio = true;
86 if (!pulseAudioDisabled.isEmpty())
87 mHasPulseAudio = false;
88#endif
89
90 connect(this, &AudioRouteManager::activeAudioOutputChanged, Tp::memFun(&mAudioModeMediator, &PowerDAudioModeMediator::audioModeChanged));
91 connect(this, &AudioRouteManager::lastChannelClosed, Tp::memFun(&mAudioModeMediator, &PowerDAudioModeMediator::audioOutputClosed));
92}
93
94void AudioRouteManager::onCallChannelAvailable(Tp::CallChannelPtr callChannel)
95{
96 connect(callChannel.data(),
97 SIGNAL(callStateChanged(Tp::CallState)),
98 SLOT(onCallStateChanged(Tp::CallState)));
99
100 mChannels.append(callChannel);
101 updateAudioRoute(true);
102}
103
104void AudioRouteManager::onCallStateChanged(Tp::CallState state)
105{
106 Tp::CallChannelPtr channel(qobject_cast<Tp::CallChannel*>(sender()));
107 if (!channel) {
108 return;
109 }
110
111 if (channel->callState() == Tp::CallStateEnded) {
112 mChannels.removeOne(channel);
113 }
114 updateAudioRoute(false);
115}
116
117void AudioRouteManager::setActiveAudioOutput(const QString &id)
118{
119#ifdef USE_PULSEAUDIO
120 // fallback to earpiece/headset
121 AudioMode mode = AudioModeWiredOrEarpiece;
122 if (id == "bluetooth") {
123 mode = AudioModeBluetooth;
124 } else if (id == "speaker") {
125 mode = AudioModeSpeaker;
126 }
127 if (mHasPulseAudio)
128 QPulseAudioEngine::instance()->setCallMode(CallActive, mode);
129#endif
130}
131
132QString AudioRouteManager::activeAudioOutput()
133{
134 return mActiveAudioOutput;
135}
136
137AudioOutputDBusList AudioRouteManager::audioOutputs() const
138{
139 return mAudioOutputs;
140}
141
142void AudioRouteManager::updateAudioRoute(bool newCall)
143{
144#ifdef USE_PULSEAUDIO
145 if (!mHasPulseAudio)
146 return;
147#endif
148
149 int currentCalls = mChannels.size();
150 if (currentCalls != 0) {
151 if (currentCalls == 1) {
152 // if we have only one call, check if it's incoming and
153 // enable speaker mode so the ringtone is audible
154 Tp::CallChannelPtr callChannel = mChannels.first();
155 AccountEntry *accountEntry = TelepathyHelper::instance()->accountForConnection(callChannel->connection());
156 if (!accountEntry || !callChannel) {
157 return;
158 }
159
160 bool incoming = callChannel->initiatorContact() != accountEntry->account()->connection()->selfContact();
161 Tp::CallState state = callChannel->callState();
162 if (incoming && newCall) {
163 enable_ringtone();
164 return;
165 }
166 if (state == Tp::CallStateEnded) {
167 enable_normal();
168 return;
169 }
170 // if only one call and dialing, or incoming call just accepted, then default to earpiece
171 if (newCall || (state == Tp::CallStateAccepted && incoming)) {
172 enable_earpiece();
173 return;
174 }
175 }
176 } else {
177 enable_normal();
178 Q_EMIT lastChannelClosed();
179 }
180}
181
182#ifdef USE_PULSEAUDIO
183void AudioRouteManager::onAudioModeChanged(AudioMode mode)
184{
185 qDebug("PulseAudio audio mode changed: 0x%x", mode);
186
187 if (mode == AudioModeEarpiece && mActiveAudioOutput != "earpiece") {
188 mActiveAudioOutput = "earpiece";
189 } else if (mode == AudioModeWiredHeadset && mActiveAudioOutput != "wired_headset") {
190 mActiveAudioOutput = "wired_headset";
191 } else if (mode == AudioModeSpeaker && mActiveAudioOutput != "speaker") {
192 mActiveAudioOutput = "speaker";
193 } else if (mode == AudioModeBluetooth && mActiveAudioOutput != "bluetooth") {
194 mActiveAudioOutput = "bluetooth";
195 }
196 Q_EMIT activeAudioOutputChanged(mActiveAudioOutput);
197}
198
199void AudioRouteManager::onAvailableAudioModesChanged(AudioModes modes)
200{
201 qDebug("PulseAudio available audio modes changed");
202 bool defaultFound = false;
203 mAudioOutputs.clear();
204 Q_FOREACH(const AudioMode &mode, modes) {
205 AudioOutputDBus output;
206 if (mode == AudioModeBluetooth) {
207 // there can be only one bluetooth
208 output.id = "bluetooth";
209 output.type = "bluetooth";
210 // we dont support names for now, so we set a default value
211 output.name = "bluetooth";
212 } else if (mode == AudioModeEarpiece || mode == AudioModeWiredHeadset) {
213 if (!defaultFound) {
214 defaultFound = true;
215 output.id = "default";
216 output.type = "default";
217 output.name = "default";
218 } else {
219 continue;
220 }
221 } else if (mode == AudioModeSpeaker) {
222 output.id = "speaker";
223 output.type = "speaker";
224 output.name = "speaker";
225 }
226 mAudioOutputs << output;
227 }
228 Q_EMIT audioOutputsChanged(mAudioOutputs);
229}
230#endif
231
0232
=== added file 'handler/audioroutemanager.h'
--- handler/audioroutemanager.h 1970-01-01 00:00:00 +0000
+++ handler/audioroutemanager.h 2017-04-05 14:05:16 +0000
@@ -0,0 +1,75 @@
1/*
2 * Copyright (C) 2016 Canonical, Ltd.
3 *
4 * Authors:
5 * Tiago Salem Herrmann <tiago.herrmann@canonical.com>
6 *
7 * This file is part of telephony-service.
8 *
9 * telephony-service is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 3.
12 *
13 * telephony-service is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#ifndef AUDIOROUTEMANAGER_H
23#define AUDIOROUTEMANAGER_H
24
25#ifdef USE_PULSEAUDIO
26#include "qpulseaudioengine.h"
27#endif
28#include "audiooutput.h"
29#include "powerdaudiomodemediator.h"
30#include "powerddbus.h"
31#include <QObject>
32#include <TelepathyQt/CallChannel>
33
34
35class AudioRouteManager : public QObject
36{
37 Q_OBJECT
38
39public:
40 static AudioRouteManager *instance();
41 void setActiveAudioOutput(const QString &id);
42 QString activeAudioOutput();
43 AudioOutputDBusList audioOutputs() const;
44 void updateAudioRoute(bool newCall = false);
45
46public Q_SLOTS:
47 void onCallChannelAvailable(Tp::CallChannelPtr callChannel);
48
49Q_SIGNALS:
50 void audioOutputsChanged(const AudioOutputDBusList &outputs);
51 void activeAudioOutputChanged(const QString &id);
52 void lastChannelClosed();
53
54protected Q_SLOTS:
55 void onCallStateChanged(Tp::CallState state);
56
57private Q_SLOTS:
58#ifdef USE_PULSEAUDIO
59 void onAudioModeChanged(AudioMode mode);
60 void onAvailableAudioModesChanged(AudioModes modes);
61#endif
62
63private:
64 explicit AudioRouteManager(QObject *parent = 0);
65 QList<Tp::CallChannelPtr> mChannels;
66 AudioOutputDBusList mAudioOutputs;
67 QString mActiveAudioOutput;
68 PowerDDBus mPowerDDBus;
69 PowerDAudioModeMediator mAudioModeMediator;
70#ifdef USE_PULSEAUDIO
71 bool mHasPulseAudio;
72#endif
73};
74
75#endif // AUDIOROUTEMANAGER_H
076
=== added file 'handler/callagent.cpp'
--- handler/callagent.cpp 1970-01-01 00:00:00 +0000
+++ handler/callagent.cpp 2017-04-05 14:05:16 +0000
@@ -0,0 +1,118 @@
1/*
2 * Copyright (C) 2017 Canonical, Ltd.
3 *
4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
6 *
7 * This file is part of telephony-service.
8 *
9 * telephony-service is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 3.
12 *
13 * telephony-service is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include "callagent.h"
23#include <TelepathyQt/CallContent>
24#include <TelepathyQt/Contact>
25#include <TelepathyQt/Farstream/Channel>
26#include <QDebug>
27
28CallAgent::CallAgent(const Tp::CallChannelPtr &channel, QObject *parent) :
29 QObject(parent), mChannel(channel), mFarstreamChannel(0)
30{
31 connect(mChannel.data(),
32 SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)),
33 SLOT(onCallChannelInvalidated()));
34 connect(mChannel.data(),
35 SIGNAL(callStateChanged(Tp::CallState)),
36 SLOT(onCallStateChanged(Tp::CallState)));
37 connect(mChannel.data(),
38 SIGNAL(contentAdded(Tp::CallContentPtr)),
39 SLOT(onContentAdded(Tp::CallContentPtr)));
40
41 Q_FOREACH(const Tp::CallContentPtr &content, mChannel->contents()) {
42 onContentAdded(content);
43 }
44
45 if (!mChannel->handlerStreamingRequired()) {
46 return;
47 }
48
49 Tp::Farstream::PendingChannel *pendingChannel = Tp::Farstream::createChannel(mChannel);
50 connect(pendingChannel,
51 SIGNAL(finished(Tp::PendingOperation*)),
52 SLOT(onFarstreamChannelCreated(Tp::PendingOperation*)));
53}
54
55CallAgent::~CallAgent()
56{
57 if (mFarstreamChannel) {
58 mFarstreamChannel->deleteLater();
59 }
60}
61
62void CallAgent::setMute(bool mute)
63{
64 if (!mFarstreamChannel) {
65 return;
66 }
67
68 mFarstreamChannel->setMute(mute);
69}
70
71void CallAgent::onCallChannelInvalidated()
72{
73 deleteLater();
74}
75
76void CallAgent::onCallStateChanged(Tp::CallState state)
77{
78 if (state == Tp::CallStatePendingInitiator) {
79 mChannel->accept();
80 }
81}
82
83void CallAgent::onContentAdded(const Tp::CallContentPtr &content)
84{
85 if (!mChannel->handlerStreamingRequired()) {
86 return;
87 }
88
89 qDebug() << "Content Added, name: " << content->name() << " type: " << content->type();
90
91 connect(content.data(),
92 SIGNAL(streamAdded(Tp::CallStreamPtr)),
93 SLOT(onStreamAdded(Tp::CallStreamPtr)));
94
95 Q_FOREACH(const Tp::CallStreamPtr &stream, content->streams()) {
96 onStreamAdded(stream);
97 }
98}
99
100void CallAgent::onStreamAdded(const Tp::CallStreamPtr &stream)
101{
102 qDebug() << "Stream present: " << stream->localSendingState();
103
104 qDebug() << " members " << stream->remoteMembers().size();
105 Q_FOREACH(const Tp::ContactPtr contact, stream->remoteMembers()) {
106 qDebug() << " member " << contact->id() << " remoteSendingState=" << stream->remoteSendingState(contact);
107 }
108}
109
110void CallAgent::onFarstreamChannelCreated(Tp::PendingOperation *op)
111{
112 Tp::Farstream::PendingChannel *pendingChannel = qobject_cast<Tp::Farstream::PendingChannel*>(op);
113 if (!pendingChannel) {
114 return;
115 }
116
117 mFarstreamChannel = new FarstreamChannel(pendingChannel->tfChannel(), this);
118}
0119
=== added file 'handler/callagent.h'
--- handler/callagent.h 1970-01-01 00:00:00 +0000
+++ handler/callagent.h 2017-04-05 14:05:16 +0000
@@ -0,0 +1,52 @@
1/*
2 * Copyright (C) 2017 Canonical, Ltd.
3 *
4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
6 *
7 * This file is part of telephony-service.
8 *
9 * telephony-service is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 3.
12 *
13 * telephony-service is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#ifndef CALLAGENT_H
23#define CALLAGENT_H
24
25#include <QObject>
26#include <TelepathyQt/CallChannel>
27#include <TelepathyQt/Farstream/Channel>
28#include "farstreamchannel.h"
29
30class CallAgent : public QObject
31{
32 Q_OBJECT
33public:
34 explicit CallAgent(const Tp::CallChannelPtr &channel, QObject *parent = 0);
35 ~CallAgent();
36
37 void setMute(bool mute);
38
39protected Q_SLOTS:
40 void onCallChannelInvalidated();
41 void onCallStateChanged(Tp::CallState state);
42 void onContentAdded(const Tp::CallContentPtr &content);
43 void onStreamAdded(const Tp::CallStreamPtr &stream);
44
45 void onFarstreamChannelCreated(Tp::PendingOperation *op);
46
47private:
48 Tp::CallChannelPtr mChannel;
49 FarstreamChannel *mFarstreamChannel;
50};
51
52#endif // CALLAGENT_H
053
=== modified file 'handler/callhandler.cpp'
--- handler/callhandler.cpp 2015-07-06 23:32:13 +0000
+++ handler/callhandler.cpp 2017-04-05 14:05:16 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2012-2014 Canonical, Ltd.2 * Copyright (C) 2012-2017 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -20,15 +20,19 @@
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 */21 */
2222
23#include "accountproperties.h"
24#include "callagent.h"
23#include "callhandler.h"25#include "callhandler.h"
24#include "phoneutils.h"
25#include "telepathyhelper.h"26#include "telepathyhelper.h"
26#include "accountentry.h"27#include "accountentry.h"
27#include "tonegenerator.h"28#include "tonegenerator.h"
28#include "greetercontacts.h"29#include "greetercontacts.h"
30#include "phoneutils.h"
29#include <TelepathyQt/ContactManager>31#include <TelepathyQt/ContactManager>
30#include <TelepathyQt/PendingContacts>32#include <TelepathyQt/PendingContacts>
31#include <TelepathyQt/PendingChannelRequest>33#include <TelepathyQt/PendingChannelRequest>
34#include <TelepathyQt/PendingVariant>
35#include <memory>
3236
33#define TELEPATHY_MUTE_IFACE "org.freedesktop.Telepathy.Call1.Interface.Mute"37#define TELEPATHY_MUTE_IFACE "org.freedesktop.Telepathy.Call1.Interface.Mute"
34#define DBUS_PROPERTIES_IFACE "org.freedesktop.DBus.Properties"38#define DBUS_PROPERTIES_IFACE "org.freedesktop.DBus.Properties"
@@ -72,8 +76,7 @@
72 bool hasActiveCalls = false;76 bool hasActiveCalls = false;
7377
74 Q_FOREACH(const Tp::CallChannelPtr channel, mCallChannels) {78 Q_FOREACH(const Tp::CallChannelPtr channel, mCallChannels) {
75 AccountEntry *accountEntry = TelepathyHelper::instance()->accountForConnection(channel->connection());79 bool incoming = isIncoming(channel);
76 bool incoming = channel->initiatorContact() != accountEntry->account()->connection()->selfContact();
77 bool dialing = !incoming && (channel->callState() == Tp::CallStateInitialised);80 bool dialing = !incoming && (channel->callState() == Tp::CallStateInitialised);
78 bool active = channel->callState() == Tp::CallStateActive;81 bool active = channel->callState() == Tp::CallStateActive;
7982
@@ -94,6 +97,7 @@
9497
95void CallHandler::startCall(const QString &targetId, const QString &accountId)98void CallHandler::startCall(const QString &targetId, const QString &accountId)
96{99{
100 QString finalId = targetId;
97 // Request the contact to start audio call101 // Request the contact to start audio call
98 AccountEntry *accountEntry = TelepathyHelper::instance()->accountForId(accountId);102 AccountEntry *accountEntry = TelepathyHelper::instance()->accountForId(accountId);
99 if (!accountEntry) {103 if (!accountEntry) {
@@ -105,7 +109,24 @@
105 return;109 return;
106 }110 }
107111
108 connect(connection->contactManager()->contactsForIdentifiers(QStringList() << targetId),112 // FIXME: this is a workaround, there might be a better way of handling this.
113 // One idea is to implement the Addressing interface on the SIP connection manager such that
114 // we can request a handle based on the vCard field "tel"
115 if (accountEntry->protocolInfo()->name() == "sip") {
116 // check if the phone number needs rewriting
117 QVariantMap accountProperties = AccountProperties::instance()->accountProperties(accountId);
118 finalId = applyNumberRewritingRules(finalId, accountProperties);
119
120 // replace the numbers by a SIP URI
121 QString domain = accountEntry->account()->parameters()["account"].toString();
122 if (domain.contains("@")) {
123 domain = domain.split("@")[1];
124
125 finalId = QString("sip:%1@%2").arg(PhoneUtils::normalizePhoneNumber(finalId)).arg(domain);
126 }
127 }
128
129 connect(connection->contactManager()->contactsForIdentifiers(QStringList() << finalId),
109 SIGNAL(finished(Tp::PendingOperation*)),130 SIGNAL(finished(Tp::PendingOperation*)),
110 SLOT(onContactsAvailable(Tp::PendingOperation*)));131 SLOT(onContactsAvailable(Tp::PendingOperation*)));
111}132}
@@ -146,41 +167,30 @@
146 return;167 return;
147 }168 }
148169
149 // FIXME: replace by a proper TpQt implementation of mute170 if (channel->handlerStreamingRequired()) {
150 QDBusInterface muteInterface(channel->busName(), channel->objectPath(), TELEPATHY_MUTE_IFACE);171 CallAgent *agent = mCallAgents[channel.data()];
151 muteInterface.call("RequestMuted", muted);172 if (!agent) {
152}173 return;
153174 }
154void CallHandler::setActiveAudioOutput(const QString &objectPath, const QString &id)175 agent->setMute(muted);
155{176 } else {
156 Tp::CallChannelPtr channel = callFromObjectPath(objectPath);177 // FIXME: replace by a proper TpQt implementation of mute
157178 QDBusInterface muteInterface(channel->busName(), channel->objectPath(), TELEPATHY_MUTE_IFACE);
158 QDBusInterface audioOutputsInterface(channel->busName(), channel->objectPath(), CANONICAL_TELEPHONY_AUDIOOUTPUTS_IFACE);179 muteInterface.call("RequestMuted", muted);
159 audioOutputsInterface.call("SetActiveAudioOutput", id);180 }
160}181}
161182
162void CallHandler::sendDTMF(const QString &objectPath, const QString &key)183void CallHandler::sendDTMF(const QString &objectPath, const QString &key)
163{184{
164 bool ok;
165 Tp::DTMFEvent event = (Tp::DTMFEvent)key.toInt(&ok);
166 if (!ok) {
167 if (!key.compare("*")) {
168 event = Tp::DTMFEventAsterisk;
169 } else if (!key.compare("#")) {
170 event = Tp::DTMFEventHash;
171 } else {
172 qWarning() << "Tone not recognized. DTMF failed";
173 return;
174 }
175 }
176 /*185 /*
177 * play locally (via tone generator) only if we are on a call, or if this is 186 * play locally (via tone generator) only if we are on a call, or if this is
178 * dialpad sounds187 * dialpad sounds
179 */188 */
180 if (GreeterContacts::instance()->dialpadSoundsEnabled() && 189 int event = toDTMFEvent(key);
190 if (GreeterContacts::instance()->dialpadSoundsEnabled() &&
181 !GreeterContacts::instance()->silentMode() && objectPath.isEmpty()191 !GreeterContacts::instance()->silentMode() && objectPath.isEmpty()
182 || !objectPath.isEmpty()) {192 || !objectPath.isEmpty()) {
183 ToneGenerator::instance()->playDTMFTone((uint)event);193 ToneGenerator::instance()->playDTMFTone(event);
184 }194 }
185195
186 Tp::CallChannelPtr channel = callFromObjectPath(objectPath);196 Tp::CallChannelPtr channel = callFromObjectPath(objectPath);
@@ -190,14 +200,15 @@
190200
191 // save the dtmfString to send to clients that request it201 // save the dtmfString to send to clients that request it
192 QString dtmfString = channel->property("dtmfString").toString();202 QString dtmfString = channel->property("dtmfString").toString();
203 QString pendingDTMF = channel->property("pendingDTMF").toString();
204 pendingDTMF += key;
193 dtmfString += key;205 dtmfString += key;
194 channel->setProperty("dtmfString", dtmfString);206 channel->setProperty("dtmfString", dtmfString);
207 channel->setProperty("pendingDTMF", pendingDTMF);
195208
196 Q_FOREACH(const Tp::CallContentPtr &content, channel->contents()) {209 // if there is only one pending DTMF event, start playing it
197 if (content->supportsDTMF()) {210 if (pendingDTMF.length() == 1) {
198 /* send DTMF to network (via telepathy and oFono) */211 playNextDTMFTone(channel);
199 content->startDTMFTone(event);
200 }
201 }212 }
202213
203 Q_EMIT callPropertiesChanged(channel->objectPath(), getCallProperties(channel->objectPath()));214 Q_EMIT callPropertiesChanged(channel->objectPath(), getCallProperties(channel->objectPath()));
@@ -266,13 +277,12 @@
266void CallHandler::onCallChannelAvailable(Tp::CallChannelPtr channel)277void CallHandler::onCallChannelAvailable(Tp::CallChannelPtr channel)
267{278{
268 QDBusInterface callChannelIface(channel->busName(), channel->objectPath(), DBUS_PROPERTIES_IFACE);279 QDBusInterface callChannelIface(channel->busName(), channel->objectPath(), DBUS_PROPERTIES_IFACE);
269 QDBusMessage reply = callChannelIface.call("GetAll", CANONICAL_TELEPHONY_AUDIOOUTPUTS_IFACE);
270 QVariantList args = reply.arguments();
271 QMap<QString, QVariant> map = qdbus_cast<QMap<QString, QVariant> >(args[0]);
272 channel->setProperty("timestamp", QDateTime::currentDateTimeUtc());280 channel->setProperty("timestamp", QDateTime::currentDateTimeUtc());
273281
274 if (channel->callState() == Tp::CallStateActive) {282 if (channel->callState() == Tp::CallStateActive) {
275 channel->setProperty("activeTimestamp", QDateTime::currentDateTimeUtc());283 channel->setProperty("activeTimestamp", QDateTime::currentDateTimeUtc());
284 } else if (channel->callState() == Tp::CallStatePendingInitiator) {
285 channel->accept();
276 }286 }
277287
278 connect(channel.data(),288 connect(channel.data(),
@@ -282,6 +292,10 @@
282 SIGNAL(callStateChanged(Tp::CallState)),292 SIGNAL(callStateChanged(Tp::CallState)),
283 SLOT(onCallStateChanged(Tp::CallState)));293 SLOT(onCallStateChanged(Tp::CallState)));
284294
295 // FIXME: save this to a list
296 CallAgent *agent = new CallAgent(channel, this);
297 mCallAgents[channel.data()] = agent;
298
285 mCallChannels.append(channel);299 mCallChannels.append(channel);
286 Q_EMIT callPropertiesChanged(channel->objectPath(), getCallProperties(channel->objectPath()));300 Q_EMIT callPropertiesChanged(channel->objectPath(), getCallProperties(channel->objectPath()));
287}301}
@@ -331,7 +345,12 @@
331 }345 }
332346
333 mCallChannels.removeAll(channel);347 mCallChannels.removeAll(channel);
348 if (mCallAgents.contains(channel.data())) {
349 CallAgent *agent = mCallAgents.take(channel.data());
350 agent->deleteLater();
351 }
334352
353 ToneGenerator::instance()->stopTone();
335 if (mCallChannels.isEmpty() && !mHangupRequested) {354 if (mCallChannels.isEmpty() && !mHangupRequested) {
336 ToneGenerator::instance()->playCallEndedTone();355 ToneGenerator::instance()->playCallEndedTone();
337 }356 }
@@ -346,10 +365,29 @@
346 }365 }
347366
348 switch (state) {367 switch (state) {
368 case Tp::CallStatePendingInitiator:
369 case Tp::CallStateInitialising:
370 if (!isIncoming(channel) && channel->handlerStreamingRequired()) {
371 ToneGenerator::instance()->playDialingTone();
372 }
373 break;
374 case Tp::CallStateInitialised:
375 if (!isIncoming(channel) && channel->handlerStreamingRequired()) {
376 ToneGenerator::instance()->stopTone();
377 ToneGenerator::instance()->playRingingTone();
378 }
379 break;
349 case Tp::CallStateActive:380 case Tp::CallStateActive:
381 if (channel->handlerStreamingRequired()) {
382 ToneGenerator::instance()->stopTone();
383 }
350 channel->setProperty("activeTimestamp", QDateTime::currentDateTimeUtc());384 channel->setProperty("activeTimestamp", QDateTime::currentDateTimeUtc());
351 Q_EMIT callPropertiesChanged(channel->objectPath(), getCallProperties(channel->objectPath()));385 Q_EMIT callPropertiesChanged(channel->objectPath(), getCallProperties(channel->objectPath()));
352 break;386 break;
387 case Tp::CallStateEnded:
388 ToneGenerator::instance()->stopTone();
389 channel->requestClose();
390 break;
353 }391 }
354}392}
355393
@@ -387,3 +425,128 @@
387425
388 return channel;426 return channel;
389}427}
428
429void CallHandler::playNextDTMFTone(Tp::CallChannelPtr channel)
430{
431 // the channel might have been closed already
432 if (!channel) {
433 return;
434 }
435
436 QString pendingDTMF = channel->property("pendingDTMF").toString();
437 QString key = "";
438 if (!pendingDTMF.isEmpty()) {
439 key = pendingDTMF[0];
440 }
441
442 int event = toDTMFEvent(key);
443
444 Q_FOREACH(const Tp::CallContentPtr &content, channel->contents()) {
445 if (content->supportsDTMF()) {
446
447 /* stop any previous DTMF tone before sending the new one*/
448 connect(content->stopDTMFTone(), &Tp::PendingOperation::finished, [=](Tp::PendingOperation *op){
449 // in case stopDTMFTone, it might mean the service automatically stops the tone,
450 // so try playing the next one
451 if (op->isError()) {
452 /* send DTMF to network (via telepathy) */
453 if (event >= 0) {
454 content->startDTMFTone((Tp::DTMFEvent)event);
455 }
456 triggerNextDTMFTone(channel);
457 return;
458 }
459
460 Tp::Client::CallContentInterfaceDTMFInterface *dtmfInterface = content->interface<Tp::Client::CallContentInterfaceDTMFInterface>();
461 Tp::PendingVariant *pv = dtmfInterface->requestPropertyCurrentlySendingTones();
462 connect(pv, &Tp::PendingOperation::finished, [=](){
463 bool sendingTones = pv->result().toBool();
464 // if we already stopped sending tones, we can send the next one
465 if (!sendingTones) {
466 /* send DTMF to network (via telepathy) */
467 if (event >= 0) {
468 content->startDTMFTone((Tp::DTMFEvent)event);
469 }
470 triggerNextDTMFTone(channel);
471 return;
472 }
473
474 // in case the previous tone is not finished, we need to wait for it
475 auto conn = std::make_shared<QMetaObject::Connection>();
476 *conn = connect(dtmfInterface, &Tp::Client::CallContentInterfaceDTMFInterface::StoppedTones, [=](){
477 QObject::disconnect(*conn);
478
479 /* send DTMF to network (via telepathy) */
480 if (event >= 0) {
481 content->startDTMFTone((Tp::DTMFEvent)event);
482 }
483 triggerNextDTMFTone(channel);
484 });
485 });
486
487 });
488 }
489 }
490}
491
492void CallHandler::triggerNextDTMFTone(Tp::CallChannelPtr channel)
493{
494 QTimer::singleShot(250, [=](){
495 QString pendingDTMF = channel->property("pendingDTMF").toString();
496 if (pendingDTMF.isEmpty()) {
497 return;
498 }
499 pendingDTMF.remove(0, 1);
500 channel->setProperty("pendingDTMF", pendingDTMF);
501 playNextDTMFTone(channel);
502 });
503}
504
505int CallHandler::toDTMFEvent(const QString &key)
506{
507 bool ok;
508 int ev = key.toInt(&ok);
509 if (!ok) {
510 if (key == "*") {
511 ev = Tp::DTMFEventAsterisk;
512 } else if (key == "#") {
513 ev = Tp::DTMFEventHash;
514 } else {
515 ev = -1;
516 }
517 }
518 return ev;
519}
520
521bool CallHandler::isIncoming(const Tp::CallChannelPtr &channel) const
522{
523 AccountEntry *accountEntry = TelepathyHelper::instance()->accountForConnection(channel->connection());
524 return channel->initiatorContact() != accountEntry->account()->connection()->selfContact();
525}
526
527QString CallHandler::applyNumberRewritingRules(const QString &originalNumber, const QVariantMap &properties)
528{
529 QString finalNumber = originalNumber;
530 if (properties.contains("numberRewrite") && properties["numberRewrite"].toBool()) {
531 // FIXME: do a proper phone number identification implementation
532 // for now consider anything bigger than 6 digits to be a phone number
533 if (finalNumber.length() <= 6) {
534 return finalNumber;
535 }
536
537 QString defaultCountryCode = properties["defaultCountryCode"].toString();
538 QString defaultAreaCode = properties["defaultAreaCode"].toString();
539 QString removeCharacters = properties["removeCharacters"].toString();
540 QString prefix = properties["prefix"].toString();
541
542 if (!defaultCountryCode.startsWith("+")) {
543 defaultCountryCode.prepend("+");
544 }
545
546 finalNumber = PhoneUtils::getFullNumber(finalNumber, defaultCountryCode, defaultAreaCode);
547 finalNumber.remove(removeCharacters);
548 finalNumber.prepend(prefix);
549 }
550
551 return finalNumber;
552}
390553
=== modified file 'handler/callhandler.h'
--- handler/callhandler.h 2015-03-04 18:02:13 +0000
+++ handler/callhandler.h 2017-04-05 14:05:16 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2012-2013 Canonical, Ltd.2 * Copyright (C) 2012-2017 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -28,6 +28,7 @@
28#include <TelepathyQt/CallChannel>28#include <TelepathyQt/CallChannel>
2929
30class TelepathyHelper;30class TelepathyHelper;
31class CallAgent;
3132
32class CallHandler : public QObject33class CallHandler : public QObject
33{34{
@@ -44,7 +45,6 @@
44 void hangUpCall(const QString &objectPath);45 void hangUpCall(const QString &objectPath);
45 void setHold(const QString &objectPath, bool hold);46 void setHold(const QString &objectPath, bool hold);
46 void setMuted(const QString &objectPath, bool muted);47 void setMuted(const QString &objectPath, bool muted);
47 void setActiveAudioOutput(const QString &objectPath, const QString &id);
48 void sendDTMF(const QString &objectPath, const QString &key);48 void sendDTMF(const QString &objectPath, const QString &key);
4949
50 // conference call related50 // conference call related
@@ -61,6 +61,13 @@
61 Tp::CallChannelPtr existingCall(const QString &targetId);61 Tp::CallChannelPtr existingCall(const QString &targetId);
62 Tp::CallChannelPtr callFromObjectPath(const QString &objectPath);62 Tp::CallChannelPtr callFromObjectPath(const QString &objectPath);
6363
64 void playNextDTMFTone(Tp::CallChannelPtr channel);
65 void triggerNextDTMFTone(Tp::CallChannelPtr channel);
66 static int toDTMFEvent(const QString &key);
67 bool isIncoming(const Tp::CallChannelPtr &channel) const;
68
69 QString applyNumberRewritingRules(const QString &originalNumber, const QVariantMap &properties);
70
64protected Q_SLOTS:71protected Q_SLOTS:
65 void onContactsAvailable(Tp::PendingOperation *op);72 void onContactsAvailable(Tp::PendingOperation *op);
66 void onCallHangupFinished(Tp::PendingOperation *op);73 void onCallHangupFinished(Tp::PendingOperation *op);
@@ -72,6 +79,7 @@
7279
73 QMap<QString, Tp::ContactPtr> mContacts;80 QMap<QString, Tp::ContactPtr> mContacts;
74 QList<Tp::CallChannelPtr> mCallChannels;81 QList<Tp::CallChannelPtr> mCallChannels;
82 QMap<Tp::CallChannel*,CallAgent*> mCallAgents;
75 QMap<Tp::PendingOperation*,Tp::CallChannelPtr> mClosingChannels;83 QMap<Tp::PendingOperation*,Tp::CallChannelPtr> mClosingChannels;
76 bool mHangupRequested;84 bool mHangupRequested;
77};85};
7886
=== added file 'handler/farstreamchannel.cpp'
--- handler/farstreamchannel.cpp 1970-01-01 00:00:00 +0000
+++ handler/farstreamchannel.cpp 2017-04-05 14:05:16 +0000
@@ -0,0 +1,326 @@
1/*
2 * Copyright (C) 2017 Canonical, Ltd.
3 *
4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
6 *
7 * This file is part of telephony-service.
8 *
9 * telephony-service is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 3.
12 *
13 * telephony-service is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include "farstreamchannel.h"
23#include <farstream/fs-utils.h>
24#include <QDebug>
25
26FarstreamChannel::FarstreamChannel(TfChannel *channel, QObject *parent) :
27 mChannel(channel), QObject(parent), mPipeline(0), mBus(0), mBusSource(0),
28 mConferenceAddedSignal(0), mConferenceRemovedSignal(0), mContentAddedSignal(0),
29 mContentRemovedSignal(0), mAudioInput(0), mAudioOutput(0)
30{
31 qDebug() << __PRETTY_FUNCTION__;
32 initialize();
33}
34
35FarstreamChannel::~FarstreamChannel()
36{
37 // stop audio input and output
38 if (mAudioInput) {
39 setState(mAudioInput, GST_STATE_NULL);
40 gst_object_unref(mAudioInput);
41 }
42
43 if (mAudioOutput) {
44 setState(mAudioOutput, GST_STATE_NULL);
45 gst_object_unref(mAudioOutput);
46 }
47
48 // now clear the notifiers
49 Q_FOREACH(FsElementAddedNotifier *notifier, mNotifiers) {
50 fs_element_added_notifier_remove(notifier, GST_BIN(mPipeline));
51 g_object_unref(notifier);
52 }
53 mNotifiers.clear();
54
55 // clear the bus stuff
56 if (mBusSource) {
57 g_source_remove(mBusSource);
58 }
59
60 if (mBus) {
61 gst_object_unref(mBus);
62 }
63
64 // and finally the pipeline
65 if (mPipeline) {
66 setState(mPipeline, GST_STATE_NULL);
67 gst_object_unref(mPipeline);
68 }
69}
70
71void FarstreamChannel::setMute(bool mute)
72{
73 GstElement *input_volume = gst_bin_get_by_name(GST_BIN(mPipeline), "input_volume");
74 g_object_set(input_volume, "mute", mute, NULL);
75 g_object_unref(input_volume);
76}
77
78void FarstreamChannel::initialize()
79{
80 qDebug() << __PRETTY_FUNCTION__;
81 // connect all the signals
82 mConferenceAddedSignal = g_signal_connect(mChannel, "fs-conference-added",
83 G_CALLBACK(&FarstreamChannel::onConferenceAdded),
84 this);
85 mConferenceRemovedSignal = g_signal_connect(mChannel, "fs-conference-removed",
86 G_CALLBACK(&FarstreamChannel::onConferenceRemoved),
87 this);
88 mContentAddedSignal = g_signal_connect(mChannel, "content-added",
89 G_CALLBACK(&FarstreamChannel::onContentAdded),
90 this);
91 mContentRemovedSignal = g_signal_connect(mChannel, "content-removed",
92 G_CALLBACK(&FarstreamChannel::onContentRemoved),
93 this);
94
95 // and initialize the gstreamer pipeline
96 mPipeline = gst_pipeline_new(NULL);
97 if (!mPipeline) {
98 qCritical() << "Failed to create GStreamer pipeline.";
99 return;
100 }
101
102 mBus = gst_pipeline_get_bus(GST_PIPELINE(mPipeline));
103 if (!mBus) {
104 qCritical() << "Failed to get GStreamer pipeline bus.";
105 return;
106 }
107
108 mBusSource = gst_bus_add_watch(mBus, (GstBusFunc) &FarstreamChannel::onBusWatch, this);
109
110 if (!setState(mPipeline, GST_STATE_PLAYING)) {
111 return;
112 }
113}
114
115GstElement *FarstreamChannel::initializeAudioSource(TfContent *content)
116{
117 qDebug() << __PRETTY_FUNCTION__;
118 GstElement *element = gst_parse_bin_from_description ("alsasrc ! audio/x-raw, rate=8000 ! queue"
119 " ! audioconvert ! audioresample"
120 " ! volume name=input_volume ! audioconvert ",
121 TRUE, NULL);
122 gint input_volume = 0;
123 g_object_get (content, "requested-input-volume", &input_volume, NULL);
124
125 if (input_volume >= 0) {
126 GstElement *volume = gst_bin_get_by_name (GST_BIN (element), "input_volume");
127 g_object_set (volume, "volume", (double)input_volume / 255.0, NULL);
128 gst_object_unref (volume);
129 }
130
131 // FIXME: we probably need to handle input volume request changes
132 mAudioOutput = element;
133 return element;
134}
135
136bool FarstreamChannel::addToPipeline(GstElement *element)
137{
138 qDebug() << __PRETTY_FUNCTION__ << GST_ELEMENT_NAME(element);
139 if (!mPipeline) {
140 qWarning() << "No gstreamer pipeline found.";
141 return false;
142 }
143
144 if (!gst_bin_add(GST_BIN(mPipeline), element)) {
145 qCritical() << "Failed to add bin" << GST_ELEMENT_NAME(element) << "to pipeline.";
146 return false;
147 }
148 qDebug() << "Succeeded adding to pipeline!";
149 return true;
150}
151
152void FarstreamChannel::removeFromPipeline(GstElement *element)
153{
154 qDebug() << __PRETTY_FUNCTION__ << GST_ELEMENT_NAME(element);
155 gst_element_set_locked_state(element, TRUE);
156 setState(element, GST_STATE_NULL);
157 gst_bin_remove (GST_BIN (mPipeline), element);
158}
159
160bool FarstreamChannel::setState(GstElement *element, GstState state)
161{
162 qDebug() << __PRETTY_FUNCTION__ << GST_ELEMENT_NAME(element) << gst_element_state_get_name(state);
163 GstStateChangeReturn result = gst_element_set_state(element, state);
164 if (result == GST_STATE_CHANGE_FAILURE) {
165 qCritical() << "Failed to set GStreamer element" << GST_ELEMENT_NAME(element) << "state to" << gst_element_state_get_name(state);
166 return false;
167 }
168 qDebug() << "Succeeded playing!";
169 return true;
170}
171
172gboolean FarstreamChannel::onBusWatch(GstBus *bus, GstMessage *message, FarstreamChannel *self)
173{
174 Q_UNUSED(bus)
175 if (!self->mChannel) {
176 return TRUE;
177 }
178
179 // FIXME: maybe we need to do some error handling here?
180 tf_channel_bus_message(self->mChannel, message);
181 return TRUE;
182}
183
184void FarstreamChannel::onConferenceAdded(TfChannel *channel, FsConference *conference, FarstreamChannel *self)
185{
186 qDebug() << __PRETTY_FUNCTION__;
187 Q_UNUSED(channel)
188
189 /* Add notifier to set the various element properties as needed */
190 GKeyFile *keyfile = fs_utils_get_default_element_properties (GST_ELEMENT(conference));
191 if (keyfile != NULL) {
192 qDebug() << "Loaded default properties for" << GST_ELEMENT_NAME(conference);
193 FsElementAddedNotifier *notifier = fs_element_added_notifier_new();
194 fs_element_added_notifier_set_properties_from_keyfile(notifier, keyfile);
195 fs_element_added_notifier_add(notifier, GST_BIN(self->mPipeline));
196
197 // FIXME: right now we are leaking the notifiers, check when to remove them
198 self->mNotifiers.append(notifier);
199 }
200
201 if (!self->addToPipeline(GST_ELEMENT(conference))) {
202 return;
203 }
204
205 self->setState(GST_ELEMENT(conference), GST_STATE_PLAYING);
206}
207
208void FarstreamChannel::onConferenceRemoved(TfChannel *channel, FsConference *conference, FarstreamChannel *self)
209{
210 qDebug() << __PRETTY_FUNCTION__;
211 Q_UNUSED(channel);
212
213 // just remove the conference from the pipeline
214 self->removeFromPipeline(GST_ELEMENT(conference));
215}
216
217void FarstreamChannel::onContentAdded(TfChannel *channel, TfContent *content, FarstreamChannel *self)
218{
219 qDebug() << __PRETTY_FUNCTION__;
220 Q_UNUSED(channel)
221
222 g_signal_connect(content, "src-pad-added",
223 G_CALLBACK(&FarstreamChannel::onSrcPadAdded), self);
224 g_signal_connect(content, "start-sending",
225 G_CALLBACK(&FarstreamChannel::onStartSending), self);
226 g_signal_connect(content, "stop-sending",
227 G_CALLBACK(&FarstreamChannel::onStopSending), self);
228}
229
230void FarstreamChannel::onContentRemoved(TfChannel *channel, TfContent *content, FarstreamChannel *self)
231{
232 qDebug() << __PRETTY_FUNCTION__;
233 // FIXME: implement
234}
235
236bool FarstreamChannel::onStartSending(TfContent *content, FarstreamChannel *self)
237{
238 qDebug() << __PRETTY_FUNCTION__;
239 GstPad *sinkPad;
240 FsMediaType mediaType;
241 GstElement *element;
242
243 g_object_get (content, "sink-pad", &sinkPad, "media-type", &mediaType, NULL);
244
245 switch (mediaType) {
246 case FS_MEDIA_TYPE_AUDIO:
247 element = self->initializeAudioSource(content);
248 break;
249 // FIXME: add video support
250 default:
251 qWarning() << "Unsupported media type:" << mediaType;
252 g_object_unref(sinkPad);
253 return false;
254 }
255
256 if (!self->addToPipeline(element)) {
257 g_object_unref(sinkPad);
258 return false;
259 }
260
261 GstPad *sourcePad = gst_element_get_static_pad (element, "src");
262 if (GST_PAD_LINK_FAILED (gst_pad_link (sourcePad, sinkPad))) {
263 qCritical() << "Failed to link source pad to content's sink pad";
264 g_object_unref(sinkPad);
265 g_object_unref(sourcePad);
266 return false;
267 }
268
269 self->setState(element, GST_STATE_PLAYING);
270 self->mAudioInput = element;
271
272 g_object_unref(sinkPad);
273 g_object_unref(sourcePad);
274
275 return true;
276}
277
278void FarstreamChannel::onStopSending(TfContent *content, FarstreamChannel *self)
279{
280 qDebug() << __PRETTY_FUNCTION__;
281 // FIXME: implement
282}
283
284void FarstreamChannel::onSrcPadAdded(TfContent *content, uint handle, FsStream *stream, GstPad *pad, FsCodec *codec, FarstreamChannel *self)
285{
286 qDebug() << __PRETTY_FUNCTION__;
287 gchar *codecString = fs_codec_to_string (codec);
288 qDebug() << __PRETTY_FUNCTION__ << "Codec:" << codecString;
289
290 FsMediaType mediaType;
291 GstElement *element;
292
293 g_object_get (content, "media-type", &mediaType, NULL);
294
295 switch (mediaType) {
296 case FS_MEDIA_TYPE_AUDIO: {
297 QString outputVolume = QString("output_volume%1").arg(codecString);
298 QString description = QString("audioconvert ! audioresample "
299 "! volume name=\"%1\" "
300 "! audioconvert ! autoaudiosink").arg(outputVolume);
301 element = gst_parse_bin_from_description (description.toUtf8().data(), TRUE, NULL);
302 GstElement *volume = gst_bin_get_by_name (GST_BIN (element), outputVolume.toUtf8().data());
303
304 // FIXME: we need to handle volume request changes in the volume element
305 gst_object_unref (volume);
306 break;
307 }
308 // FIXME: handle video
309 default:
310 qWarning() << "Unsupported media type:" << mediaType;
311 return;
312 }
313
314 if (!self->addToPipeline(element)) {
315 return;
316 }
317
318 GstPad *sinkPad = gst_element_get_static_pad (element, "sink");
319 if (GST_PAD_LINK_FAILED (gst_pad_link (pad, sinkPad))) {
320 qCritical() << "Failed to link content's source pad to local sink pad";
321 }
322
323 self->setState(element, GST_STATE_PLAYING);
324
325 g_object_unref (sinkPad);
326}
0327
=== added file 'handler/farstreamchannel.h'
--- handler/farstreamchannel.h 1970-01-01 00:00:00 +0000
+++ handler/farstreamchannel.h 2017-04-05 14:05:16 +0000
@@ -0,0 +1,80 @@
1/*
2 * Copyright (C) 2017 Canonical, Ltd.
3 *
4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
6 *
7 * This file is part of telephony-service.
8 *
9 * telephony-service is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 3.
12 *
13 * telephony-service is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#ifndef FARSTREAMCHANNEL_H
23#define FARSTREAMCHANNEL_H
24
25#include <QObject>
26#include <QList>
27#include <telepathy-farstream/telepathy-farstream.h>
28#include <farstream/fs-conference.h>
29#include <farstream/fs-stream.h>
30#include <farstream/fs-element-added-notifier.h>
31
32class FarstreamChannel : public QObject
33{
34 Q_OBJECT
35public:
36 explicit FarstreamChannel(TfChannel *channel, QObject *parent = 0);
37 ~FarstreamChannel();
38
39 void setMute(bool mute);
40
41protected:
42 void initialize();
43 GstElement *initializeAudioSource(TfContent *content);
44
45 // gstreamer helpers
46 bool addToPipeline(GstElement *element);
47 void removeFromPipeline(GstElement *bin);
48 bool setState(GstElement *element, GstState state);
49
50 // glib signal handlers
51 static gboolean onBusWatch(GstBus *bus, GstMessage *message, FarstreamChannel *self);
52 static void onConferenceAdded(TfChannel *channel, FsConference *conference, FarstreamChannel *self);
53 static void onConferenceRemoved(TfChannel *channel, FsConference *conference, FarstreamChannel *self);
54 static void onContentAdded(TfChannel *channel, TfContent * content, FarstreamChannel *self);
55 static void onContentRemoved(TfChannel *channel, TfContent * content, FarstreamChannel *self);
56 static bool onStartSending(TfContent *content, FarstreamChannel *self);
57 static void onStopSending(TfContent *content, FarstreamChannel *self);
58 static void onSrcPadAdded(TfContent *content, uint handle, FsStream *stream, GstPad *pad, FsCodec *codec, FarstreamChannel *self);
59
60private:
61 TfChannel *mChannel;
62
63 // gstreamer stuff
64 GstElement *mPipeline;
65 GstBus *mBus;
66 uint mBusSource;
67 GstElement *mAudioInput;
68 GstElement *mAudioOutput;
69
70 // signal IDs
71 gulong mConferenceAddedSignal;
72 gulong mConferenceRemovedSignal;
73 gulong mContentAddedSignal;
74 gulong mContentRemovedSignal;
75
76 // farstream stuff
77 QList<FsElementAddedNotifier*> mNotifiers;
78};
79
80#endif // FARSTREAMCHANNEL_H
081
=== modified file 'handler/handler.cpp'
--- handler/handler.cpp 2016-11-23 19:28:18 +0000
+++ handler/handler.cpp 2017-04-05 14:05:16 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2012-2016 Canonical, Ltd.2 * Copyright (C) 2012-2017 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Tiago Salem Herrmann <tiago.herrmann@canonical.com>5 * Tiago Salem Herrmann <tiago.herrmann@canonical.com>
@@ -32,7 +32,7 @@
32#include <TelepathyQt/PendingReady>32#include <TelepathyQt/PendingReady>
3333
34Handler::Handler(QObject *parent)34Handler::Handler(QObject *parent)
35 : QObject(parent), Tp::AbstractClientHandler(channelFilters())35 : QObject(parent), Tp::AbstractClientHandler(channelFilters(), capabilities())
36{36{
37}37}
3838
@@ -102,9 +102,22 @@
102 specList << Tp::ChannelClassSpec::textChatroom();102 specList << Tp::ChannelClassSpec::textChatroom();
103 specList << Tp::ChannelClassSpec::unnamedTextChat();103 specList << Tp::ChannelClassSpec::unnamedTextChat();
104104
105 QVariantMap props;
106 props[TP_QT_IFACE_CHANNEL_TYPE_CALL + ".InitialAudio"] = true;
107 specList << Tp::ChannelClassSpec::audioCall(props);
108
105 return specList;109 return specList;
106}110}
107111
112Tp::AbstractClientHandler::Capabilities Handler::capabilities()
113{
114 QStringList caps;
115 caps << TP_QT_IFACE_CHANNEL_TYPE_CALL + "/shm"
116 << TP_QT_IFACE_CHANNEL_TYPE_CALL + "/ice"
117 << TP_QT_IFACE_CHANNEL_TYPE_CALL + "/gtalk-p2p";
118 return Tp::AbstractClientHandler::Capabilities(caps);
119}
120
108void Handler::onTextChannelReady(Tp::PendingOperation *op)121void Handler::onTextChannelReady(Tp::PendingOperation *op)
109{122{
110 Tp::PendingReady *pr = qobject_cast<Tp::PendingReady*>(op);123 Tp::PendingReady *pr = qobject_cast<Tp::PendingReady*>(op);
@@ -155,10 +168,12 @@
155 // if the call is neither Accepted nor Active, it means it got dispatched directly to the handler without passing168 // if the call is neither Accepted nor Active, it means it got dispatched directly to the handler without passing
156 // through any approver. For phone calls, this would mean calls getting auto-accepted which is not desirable169 // through any approver. For phone calls, this would mean calls getting auto-accepted which is not desirable
157 // so we return an error here170 // so we return an error here
158 bool incoming = false;171 bool incoming = !callChannel->isRequested();
159 AccountEntry *accountEntry = TelepathyHelper::instance()->accountForConnection(callChannel->connection());172 AccountEntry *accountEntry = TelepathyHelper::instance()->accountForConnection(callChannel->connection());
160 if (accountEntry) {173 if (accountEntry &&
161 incoming = callChannel->initiatorContact() != accountEntry->account()->connection()->selfContact();174 !callChannel->initiatorContact().isNull() &&
175 callChannel->initiatorContact() != accountEntry->account()->connection()->selfContact()) {
176 incoming = true;
162 }177 }
163 if (incoming && callChannel->callState() != Tp::CallStateAccepted && callChannel->callState() != Tp::CallStateActive) {178 if (incoming && callChannel->callState() != Tp::CallStateAccepted && callChannel->callState() != Tp::CallStateActive) {
164 qWarning() << "Available channel was not approved by telephony-service-approver, ignoring it.";179 qWarning() << "Available channel was not approved by telephony-service-approver, ignoring it.";
165180
=== modified file 'handler/handler.h'
--- handler/handler.h 2015-07-01 22:04:30 +0000
+++ handler/handler.h 2017-04-05 14:05:16 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2012-2013 Canonical, Ltd.2 * Copyright (C) 2012-2017 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Tiago Salem Herrmann <tiago.herrmann@canonical.com>5 * Tiago Salem Herrmann <tiago.herrmann@canonical.com>
@@ -45,6 +45,7 @@
45 const QDateTime &userActionTime,45 const QDateTime &userActionTime,
46 const Tp::AbstractClientHandler::HandlerInfo &handlerInfo);46 const Tp::AbstractClientHandler::HandlerInfo &handlerInfo);
47 Tp::ChannelClassSpecList channelFilters();47 Tp::ChannelClassSpecList channelFilters();
48 Tp::AbstractClientHandler::Capabilities capabilities();
4849
49Q_SIGNALS:50Q_SIGNALS:
50 void textChannelAvailable(Tp::TextChannelPtr textChannel);51 void textChannelAvailable(Tp::TextChannelPtr textChannel);
5152
=== modified file 'handler/handlerdbus.cpp'
--- handler/handlerdbus.cpp 2016-12-06 16:45:01 +0000
+++ handler/handlerdbus.cpp 2017-04-05 14:05:16 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2012-2016 Canonical, Ltd.2 * Copyright (C) 2012-2017 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Ugo Riboni <ugo.riboni@canonical.com>5 * Ugo Riboni <ugo.riboni@canonical.com>
@@ -21,6 +21,9 @@
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 */22 */
2323
24#include "accountproperties.h"
25#include "audiooutput.h"
26#include "audioroutemanager.h"
24#include "callhandler.h"27#include "callhandler.h"
25#include "handlerdbus.h"28#include "handlerdbus.h"
26#include "handleradaptor.h"29#include "handleradaptor.h"
@@ -53,6 +56,12 @@
53 &ProtocolManager::protocolsChanged, [this]() {56 &ProtocolManager::protocolsChanged, [this]() {
54 Q_EMIT ProtocolsChanged(ProtocolManager::instance()->protocols().dbusType());57 Q_EMIT ProtocolsChanged(ProtocolManager::instance()->protocols().dbusType());
55 });58 });
59 connect(AudioRouteManager::instance(),
60 SIGNAL(audioOutputsChanged(AudioOutputDBusList)),
61 SIGNAL(AudioOutputsChanged(AudioOutputDBusList)));
62 connect(AudioRouteManager::instance(),
63 SIGNAL(activeAudioOutputChanged(QString)),
64 SIGNAL(ActiveAudioOutputChanged(QString)));
56}65}
5766
58HandlerDBus::~HandlerDBus()67HandlerDBus::~HandlerDBus()
@@ -95,6 +104,21 @@
95 return ProtocolManager::instance()->protocols().dbusType();104 return ProtocolManager::instance()->protocols().dbusType();
96}105}
97106
107AllAccountsProperties HandlerDBus::GetAllAccountsProperties()
108{
109 return AccountProperties::instance()->allProperties();
110}
111
112QVariantMap HandlerDBus::GetAccountProperties(const QString &accountId)
113{
114 return AccountProperties::instance()->accountProperties(accountId);
115}
116
117void HandlerDBus::SetAccountProperties(const QString &accountId, const QVariantMap &properties)
118{
119 AccountProperties::instance()->setAccountProperties(accountId, properties);
120}
121
98QString HandlerDBus::registerObject(QObject *object, const QString &path)122QString HandlerDBus::registerObject(QObject *object, const QString &path)
99{123{
100 QString fullPath = QString("%1/%2").arg(DBUS_OBJECT_PATH, path);124 QString fullPath = QString("%1/%2").arg(DBUS_OBJECT_PATH, path);
@@ -125,6 +149,11 @@
125 TextHandler::instance()->removeParticipants(objectPath, participants, message);149 TextHandler::instance()->removeParticipants(objectPath, participants, message);
126}150}
127151
152void HandlerDBus::LeaveRooms(const QString &accountId, const QString &message)
153{
154 return TextHandler::instance()->leaveRooms(accountId, message);
155}
156
128bool HandlerDBus::LeaveChat(const QString &objectPath, const QString &message)157bool HandlerDBus::LeaveChat(const QString &objectPath, const QString &message)
129{158{
130 return TextHandler::instance()->leaveChat(objectPath, message);159 return TextHandler::instance()->leaveChat(objectPath, message);
@@ -140,6 +169,21 @@
140 return TextHandler::instance()->changeRoomTitle(objectPath, title);169 return TextHandler::instance()->changeRoomTitle(objectPath, title);
141}170}
142171
172void HandlerDBus::setActiveAudioOutput(const QString &id)
173{
174 AudioRouteManager::instance()->setActiveAudioOutput(id);
175}
176
177QString HandlerDBus::activeAudioOutput() const
178{
179 return AudioRouteManager::instance()->activeAudioOutput();
180}
181
182AudioOutputDBusList HandlerDBus::AudioOutputs() const
183{
184 return AudioRouteManager::instance()->audioOutputs();
185}
186
143bool HandlerDBus::connectToBus()187bool HandlerDBus::connectToBus()
144{188{
145 new TelephonyServiceHandlerAdaptor(this);189 new TelephonyServiceHandlerAdaptor(this);
@@ -187,11 +231,6 @@
187 CallHandler::instance()->setMuted(objectPath, muted);231 CallHandler::instance()->setMuted(objectPath, muted);
188}232}
189233
190void HandlerDBus::SetActiveAudioOutput(const QString &objectPath, const QString &id)
191{
192 CallHandler::instance()->setActiveAudioOutput(objectPath, id);
193}
194
195void HandlerDBus::SendDTMF(const QString &objectPath, const QString &key)234void HandlerDBus::SendDTMF(const QString &objectPath, const QString &key)
196{235{
197 CallHandler::instance()->sendDTMF(objectPath, key);236 CallHandler::instance()->sendDTMF(objectPath, key);
198237
=== modified file 'handler/handlerdbus.h'
--- handler/handlerdbus.h 2016-12-06 16:45:01 +0000
+++ handler/handlerdbus.h 2017-04-05 14:05:16 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2012-2016 Canonical, Ltd.2 * Copyright (C) 2012-2017 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Ugo Riboni <ugo.riboni@canonical.com>5 * Ugo Riboni <ugo.riboni@canonical.com>
@@ -28,6 +28,9 @@
28#include <QtDBus/QDBusContext>28#include <QtDBus/QDBusContext>
29#include "chatmanager.h"29#include "chatmanager.h"
30#include "dbustypes.h"30#include "dbustypes.h"
31#include "audiooutput.h"
32
33typedef QMap<QString,QVariantMap> AllAccountsProperties;
3134
32/**35/**
33 * DBus interface for the phone handler36 * DBus interface for the phone handler
@@ -40,6 +43,11 @@
40 WRITE setCallIndicatorVisible43 WRITE setCallIndicatorVisible
41 NOTIFY CallIndicatorVisibleChanged)44 NOTIFY CallIndicatorVisibleChanged)
4245
46 Q_PROPERTY(QString ActiveAudioOutput
47 READ activeAudioOutput
48 WRITE setActiveAudioOutput
49 NOTIFY ActiveAudioOutputChanged)
50
43public:51public:
44 HandlerDBus(QObject* parent=0);52 HandlerDBus(QObject* parent=0);
45 ~HandlerDBus();53 ~HandlerDBus();
@@ -52,11 +60,16 @@
52 void setCallIndicatorVisible(bool visible);60 void setCallIndicatorVisible(bool visible);
53 // configuration related61 // configuration related
54 ProtocolList GetProtocols();62 ProtocolList GetProtocols();
63 AllAccountsProperties GetAllAccountsProperties();
64 QVariantMap GetAccountProperties(const QString &accountId);
65 void SetAccountProperties(const QString &accountId, const QVariantMap &properties);
5566
56 QString registerObject(QObject *object, const QString &path);67 QString registerObject(QObject *object, const QString &path);
57 void unregisterObject(const QString &path);68 void unregisterObject(const QString &path);
5869
59 static HandlerDBus *instance();70 static HandlerDBus *instance();
71 QString activeAudioOutput() const;
72 void setActiveAudioOutput(const QString &id);
6073
61public Q_SLOTS:74public Q_SLOTS:
62 bool connectToBus();75 bool connectToBus();
@@ -71,27 +84,33 @@
71 Q_NOREPLY void InviteParticipants(const QString &objectPath, const QStringList &participants, const QString &message);84 Q_NOREPLY void InviteParticipants(const QString &objectPath, const QStringList &participants, const QString &message);
72 Q_NOREPLY void RemoveParticipants(const QString &objectPath, const QStringList &participants, const QString &message);85 Q_NOREPLY void RemoveParticipants(const QString &objectPath, const QStringList &participants, const QString &message);
73 bool LeaveChat(const QString &objectPath, const QString &message);86 bool LeaveChat(const QString &objectPath, const QString &message);
87 Q_NOREPLY void LeaveRooms(const QString &accountId, const QString &message);
7488
75 // call related89 // call related
76 Q_NOREPLY void StartCall(const QString &number, const QString &accountId);90 Q_NOREPLY void StartCall(const QString &number, const QString &accountId);
77 Q_NOREPLY void HangUpCall(const QString &objectPath);91 Q_NOREPLY void HangUpCall(const QString &objectPath);
78 Q_NOREPLY void SetHold(const QString &objectPath, bool hold);92 Q_NOREPLY void SetHold(const QString &objectPath, bool hold);
79 Q_NOREPLY void SetMuted(const QString &objectPath, bool muted);93 Q_NOREPLY void SetMuted(const QString &objectPath, bool muted);
80 Q_NOREPLY void SetActiveAudioOutput(const QString &objectPath, const QString &id);
81 Q_NOREPLY void SendDTMF(const QString &objectPath, const QString &key);94 Q_NOREPLY void SendDTMF(const QString &objectPath, const QString &key);
8295
83 // conference call related96 // conference call related
84 Q_NOREPLY void CreateConferenceCall(const QStringList &objectPaths);97 Q_NOREPLY void CreateConferenceCall(const QStringList &objectPaths);
85 Q_NOREPLY void MergeCall(const QString &conferenceObjectPath, const QString &callObjectPath);98 Q_NOREPLY void MergeCall(const QString &conferenceObjectPath, const QString &callObjectPath);
86 Q_NOREPLY void SplitCall(const QString &objectPath);99 Q_NOREPLY void SplitCall(const QString &objectPath);
100 AudioOutputDBusList AudioOutputs() const;
101
102
87103
88Q_SIGNALS:104Q_SIGNALS:
89 void onMessageSent(const QString &number, const QString &message);105 void onMessageSent(const QString &number, const QString &message);
90 void CallPropertiesChanged(const QString &objectPath, const QVariantMap &properties);106 void CallPropertiesChanged(const QString &objectPath, const QVariantMap &properties);
107 void AccountPropertiesChanged(const QString &accountId, const QVariantMap &properties);
91 void CallIndicatorVisibleChanged(bool visible);108 void CallIndicatorVisibleChanged(bool visible);
92 void ConferenceCallRequestFinished(bool succeeded);109 void ConferenceCallRequestFinished(bool succeeded);
93 void CallHoldingFailed(const QString &objectPath);110 void CallHoldingFailed(const QString &objectPath);
94 void ProtocolsChanged(const ProtocolList &protocols);111 void ProtocolsChanged(const ProtocolList &protocols);
112 void ActiveAudioOutputChanged(const QString &id);
113 void AudioOutputsChanged(const AudioOutputDBusList &audioOutputs);
95114
96private:115private:
97 bool mCallIndicatorVisible;116 bool mCallIndicatorVisible;
98117
=== modified file 'handler/main.cpp'
--- handler/main.cpp 2016-11-23 19:28:18 +0000
+++ handler/main.cpp 2017-04-05 14:05:16 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013-2016 Canonical, Ltd.2 * Copyright (C) 2013-2017 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -30,6 +30,10 @@
30#include <TelepathyQt/AbstractClient>30#include <TelepathyQt/AbstractClient>
31#include <TelepathyQt/AccountManager>31#include <TelepathyQt/AccountManager>
32#include <TelepathyQt/Contact>32#include <TelepathyQt/Contact>
33#include <telepathy-farstream/telepathy-farstream.h>
34#include <TelepathyQt/CallChannel>
35
36Q_DECLARE_METATYPE(Tp::CallChannelPtr)
3337
34int main(int argc, char **argv)38int main(int argc, char **argv)
35{39{
@@ -37,6 +41,13 @@
37 QCoreApplication::setApplicationName("telephony-service-handler");41 QCoreApplication::setApplicationName("telephony-service-handler");
3842
39 Tp::registerTypes();43 Tp::registerTypes();
44 gst_init(&argc, &argv);
45 qRegisterMetaType<Tp::CallChannelPtr>();
46 qRegisterMetaType<AudioOutputDBus>();
47 qRegisterMetaType<AudioOutputDBusList>();
48
49 qDBusRegisterMetaType<AudioOutputDBus>();
50 qDBusRegisterMetaType<AudioOutputDBusList>();
4051
41 // check if there is already an instance of the handler running52 // check if there is already an instance of the handler running
42 if (ApplicationUtils::checkApplicationRunning(TP_QT_IFACE_CLIENT + ".TelephonyServiceHandler")) {53 if (ApplicationUtils::checkApplicationRunning(TP_QT_IFACE_CLIENT + ".TelephonyServiceHandler")) {
@@ -44,11 +55,13 @@
44 return 1;55 return 1;
45 }56 }
4657
58 HandlerDBus dbus;
47 Handler *handler = new Handler();59 Handler *handler = new Handler();
48 QObject::connect(TelepathyHelper::instance(), &TelepathyHelper::setupReady, [handler]() {60 QObject::connect(TelepathyHelper::instance(), &TelepathyHelper::setupReady, [&]() {
49 TelepathyHelper::instance()->registerClient(handler, "TelephonyServiceHandler");61 TelepathyHelper::instance()->registerClient(handler, "TelephonyServiceHandler");
62 dbus.connectToBus();
50 });63 });
51 64
52 QObject::connect(handler, SIGNAL(callChannelAvailable(Tp::CallChannelPtr)),65 QObject::connect(handler, SIGNAL(callChannelAvailable(Tp::CallChannelPtr)),
53 CallHandler::instance(), SLOT(onCallChannelAvailable(Tp::CallChannelPtr)));66 CallHandler::instance(), SLOT(onCallChannelAvailable(Tp::CallChannelPtr)));
54 QObject::connect(handler, SIGNAL(textChannelAvailable(Tp::TextChannelPtr)),67 QObject::connect(handler, SIGNAL(textChannelAvailable(Tp::TextChannelPtr)),
5568
=== added file 'handler/powerd.h'
--- handler/powerd.h 1970-01-01 00:00:00 +0000
+++ handler/powerd.h 2017-04-05 14:05:16 +0000
@@ -0,0 +1,34 @@
1/**
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors: Andreas Pokorny <andreas.pokorny@canonical.com>
17 */
18
19#ifndef POWERD_H
20#define POWERD_H
21
22class PowerD
23{
24public:
25 PowerD() = default;
26 virtual ~PowerD() = default;
27 PowerD(PowerD const&) = delete;
28 PowerD& operator=(PowerD const&) = delete;
29
30 virtual void enableProximityHandling() = 0;
31 virtual void disableProximityHandling() = 0;
32};
33
34#endif // POWERD_H
035
=== added file 'handler/powerdaudiomodemediator.cpp'
--- handler/powerdaudiomodemediator.cpp 1970-01-01 00:00:00 +0000
+++ handler/powerdaudiomodemediator.cpp 2017-04-05 14:05:16 +0000
@@ -0,0 +1,63 @@
1/**
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors: Andreas Pokorny <andreas.pokorny@canonical.com>
17 */
18
19#include <QDBusInterface>
20#include "powerdaudiomodemediator.h"
21
22PowerDAudioModeMediator::PowerDAudioModeMediator(PowerD &powerd)
23 : powerd(powerd)
24{
25}
26
27void PowerDAudioModeMediator::audioModeChanged(const QString &mode)
28{
29 bool enableProximity = !(mode == "speaker" || mode == "bluetooth" || mode == "wired_headset");
30
31 if (mProximityEnabled != enableProximity)
32 {
33 mProximityEnabled = enableProximity;
34 apply();
35 }
36}
37
38void PowerDAudioModeMediator::apply() const
39{
40 if (mProximityEnabled) {
41 powerd.enableProximityHandling();
42 } else {
43 // we need to power the screen on before disabling the proximity handling
44 QDBusInterface unityIface("com.canonical.Unity.Screen",
45 "/com/canonical/Unity/Screen",
46 "com.canonical.Unity.Screen",
47 QDBusConnection::systemBus());
48 QList<QVariant> args;
49 args.append("on");
50 args.append(3);
51 unityIface.callWithArgumentList(QDBus::NoBlock, "setScreenPowerMode", args);
52 powerd.disableProximityHandling();
53 }
54}
55
56void PowerDAudioModeMediator::audioOutputClosed()
57{
58 if (mProximityEnabled)
59 {
60 mProximityEnabled = false;
61 apply();
62 }
63}
064
=== added file 'handler/powerdaudiomodemediator.h'
--- handler/powerdaudiomodemediator.h 1970-01-01 00:00:00 +0000
+++ handler/powerdaudiomodemediator.h 2017-04-05 14:05:16 +0000
@@ -0,0 +1,46 @@
1/****************************************************************************
2**
3** Copyright (C) 2014 Canonical, Ltd.
4**
5** Authors:
6** Andreas Pokorny <andreas.pokorny@canonical.com>
7**
8** GNU Lesser General Public License Usage
9** Alternatively, this file may be used under the terms of the GNU Lesser
10** General Public License version 2.1 as published by the Free Software
11** Foundation and appearing in the file LICENSE.LGPL included in the
12** packaging of this file. Please review the following information to
13** ensure the GNU Lesser General Public License version 2.1 requirements
14** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
15**
16****************************************************************************/
17
18#ifndef POWERDAUDIOMODEMEDIATOR_H
19#define POWERDAUDIOMODEMEDIATOR_H
20
21#include "powerd.h"
22
23#include <QString>
24#include <fstream>
25#include <memory>
26
27class PowerD;
28/*!
29 * \brief PowerDAudioModeMediator is responsible for configuring proximity
30 * handling of powerd during different call states and used audio outputs.
31 * In General that mean enabling sreen blanking on proximity events, when
32 * a call is active and neither a bluetooth headset nor the speakers are used.
33 */
34class PowerDAudioModeMediator
35{
36public:
37 PowerDAudioModeMediator(PowerD &powerd);
38 void audioModeChanged(const QString &mode);
39 void audioOutputClosed();
40private:
41 void apply() const;
42 PowerD &powerd;
43 bool mProximityEnabled{false};
44};
45
46#endif
047
=== added file 'handler/powerddbus.cpp'
--- handler/powerddbus.cpp 1970-01-01 00:00:00 +0000
+++ handler/powerddbus.cpp 2017-04-05 14:05:16 +0000
@@ -0,0 +1,43 @@
1/**
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors: Andreas Pokorny <andreas.pokorny@canonical.com>
17 */
18
19#include "powerddbus.h"
20
21#include <QDBusConnection>
22#include <QDBusInterface>
23#include <QDBusReply>
24
25PowerDDBus::PowerDDBus()
26 : mPowerDIface{
27 new QDBusInterface(
28 "com.canonical.powerd",
29 "/com/canonical/powerd",
30 "com.canonical.powerd",
31 QDBusConnection::systemBus())}
32{
33}
34
35void PowerDDBus::enableProximityHandling()
36{
37 mPowerDIface->call("enableProximityHandling", "telephony-service-handler");
38}
39
40void PowerDDBus::disableProximityHandling()
41{
42 mPowerDIface->call("disableProximityHandling", "telephony-service-handler");
43}
044
=== added file 'handler/powerddbus.h'
--- handler/powerddbus.h 1970-01-01 00:00:00 +0000
+++ handler/powerddbus.h 2017-04-05 14:05:16 +0000
@@ -0,0 +1,38 @@
1/**
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors: Andreas Pokorny <andreas.pokorny@canonical.com>
17 */
18
19#ifndef POWERD_DUBS_H
20#define POWERD_DBUS_H
21
22#include "powerd.h"
23
24#include <memory>
25
26class QDBusInterface;
27
28class PowerDDBus : public PowerD
29{
30public:
31 PowerDDBus();
32 void enableProximityHandling() override;
33 void disableProximityHandling() override;
34private:
35 std::unique_ptr<QDBusInterface> mPowerDIface;
36};
37
38#endif
039
=== added file 'handler/qpulseaudioengine.cpp'
--- handler/qpulseaudioengine.cpp 1970-01-01 00:00:00 +0000
+++ handler/qpulseaudioengine.cpp 2017-04-05 14:05:16 +0000
@@ -0,0 +1,853 @@
1/****************************************************************************
2**
3** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4** Contact: http://www.qt-project.org/legal
5**
6** This file was taken from qt5 and modified by
7** David Henningsson <david.henningsson@canonical.com> for usage in
8** telepathy-ofono.
9**
10** GNU Lesser General Public License Usage
11** Alternatively, this file may be used under the terms of the GNU Lesser
12** General Public License version 2.1 as published by the Free Software
13** Foundation and appearing in the file LICENSE.LGPL included in the
14** packaging of this file. Please review the following information to
15** ensure the GNU Lesser General Public License version 2.1 requirements
16** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17**
18****************************************************************************/
19
20#include <QtCore/qdebug.h>
21
22#include "qpulseaudioengine.h"
23#include <sys/types.h>
24#include <unistd.h>
25
26#define PULSEAUDIO_PROFILE_HSP "headset_head_unit"
27#define PULSEAUDIO_PROFILE_A2DP "a2dp_sink"
28
29QT_BEGIN_NAMESPACE
30
31static void contextStateCallbackInit(pa_context *context, void *userdata)
32{
33 Q_UNUSED(context);
34 QPulseAudioEngineWorker *pulseEngine = reinterpret_cast<QPulseAudioEngineWorker*>(userdata);
35 pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
36}
37
38static void contextStateCallback(pa_context *context, void *userdata)
39{
40 Q_UNUSED(userdata);
41 Q_UNUSED(context);
42}
43
44static void success_cb(pa_context *context, int success, void *userdata)
45{
46 QPulseAudioEngineWorker *pulseEngine = reinterpret_cast<QPulseAudioEngineWorker*>(userdata);
47 pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
48}
49
50/* Callbacks used when handling events from PulseAudio */
51static void plug_card_cb(pa_context *c, const pa_card_info *info, int isLast, void *userdata)
52{
53 QPulseAudioEngineWorker *pulseEngine = static_cast<QPulseAudioEngineWorker*>(userdata);
54 if (isLast != 0 || !pulseEngine || !info) {
55 pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
56 return;
57 }
58 pulseEngine->plugCardCallback(info);
59}
60
61static void update_card_cb(pa_context *c, const pa_card_info *info, int isLast, void *userdata)
62{
63 QPulseAudioEngineWorker *pulseEngine = static_cast<QPulseAudioEngineWorker*>(userdata);
64 if (isLast != 0 || !pulseEngine || !info) {
65 pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
66 return;
67 }
68 pulseEngine->updateCardCallback(info);
69}
70
71static void unplug_card_cb(pa_context *c, const pa_card_info *info, int isLast, void *userdata)
72{
73 QPulseAudioEngineWorker *pulseEngine = static_cast<QPulseAudioEngineWorker*>(userdata);
74 if (!pulseEngine) {
75 pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
76 return;
77 }
78
79 if (info == NULL) {
80 /* That means that the card used to query card_info was removed */
81 pulseEngine->unplugCardCallback();
82 }
83}
84
85static void subscribeCallback(pa_context *context, pa_subscription_event_type_t t, uint32_t idx, void *userdata)
86{
87 /* For card change events (slot plug/unplug and add/remove card) */
88 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_CARD) {
89 if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) {
90 QMetaObject::invokeMethod((QPulseAudioEngineWorker *) userdata, "handleCardEvent",
91 Qt::QueuedConnection, Q_ARG(int, PA_SUBSCRIPTION_EVENT_CHANGE), Q_ARG(unsigned int, idx));
92 } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
93 QMetaObject::invokeMethod((QPulseAudioEngineWorker *) userdata, "handleCardEvent",
94 Qt::QueuedConnection, Q_ARG(int, PA_SUBSCRIPTION_EVENT_NEW), Q_ARG(unsigned int, idx));
95 } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
96 QMetaObject::invokeMethod((QPulseAudioEngineWorker *) userdata, "handleCardEvent",
97 Qt::QueuedConnection, Q_ARG(int, PA_SUBSCRIPTION_EVENT_REMOVE), Q_ARG(unsigned int, idx));
98 }
99 }
100}
101
102QPulseAudioEngineWorker::QPulseAudioEngineWorker(QObject *parent)
103 : QObject(parent)
104 , m_mainLoopApi(0)
105 , m_context(0)
106 , m_callstatus(CallEnded)
107 , m_audiomode(AudioModeSpeaker)
108 , m_micmute(false)
109 , m_defaultsink("sink.primary")
110 , m_defaultsource("source.primary")
111 , m_voicecallcard("")
112 , m_voicecallhighest("")
113 , m_voicecallprofile("")
114 , m_bt_hsp("")
115 , m_bt_hsp_a2dp("")
116 , m_default_bt_card_fallback("")
117
118{
119 m_mainLoop = pa_threaded_mainloop_new();
120 if (m_mainLoop == 0) {
121 qWarning("Unable to create pulseaudio mainloop");
122 return;
123 }
124
125 if (pa_threaded_mainloop_start(m_mainLoop) != 0) {
126 qWarning("Unable to start pulseaudio mainloop");
127 pa_threaded_mainloop_free(m_mainLoop);
128 m_mainLoop = 0;
129 return;
130 }
131
132 createPulseContext();
133}
134
135bool QPulseAudioEngineWorker::createPulseContext()
136{
137 bool keepGoing = true;
138 bool ok = true;
139
140 if (m_context)
141 return true;
142
143 m_mainLoopApi = pa_threaded_mainloop_get_api(m_mainLoop);
144
145 pa_threaded_mainloop_lock(m_mainLoop);
146
147 m_context = pa_context_new(m_mainLoopApi, QString(QLatin1String("QtmPulseContext:%1")).arg(::getpid()).toLatin1().constData());
148 pa_context_set_state_callback(m_context, contextStateCallbackInit, this);
149
150 if (!m_context) {
151 qWarning("Unable to create new pulseaudio context");
152 pa_threaded_mainloop_unlock(m_mainLoop);
153 return false;
154 }
155
156 if (pa_context_connect(m_context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) < 0) {
157 qWarning("Unable to create a connection to the pulseaudio context");
158 pa_threaded_mainloop_unlock(m_mainLoop);
159 releasePulseContext();
160 return false;
161 }
162
163 pa_threaded_mainloop_wait(m_mainLoop);
164
165 while (keepGoing) {
166 switch (pa_context_get_state(m_context)) {
167 case PA_CONTEXT_CONNECTING:
168 case PA_CONTEXT_AUTHORIZING:
169 case PA_CONTEXT_SETTING_NAME:
170 break;
171
172 case PA_CONTEXT_READY:
173 qDebug("Pulseaudio connection established.");
174 keepGoing = false;
175 break;
176
177 case PA_CONTEXT_TERMINATED:
178 qCritical("Pulseaudio context terminated.");
179 keepGoing = false;
180 ok = false;
181 break;
182
183 case PA_CONTEXT_FAILED:
184 default:
185 qCritical() << QString("Pulseaudio connection failure: %1").arg(pa_strerror(pa_context_errno(m_context)));
186 keepGoing = false;
187 ok = false;
188 }
189
190 if (keepGoing) {
191 pa_threaded_mainloop_wait(m_mainLoop);
192 }
193 }
194
195 if (ok) {
196 pa_context_set_state_callback(m_context, contextStateCallback, this);
197 pa_context_set_subscribe_callback(m_context, subscribeCallback, this);
198 pa_context_subscribe(m_context, PA_SUBSCRIPTION_MASK_CARD, NULL, this);
199 } else {
200 if (m_context) {
201 pa_context_unref(m_context);
202 m_context = 0;
203 }
204 }
205
206 pa_threaded_mainloop_unlock(m_mainLoop);
207 return true;
208}
209
210
211void QPulseAudioEngineWorker::releasePulseContext()
212{
213 if (m_context) {
214 pa_threaded_mainloop_lock(m_mainLoop);
215 pa_context_disconnect(m_context);
216 pa_context_unref(m_context);
217 pa_threaded_mainloop_unlock(m_mainLoop);
218 m_context = 0;
219 }
220
221}
222
223QPulseAudioEngineWorker::~QPulseAudioEngineWorker()
224{
225 releasePulseContext();
226
227 if (m_mainLoop) {
228 pa_threaded_mainloop_stop(m_mainLoop);
229 pa_threaded_mainloop_free(m_mainLoop);
230 m_mainLoop = 0;
231 }
232}
233
234void QPulseAudioEngineWorker::cardInfoCallback(const pa_card_info *info)
235{
236 pa_card_profile_info2 *voice_call = NULL, *highest = NULL;
237 pa_card_profile_info2 *hsp = NULL, *a2dp = NULL;
238
239 /* For now we only support one card with the voicecall feature */
240 for (int i = 0; i < info->n_profiles; i++) {
241 if (!highest || info->profiles2[i]->priority > highest->priority)
242 highest = info->profiles2[i];
243 if (!strcmp(info->profiles2[i]->name, "voicecall"))
244 voice_call = info->profiles2[i];
245 else if (!strcmp(info->profiles2[i]->name, PULSEAUDIO_PROFILE_HSP) &&
246 info->profiles2[i]->available != 0)
247 hsp = info->profiles2[i];
248 else if (!strcmp(info->profiles2[i]->name, PULSEAUDIO_PROFILE_A2DP) &&
249 info->profiles2[i]->available != 0)
250 a2dp = info->profiles2[i];
251 }
252
253 /* Record the card that supports voicecall (default one to be used) */
254 if (voice_call) {
255 qDebug("Found card that supports voicecall: '%s'", info->name);
256 m_voicecallcard = info->name;
257 m_voicecallhighest = highest->name;
258 qDebug("1");
259 m_voicecallprofile = voice_call->name;
260 qDebug("2");
261 }
262
263 /* Handle the use cases needed for bluetooth */
264 if (hsp && a2dp) {
265 qDebug("Found card that supports hsp and a2dp: '%s'", info->name);
266 m_bt_hsp_a2dp = info->name;
267 } else if (hsp && (a2dp == NULL)) {
268 /* This card only provides the hsp profile */
269 qDebug("Found card that supports only hsp: '%s'", info->name);
270 m_bt_hsp = info->name;
271 }
272 qDebug("3");
273}
274
275void QPulseAudioEngineWorker::sinkInfoCallback(const pa_sink_info *info)
276{
277 pa_sink_port_info *earpiece = NULL, *speaker = NULL;
278 pa_sink_port_info *wired_headset = NULL, *wired_headphone = NULL;
279 pa_sink_port_info *preferred = NULL;
280 pa_sink_port_info *bluetooth_sco = NULL;
281 pa_sink_port_info *speaker_and_wired_headphone = NULL;
282 AudioMode audiomodetoset;
283 AudioModes modes;
284
285 for (int i = 0; i < info->n_ports; i++) {
286 if (!strcmp(info->ports[i]->name, "output-earpiece"))
287 earpiece = info->ports[i];
288 else if (!strcmp(info->ports[i]->name, "output-wired_headset") &&
289 (info->ports[i]->available != PA_PORT_AVAILABLE_NO))
290 wired_headset = info->ports[i];
291 else if (!strcmp(info->ports[i]->name, "output-wired_headphone") &&
292 (info->ports[i]->available != PA_PORT_AVAILABLE_NO))
293 wired_headphone = info->ports[i];
294 else if (!strcmp(info->ports[i]->name, "output-speaker"))
295 speaker = info->ports[i];
296 else if (!strcmp(info->ports[i]->name, "output-bluetooth_sco"))
297 bluetooth_sco = info->ports[i];
298 else if (!strcmp(info->ports[i]->name, "output-speaker+wired_headphone"))
299 speaker_and_wired_headphone = info->ports[i];
300 }
301
302 if (!earpiece || !speaker)
303 return; /* Not the right sink */
304
305 /* Refresh list of available audio modes */
306 modes.append(AudioModeEarpiece);
307 modes.append(AudioModeSpeaker);
308 if (wired_headset || wired_headphone)
309 modes.append(AudioModeWiredHeadset);
310 if (bluetooth_sco && ((m_bt_hsp != "") || (m_bt_hsp_a2dp != "")))
311 modes.append(AudioModeBluetooth);
312
313 /* Check if the requested mode is available (earpiece*/
314 if (((m_audiomode == AudioModeWiredHeadset) && !modes.contains(AudioModeWiredHeadset)) ||
315 ((m_audiomode == AudioModeBluetooth) && !modes.contains(AudioModeBluetooth)))
316 return;
317
318 /* Now to decide which output to be used, depending on the active mode */
319 if (m_audiomode & AudioModeEarpiece) {
320 preferred = earpiece;
321 audiomodetoset = AudioModeEarpiece;
322 }
323 if (m_audiomode & AudioModeSpeaker) {
324 preferred = speaker;
325 audiomodetoset = AudioModeSpeaker;
326 }
327 if ((m_audiomode & AudioModeWiredHeadset) && (modes.contains(AudioModeWiredHeadset))) {
328 preferred = wired_headset ? wired_headset : wired_headphone;
329 audiomodetoset = AudioModeWiredHeadset;
330 }
331 if (m_callstatus == CallRinging && speaker_and_wired_headphone) {
332 preferred = speaker_and_wired_headphone;
333 }
334 if ((m_audiomode & AudioModeBluetooth) && (modes.contains(AudioModeBluetooth))) {
335 preferred = bluetooth_sco;
336 audiomodetoset = AudioModeBluetooth;
337 }
338
339 m_audiomode = audiomodetoset;
340
341 m_nametoset = info->name;
342 if (info->active_port != preferred)
343 m_valuetoset = preferred->name;
344
345 if (modes != m_availableAudioModes)
346 m_availableAudioModes = modes;
347}
348
349void QPulseAudioEngineWorker::sourceInfoCallback(const pa_source_info *info)
350{
351 pa_source_port_info *builtin_mic = NULL, *preferred = NULL;
352 pa_source_port_info *wired_headset = NULL, *bluetooth_sco = NULL;
353
354 if (info->monitor_of_sink != PA_INVALID_INDEX)
355 return; /* Not the right source */
356
357 for (int i = 0; i < info->n_ports; i++) {
358 if (!strcmp(info->ports[i]->name, "input-builtin_mic"))
359 builtin_mic = info->ports[i];
360 else if (!strcmp(info->ports[i]->name, "input-wired_headset") &&
361 (info->ports[i]->available != PA_PORT_AVAILABLE_NO))
362 wired_headset = info->ports[i];
363 else if (!strcmp(info->ports[i]->name, "input-bluetooth_sco_headset"))
364 bluetooth_sco = info->ports[i];
365 }
366
367 if (!builtin_mic)
368 return; /* Not the right source */
369
370 /* Now to decide which output to be used, depending on the active mode */
371 if ((m_audiomode & AudioModeEarpiece) || (m_audiomode & AudioModeSpeaker))
372 preferred = builtin_mic;
373 if ((m_audiomode & AudioModeWiredHeadset) && (m_availableAudioModes.contains(AudioModeWiredHeadset)))
374 preferred = wired_headset ? wired_headset : builtin_mic;
375 if ((m_audiomode & AudioModeBluetooth) && (m_availableAudioModes.contains(AudioModeBluetooth)))
376 preferred = bluetooth_sco;
377
378 m_nametoset = info->name;
379 if (info->active_port != preferred)
380 m_valuetoset = preferred->name;
381}
382
383void QPulseAudioEngineWorker::serverInfoCallback(const pa_server_info *info)
384{
385 /* Saving default sink/source to restore after call hangup */
386 m_defaultsink = info->default_sink_name;
387 m_defaultsource = info->default_source_name;
388
389 /* In the case of a server callback we need to signal the mainloop */
390 pa_threaded_mainloop_signal(mainloop(), 0);
391}
392
393static void cardinfo_cb(pa_context *context, const pa_card_info *info, int isLast, void *userdata)
394{
395 QPulseAudioEngineWorker *pulseEngine = static_cast<QPulseAudioEngineWorker*>(userdata);
396 if (isLast != 0 || !pulseEngine || !info) {
397 pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
398 return;
399 }
400 pulseEngine->cardInfoCallback(info);
401}
402
403static void sinkinfo_cb(pa_context *context, const pa_sink_info *info, int isLast, void *userdata)
404{
405 QPulseAudioEngineWorker *pulseEngine = static_cast<QPulseAudioEngineWorker*>(userdata);
406 if (isLast != 0 || !pulseEngine || !info) {
407 pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
408 return;
409 }
410 pulseEngine->sinkInfoCallback(info);
411}
412
413static void sourceinfo_cb(pa_context *context, const pa_source_info *info, int isLast, void *userdata)
414{
415 QPulseAudioEngineWorker *pulseEngine = static_cast<QPulseAudioEngineWorker*>(userdata);
416 if (isLast != 0 || !pulseEngine || !info) {
417 pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
418 return;
419 }
420 pulseEngine->sourceInfoCallback(info);
421}
422
423static void serverinfo_cb(pa_context *context, const pa_server_info *info, void *userdata)
424{
425 QPulseAudioEngineWorker *pulseEngine = static_cast<QPulseAudioEngineWorker*>(userdata);
426 if (!pulseEngine || !info) {
427 pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
428 return;
429 }
430 pulseEngine->serverInfoCallback(info);
431}
432
433bool QPulseAudioEngineWorker::handleOperation(pa_operation *operation, const char *func_name)
434{
435 if (!operation) {
436 qCritical("'%s' failed (lost PulseAudio connection?)", func_name);
437 /* Free resources so it can retry a new connection during next operation */
438 pa_threaded_mainloop_unlock(m_mainLoop);
439 releasePulseContext();
440 return false;
441 }
442
443 while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
444 pa_threaded_mainloop_wait(m_mainLoop);
445 pa_operation_unref(operation);
446 return true;
447}
448
449int QPulseAudioEngineWorker::setupVoiceCall()
450{
451 pa_operation *o;
452
453 qDebug("Setting up pulseaudio for voice call");
454
455 pa_threaded_mainloop_lock(m_mainLoop);
456
457 /* Get and set the default sink/source to be restored later */
458 o = pa_context_get_server_info(m_context, serverinfo_cb, this);
459 if (!handleOperation(o, "pa_context_get_server_info"))
460 return -1;
461
462 qDebug("Recorded default sink: %s default source: %s",
463 m_defaultsink.c_str(), m_defaultsource.c_str());
464
465 /* Walk through the list of devices, find the voice call capable card and
466 * identify if we have bluetooth capable devices (hsp and a2dp) */
467 m_voicecallcard = m_voicecallhighest = m_voicecallprofile = "";
468 m_bt_hsp = m_bt_hsp_a2dp = "";
469 o = pa_context_get_card_info_list(m_context, cardinfo_cb, this);
470 if (!handleOperation(o, "pa_context_get_card_info_list"))
471 return -1;
472 /* In case we have only one bt device that provides hsp and a2dp, we need
473 * to make sure we switch the default profile for that card (to hsp) */
474 if ((m_bt_hsp_a2dp != "") && (m_bt_hsp == "")) {
475 qDebug("Setting PulseAudio card '%s' profile '%s'",
476 m_bt_hsp_a2dp.c_str(), PULSEAUDIO_PROFILE_HSP);
477 o = pa_context_set_card_profile_by_name(m_context,
478 m_bt_hsp_a2dp.c_str(), PULSEAUDIO_PROFILE_HSP, success_cb, this);
479 if (!handleOperation(o, "pa_context_set_card_profile_by_name"))
480 return -1;
481 }
482
483 pa_threaded_mainloop_unlock(m_mainLoop);
484
485 return 0;
486}
487
488void QPulseAudioEngineWorker::restoreVoiceCall()
489{
490 pa_operation *o;
491
492 qDebug("Restoring pulseaudio previous state");
493
494 /* Then restore previous settings */
495 pa_threaded_mainloop_lock(m_mainLoop);
496
497 /* See if we need to restore any HSP+AD2P device state */
498 if ((m_bt_hsp_a2dp != "") && (m_bt_hsp == "")) {
499 qDebug("Restoring PulseAudio card '%s' to profile '%s'",
500 m_bt_hsp_a2dp.c_str(), PULSEAUDIO_PROFILE_A2DP);
501 o = pa_context_set_card_profile_by_name(m_context,
502 m_bt_hsp_a2dp.c_str(), PULSEAUDIO_PROFILE_A2DP, success_cb, this);
503 if (!handleOperation(o, "pa_context_set_card_profile_by_name"))
504 return;
505 }
506
507 /* Restore default sink/source */
508 if (m_defaultsink != "") {
509 qDebug("Restoring PulseAudio default sink to '%s'", m_defaultsink.c_str());
510 o = pa_context_set_default_sink(m_context, m_defaultsink.c_str(), success_cb, this);
511 if (!handleOperation(o, "pa_context_set_default_sink"))
512 return;
513 }
514 if (m_defaultsource != "") {
515 qDebug("Restoring PulseAudio default source to '%s'", m_defaultsource.c_str());
516 o = pa_context_set_default_source(m_context, m_defaultsource.c_str(), success_cb, this);
517 if (!handleOperation(o, "pa_context_set_default_source"))
518 return;
519 }
520
521 pa_threaded_mainloop_unlock(m_mainLoop);
522}
523
524void QPulseAudioEngineWorker::setCallMode(CallStatus callstatus, AudioMode audiomode)
525{
526 if (!createPulseContext()) {
527 return;
528 }
529 CallStatus p_callstatus = m_callstatus;
530 AudioMode p_audiomode = m_audiomode;
531 AudioModes p_availableAudioModes = m_availableAudioModes;
532 pa_operation *o;
533
534 /* Check if we need to save the current pulseaudio state (e.g. when starting a call) */
535 if ((callstatus != CallEnded) && (p_callstatus == CallEnded)) {
536 if (setupVoiceCall() < 0) {
537 qCritical("Failed to setup PulseAudio for Voice Call");
538 return;
539 }
540 }
541
542 /* If we have an active call, update internal state (used later when updating sink/source ports) */
543 m_callstatus = callstatus;
544 m_audiomode = audiomode;
545
546 pa_threaded_mainloop_lock(m_mainLoop);
547
548 /* Switch the virtual card mode when call is active and not active
549 * This needs to be done before sink/source gets updated, because after changing mode
550 * it will automatically move to input/output-parking */
551 if ((m_callstatus == CallActive) && (p_callstatus != CallActive) &&
552 (m_voicecallcard != "") && (m_voicecallprofile != "")) {
553 qDebug("Setting PulseAudio card '%s' profile '%s'",
554 m_voicecallcard.c_str(), m_voicecallprofile.c_str());
555 o = pa_context_set_card_profile_by_name(m_context,
556 m_voicecallcard.c_str(), m_voicecallprofile.c_str(), success_cb, this);
557 if (!handleOperation(o, "pa_context_set_card_profile_by_name"))
558 return;
559 } else if ((m_callstatus == CallEnded) && (m_voicecallcard != "") && (m_voicecallhighest != "")) {
560 /* If using droid, make sure to restore to the profile that has the highest score */
561 qDebug("Restoring PulseAudio card '%s' to profile '%s'",
562 m_voicecallcard.c_str(), m_voicecallhighest.c_str());
563 o = pa_context_set_card_profile_by_name(m_context,
564 m_voicecallcard.c_str(), m_voicecallhighest.c_str(), success_cb, this);
565 if (!handleOperation(o, "pa_context_set_card_profile_by_name"))
566 return;
567 }
568
569 /* Find highest compatible sink/source elements from the voicecall
570 compatible card (on touch this means the pulse droid element) */
571 m_nametoset = m_valuetoset = "";
572 o = pa_context_get_sink_info_list(m_context, sinkinfo_cb, this);
573 if (!handleOperation(o, "pa_context_get_sink_info_list"))
574 return;
575 if ((m_nametoset != "") && (m_nametoset != m_defaultsink)) {
576 qDebug("Setting PulseAudio default sink to '%s'", m_nametoset.c_str());
577 o = pa_context_set_default_sink(m_context, m_nametoset.c_str(), success_cb, this);
578 if (!handleOperation(o, "pa_context_set_default_sink"))
579 return;
580 }
581 if (m_valuetoset != "") {
582 qDebug("Setting PulseAudio sink '%s' port '%s'",
583 m_nametoset.c_str(), m_valuetoset.c_str());
584 o = pa_context_set_sink_port_by_name(m_context, m_nametoset.c_str(),
585 m_valuetoset.c_str(), success_cb, this);
586 if (!handleOperation(o, "pa_context_set_sink_port_by_name"))
587 return;
588 }
589
590 /* Same for source */
591 m_nametoset = m_valuetoset = "";
592 o = pa_context_get_source_info_list(m_context, sourceinfo_cb, this);
593 if (!handleOperation(o, "pa_context_get_source_info_list"))
594 return;
595 if ((m_nametoset != "") && (m_nametoset != m_defaultsource)) {
596 qDebug("Setting PulseAudio default source to '%s'", m_nametoset.c_str());
597 o = pa_context_set_default_source(m_context, m_nametoset.c_str(), success_cb, this);
598 if (!handleOperation(o, "pa_context_set_default_source"))
599 return;
600 }
601 if (m_valuetoset != "") {
602 qDebug("Setting PulseAudio source '%s' port '%s'",
603 m_nametoset.c_str(), m_valuetoset.c_str());
604 o = pa_context_set_source_port_by_name(m_context, m_nametoset.c_str(),
605 m_valuetoset.c_str(), success_cb, this);
606 if (!handleOperation(o, "pa_context_set_source_port_by_name"))
607 return;
608 }
609
610 pa_threaded_mainloop_unlock(m_mainLoop);
611
612 /* Notify if the list of audio modes changed */
613 if (p_availableAudioModes != m_availableAudioModes)
614 Q_EMIT availableAudioModesChanged(m_availableAudioModes);
615
616 /* Notify if call mode changed */
617 if (p_audiomode != m_audiomode) {
618 Q_EMIT audioModeChanged(m_audiomode);
619 }
620
621 /* If no more active voicecall, restore previous saved pulseaudio state */
622 if (callstatus == CallEnded) {
623 restoreVoiceCall();
624 }
625
626 /* In case the app had set mute when the call wasn't active, make sure we reflect it here */
627 if (m_callstatus != CallEnded)
628 setMicMute(m_micmute);
629}
630
631void QPulseAudioEngineWorker::setMicMute(bool muted)
632{
633 if (!createPulseContext()) {
634 return;
635 }
636
637 m_micmute = muted;
638
639 if (m_callstatus == CallEnded)
640 return;
641
642 pa_threaded_mainloop_lock(m_mainLoop);
643
644 m_nametoset = "";
645 pa_operation *o = pa_context_get_source_info_list(m_context, sourceinfo_cb, this);
646 if (!handleOperation(o, "pa_context_get_source_info_list"))
647 return;
648
649 if (m_nametoset != "") {
650 int m = m_micmute ? 1 : 0;
651 qDebug("Setting PulseAudio source '%s' muted '%d'", m_nametoset.c_str(), m);
652 o = pa_context_set_source_mute_by_name(m_context,
653 m_nametoset.c_str(), m, success_cb, this);
654 if (!handleOperation(o, "pa_context_set_source_mute_by_name"))
655 return;
656 }
657
658 pa_threaded_mainloop_unlock(m_mainLoop);
659}
660
661void QPulseAudioEngineWorker::plugCardCallback(const pa_card_info *info)
662{
663 qDebug("Notified about card (%s) add event from PulseAudio", info->name);
664
665 /* Check if it's indeed a BT device (with at least one hsp profile) */
666 pa_card_profile_info2 *hsp = NULL, *a2dp = NULL;
667 for (int i = 0; i < info->n_profiles; i++) {
668 if (!strcmp(info->profiles2[i]->name, PULSEAUDIO_PROFILE_HSP))
669 hsp = info->profiles2[i];
670 else if (!strcmp(info->profiles2[i]->name, PULSEAUDIO_PROFILE_A2DP) &&
671 info->profiles2[i]->available != 0) {
672 qDebug("Found a2dp");
673 a2dp = info->profiles2[i];
674 }
675 qDebug("%s", info->profiles2[i]->name);
676 }
677
678 if ((!info->active_profile || !strcmp(info->active_profile->name, "off")) && a2dp) {
679 qDebug("No profile set");
680 m_default_bt_card_fallback = info->name;
681 }
682
683 /* We only care about BT (HSP) devices, and if one is not already available */
684 if ((m_callstatus != CallEnded) && ((m_bt_hsp == "") || (m_bt_hsp_a2dp == ""))) {
685 if (hsp)
686 m_handleevent = true;
687 }
688}
689
690void QPulseAudioEngineWorker::updateCardCallback(const pa_card_info *info)
691{
692 qDebug("Notified about card (%s) changes event from PulseAudio", info->name);
693
694 /* Check if it's indeed a BT device (with at least one hsp profile) */
695 pa_card_profile_info2 *hsp = NULL, *a2dp = NULL;
696 for (int i = 0; i < info->n_profiles; i++) {
697 if (!strcmp(info->profiles2[i]->name, PULSEAUDIO_PROFILE_HSP))
698 hsp = info->profiles2[i];
699 else if (!strcmp(info->profiles2[i]->name, PULSEAUDIO_PROFILE_A2DP) &&
700 info->profiles2[i]->available != 0) {
701 qDebug("Found a2dp");
702 a2dp = info->profiles2[i];
703 }
704 qDebug("%s", info->profiles2[i]->name);
705 }
706
707 if ((!info->active_profile || !strcmp(info->active_profile->name, "off")) && a2dp) {
708 qDebug("No profile set");
709 m_default_bt_card_fallback = info->name;
710 }
711
712
713 /* We only care if the card event for the voicecall capable card */
714 if ((m_callstatus == CallActive) && (!strcmp(info->name, m_voicecallcard.c_str()))) {
715 if (m_audiomode == AudioModeWiredHeadset) {
716 /* If previous mode is wired, it means it got unplugged */
717 m_handleevent = true;
718 m_audiomodetoset = AudioModeBtOrWiredOrEarpiece;
719 } else if ((m_audiomode == AudioModeEarpiece) || ((m_audiomode == AudioModeSpeaker))) {
720 /* Now only trigger the event in case wired headset/headphone is now available */
721 pa_card_port_info *port_info = NULL;
722 for (int i = 0; i < info->n_ports; i++) {
723 if (info->ports[i] && (info->ports[i]->available == PA_PORT_AVAILABLE_YES) && (
724 !strcmp(info->ports[i]->name, "output-wired_headset") ||
725 !strcmp(info->ports[i]->name, "output-wired_headphone"))) {
726 m_handleevent = true;
727 m_audiomodetoset = AudioModeWiredOrEarpiece;
728 }
729 }
730 } else if (m_audiomode == AudioModeBluetooth) {
731 /* Handle the event so we can update the audiomodes */
732 m_handleevent = true;
733 m_audiomodetoset = AudioModeBluetooth;
734 }
735 }
736}
737
738void QPulseAudioEngineWorker::unplugCardCallback()
739{
740 if (m_callstatus != CallEnded) {
741 m_handleevent = true;
742 }
743}
744
745void QPulseAudioEngineWorker::handleCardEvent(const int evt, const unsigned int idx)
746{
747 pa_operation *o = NULL;
748
749 /* Internal state var used to know if we need to update our internal state */
750 m_handleevent = false;
751
752 if (evt == PA_SUBSCRIPTION_EVENT_NEW) {
753 o = pa_context_get_card_info_by_index(m_context, idx, plug_card_cb, this);
754 if (!handleOperation(o, "pa_context_get_card_info_by_index"))
755 return;
756
757 if (m_default_bt_card_fallback != "") {
758 o = pa_context_set_card_profile_by_name(m_context,
759 m_default_bt_card_fallback.c_str(), PULSEAUDIO_PROFILE_A2DP, success_cb, this);
760 if (!handleOperation(o, "pa_context_set_card_profile_by_name"))
761 return;
762 m_default_bt_card_fallback = "";
763 }
764
765 if (m_handleevent) {
766 qDebug("Adding new BT-HSP capable device");
767 /* In case A2DP is available, switch to HSP */
768 if (setupVoiceCall() < 0)
769 return;
770 /* Enable the HSP output port */
771 setCallMode(m_callstatus, AudioModeBluetooth);
772 }
773 } else if (evt == PA_SUBSCRIPTION_EVENT_CHANGE) {
774 o = pa_context_get_card_info_by_index(m_context, idx, update_card_cb, this);
775 if (!handleOperation(o, "pa_context_get_card_info_by_index"))
776 return;
777
778 if (m_default_bt_card_fallback != "") {
779 o = pa_context_set_card_profile_by_name(m_context,
780 m_default_bt_card_fallback.c_str(), PULSEAUDIO_PROFILE_A2DP, success_cb, this);
781 if (!handleOperation(o, "pa_context_set_card_profile_by_name"))
782 return;
783 m_default_bt_card_fallback = "";
784 }
785
786 if (m_handleevent) {
787 /* In this case it means the handset state changed */
788 qDebug("Notifying card changes for the voicecall capable card");
789 setCallMode(m_callstatus, m_audiomodetoset);
790 }
791 } else if (evt == PA_SUBSCRIPTION_EVENT_REMOVE) {
792 /* Check if the main HSP card was removed */
793 if (m_bt_hsp != "") {
794 o = pa_context_get_card_info_by_name(m_context, m_bt_hsp.c_str(), unplug_card_cb, this);
795 if (!handleOperation(o, "pa_context_get_sink_info_by_name"))
796 return;
797 }
798 if (m_bt_hsp_a2dp != "") {
799 o = pa_context_get_card_info_by_name(m_context, m_bt_hsp_a2dp.c_str(), unplug_card_cb, this);
800 if (!handleOperation(o, "pa_context_get_sink_info_by_name"))
801 return;
802 }
803 if (m_handleevent) {
804 qDebug("Notifying about BT-HSP card removal");
805 /* Needed in order to save the default sink/source */
806 if (setupVoiceCall() < 0)
807 return;
808 /* Enable the default handset output port */
809 setCallMode(m_callstatus, AudioModeWiredOrEarpiece);
810 }
811 }
812}
813
814Q_GLOBAL_STATIC(QPulseAudioEngine, pulseEngine);
815
816QPulseAudioEngine::QPulseAudioEngine(QObject *parent) :
817 QObject(parent)
818{
819 qRegisterMetaType<CallStatus>();
820 qRegisterMetaType<AudioMode>();
821 qRegisterMetaType<AudioModes>();
822 mWorker = new QPulseAudioEngineWorker();
823 QObject::connect(mWorker, SIGNAL(audioModeChanged(const AudioMode)), this, SIGNAL(audioModeChanged(const AudioMode)), Qt::QueuedConnection);
824 QObject::connect(mWorker, SIGNAL(availableAudioModesChanged(const AudioModes)), this, SIGNAL(availableAudioModesChanged(const AudioModes)), Qt::QueuedConnection);
825 mWorker->createPulseContext();
826 mWorker->moveToThread(&mThread);
827 mThread.start();
828}
829
830QPulseAudioEngine::~QPulseAudioEngine()
831{
832 mThread.quit();
833 mThread.wait();
834}
835
836QPulseAudioEngine *QPulseAudioEngine::instance()
837{
838 QPulseAudioEngine *engine = pulseEngine();
839 return engine;
840}
841
842void QPulseAudioEngine::setCallMode(CallStatus callstatus, AudioMode audiomode)
843{
844 QMetaObject::invokeMethod(mWorker, "setCallMode", Qt::QueuedConnection, Q_ARG(CallStatus, callstatus), Q_ARG(AudioMode, audiomode));
845}
846
847void QPulseAudioEngine::setMicMute(bool muted)
848{
849 QMetaObject::invokeMethod(mWorker, "setMicMute", Qt::QueuedConnection, Q_ARG(bool, muted));
850}
851
852QT_END_NAMESPACE
853
0854
=== added file 'handler/qpulseaudioengine.h'
--- handler/qpulseaudioengine.h 1970-01-01 00:00:00 +0000
+++ handler/qpulseaudioengine.h 2017-04-05 14:05:16 +0000
@@ -0,0 +1,126 @@
1/****************************************************************************
2**
3** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4** Contact: http://www.qt-project.org/legal
5**
6** This file was taken from qt5 and modified by
7** David Henningsson <david.henningsson@canonical.com> for usage in
8** telepathy-ofono.
9**
10** GNU Lesser General Public License Usage
11** Alternatively, this file may be used under the terms of the GNU Lesser
12** General Public License version 2.1 as published by the Free Software
13** Foundation and appearing in the file LICENSE.LGPL included in the
14** packaging of this file. Please review the following information to
15** ensure the GNU Lesser General Public License version 2.1 requirements
16** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17**
18****************************************************************************/
19
20#ifndef QPULSEAUDIOENGINE_H
21#define QPULSEAUDIOENGINE_H
22
23#include <QtCore/qmap.h>
24#include <QtCore/qbytearray.h>
25#include <QThread>
26#include <pulse/pulseaudio.h>
27
28enum AudioMode {
29 AudioModeEarpiece = 0x0001,
30 AudioModeWiredHeadset = 0x0002,
31 AudioModeSpeaker = 0x0004,
32 AudioModeBluetooth = 0x0008,
33 AudioModeBtOrWiredOrEarpiece = AudioModeBluetooth | AudioModeWiredHeadset | AudioModeEarpiece,
34 AudioModeWiredOrEarpiece = AudioModeWiredHeadset | AudioModeEarpiece,
35 AudioModeWiredOrSpeaker = AudioModeWiredHeadset | AudioModeSpeaker,
36 AudioModeBtOrWiredOrSpeaker = AudioModeBluetooth | AudioModeWiredOrSpeaker
37};
38
39Q_DECLARE_METATYPE(AudioMode)
40
41typedef QList<AudioMode> AudioModes;
42Q_DECLARE_METATYPE(AudioModes)
43
44enum CallStatus {
45 CallRinging,
46 CallActive,
47 CallEnded
48};
49
50Q_DECLARE_METATYPE(CallStatus)
51
52QT_BEGIN_NAMESPACE
53
54class QPulseAudioEngineWorker : public QObject
55{
56 Q_OBJECT
57
58public:
59 QPulseAudioEngineWorker(QObject *parent = 0);
60 ~QPulseAudioEngineWorker();
61
62 pa_threaded_mainloop *mainloop() { return m_mainLoop; }
63 pa_context *context() { return m_context; }
64 bool createPulseContext(void);
65 int setupVoiceCall(void);
66 void restoreVoiceCall(void);
67 /* Callbacks to be used internally */
68 void cardInfoCallback(const pa_card_info *card);
69 void sinkInfoCallback(const pa_sink_info *sink);
70 void sourceInfoCallback(const pa_source_info *source);
71 void serverInfoCallback(const pa_server_info *server);
72 void plugCardCallback(const pa_card_info *card);
73 void updateCardCallback(const pa_card_info *card);
74 void unplugCardCallback();
75
76Q_SIGNALS:
77 void audioModeChanged(const AudioMode mode);
78 void availableAudioModesChanged(const AudioModes modes);
79
80public Q_SLOTS:
81 void handleCardEvent(const int evt, const unsigned int idx);
82 void setCallMode(CallStatus callstatus, AudioMode audiomode);
83 void setMicMute(bool muted); /* True if muted, false if unmuted */
84
85private:
86 pa_mainloop_api *m_mainLoopApi;
87 pa_threaded_mainloop *m_mainLoop;
88 pa_context *m_context;
89
90 AudioModes m_availableAudioModes;
91 CallStatus m_callstatus;
92 AudioMode m_audiomode;
93 AudioMode m_audiomodetoset;
94 bool m_micmute, m_handleevent;
95 std::string m_nametoset, m_valuetoset;
96 std::string m_defaultsink, m_defaultsource;
97 std::string m_bt_hsp, m_bt_hsp_a2dp;
98 std::string m_default_bt_card_fallback;
99 std::string m_voicecallcard, m_voicecallhighest, m_voicecallprofile;
100
101 bool handleOperation(pa_operation *operation, const char *func_name);
102 void releasePulseContext(void);
103};
104
105class QPulseAudioEngine : public QObject
106{
107 Q_OBJECT
108public:
109 explicit QPulseAudioEngine(QObject *parent = 0);
110 ~QPulseAudioEngine();
111 static QPulseAudioEngine *instance();
112
113 void setCallMode(CallStatus callstatus, AudioMode audiomode);
114 void setMicMute(bool muted); /* True if muted, false if unmuted */
115
116Q_SIGNALS:
117 void audioModeChanged(const AudioMode mode);
118 void availableAudioModesChanged(const AudioModes modes);
119private:
120 QPulseAudioEngineWorker *mWorker;
121 QThread mThread;
122};
123
124QT_END_NAMESPACE
125
126#endif
0127
=== modified file 'handler/texthandler.cpp'
--- handler/texthandler.cpp 2016-11-23 19:37:30 +0000
+++ handler/texthandler.cpp 2017-04-05 14:05:16 +0000
@@ -35,10 +35,39 @@
3535
36TextHandler::TextHandler(QObject *parent)36TextHandler::TextHandler(QObject *parent)
37: QObject(parent)37: QObject(parent)
38 , mMessagingAppMonitor("com.canonical.MessagingApp", QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForRegistration|QDBusServiceWatcher::WatchForUnregistration), mMessagingAppRegistered(false)
38{39{
39 qDBusRegisterMetaType<AttachmentStruct>();40 qDBusRegisterMetaType<AttachmentStruct>();
40 qDBusRegisterMetaType<AttachmentList>();41 qDBusRegisterMetaType<AttachmentList>();
41 qRegisterMetaType<PendingMessage>();42 qRegisterMetaType<PendingMessage>();
43 connect(&mMessagingAppMonitor, SIGNAL(serviceRegistered(const QString&)), SLOT(onMessagingAppOpen()));
44 connect(&mMessagingAppMonitor, SIGNAL(serviceUnregistered(const QString&)), SLOT(onMessagingAppClosed()));
45 connect(TelepathyHelper::instance(), &TelepathyHelper::accountAdded, [=](AccountEntry *account) {
46 if (mMessagingAppRegistered && !account->active() && account->protocolInfo()->leaveRoomsOnClose()) {
47 account->reconnect();
48 }
49 });
50}
51
52void TextHandler::onMessagingAppOpen()
53{
54 mMessagingAppRegistered = true;
55 Q_FOREACH(AccountEntry *account, TelepathyHelper::instance()->accounts()) {
56 if (!account->active() && account->protocolInfo()->leaveRoomsOnClose()) {
57 account->reconnect();
58 }
59 }
60}
61
62void TextHandler::onMessagingAppClosed()
63{
64 mMessagingAppRegistered = false;
65 Q_FOREACH(AccountEntry *account, TelepathyHelper::instance()->accounts()) {
66 if (account->protocolInfo()->leaveRoomsOnClose()) {
67 account->requestDisconnect();
68 }
69 }
70
42}71}
4372
44TextHandler *TextHandler::instance()73TextHandler *TextHandler::instance()
@@ -168,8 +197,14 @@
168 if (chatType == Tp::HandleTypeNone && targetIds.size() == 1) {197 if (chatType == Tp::HandleTypeNone && targetIds.size() == 1) {
169 chatType = Tp::HandleTypeContact;198 chatType = Tp::HandleTypeContact;
170 }199 }
200
171 QString roomId = properties["threadId"].toString();201 QString roomId = properties["threadId"].toString();
172202
203 // try to use the threadId as participantId if empty
204 if (chatType == Tp::HandleTypeContact && targetIds.isEmpty()) {
205 targetIds << roomId;
206 }
207
173 Q_FOREACH(const Tp::TextChannelPtr &channel, mChannels) {208 Q_FOREACH(const Tp::TextChannelPtr &channel, mChannels) {
174 int count = 0;209 int count = 0;
175 AccountEntry *channelAccount = TelepathyHelper::instance()->accountForConnection(channel->connection());210 AccountEntry *channelAccount = TelepathyHelper::instance()->accountForConnection(channel->connection());
@@ -275,3 +310,15 @@
275 return true;310 return true;
276}311}
277312
313void TextHandler::leaveRooms(const QString &accountId, const QString &message)
314{
315 Q_FOREACH(const Tp::TextChannelPtr &channel, mChannels) {
316 if (channel->targetHandleType() != Tp::HandleTypeRoom) {
317 continue;
318 }
319 AccountEntry *account = TelepathyHelper::instance()->accountForConnection(channel->connection());
320 if (account && account->accountId() == accountId) {
321 leaveChat(channel->objectPath(), message);
322 }
323 }
324}
278325
=== modified file 'handler/texthandler.h'
--- handler/texthandler.h 2016-11-23 19:28:18 +0000
+++ handler/texthandler.h 2017-04-05 14:05:16 +0000
@@ -48,11 +48,15 @@
48 void inviteParticipants(const QString &objectPath, const QStringList &participants, const QString &message);48 void inviteParticipants(const QString &objectPath, const QStringList &participants, const QString &message);
49 void removeParticipants(const QString &objectPath, const QStringList &participants, const QString &message);49 void removeParticipants(const QString &objectPath, const QStringList &participants, const QString &message);
50 bool leaveChat(const QString &objectPath, const QString &message);50 bool leaveChat(const QString &objectPath, const QString &message);
51 void leaveRooms(const QString &accountId, const QString &message);
5152
52protected Q_SLOTS:53protected Q_SLOTS:
53 void onTextChannelAvailable(Tp::TextChannelPtr channel);54 void onTextChannelAvailable(Tp::TextChannelPtr channel);
54 void onTextChannelInvalidated();55 void onTextChannelInvalidated();
5556
57 void onMessagingAppOpen();
58 void onMessagingAppClosed();
59
56protected:60protected:
57 QList<Tp::TextChannelPtr> existingChannels(const QString &accountId, const QVariantMap &properties);61 QList<Tp::TextChannelPtr> existingChannels(const QString &accountId, const QVariantMap &properties);
58 Tp::TextChannelPtr existingChannelFromObjectPath(const QString &objectPath);62 Tp::TextChannelPtr existingChannelFromObjectPath(const QString &objectPath);
@@ -60,6 +64,8 @@
60private:64private:
61 explicit TextHandler(QObject *parent = 0);65 explicit TextHandler(QObject *parent = 0);
62 QList<Tp::TextChannelPtr> mChannels;66 QList<Tp::TextChannelPtr> mChannels;
67 QDBusServiceWatcher mMessagingAppMonitor;
68 bool mMessagingAppRegistered;
63};69};
6470
65#endif // TEXTHANDLER_H71#endif // TEXTHANDLER_H
6672
=== modified file 'indicator/callchannelobserver.cpp'
--- indicator/callchannelobserver.cpp 2014-11-19 21:45:35 +0000
+++ indicator/callchannelobserver.cpp 2017-04-05 14:05:16 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013 Canonical, Ltd.2 * Copyright (C) 2013-2017 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -21,6 +21,7 @@
2121
22#include "callchannelobserver.h"22#include "callchannelobserver.h"
23#include "callnotification.h"23#include "callnotification.h"
24#include "contactwatcher.h"
24#include "messagingmenu.h"25#include "messagingmenu.h"
25#include "metrics.h"26#include "metrics.h"
26#include "telepathyhelper.h"27#include "telepathyhelper.h"
@@ -49,6 +50,13 @@
49 SLOT(onHoldChanged()));50 SLOT(onHoldChanged()));
5051
51 mChannels.append(callChannel);52 mChannels.append(callChannel);
53 if (callChannel->isReady(Tp::CallChannel::FeatureCallState)) {
54 mCallStates[callChannel.data()] = callChannel->callState();
55 } else {
56 connect(callChannel->becomeReady(Tp::CallChannel::FeatureCallState), &Tp::PendingReady::finished, [&](){
57 mCallStates[callChannel.data()] = callChannel->callState();
58 });
59 }
52}60}
5361
54void CallChannelObserver::onCallStateChanged(Tp::CallState state)62void CallChannelObserver::onCallStateChanged(Tp::CallState state)
@@ -70,16 +78,23 @@
70 switch (state) {78 switch (state) {
71 case Tp::CallStateEnded:79 case Tp::CallStateEnded:
72 Q_EMIT callEnded(channel);80 Q_EMIT callEnded(channel);
81
82 // if the missed flag is false, we still have to check if transitioning directly from Initialized to Ended
83 if (!missed && incoming) {
84 missed = mCallStates[channel.data()] == Tp::CallStateInitialised;
85 }
86
73 // add the missed call to the messaging menu87 // add the missed call to the messaging menu
74 if (missed) {88 if (missed) {
75 // FIXME: handle conf call89 // FIXME: handle conf call
76 MessagingMenu::instance()->addCall(channel->targetContact()->id(), accountEntry->accountId(), QDateTime::currentDateTime());90 MessagingMenu::instance()->addCall(ContactWatcher::normalizeIdentifier(channel->targetContact()->id()), accountEntry->accountId(), QDateTime::currentDateTime());
77 } else {91 } else {
78 // and show a notification92 // and show a notification
79 // FIXME: handle conf call93 // FIXME: handle conf call
80 CallNotification::instance()->showNotificationForCall(QStringList() << channel->targetContact()->id(), CallNotification::CallEnded);94 CallNotification::instance()->showNotificationForCall(QStringList() << ContactWatcher::normalizeIdentifier(channel->targetContact()->id()), CallNotification::CallEnded);
81 }95 }
8296
97 mCallStates.remove(channel.data());
83 mChannels.removeOne(channel);98 mChannels.removeOne(channel);
8499
85 // update the metrics100 // update the metrics
@@ -93,6 +108,7 @@
93 channel->setProperty("activeTimestamp", QDateTime::currentDateTime());108 channel->setProperty("activeTimestamp", QDateTime::currentDateTime());
94 break;109 break;
95 }110 }
111 mCallStates[channel.data()] = state;
96}112}
97113
98void CallChannelObserver::onHoldChanged()114void CallChannelObserver::onHoldChanged()
99115
=== modified file 'indicator/callchannelobserver.h'
--- indicator/callchannelobserver.h 2014-01-20 12:57:43 +0000
+++ indicator/callchannelobserver.h 2017-04-05 14:05:16 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013 Canonical, Ltd.2 * Copyright (C) 2013-2017 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -43,6 +43,7 @@
4343
44private:44private:
45 QList<Tp::CallChannelPtr> mChannels;45 QList<Tp::CallChannelPtr> mChannels;
46 QMap<Tp::CallChannel*,Tp::CallState> mCallStates;
46};47};
4748
48#endif // CALLCHANNELOBSERVER_H49#endif // CALLCHANNELOBSERVER_H
4950
=== modified file 'indicator/displaynamesettings.cpp'
--- indicator/displaynamesettings.cpp 2016-03-18 19:02:50 +0000
+++ indicator/displaynamesettings.cpp 2017-04-05 14:05:16 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2016 Canonical, Ltd.2 * Copyright (C) 2016-2017 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -86,7 +86,7 @@
8686
87void DisplayNameSettings::onAccountsChanged()87void DisplayNameSettings::onAccountsChanged()
88{88{
89 Q_FOREACH(AccountEntry *account, TelepathyHelper::instance()->accounts()) {89 Q_FOREACH(AccountEntry *account, TelepathyHelper::instance()->phoneAccounts()) {
90 QString modemObjName = account->account()->parameters().value("modem-objpath").toString();90 QString modemObjName = account->account()->parameters().value("modem-objpath").toString();
91 if (mAccountNames.contains(modemObjName) && account->displayName() != mAccountNames[modemObjName]) {91 if (mAccountNames.contains(modemObjName) && account->displayName() != mAccountNames[modemObjName]) {
92 account->setDisplayName(mAccountNames[modemObjName]);92 account->setDisplayName(mAccountNames[modemObjName]);
9393
=== modified file 'indicator/messagingmenu.cpp'
--- indicator/messagingmenu.cpp 2016-11-23 19:28:18 +0000
+++ indicator/messagingmenu.cpp 2017-04-05 14:05:16 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2012-2016 Canonical, Ltd.2 * Copyright (C) 2012-2017 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -244,6 +244,8 @@
244 file = g_file_new_for_uri(avatar.toString().toUtf8().data());244 file = g_file_new_for_uri(avatar.toString().toUtf8().data());
245 icon = g_file_icon_new(file);245 icon = g_file_icon_new(file);
246 }246 }
247
248 qDebug() << "notify message received:" << notificationData.encodedEventId.toUtf8();
247 MessagingMenuMessage *message = messaging_menu_message_new(notificationData.encodedEventId.toUtf8().data(),249 MessagingMenuMessage *message = messaging_menu_message_new(notificationData.encodedEventId.toUtf8().data(),
248 icon,250 icon,
249 displayLabel.toUtf8().data(),251 displayLabel.toUtf8().data(),
@@ -287,8 +289,9 @@
287 mMessages.remove(messageId);289 mMessages.remove(messageId);
288}290}
289291
290void MessagingMenu::addCallToMessagingMenu(Call call, const QString &text)292void MessagingMenu::addCallToMessagingMenu(Call call, const QString &text, bool supportsTextReply)
291{293{
294 qDebug() << __PRETTY_FUNCTION__;
292 GVariant *messages = NULL;295 GVariant *messages = NULL;
293 GFile *file = g_file_new_for_uri(call.contactIcon.toString().toUtf8().data());296 GFile *file = g_file_new_for_uri(call.contactIcon.toString().toUtf8().data());
294 GIcon *icon = g_file_icon_new(file);297 GIcon *icon = g_file_icon_new(file);
@@ -300,7 +303,7 @@
300 call.timestamp.toMSecsSinceEpoch() * 1000); // the value is expected to be in microseconds303 call.timestamp.toMSecsSinceEpoch() * 1000); // the value is expected to be in microseconds
301304
302 call.messageId = messaging_menu_message_get_id(message);305 call.messageId = messaging_menu_message_get_id(message);
303 if (call.targetId != OFONO_PRIVATE_NUMBER && call.targetId != OFONO_UNKNOWN_NUMBER) {306 if (supportsTextReply && call.targetId != OFONO_PRIVATE_NUMBER && call.targetId != OFONO_UNKNOWN_NUMBER) {
304 messaging_menu_message_add_action(message,307 messaging_menu_message_add_action(message,
305 "callBack",308 "callBack",
306 C::gettext("Call back"), // label309 C::gettext("Call back"), // label
@@ -334,6 +337,7 @@
334337
335void MessagingMenu::addCall(const QString &targetId, const QString &accountId, const QDateTime &timestamp)338void MessagingMenu::addCall(const QString &targetId, const QString &accountId, const QDateTime &timestamp)
336{339{
340 qDebug() << __PRETTY_FUNCTION__;
337 Call call;341 Call call;
338 bool found = false;342 bool found = false;
339 AccountEntry *account = TelepathyHelper::instance()->accountForId(accountId);343 AccountEntry *account = TelepathyHelper::instance()->accountForId(accountId);
@@ -392,7 +396,7 @@
392 // so we just disable it396 // so we just disable it
393#ifndef __aarch64__397#ifndef __aarch64__
394 // place the messaging-menu item only after the contact fetch request is finished, as we can´t simply update398 // place the messaging-menu item only after the contact fetch request is finished, as we can´t simply update
395 QObject::connect(request, &QContactAbstractRequest::stateChanged, [request, call, text, this]() {399 QObject::connect(request, &QContactAbstractRequest::stateChanged, [=]() {
396 // only process the results after the finished state is reached400 // only process the results after the finished state is reached
397 if (request->state() != QContactAbstractRequest::FinishedState) {401 if (request->state() != QContactAbstractRequest::FinishedState) {
398 return;402 return;
@@ -412,19 +416,13 @@
412 newCall.contactIcon = avatar;416 newCall.contactIcon = avatar;
413 }417 }
414 }418 }
415 addCallToMessagingMenu(newCall, text);419 addCallToMessagingMenu(newCall, text, account->protocolInfo()->features() & Protocol::TextChats);
416#ifndef __aarch64__420#ifndef __aarch64__
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to all changes: