blob: c1b32402137e859f39bbcbd847dbe01e7a189a11 [file] [log] [blame]
Sébastien Blin1f915762020-08-03 13:27:42 -04001/*
2 * Copyright (C) 2020 by Savoir-faire Linux
3 * Author: Edric Ladent Milaret <edric.ladent-milaret@savoirfairelinux.com>
4 * Author: Anthony Léonard <anthony.leonard@savoirfairelinux.com>
5 * Author: Olivier Soldano <olivier.soldano@savoirfairelinux.com>
6 * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
7 * Author: Isa Nanic <isa.nanic@savoirfairelinux.com>
8 * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
9 *
10 * This program 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; either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 */
23
24#include "calladapter.h"
25
26#include "globalsystemtray.h"
27#include "utils.h"
28
29CallAdapter::CallAdapter(QObject *parent)
30 : QmlAdapterBase(parent)
31 , oneSecondTimer_(new QTimer(this))
32{}
33
34CallAdapter::~CallAdapter() {}
35
36void
37CallAdapter::initQmlObject()
38{
39 connectCallStatusChanged(LRCInstance::getCurrAccId());
40
41 connect(&LRCInstance::behaviorController(),
42 &BehaviorController::showIncomingCallView,
43 this,
44 &CallAdapter::slotShowIncomingCallView);
45 connect(&LRCInstance::behaviorController(),
46 &BehaviorController::showCallView,
47 this,
48 &CallAdapter::slotShowCallView);
49}
50
51void
52CallAdapter::placeAudioOnlyCall()
53{
54 auto convInfo = LRCInstance::getCurrentConversation();
55 if (!convInfo.uid.isEmpty()) {
56 LRCInstance::getCurrentConversationModel()->placeAudioOnlyCall(convInfo.uid);
57 }
58}
59
60void
61CallAdapter::placeCall()
62{
63 auto convInfo = LRCInstance::getCurrentConversation();
64 if (!convInfo.uid.isEmpty()) {
65 LRCInstance::getCurrentConversationModel()->placeCall(convInfo.uid);
66 }
67}
68
69void
70CallAdapter::hangUpACall(const QString &accountId, const QString &convUid)
71{
72 auto convInfo = LRCInstance::getConversationFromConvUid(convUid, accountId);
73 if (!convInfo.uid.isEmpty()) {
74 LRCInstance::getAccountInfo(accountId).callModel->hangUp(convInfo.callId);
75 }
76}
77
78void
79CallAdapter::refuseACall(const QString &accountId, const QString &convUid)
80{
81 auto convInfo = LRCInstance::getConversationFromConvUid(convUid, accountId);
82 if (!convInfo.uid.isEmpty()) {
83 LRCInstance::getAccountInfo(accountId).callModel->refuse(convInfo.callId);
84 }
85}
86
87void
88CallAdapter::acceptACall(const QString &accountId, const QString &convUid)
89{
90 emit incomingCallNeedToSetupMainView(accountId, convUid);
91 auto convInfo = LRCInstance::getConversationFromConvUid(convUid, accountId);
92 if (!convInfo.uid.isEmpty()) {
93 LRCInstance::getAccountInfo(accountId).callModel->accept(convInfo.callId);
94 auto &accInfo = LRCInstance::getAccountInfo(convInfo.accountId);
95 accInfo.callModel->setCurrentCall(convInfo.callId);
96 }
97}
98
99void
100CallAdapter::slotShowIncomingCallView(const QString &accountId, const conversation::Info &convInfo)
101{
102 auto callModel = LRCInstance::getCurrentCallModel();
103
104 if (!callModel->hasCall(convInfo.callId)) {
105 /*
106 * Connection to close potential incoming call page when it is not current account.
107 */
108 auto &accInfo = LRCInstance::accountModel().getAccountInfo(accountId);
109
110 QObject::disconnect(closeIncomingCallPageConnection_);
111
112 closeIncomingCallPageConnection_
113 = QObject::connect(accInfo.callModel.get(),
114 &lrc::api::NewCallModel::callStatusChanged,
115 [this, accountId, uid = convInfo.uid](const QString &callId) {
116 auto &accInfo = LRCInstance::accountModel().getAccountInfo(
117 accountId);
118 auto &callModel = accInfo.callModel;
119 auto call = callModel->getCall(callId);
120
121 switch (call.status) {
122 case lrc::api::call::Status::INVALID:
123 case lrc::api::call::Status::INACTIVE:
124 case lrc::api::call::Status::ENDED:
125 case lrc::api::call::Status::PEER_BUSY:
126 case lrc::api::call::Status::TIMEOUT:
127 case lrc::api::call::Status::TERMINATING: {
128 if (!uid.isEmpty())
129 emit closePotentialIncomingCallPageWindow(accountId, uid);
130 break;
131 }
132 default:
133 break;
134 }
135
136 emit updateConversationSmartList();
137 QObject::disconnect(closeIncomingCallPageConnection_);
138 });
139 /*
140 * Show incoming call page only.
141 */
142 emit showIncomingCallPage(accountId, convInfo.uid);
143 return;
144 }
145
146 auto call = callModel->getCall(convInfo.callId);
147 auto isCallSelected = LRCInstance::getCurrentConvUid() == convInfo.uid;
148
149 if (call.isOutgoing) {
150 if (isCallSelected) {
151 emit showOutgoingCallPage(accountId, convInfo.uid);
152 emit showCallStack(accountId, convInfo.uid);
153 }
154 } else {
155 auto selectedAccountId = LRCInstance::getCurrentAccountInfo().id;
156 auto accountProperties = LRCInstance::accountModel().getAccountConfig(selectedAccountId);
157 if (accountProperties.autoAnswer) {
158 /*
159 * TODO: Auto answer
160 */
161 } else {
162 emit showIncomingCallPage(accountId, convInfo.uid);
163 }
164 }
165
166 emit callStatusChanged(lrc::api::call::to_string(call.status), accountId, convInfo.uid);
167
168 emit updateConversationSmartList();
169}
170
171void
172CallAdapter::slotShowCallView(const QString &accountId, const lrc::api::conversation::Info &convInfo)
173{
174 updateCall(convInfo.uid, accountId);
175}
176
177void
178CallAdapter::updateCall(const QString &convUid, const QString &accountId, bool forceCallOnly)
179{
180 accountId_ = accountId.isEmpty() ? accountId_ : accountId;
181 convUid_ = convUid.isEmpty() ? convUid_ : convUid;
182
183 auto convInfo = LRCInstance::getConversationFromConvUid(convUid_, accountId_);
184 if (convInfo.uid.isEmpty()) {
185 return;
186 }
187
188 auto call = LRCInstance::getCallInfoForConversation(convInfo, forceCallOnly);
189 if (!call) {
190 return;
191 }
192
193 if (call->isAudioOnly) {
194 emit showAudioCallPage(accountId_, convUid_);
195 } else {
196 emit showVideoCallPage(accountId_, convUid_, call->id);
197 }
198
199 updateCallOverlay(convInfo);
200
201 /*
202 * Preview.
203 */
204 emit previewVisibilityNeedToChange(shouldShowPreview(forceCallOnly));
205
206 emit showCallStack(accountId_, convUid_);
207}
208
209bool
210CallAdapter::shouldShowPreview(bool force)
211{
212 bool shouldShowPreview{false};
213 auto convInfo = LRCInstance::getConversationFromConvUid(convUid_, accountId_);
214 if (convInfo.uid.isEmpty()) {
215 return shouldShowPreview;
216 }
217 auto call = LRCInstance::getCallInfoForConversation(convInfo, force);
218 if (call) {
219 shouldShowPreview = !call->isAudioOnly && !(call->status == lrc::api::call::Status::PAUSED)
220 && !call->videoMuted && call->type != lrc::api::call::Type::CONFERENCE;
221 }
222 return shouldShowPreview;
223}
224
225void
226CallAdapter::connectCallStatusChanged(const QString &accountId)
227{
228 auto &accInfo = LRCInstance::accountModel().getAccountInfo(accountId);
229
230 QObject::disconnect(callStatusChangedConnection_);
231
232 callStatusChangedConnection_ = QObject::connect(
233 accInfo.callModel.get(),
234 &lrc::api::NewCallModel::callStatusChanged,
235 [this, accountId](const QString &callId) {
236 auto &accInfo = LRCInstance::accountModel().getAccountInfo(accountId);
237 auto &callModel = accInfo.callModel;
238 auto call = callModel->getCall(callId);
239
240 /*
241 * Change status label text.
242 */
243 auto convInfo = LRCInstance::getConversationFromCallId(callId);
244 if (!convInfo.uid.isEmpty()) {
245 emit callStatusChanged(lrc::api::call::to_string(call.status),
246 accountId,
247 convInfo.uid);
248 }
249
250 switch (call.status) {
251 case lrc::api::call::Status::INVALID:
252 case lrc::api::call::Status::INACTIVE:
253 case lrc::api::call::Status::ENDED:
254 case lrc::api::call::Status::PEER_BUSY:
255 case lrc::api::call::Status::TIMEOUT:
256 case lrc::api::call::Status::TERMINATING: {
257 LRCInstance::renderer()->removeDistantRenderer(callId);
258 if (convInfo.uid.isEmpty()) {
259 break;
260 }
261 /*
262 * If it's a conference, change the smartlist index
263 * to the next remaining participant.
264 */
265 bool forceCallOnly{false};
266 if (!convInfo.confId.isEmpty()) {
267 auto callList = LRCInstance::getAPI().getConferenceSubcalls(convInfo.confId);
268 if (callList.empty()) {
269 auto lastConferencee = LRCInstance::instance().popLastConferencee(
270 convInfo.confId);
271 callList.append(lastConferencee);
272 forceCallOnly = true;
273 }
274 for (const auto &callId : callList) {
275 if (!callModel->hasCall(callId)) {
276 continue;
277 }
278 auto otherConv = LRCInstance::getConversationFromCallId(callId);
279 if (!otherConv.uid.isEmpty() && otherConv.uid != convInfo.uid) {
280 /*
281 * Reset the call view corresponding accountId, uid.
282 */
283 LRCInstance::setSelectedConvId(otherConv.uid);
284 showCallStack(otherConv.accountId, otherConv.uid, true);
285 updateCall(otherConv.uid, otherConv.accountId, forceCallOnly);
286 }
287 }
288 } else {
289 emit closeCallStack(accountId, convInfo.uid);
290 emit closePotentialIncomingCallPageWindow(accountId, convInfo.uid);
291 }
292
293 break;
294 }
295 case lrc::api::call::Status::CONNECTED:
296 case lrc::api::call::Status::IN_PROGRESS: {
297 auto convInfo = LRCInstance::getConversationFromCallId(callId, accountId);
298 if (!convInfo.uid.isEmpty() && convInfo.uid == LRCInstance::getCurrentConvUid()) {
299 accInfo.conversationModel->selectConversation(convInfo.uid);
300 }
301 LRCInstance::renderer()->addDistantRenderer(callId);
302 updateCall();
303 LRCInstance::getAccountInfo(accountId).callModel->setCurrentCall(callId);
304 break;
305 }
306 case lrc::api::call::Status::PAUSED:
307 updateCall();
308 default:
309 break;
310 }
311
312 emit updateConversationSmartList();
313 });
314}
315
316/*
317 * For Call Overlay
318 */
319void
320CallAdapter::updateCallOverlay(const lrc::api::conversation::Info &convInfo)
321{
322 setTime(accountId_, convUid_);
323 QObject::disconnect(oneSecondTimer_);
324 QObject::connect(oneSecondTimer_, &QTimer::timeout, [this] { setTime(accountId_, convUid_); });
325 oneSecondTimer_->start(20);
326
327 auto &accInfo = LRCInstance::accountModel().getAccountInfo(accountId_);
328
329 auto call = LRCInstance::getCallInfoForConversation(convInfo);
330 if (!call) {
331 return;
332 }
333
334 bool isPaused = call->status == lrc::api::call::Status::PAUSED;
335 bool isAudioOnly = call->isAudioOnly && !isPaused;
336 bool isAudioMuted = call->audioMuted && (call->status != lrc::api::call::Status::PAUSED);
337 bool isVideoMuted = call->videoMuted && !isPaused && !call->isAudioOnly;
338 bool isRecording = accInfo.callModel->isRecording(convInfo.callId);
339
340 emit updateOverlay(isPaused,
341 isAudioOnly,
342 isAudioMuted,
343 isVideoMuted,
344 isRecording,
345 accInfo.profileInfo.type == lrc::api::profile::Type::SIP,
346 !convInfo.confId.isEmpty(),
347 Utils::bestNameForConversation(convInfo,
348 *LRCInstance::getCurrentConversationModel()));
349}
350
351void
352CallAdapter::hangUpThisCall()
353{
354 auto convInfo = LRCInstance::getConversationFromConvUid(convUid_, accountId_);
355 if (!convInfo.uid.isEmpty()) {
356 auto callModel = LRCInstance::getAccountInfo(accountId_).callModel.get();
357 if (callModel->hasCall(convInfo.callId)) {
358 /*
359 * Store the last remaining participant of the conference,
360 * so we can switch the smartlist index after termination.
361 */
362 if (!convInfo.confId.isEmpty()) {
363 auto callList = LRCInstance::getAPI().getConferenceSubcalls(convInfo.confId);
364 if (callList.size() == 2) {
365 for (const auto &cId : callList) {
366 if (cId != convInfo.callId) {
367 LRCInstance::instance().pushLastConferencee(convInfo.confId, cId);
368 }
369 }
370 }
371 }
372 callModel->hangUp(convInfo.callId);
373 }
374 }
375}
376
377void
378CallAdapter::holdThisCallToggle()
379{
380 auto callId = LRCInstance::getCallIdForConversationUid(convUid_, accountId_);
381 if (callId.isEmpty() || !LRCInstance::getCurrentCallModel()->hasCall(callId)) {
382 return;
383 }
384 auto callModel = LRCInstance::getCurrentCallModel();
385 if (callModel->hasCall(callId)) {
386 callModel->togglePause(callId);
387 }
388 emit showOnHoldLabel(true);
389}
390
391void
392CallAdapter::muteThisCallToggle()
393{
394 auto callId = LRCInstance::getCallIdForConversationUid(convUid_, accountId_);
395 if (callId.isEmpty() || !LRCInstance::getCurrentCallModel()->hasCall(callId)) {
396 return;
397 }
398 auto callModel = LRCInstance::getCurrentCallModel();
399 if (callModel->hasCall(callId)) {
400 callModel->toggleMedia(callId, lrc::api::NewCallModel::Media::AUDIO);
401 }
402}
403
404void
405CallAdapter::recordThisCallToggle()
406{
407 auto callId = LRCInstance::getCallIdForConversationUid(convUid_, accountId_);
408 if (callId.isEmpty() || !LRCInstance::getCurrentCallModel()->hasCall(callId)) {
409 return;
410 }
411 auto callModel = LRCInstance::getCurrentCallModel();
412 if (callModel->hasCall(callId)) {
413 callModel->toggleAudioRecord(callId);
414 }
415}
416
417void
418CallAdapter::videoPauseThisCallToggle()
419{
420 auto callId = LRCInstance::getCallIdForConversationUid(convUid_, accountId_);
421 if (callId.isEmpty() || !LRCInstance::getCurrentCallModel()->hasCall(callId)) {
422 return;
423 }
424 auto callModel = LRCInstance::getCurrentCallModel();
425 if (callModel->hasCall(callId)) {
426 callModel->toggleMedia(callId, lrc::api::NewCallModel::Media::VIDEO);
427 }
428 emit previewVisibilityNeedToChange(shouldShowPreview(false));
429}
430
431void
432CallAdapter::setTime(const QString &accountId, const QString &convUid)
433{
434 auto callId = LRCInstance::getCallIdForConversationUid(convUid, accountId);
435 if (callId.isEmpty() || !LRCInstance::getCurrentCallModel()->hasCall(callId)) {
436 return;
437 }
438 auto callInfo = LRCInstance::getCurrentCallModel()->getCall(callId);
439 if (callInfo.status == lrc::api::call::Status::IN_PROGRESS
440 || callInfo.status == lrc::api::call::Status::PAUSED) {
441 auto timeString = LRCInstance::getCurrentCallModel()->getFormattedCallDuration(callId);
442 emit updateTimeText(timeString);
443 }
444}