Music Hub  ..
A session-wide music playback service
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
call_monitor.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2014 Canonical Ltd
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License version 3 as
6  * published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU 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  * Author: Justin McPherson <justin.mcpherson@canonical.com>
17  */
18 
19 
20 #include "call_monitor.h"
21 
22 #include "qtbridge.h"
23 #include <TelepathyQt/AccountManager>
24 #include <TelepathyQt/SimpleCallObserver>
25 #include <TelepathyQt/PendingOperation>
26 #include <TelepathyQt/PendingReady>
27 #include <TelepathyQt/PendingAccount>
28 
29 #include <list>
30 #include <mutex>
31 
32 
33 namespace {
34 class TelepathyCallMonitor : public QObject
35 {
36  Q_OBJECT
37 public:
38  TelepathyCallMonitor(const Tp::AccountPtr& account):
39  mAccount(account),
40  mCallObserver(Tp::SimpleCallObserver::create(mAccount)) {
41  connect(mCallObserver.data(), SIGNAL(callStarted(Tp::CallChannelPtr)), SIGNAL(offHook()));
42  connect(mCallObserver.data(), SIGNAL(callEnded(Tp::CallChannelPtr,QString,QString)), SIGNAL(onHook()));
43  connect(mCallObserver.data(), SIGNAL(streamedMediaCallStarted(Tp::StreamedMediaChannelPtr)), SIGNAL(offHook()));
44  connect(mCallObserver.data(), SIGNAL(streamedMediaCallEnded(Tp::StreamedMediaChannelPtr,QString,QString)), SIGNAL(onHook()));
45  }
46 
47 Q_SIGNALS:
48  void offHook();
49  void onHook();
50 
51 private:
52  Tp::AccountPtr mAccount;
53  Tp::SimpleCallObserverPtr mCallObserver;
54 };
55 
56 
57 class TelepathyBridge : public QObject
58 {
59  Q_OBJECT
60 public:
61  TelepathyBridge():
62  QObject(0) {
63  Tp::registerTypes();
64 
65  QTimer::singleShot(0, this, SLOT(accountManagerSetup()));
66  }
67 
68  ~TelepathyBridge() {
69  for (std::list<TelepathyCallMonitor*>::iterator it = mCallMonitors.begin();
70  it != mCallMonitors.end();
71  ++it) {
72  delete *it;
73  }
74  }
75 
76  void on_change(const std::function<void(CallMonitor::State)>& func) {
77  std::lock_guard<std::mutex> l(cb_lock);
78  cb = func;
79  }
80 
81 private Q_SLOTS:
82  void accountManagerSetup() {
83  mAccountManager = Tp::AccountManager::create();
84  connect(mAccountManager->becomeReady(),
85  SIGNAL(finished(Tp::PendingOperation*)),
86  SLOT(accountManagerReady(Tp::PendingOperation*)));
87  }
88 
89  void accountManagerReady(Tp::PendingOperation* operation) {
90  if (operation->isError()) {
91  std::cerr << "TelepathyBridge: Operation failed (accountManagerReady)" << std::endl;
92  QTimer::singleShot(1000, this, SLOT(accountManagerSetup())); // again
93  return;
94  }
95 
96  Q_FOREACH(const Tp::AccountPtr& account, mAccountManager->allAccounts()) {
97  connect(account->becomeReady(Tp::Account::FeatureCapabilities),
98  SIGNAL(finished(Tp::PendingOperation*)),
99  SLOT(accountReady(Tp::PendingOperation*)));
100  }
101 
102  connect(mAccountManager.data(), SIGNAL(newAccount(Tp::AccountPtr)), SLOT(newAccount(Tp::AccountPtr)));
103  }
104 
105  void newAccount(const Tp::AccountPtr& account)
106  {
107  connect(account->becomeReady(Tp::Account::FeatureCapabilities),
108  SIGNAL(finished(Tp::PendingOperation*)),
109  SLOT(accountReady(Tp::PendingOperation*)));
110  }
111 
112  void accountReady(Tp::PendingOperation* operation) {
113  if (operation->isError()) {
114  std::cerr << "TelepathyAccount: Operation failed (accountReady)" << std::endl;
115  return;
116  }
117 
118  Tp::PendingReady* pendingReady = qobject_cast<Tp::PendingReady*>(operation);
119  if (pendingReady == 0) {
120  std::cerr << "Rejecting account because could not understand ready status" << std::endl;
121  return;
122  }
123 
124  checkAndAddAccount(Tp::AccountPtr::qObjectCast(pendingReady->proxy()));
125  }
126 
127  void offHook()
128  {
129  std::lock_guard<std::mutex> l(cb_lock);
130  if (cb)
132  }
133 
134  void onHook()
135  {
136  std::lock_guard<std::mutex> l(cb_lock);
137  if (cb)
139  }
140 
141 private:
142  std::mutex cb_lock;
143  std::function<void (CallMonitor::State)> cb;
144  Tp::AccountManagerPtr mAccountManager;
145  std::list<TelepathyCallMonitor*> mCallMonitors;
146 
147  void checkAndAddAccount(const Tp::AccountPtr& account)
148  {
149  Tp::ConnectionCapabilities caps = account->capabilities();
150 
151  // anything call like, perhaps overkill?
152  if (caps.audioCalls() || caps.videoCalls() || caps.videoCallsWithAudio() || caps.streamedMediaCalls()) {
153  auto tcm = new TelepathyCallMonitor(account);
154  connect(tcm, SIGNAL(offHook()), SLOT(offHook()));
155  connect(tcm, SIGNAL(onHook()), SLOT(onHook()));
156  mCallMonitors.push_back(tcm);
157  }
158  }
159 };
160 }
161 
163 {
164 public:
166  try {
167  std::thread([this]() {
168  qt::core::world::build_and_run(0, nullptr, [this]() {
170  mBridge = new TelepathyBridge();
171  });
172  });
173  }).detach();
174  } catch(const std::system_error& error) {
175  std::cerr << "exception(std::system_error) in CallMonitor thread start" << error.what() << std::endl;
176  } catch(...) {
177  std::cerr << "exception(...) in CallMonitor thread start" << std::endl;
178  }
179  }
180 
183  }
184 
185  TelepathyBridge *mBridge;
186 };
187 
188 
190  d(new CallMonitorPrivate)
191 {
192 }
193 
195 {
196  delete d->mBridge;
197  delete d;
198 }
199 
200 void CallMonitor::on_change(const std::function<void(CallMonitor::State)>& func)
201 {
202  d->mBridge->on_change(func);
203 }
204 
205 #include "call_monitor.moc"
206 
void destroy()
Destroys the Qt core world and quits its event loop.
Definition: qtbridge.cpp:154
void build_and_run(int argc, char **argv, const std::function< void()> &ready)
Sets up the Qt core world and executes its event loop. Blocks until destroy() is called.
Definition: qtbridge.cpp:132
void on_change(const std::function< void(CallMonitor::State)> &func)
TelepathyBridge * mBridge
std::future< void > enter_with_task(const std::function< void()> &task)
Enters the Qt core world and schedules the given task for execution.
Definition: qtbridge.cpp:163