mirror of
https://git.jami.net/savoirfairelinux/jami-client-qt.git
synced 2025-07-13 20:15:23 +02:00
978 lines
35 KiB
C++
978 lines
35 KiB
C++
/*
|
|
* Copyright (C) 2020 by Savoir-faire Linux
|
|
* Author: Edric Ladent Milaret <edric.ladent-milaret@savoirfairelinux.com>
|
|
* Author: Anthony Léonard <anthony.leonard@savoirfairelinux.com>
|
|
* Author: Olivier Soldano <olivier.soldano@savoirfairelinux.com>
|
|
* Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
|
|
* Author: Isa Nanic <isa.nanic@savoirfairelinux.com>
|
|
* Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
|
|
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "calladapter.h"
|
|
|
|
#include "systemtray.h"
|
|
#include "utils.h"
|
|
#include "qmlregister.h"
|
|
|
|
#include <QApplication>
|
|
#include <QTimer>
|
|
#include <QJsonObject>
|
|
|
|
CallAdapter::CallAdapter(SystemTray* systemTray, LRCInstance* instance, QObject* parent)
|
|
: QmlAdapterBase(instance, parent)
|
|
, systemTray_(systemTray)
|
|
{
|
|
overlayModel_.reset(new CallOverlayModel(lrcInstance_, this));
|
|
QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, overlayModel_.get(), "CallOverlayModel");
|
|
|
|
accountId_ = lrcInstance_->getCurrentAccountId();
|
|
if (!accountId_.isEmpty())
|
|
connectCallModel(accountId_);
|
|
|
|
connect(&lrcInstance_->behaviorController(),
|
|
&BehaviorController::showIncomingCallView,
|
|
this,
|
|
&CallAdapter::onShowIncomingCallView);
|
|
|
|
connect(&lrcInstance_->behaviorController(),
|
|
&BehaviorController::showCallView,
|
|
this,
|
|
&CallAdapter::onShowCallView);
|
|
|
|
connect(lrcInstance_,
|
|
&LRCInstance::currentAccountIdChanged,
|
|
this,
|
|
&CallAdapter::onAccountChanged);
|
|
|
|
#ifdef Q_OS_LINUX
|
|
// notification responses (gnu/linux currently)
|
|
connect(systemTray_,
|
|
&SystemTray::answerCallActivated,
|
|
[this](const QString& accountId, const QString& convUid) {
|
|
acceptACall(accountId, convUid);
|
|
Q_EMIT lrcInstance_->notificationClicked();
|
|
lrcInstance_->selectConversation(convUid, accountId);
|
|
updateCall(convUid, accountId);
|
|
Q_EMIT lrcInstance_->conversationUpdated(convUid, accountId);
|
|
});
|
|
connect(systemTray_,
|
|
&SystemTray::declineCallActivated,
|
|
[this](const QString& accountId, const QString& convUid) {
|
|
hangUpACall(accountId, convUid);
|
|
});
|
|
#endif
|
|
|
|
connect(&lrcInstance_->behaviorController(),
|
|
&BehaviorController::callStatusChanged,
|
|
this,
|
|
QOverload<const QString&, const QString&>::of(&CallAdapter::onCallStatusChanged));
|
|
}
|
|
|
|
void
|
|
CallAdapter::onAccountChanged()
|
|
{
|
|
accountId_ = lrcInstance_->getCurrentAccountId();
|
|
connectCallModel(accountId_);
|
|
}
|
|
|
|
void
|
|
CallAdapter::onCallStatusChanged(const QString& accountId, const QString& callId)
|
|
{
|
|
set_hasCall(lrcInstance_->hasActiveCall());
|
|
|
|
#ifdef Q_OS_LINUX
|
|
auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId);
|
|
auto& callModel = accInfo.callModel;
|
|
const auto call = callModel->getCall(callId);
|
|
|
|
const auto& convInfo = lrcInstance_->getConversationFromCallId(callId, accountId);
|
|
if (convInfo.uid.isEmpty())
|
|
return;
|
|
|
|
// handle notifications
|
|
if (call.status == lrc::api::call::Status::IN_PROGRESS) {
|
|
// Call answered and in progress; close the notification
|
|
systemTray_->hideNotification(QString("%1;%2").arg(accountId).arg(convInfo.uid));
|
|
} else if (call.status == lrc::api::call::Status::ENDED) {
|
|
// Call ended; close the notification
|
|
if (systemTray_->hideNotification(QString("%1;%2").arg(accountId).arg(convInfo.uid))
|
|
&& call.startTime.time_since_epoch().count() == 0) {
|
|
// This was a missed call; show a missed call notification
|
|
// TODO: swarmify(avoid using convInfo.participants[0])
|
|
auto contactPhoto = Utils::contactPhoto(lrcInstance_,
|
|
convInfo.participants[0],
|
|
QSize(50, 50),
|
|
accountId);
|
|
auto& accInfo = lrcInstance_->getAccountInfo(accountId);
|
|
auto from = accInfo.contactModel->bestNameForContact(convInfo.participants[0]);
|
|
auto notifId = QString("%1;%2").arg(accountId).arg(convInfo.uid);
|
|
systemTray_->showNotification(notifId,
|
|
tr("Missed call"),
|
|
tr("Missed call from %1").arg(from),
|
|
NotificationType::CHAT,
|
|
Utils::QImageToByteArray(contactPhoto));
|
|
}
|
|
}
|
|
#else
|
|
Q_UNUSED(accountId)
|
|
Q_UNUSED(callId)
|
|
#endif
|
|
}
|
|
|
|
void
|
|
CallAdapter::onParticipantsChanged(const QString& confId)
|
|
{
|
|
auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId_);
|
|
auto& callModel = accInfo.callModel;
|
|
try {
|
|
auto call = callModel->getCall(confId);
|
|
const auto& convInfo = lrcInstance_->getConversationFromCallId(confId);
|
|
if (!convInfo.uid.isEmpty()) {
|
|
QVariantList map;
|
|
for (const auto& participant : call.participantsInfos) {
|
|
QJsonObject data = fillParticipantData(participant);
|
|
map.push_back(QVariant(data));
|
|
updateCallOverlay(convInfo);
|
|
}
|
|
Q_EMIT updateParticipantsInfos(map, accountId_, confId);
|
|
}
|
|
} catch (...) {
|
|
}
|
|
}
|
|
|
|
void
|
|
CallAdapter::onCallStatusChanged(const QString& callId, int code)
|
|
{
|
|
Q_UNUSED(code)
|
|
|
|
auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId_);
|
|
auto& callModel = accInfo.callModel;
|
|
|
|
try {
|
|
const auto call = callModel->getCall(callId);
|
|
/*
|
|
* Change status label text.
|
|
*/
|
|
const auto& convInfo = lrcInstance_->getConversationFromCallId(callId);
|
|
if (!convInfo.uid.isEmpty()) {
|
|
Q_EMIT callStatusChanged(static_cast<int>(call.status), accountId_, convInfo.uid);
|
|
updateCallOverlay(convInfo);
|
|
}
|
|
|
|
switch (call.status) {
|
|
case lrc::api::call::Status::INVALID:
|
|
case lrc::api::call::Status::INACTIVE:
|
|
case lrc::api::call::Status::ENDED:
|
|
case lrc::api::call::Status::PEER_BUSY:
|
|
case lrc::api::call::Status::TIMEOUT:
|
|
case lrc::api::call::Status::TERMINATING: {
|
|
lrcInstance_->renderer()->removeDistantRenderer(callId);
|
|
Q_EMIT lrcInstance_->conversationUpdated(convInfo.uid, accountId_);
|
|
if (convInfo.uid.isEmpty()) {
|
|
break;
|
|
}
|
|
/*
|
|
* If it's a conference, change the smartlist index
|
|
* to the next remaining participant.
|
|
*/
|
|
bool forceCallOnly {false};
|
|
if (!convInfo.confId.isEmpty()) {
|
|
auto callList = lrcInstance_->getConferenceSubcalls(convInfo.confId);
|
|
if (callList.empty()) {
|
|
auto lastConference = lrcInstance_->poplastConference(convInfo.confId);
|
|
if (!lastConference.isEmpty()) {
|
|
callList.append(lastConference);
|
|
forceCallOnly = true;
|
|
}
|
|
}
|
|
if (callList.isEmpty()) {
|
|
callList = lrcInstance_->getActiveCalls();
|
|
forceCallOnly = true;
|
|
}
|
|
for (const auto& callId : callList) {
|
|
if (!callModel->hasCall(callId)) {
|
|
continue;
|
|
}
|
|
auto currentCall = callModel->getCall(callId);
|
|
if (currentCall.status == lrc::api::call::Status::IN_PROGRESS) {
|
|
const auto& otherConv = lrcInstance_->getConversationFromCallId(callId);
|
|
if (!otherConv.uid.isEmpty() && otherConv.uid != convInfo.uid) {
|
|
/*
|
|
* Reset the call view corresponding accountId, uid.
|
|
*/
|
|
lrcInstance_->selectConversation(otherConv.uid);
|
|
updateCall(otherConv.uid, otherConv.accountId, forceCallOnly);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
preventScreenSaver(false);
|
|
break;
|
|
}
|
|
case lrc::api::call::Status::CONNECTED:
|
|
case lrc::api::call::Status::IN_PROGRESS: {
|
|
const auto& convInfo = lrcInstance_->getConversationFromCallId(callId, accountId_);
|
|
if (!convInfo.uid.isEmpty() && convInfo.uid == lrcInstance_->get_selectedConvUid()) {
|
|
accInfo.conversationModel->selectConversation(convInfo.uid);
|
|
}
|
|
updateCall(convInfo.uid, accountId_);
|
|
preventScreenSaver(true);
|
|
break;
|
|
}
|
|
case lrc::api::call::Status::PAUSED:
|
|
updateCall();
|
|
default:
|
|
break;
|
|
}
|
|
} catch (...) {
|
|
}
|
|
}
|
|
|
|
void
|
|
CallAdapter::onRemoteRecordingChanged(const QString& callId,
|
|
const QSet<QString>& peerRec,
|
|
bool state)
|
|
{
|
|
Q_UNUSED(peerRec)
|
|
Q_UNUSED(state)
|
|
const auto currentCallId = lrcInstance_->getCallIdForConversationUid(convUid_, accountId_);
|
|
if (callId == currentCallId)
|
|
updateRecordingPeers();
|
|
}
|
|
|
|
void
|
|
CallAdapter::placeAudioOnlyCall()
|
|
{
|
|
const auto convUid = lrcInstance_->get_selectedConvUid();
|
|
if (!convUid.isEmpty()) {
|
|
lrcInstance_->getCurrentConversationModel()->placeAudioOnlyCall(convUid);
|
|
}
|
|
}
|
|
|
|
void
|
|
CallAdapter::placeCall()
|
|
{
|
|
const auto convUid = lrcInstance_->get_selectedConvUid();
|
|
if (!convUid.isEmpty()) {
|
|
lrcInstance_->getCurrentConversationModel()->placeCall(convUid);
|
|
}
|
|
}
|
|
|
|
void
|
|
CallAdapter::hangUpACall(const QString& accountId, const QString& convUid)
|
|
{
|
|
const auto& convInfo = lrcInstance_->getConversationFromConvUid(convUid, accountId);
|
|
if (!convInfo.uid.isEmpty()) {
|
|
lrcInstance_->getAccountInfo(accountId).callModel->hangUp(convInfo.callId);
|
|
}
|
|
}
|
|
|
|
void
|
|
CallAdapter::acceptACall(const QString& accountId, const QString& convUid)
|
|
{
|
|
const auto& convInfo = lrcInstance_->getConversationFromConvUid(convUid, accountId);
|
|
if (!convInfo.uid.isEmpty()) {
|
|
lrcInstance_->getAccountInfo(accountId).callModel->accept(convInfo.callId);
|
|
auto& accInfo = lrcInstance_->getAccountInfo(convInfo.accountId);
|
|
accInfo.callModel->setCurrentCall(convInfo.callId);
|
|
|
|
auto contactUri = convInfo.participants.front();
|
|
if (contactUri.isEmpty()) {
|
|
return;
|
|
}
|
|
try {
|
|
auto& contact = accInfo.contactModel->getContact(contactUri);
|
|
if (contact.profileInfo.type == lrc::api::profile::Type::PENDING) {
|
|
lrcInstance_->getCurrentConversationModel()->makePermanent(convInfo.uid);
|
|
}
|
|
} catch (...) {
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
CallAdapter::onShowIncomingCallView(const QString& accountId, const QString& convUid)
|
|
{
|
|
const auto& convInfo = lrcInstance_->getConversationFromConvUid(convUid, accountId);
|
|
if (convInfo.uid.isEmpty()) {
|
|
return;
|
|
}
|
|
auto selectedAccountId = lrcInstance_->getCurrentAccountId();
|
|
auto* callModel = lrcInstance_->getCurrentCallModel();
|
|
|
|
// new call
|
|
if (!callModel->hasCall(convInfo.callId)) {
|
|
if (QApplication::focusObject() == nullptr || accountId != selectedAccountId) {
|
|
showNotification(accountId, convInfo.uid);
|
|
return;
|
|
}
|
|
|
|
const auto& currentConvInfo = lrcInstance_->getConversationFromConvUid(
|
|
lrcInstance_->get_selectedConvUid());
|
|
|
|
// Current call
|
|
auto currentConvHasCall = callModel->hasCall(currentConvInfo.callId);
|
|
if (currentConvHasCall) {
|
|
auto currentCall = callModel->getCall(currentConvInfo.callId);
|
|
if (currentCall.status == lrc::api::call::Status::CONNECTED
|
|
|| currentCall.status == lrc::api::call::Status::IN_PROGRESS) {
|
|
showNotification(accountId, convInfo.uid);
|
|
return;
|
|
}
|
|
}
|
|
// select
|
|
lrcInstance_->selectConversation(convInfo.uid, accountId);
|
|
return;
|
|
}
|
|
|
|
// this slot has been triggered as a result of either selecting a conversation
|
|
// with an active call, placing a call, or an incoming call for the current
|
|
// or any other conversation
|
|
auto call = callModel->getCall(convInfo.callId);
|
|
auto isCallSelected = lrcInstance_->get_selectedConvUid() == convInfo.uid;
|
|
|
|
if (call.isOutgoing) {
|
|
if (isCallSelected) {
|
|
// don't reselect
|
|
// TODO: this signal can be renamed to conversationReselected,
|
|
// isCallSelected and any other similar logic can be removed
|
|
// and calling selectConversation should be sufficient
|
|
Q_EMIT lrcInstance_->conversationUpdated(convInfo.uid, accountId);
|
|
}
|
|
} else {
|
|
auto accountProperties = lrcInstance_->accountModel().getAccountConfig(selectedAccountId);
|
|
if (!accountProperties.isRendezVous) {
|
|
// App not focused or in different account
|
|
if (QApplication::focusObject() == nullptr || accountId != selectedAccountId) {
|
|
showNotification(accountId, convInfo.uid);
|
|
return;
|
|
}
|
|
|
|
const auto& currentConvInfo = lrcInstance_->getConversationFromConvUid(
|
|
lrcInstance_->get_selectedConvUid());
|
|
|
|
// Call in current conversation
|
|
auto currentConvHasCall = callModel->hasCall(currentConvInfo.callId);
|
|
|
|
// Check INCOMING / OUTGOING call in current conversation
|
|
if (isCallSelected) {
|
|
if (currentConvHasCall) {
|
|
auto currentCall = callModel->getCall(currentConvInfo.callId);
|
|
if (currentCall.status == lrc::api::call::Status::OUTGOING_RINGING) {
|
|
showNotification(accountId, convInfo.uid);
|
|
return;
|
|
} else {
|
|
// only update
|
|
Q_EMIT lrcInstance_->conversationUpdated(convInfo.uid, accountId);
|
|
}
|
|
} else {
|
|
// only update
|
|
Q_EMIT lrcInstance_->conversationUpdated(convInfo.uid, accountId);
|
|
}
|
|
} else { // Not current conversation
|
|
if (currentConvHasCall) {
|
|
auto currentCall = callModel->getCall(currentConvInfo.callId);
|
|
if ((currentCall.status == lrc::api::call::Status::CONNECTED
|
|
|| currentCall.status == lrc::api::call::Status::IN_PROGRESS)
|
|
&& !accountProperties.autoAnswer) {
|
|
showNotification(accountId, convInfo.uid);
|
|
return;
|
|
}
|
|
}
|
|
// reselect
|
|
lrcInstance_->selectConversation(convInfo.uid, accountId);
|
|
}
|
|
}
|
|
}
|
|
Q_EMIT callStatusChanged(static_cast<int>(call.status), accountId, convInfo.uid);
|
|
}
|
|
|
|
void
|
|
CallAdapter::onShowCallView(const QString& accountId, const QString& convUid)
|
|
{
|
|
updateCall(convUid, accountId);
|
|
Q_EMIT lrcInstance_->conversationUpdated(convUid, accountId);
|
|
}
|
|
|
|
void
|
|
CallAdapter::updateCall(const QString& convUid, const QString& accountId, bool forceCallOnly)
|
|
{
|
|
accountId_ = accountId.isEmpty() ? accountId_ : accountId;
|
|
convUid_ = convUid.isEmpty() ? convUid_ : convUid;
|
|
|
|
const auto& convInfo = lrcInstance_->getConversationFromConvUid(convUid_);
|
|
if (convInfo.uid.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
auto call = lrcInstance_->getCallInfoForConversation(convInfo, forceCallOnly);
|
|
if (!call) {
|
|
return;
|
|
}
|
|
|
|
if (convInfo.uid == lrcInstance_->get_selectedConvUid()) {
|
|
auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId_);
|
|
if (accInfo.profileInfo.type != lrc::api::profile::Type::SIP)
|
|
accInfo.callModel->setCurrentCall(call->id);
|
|
lrcInstance_->renderer()->addDistantRenderer(call->id);
|
|
}
|
|
|
|
updateCallOverlay(convInfo);
|
|
updateRecordingPeers(true);
|
|
Q_EMIT previewVisibilityNeedToChange(shouldShowPreview(forceCallOnly));
|
|
}
|
|
|
|
bool
|
|
CallAdapter::shouldShowPreview(bool force)
|
|
{
|
|
bool shouldShowPreview {false};
|
|
const auto& convInfo = lrcInstance_->getConversationFromConvUid(convUid_);
|
|
|
|
if (convInfo.uid.isEmpty()) {
|
|
return shouldShowPreview;
|
|
}
|
|
auto call = lrcInstance_->getCallInfoForConversation(convInfo, force);
|
|
if (call) {
|
|
shouldShowPreview = !call->isAudioOnly && !(call->status == lrc::api::call::Status::PAUSED)
|
|
&& !call->videoMuted && call->participantsInfos.isEmpty();
|
|
}
|
|
return shouldShowPreview;
|
|
}
|
|
|
|
QJsonObject
|
|
CallAdapter::fillParticipantData(QMap<QString, QString> participant)
|
|
{
|
|
QJsonObject data;
|
|
data["x"] = participant["x"].toInt();
|
|
data["y"] = participant["y"].toInt();
|
|
data["w"] = participant["w"].toInt();
|
|
data["h"] = participant["h"].toInt();
|
|
data["uri"] = participant["uri"];
|
|
data["active"] = participant["active"] == "true";
|
|
data["videoMuted"] = participant["videoMuted"] == "true";
|
|
data["audioLocalMuted"] = participant["audioLocalMuted"] == "true";
|
|
data["audioModeratorMuted"] = participant["audioModeratorMuted"] == "true";
|
|
data["isContact"] = false;
|
|
|
|
auto bestName = participant["uri"];
|
|
auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId_);
|
|
data["isLocal"] = false;
|
|
if (bestName == accInfo.profileInfo.uri) {
|
|
bestName = tr("me");
|
|
data["isLocal"] = true;
|
|
if (participant["videoMuted"] == "true")
|
|
data["avatar"] = accInfo.profileInfo.avatar;
|
|
} else {
|
|
try {
|
|
auto& contact = lrcInstance_->getCurrentAccountInfo().contactModel->getContact(
|
|
participant["uri"]);
|
|
bestName = lrcInstance_->getCurrentAccountInfo().contactModel->bestNameForContact(
|
|
participant["uri"]);
|
|
if (participant["videoMuted"] == "true")
|
|
data["avatar"] = contact.profileInfo.avatar;
|
|
data["isContact"] = true;
|
|
} catch (...) {
|
|
}
|
|
}
|
|
data["bestName"] = bestName;
|
|
|
|
return data;
|
|
}
|
|
|
|
QVariantList
|
|
CallAdapter::getConferencesInfos()
|
|
{
|
|
QVariantList map;
|
|
const auto& convInfo = lrcInstance_->getConversationFromConvUid(convUid_);
|
|
if (convInfo.uid.isEmpty())
|
|
return map;
|
|
auto callId = convInfo.confId.isEmpty() ? convInfo.callId : convInfo.confId;
|
|
if (!callId.isEmpty()) {
|
|
try {
|
|
auto call = lrcInstance_->getCurrentCallModel()->getCall(callId);
|
|
for (const auto& participant : call.participantsInfos) {
|
|
QJsonObject data = fillParticipantData(participant);
|
|
map.push_back(QVariant(data));
|
|
}
|
|
return map;
|
|
} catch (...) {
|
|
}
|
|
}
|
|
return map;
|
|
}
|
|
|
|
void
|
|
CallAdapter::showNotification(const QString& accountId, const QString& convUid)
|
|
{
|
|
QString from {};
|
|
const auto& convInfo = lrcInstance_->getConversationFromConvUid(convUid, accountId);
|
|
if (!accountId.isEmpty() && !convInfo.uid.isEmpty()) {
|
|
auto& accInfo = lrcInstance_->getAccountInfo(accountId);
|
|
if (!convInfo.participants.isEmpty())
|
|
from = accInfo.contactModel->bestNameForContact(convInfo.participants[0]);
|
|
}
|
|
|
|
#ifdef Q_OS_LINUX
|
|
auto contactPhoto = Utils::contactPhoto(lrcInstance_,
|
|
convInfo.participants[0],
|
|
QSize(50, 50),
|
|
accountId);
|
|
auto notifId = QString("%1;%2").arg(accountId).arg(convUid);
|
|
systemTray_->showNotification(notifId,
|
|
tr("Incoming call"),
|
|
tr("%1 is calling you").arg(from),
|
|
NotificationType::CALL,
|
|
Utils::QImageToByteArray(contactPhoto));
|
|
#else
|
|
auto onClicked = [this, accountId, convUid = convInfo.uid]() {
|
|
Q_EMIT lrcInstance_->notificationClicked();
|
|
const auto& convInfo = lrcInstance_->getConversationFromConvUid(convUid, accountId);
|
|
if (convInfo.uid.isEmpty())
|
|
return;
|
|
lrcInstance_->selectConversation(convInfo.uid, accountId);
|
|
};
|
|
systemTray_->showNotification(tr("is calling you"), from, onClicked);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
CallAdapter::connectCallModel(const QString& accountId)
|
|
{
|
|
auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId);
|
|
|
|
connect(accInfo.callModel.get(),
|
|
&NewCallModel::onParticipantsChanged,
|
|
this,
|
|
&CallAdapter::onParticipantsChanged,
|
|
Qt::UniqueConnection);
|
|
|
|
connect(accInfo.callModel.get(),
|
|
&NewCallModel::callStatusChanged,
|
|
this,
|
|
QOverload<const QString&, int>::of(&CallAdapter::onCallStatusChanged),
|
|
Qt::UniqueConnection);
|
|
|
|
connect(accInfo.callModel.get(),
|
|
&NewCallModel::remoteRecordingChanged,
|
|
this,
|
|
&CallAdapter::onRemoteRecordingChanged,
|
|
Qt::UniqueConnection);
|
|
}
|
|
|
|
void
|
|
CallAdapter::updateRecordingPeers(bool eraseLabelOnEmpty)
|
|
{
|
|
const auto& convInfo = lrcInstance_->getConversationFromConvUid(convUid_);
|
|
auto* call = lrcInstance_->getCallInfoForConversation(convInfo);
|
|
if (!call) {
|
|
return;
|
|
}
|
|
|
|
const auto& accInfo = lrcInstance_->getCurrentAccountInfo();
|
|
QStringList peers {};
|
|
for (const auto& uri : call->peerRec) {
|
|
auto bestName = accInfo.contactModel->bestNameForContact(uri);
|
|
if (!bestName.isEmpty()) {
|
|
peers.append(bestName);
|
|
}
|
|
}
|
|
if (!peers.isEmpty())
|
|
Q_EMIT remoteRecordingChanged(peers, true);
|
|
else if (eraseLabelOnEmpty)
|
|
Q_EMIT eraseRemoteRecording();
|
|
else
|
|
Q_EMIT remoteRecordingChanged(peers, false);
|
|
}
|
|
|
|
void
|
|
CallAdapter::sipInputPanelPlayDTMF(const QString& key)
|
|
{
|
|
auto callId = lrcInstance_->getCallIdForConversationUid(convUid_, accountId_);
|
|
if (callId.isEmpty() || !lrcInstance_->getCurrentCallModel()->hasCall(callId)) {
|
|
return;
|
|
}
|
|
|
|
lrcInstance_->getCurrentCallModel()->playDTMF(callId, key);
|
|
}
|
|
|
|
/*
|
|
* For Call Overlay
|
|
*/
|
|
void
|
|
CallAdapter::updateCallOverlay(const lrc::api::conversation::Info& convInfo)
|
|
{
|
|
auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId_);
|
|
|
|
auto* call = lrcInstance_->getCallInfoForConversation(convInfo);
|
|
if (!call) {
|
|
return;
|
|
}
|
|
|
|
bool isPaused = call->status == lrc::api::call::Status::PAUSED;
|
|
bool isAudioOnly = call->isAudioOnly && !isPaused;
|
|
bool isAudioMuted = call->audioMuted && (call->status != lrc::api::call::Status::PAUSED);
|
|
bool isVideoMuted = call->videoMuted && !isPaused && !call->isAudioOnly;
|
|
bool isRecording = isRecordingThisCall();
|
|
auto bestName = convInfo.participants.isEmpty()
|
|
? QString()
|
|
: accInfo.contactModel->bestNameForContact(convInfo.participants[0]);
|
|
|
|
Q_EMIT updateOverlay(isPaused,
|
|
isAudioOnly,
|
|
isAudioMuted,
|
|
isVideoMuted,
|
|
isRecording,
|
|
accInfo.profileInfo.type == lrc::api::profile::Type::SIP,
|
|
bestName);
|
|
}
|
|
|
|
void
|
|
CallAdapter::hangUpCall(const QString& callId)
|
|
{
|
|
lrcInstance_->getCurrentCallModel()->hangUp(callId);
|
|
}
|
|
|
|
void
|
|
CallAdapter::maximizeParticipant(const QString& uri)
|
|
{
|
|
auto* callModel = lrcInstance_->getAccountInfo(accountId_).callModel.get();
|
|
const auto& convInfo
|
|
= lrcInstance_->getConversationFromConvUid(lrcInstance_->get_selectedConvUid(), accountId_);
|
|
|
|
auto confId = convInfo.confId;
|
|
if (confId.isEmpty())
|
|
confId = convInfo.callId;
|
|
try {
|
|
auto call = callModel->getCall(confId);
|
|
if (call.participantsInfos.size() > 0) {
|
|
for (const auto& participant : call.participantsInfos) {
|
|
if (participant["uri"] == uri) {
|
|
if (participant["active"] == "false") {
|
|
callModel->setActiveParticipant(confId, uri);
|
|
callModel->setConferenceLayout(confId,
|
|
lrc::api::call::Layout::ONE_WITH_SMALL);
|
|
} else if (participant["y"].toInt() != 0) {
|
|
callModel->setActiveParticipant(confId, uri);
|
|
callModel->setConferenceLayout(confId, lrc::api::call::Layout::ONE);
|
|
} else {
|
|
callModel->setConferenceLayout(confId, lrc::api::call::Layout::GRID);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
} catch (...) {
|
|
}
|
|
}
|
|
|
|
void
|
|
CallAdapter::minimizeParticipant(const QString& uri)
|
|
{
|
|
auto* callModel = lrcInstance_->getAccountInfo(accountId_).callModel.get();
|
|
const auto& convInfo
|
|
= lrcInstance_->getConversationFromConvUid(lrcInstance_->get_selectedConvUid(), accountId_);
|
|
auto confId = convInfo.confId;
|
|
|
|
if (confId.isEmpty())
|
|
confId = convInfo.callId;
|
|
try {
|
|
auto call = callModel->getCall(confId);
|
|
if (call.participantsInfos.size() > 0) {
|
|
for (const auto& participant : call.participantsInfos) {
|
|
if (participant["uri"] == uri) {
|
|
if (participant["active"] == "true") {
|
|
if (participant["y"].toInt() == 0) {
|
|
callModel->setConferenceLayout(confId,
|
|
lrc::api::call::Layout::ONE_WITH_SMALL);
|
|
} else {
|
|
callModel->setConferenceLayout(confId, lrc::api::call::Layout::GRID);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
} catch (...) {
|
|
}
|
|
}
|
|
|
|
void
|
|
CallAdapter::hangUpThisCall()
|
|
{
|
|
const auto& convInfo = lrcInstance_->getConversationFromConvUid(convUid_, accountId_);
|
|
if (!convInfo.uid.isEmpty()) {
|
|
auto* callModel = lrcInstance_->getAccountInfo(accountId_).callModel.get();
|
|
if (!convInfo.confId.isEmpty() && callModel->hasCall(convInfo.confId)) {
|
|
callModel->hangUp(convInfo.confId);
|
|
} else if (callModel->hasCall(convInfo.callId)) {
|
|
callModel->hangUp(convInfo.callId);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
CallAdapter::isRecordingThisCall()
|
|
{
|
|
const auto& convInfo = lrcInstance_->getConversationFromConvUid(convUid_, accountId_);
|
|
auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId_);
|
|
return accInfo.callModel->isRecording(convInfo.confId)
|
|
|| accInfo.callModel->isRecording(convInfo.callId);
|
|
}
|
|
|
|
bool
|
|
CallAdapter::isCurrentHost() const
|
|
{
|
|
const auto& convInfo = lrcInstance_->getConversationFromConvUid(convUid_, accountId_);
|
|
if (!convInfo.uid.isEmpty()) {
|
|
auto* callModel = lrcInstance_->getAccountInfo(accountId_).callModel.get();
|
|
try {
|
|
auto confId = convInfo.confId;
|
|
if (confId.isEmpty())
|
|
confId = convInfo.callId;
|
|
auto call = callModel->getCall(confId);
|
|
if (call.participantsInfos.size() == 0) {
|
|
return true;
|
|
} else {
|
|
return !convInfo.confId.isEmpty() && callModel->hasCall(convInfo.confId);
|
|
}
|
|
} catch (...) {
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
CallAdapter::participantIsHost(const QString& uri) const
|
|
{
|
|
const auto& convInfo = lrcInstance_->getConversationFromConvUid(convUid_);
|
|
if (!convInfo.uid.isEmpty()) {
|
|
auto& accInfo = lrcInstance_->getAccountInfo(accountId_);
|
|
auto* callModel = accInfo.callModel.get();
|
|
try {
|
|
if (isCurrentHost()) {
|
|
return uri == accInfo.profileInfo.uri;
|
|
} else {
|
|
auto call = callModel->getCall(convInfo.callId);
|
|
auto peer = call.peerUri.remove("ring:");
|
|
return (uri == peer);
|
|
}
|
|
} catch (...) {
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
CallAdapter::isModerator(const QString& uri) const
|
|
{
|
|
auto* callModel = lrcInstance_->getAccountInfo(accountId_).callModel.get();
|
|
const auto& convInfo = lrcInstance_->getConversationFromConvUid(convUid_);
|
|
auto confId = convInfo.confId;
|
|
|
|
if (confId.isEmpty())
|
|
confId = convInfo.callId;
|
|
try {
|
|
return callModel->isModerator(confId, uri);
|
|
} catch (...) {
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
CallAdapter::isCurrentModerator() const
|
|
{
|
|
const auto& convInfo = lrcInstance_->getConversationFromConvUid(convUid_);
|
|
if (!convInfo.uid.isEmpty()) {
|
|
auto* callModel = lrcInstance_->getAccountInfo(accountId_).callModel.get();
|
|
try {
|
|
auto call = callModel->getCall(convInfo.callId);
|
|
if (call.participantsInfos.size() == 0) {
|
|
return true;
|
|
} else {
|
|
auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId_);
|
|
for (const auto& participant : call.participantsInfos) {
|
|
if (participant["uri"] == accInfo.profileInfo.uri)
|
|
return participant["isModerator"] == "true";
|
|
}
|
|
}
|
|
return false;
|
|
} catch (...) {
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void
|
|
CallAdapter::setModerator(const QString& uri, const bool state)
|
|
{
|
|
auto* callModel = lrcInstance_->getAccountInfo(accountId_).callModel.get();
|
|
const auto& convInfo = lrcInstance_->getConversationFromConvUid(convUid_);
|
|
auto confId = convInfo.confId;
|
|
if (confId.isEmpty())
|
|
confId = convInfo.callId;
|
|
try {
|
|
callModel->setModerator(confId, uri, state);
|
|
} catch (...) {
|
|
}
|
|
}
|
|
|
|
void
|
|
CallAdapter::muteParticipant(const QString& uri, const bool state)
|
|
{
|
|
auto* callModel = lrcInstance_->getAccountInfo(accountId_).callModel.get();
|
|
const auto& convInfo = lrcInstance_->getConversationFromConvUid(convUid_);
|
|
auto confId = convInfo.confId;
|
|
|
|
if (confId.isEmpty())
|
|
confId = convInfo.callId;
|
|
try {
|
|
const auto call = callModel->getCall(confId);
|
|
callModel->muteParticipant(confId, uri, state);
|
|
} catch (...) {
|
|
}
|
|
}
|
|
|
|
CallAdapter::MuteStates
|
|
CallAdapter::getMuteState(const QString& uri) const
|
|
{
|
|
const auto& convInfo = lrcInstance_->getConversationFromConvUid(convUid_);
|
|
auto* callModel = lrcInstance_->getAccountInfo(accountId_).callModel.get();
|
|
auto confId = convInfo.confId.isEmpty() ? convInfo.callId : convInfo.confId;
|
|
try {
|
|
auto call = callModel->getCall(confId);
|
|
if (call.participantsInfos.size() == 0) {
|
|
return MuteStates::UNMUTED;
|
|
} else {
|
|
for (const auto& participant : call.participantsInfos) {
|
|
if (participant["uri"] == uri) {
|
|
if (participant["audioLocalMuted"] == "true") {
|
|
if (participant["audioModeratorMuted"] == "true") {
|
|
return MuteStates::BOTH_MUTED;
|
|
} else {
|
|
return MuteStates::LOCAL_MUTED;
|
|
}
|
|
} else if (participant["audioModeratorMuted"] == "true") {
|
|
return MuteStates::MODERATOR_MUTED;
|
|
}
|
|
return MuteStates::UNMUTED;
|
|
}
|
|
}
|
|
}
|
|
return MuteStates::UNMUTED;
|
|
} catch (...) {
|
|
}
|
|
return MuteStates::UNMUTED;
|
|
}
|
|
|
|
void
|
|
CallAdapter::hangupParticipant(const QString& uri)
|
|
{
|
|
auto* callModel = lrcInstance_->getAccountInfo(accountId_).callModel.get();
|
|
const auto& convInfo = lrcInstance_->getConversationFromConvUid(convUid_);
|
|
auto confId = convInfo.confId;
|
|
|
|
if (confId.isEmpty())
|
|
confId = convInfo.callId;
|
|
try {
|
|
const auto call = callModel->getCall(confId);
|
|
callModel->hangupParticipant(confId, uri);
|
|
} catch (...) {
|
|
}
|
|
}
|
|
|
|
void
|
|
CallAdapter::holdThisCallToggle()
|
|
{
|
|
const auto callId = lrcInstance_->getCallIdForConversationUid(convUid_, accountId_);
|
|
if (callId.isEmpty() || !lrcInstance_->getCurrentCallModel()->hasCall(callId)) {
|
|
return;
|
|
}
|
|
auto* callModel = lrcInstance_->getCurrentCallModel();
|
|
if (callModel->hasCall(callId)) {
|
|
callModel->togglePause(callId);
|
|
}
|
|
Q_EMIT showOnHoldLabel(true);
|
|
}
|
|
|
|
void
|
|
CallAdapter::muteThisCallToggle()
|
|
{
|
|
const auto callId = lrcInstance_->getCallIdForConversationUid(convUid_, accountId_);
|
|
if (callId.isEmpty() || !lrcInstance_->getCurrentCallModel()->hasCall(callId)) {
|
|
return;
|
|
}
|
|
auto* callModel = lrcInstance_->getCurrentCallModel();
|
|
if (callModel->hasCall(callId)) {
|
|
callModel->toggleMedia(callId, lrc::api::NewCallModel::Media::AUDIO);
|
|
}
|
|
}
|
|
|
|
void
|
|
CallAdapter::recordThisCallToggle()
|
|
{
|
|
const auto callId = lrcInstance_->getCallIdForConversationUid(convUid_, accountId_);
|
|
if (callId.isEmpty() || !lrcInstance_->getCurrentCallModel()->hasCall(callId)) {
|
|
return;
|
|
}
|
|
auto* callModel = lrcInstance_->getCurrentCallModel();
|
|
if (callModel->hasCall(callId)) {
|
|
callModel->toggleAudioRecord(callId);
|
|
}
|
|
}
|
|
|
|
void
|
|
CallAdapter::videoPauseThisCallToggle()
|
|
{
|
|
const auto callId = lrcInstance_->getCallIdForConversationUid(convUid_, accountId_);
|
|
if (callId.isEmpty() || !lrcInstance_->getCurrentCallModel()->hasCall(callId)) {
|
|
return;
|
|
}
|
|
auto* callModel = lrcInstance_->getCurrentCallModel();
|
|
if (callModel->hasCall(callId)) {
|
|
callModel->toggleMedia(callId, lrc::api::NewCallModel::Media::VIDEO);
|
|
}
|
|
Q_EMIT previewVisibilityNeedToChange(shouldShowPreview(false));
|
|
}
|
|
|
|
QString
|
|
CallAdapter::getCallDurationTime(const QString& accountId, const QString& convUid)
|
|
{
|
|
const auto callId = lrcInstance_->getCallIdForConversationUid(convUid, accountId);
|
|
if (callId.isEmpty() || !lrcInstance_->getCurrentCallModel()->hasCall(callId)) {
|
|
return QString();
|
|
}
|
|
const auto callInfo = lrcInstance_->getCurrentCallModel()->getCall(callId);
|
|
if (callInfo.status == lrc::api::call::Status::IN_PROGRESS
|
|
|| callInfo.status == lrc::api::call::Status::PAUSED) {
|
|
return lrcInstance_->getCurrentCallModel()->getFormattedCallDuration(callId);
|
|
}
|
|
|
|
return QString();
|
|
}
|
|
|
|
void
|
|
CallAdapter::preventScreenSaver(bool state)
|
|
{
|
|
if (state) {
|
|
if (!screenSaver.isInhibited())
|
|
screenSaver.inhibit();
|
|
} else if (screenSaver.isInhibited()) {
|
|
screenSaver.uninhibit();
|
|
}
|
|
};
|