mirror of
https://git.jami.net/savoirfairelinux/jami-client-qt.git
synced 2025-07-14 20:45:23 +02:00

Using a custom getter setter cause the setting of the value to require manual changed signal emission and in addition to being error prone, this prevents the changed signal from being emitted upon initial selection. Some excess workaround code may be removed in following commits. This patch also refactors the banned contact signaling as part of an accountadapter connection cleanup. Change-Id: I73fb751001e53e086adc7a6a8d706671c2878a77
1039 lines
37 KiB
C++
1039 lines
37 KiB
C++
/*
|
|
* Copyright (C) 2020-2021 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_->get_currentAccountId();
|
|
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));
|
|
|
|
connect(lrcInstance_,
|
|
&LRCInstance::selectedConvUidChanged,
|
|
this,
|
|
&CallAdapter::saveConferenceSubcalls);
|
|
}
|
|
|
|
void
|
|
CallAdapter::onAccountChanged()
|
|
{
|
|
accountId_ = lrcInstance_->get_currentAccountId();
|
|
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);
|
|
const auto& convInfo = lrcInstance_->getConversationFromCallId(callId);
|
|
if (convInfo.uid.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
const auto& currentConvId = lrcInstance_->get_selectedConvUid();
|
|
const auto& currentConvInfo = lrcInstance_->getConversationFromConvUid(currentConvId);
|
|
|
|
// was it a conference and now is a dialog?
|
|
if (currentConvInfo.confId.isEmpty() && currentConfSubcalls_.size() == 2) {
|
|
auto it = std::find_if(currentConfSubcalls_.cbegin(),
|
|
currentConfSubcalls_.cend(),
|
|
[&callId](const QString& cid) { return cid != callId; });
|
|
if (it != currentConfSubcalls_.cend()) {
|
|
// select the conversation using the other callId
|
|
auto otherCall = lrcInstance_->getCurrentCallModel()->getCall(*it);
|
|
if (otherCall.status == lrc::api::call::Status::IN_PROGRESS) {
|
|
const auto& otherConv = lrcInstance_->getConversationFromCallId(*it);
|
|
if (!otherConv.uid.isEmpty() && otherConv.uid != convInfo.uid) {
|
|
lrcInstance_->selectConversation(otherConv.uid);
|
|
Q_EMIT lrcInstance_->conversationUpdated(otherConv.uid, accountId_);
|
|
updateCall(otherConv.uid);
|
|
}
|
|
}
|
|
// then clear the list
|
|
currentConfSubcalls_.clear();
|
|
return;
|
|
}
|
|
} else {
|
|
// okay, still a conference, so just update the subcall list and this call
|
|
saveConferenceSubcalls();
|
|
Q_EMIT lrcInstance_->conversationUpdated(currentConvInfo.uid, accountId_);
|
|
updateCall(currentConvInfo.uid);
|
|
return;
|
|
}
|
|
|
|
Q_EMIT lrcInstance_->conversationUpdated(convInfo.uid, accountId_);
|
|
updateCall(currentConvInfo.uid);
|
|
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);
|
|
}
|
|
saveConferenceSubcalls();
|
|
updateCall(convInfo.uid, accountId_);
|
|
preventScreenSaver(true);
|
|
break;
|
|
}
|
|
case lrc::api::call::Status::PAUSED:
|
|
updateCall();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} catch (...) {
|
|
}
|
|
}
|
|
|
|
void
|
|
CallAdapter::onCallInfosChanged(const QString& accountId, const QString& callId)
|
|
{
|
|
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 callInfosChanged(call.isAudioOnly, accountId, convInfo.uid);
|
|
updateCallOverlay(convInfo);
|
|
}
|
|
} 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::onCallAddedToConference(const QString& callId, const QString& confId)
|
|
{
|
|
Q_UNUSED(callId)
|
|
lrcInstance_->renderer()->addDistantRenderer(confId);
|
|
saveConferenceSubcalls();
|
|
}
|
|
|
|
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::setCallMedia(const QString& accountId, const QString& convUid, bool video)
|
|
{
|
|
const auto& convInfo = lrcInstance_->getConversationFromConvUid(convUid, accountId);
|
|
if (convInfo.uid.isEmpty())
|
|
return;
|
|
try {
|
|
lrcInstance_->getAccountInfo(accountId).callModel->updateCallMediaList(convInfo.callId,
|
|
video);
|
|
} catch (...) {
|
|
}
|
|
}
|
|
|
|
void
|
|
CallAdapter::acceptACall(const QString& accountId, const QString& convUid)
|
|
{
|
|
const auto& convInfo = lrcInstance_->getConversationFromConvUid(convUid, accountId);
|
|
if (convInfo.uid.isEmpty())
|
|
return;
|
|
|
|
lrcInstance_->getAccountInfo(accountId).callModel->accept(convInfo.callId);
|
|
auto& accInfo = lrcInstance_->getAccountInfo(convInfo.accountId);
|
|
accInfo.callModel->setCurrentCall(convInfo.callId);
|
|
if (convInfo.isRequest) {
|
|
lrcInstance_->makeConversationPermanent(convInfo.uid, accountId);
|
|
}
|
|
}
|
|
|
|
void
|
|
CallAdapter::onShowIncomingCallView(const QString& accountId, const QString& convUid)
|
|
{
|
|
const auto& convInfo = lrcInstance_->getConversationFromConvUid(convUid, accountId);
|
|
if (convInfo.uid.isEmpty()) {
|
|
qWarning() << Q_FUNC_INFO << "No conversation for id: " << convUid;
|
|
return;
|
|
}
|
|
|
|
const auto& accInfo = lrcInstance_->getAccountInfo(accountId);
|
|
if (!accInfo.callModel->hasCall(convInfo.callId)) {
|
|
qWarning() << Q_FUNC_INFO << "No call for id: " << convInfo.callId;
|
|
return;
|
|
}
|
|
auto call = accInfo.callModel->getCall(convInfo.callId);
|
|
|
|
// this will update various UI elements that portray the call state
|
|
Q_EMIT callStatusChanged(static_cast<int>(call.status), accountId, convInfo.uid);
|
|
|
|
auto callBelongsToSelectedAccount = accountId == lrcInstance_->get_currentAccountId();
|
|
auto accountProperties = lrcInstance_->accountModel().getAccountConfig(accountId);
|
|
|
|
// do nothing but update the status UI for incoming calls on RendezVous accounts
|
|
if (accountProperties.isRendezVous && !call.isOutgoing) {
|
|
qInfo() << Q_FUNC_INFO << "The call's associated account is a RendezVous point";
|
|
return;
|
|
}
|
|
|
|
auto currentConvId = lrcInstance_->get_selectedConvUid();
|
|
auto isCallSelected = currentConvId == convInfo.uid;
|
|
|
|
// pop a notification when:
|
|
// - the window is not focused OR the call is for another account
|
|
// - the call is incoming AND the call's target account is
|
|
// not a RendezVous point
|
|
// - the call has just transitioned to the INCOMING_RINGING state
|
|
if ((QApplication::focusObject() == nullptr || !callBelongsToSelectedAccount)
|
|
&& !call.isOutgoing && !accountProperties.isRendezVous
|
|
&& call.status == call::Status::INCOMING_RINGING) {
|
|
// if the window is not focused but the call belongs to the selected account
|
|
// then select the conversation immediately to show the call view
|
|
if (callBelongsToSelectedAccount) {
|
|
if (isCallSelected) {
|
|
Q_EMIT lrcInstance_->conversationUpdated(convInfo.uid, accountId);
|
|
} else {
|
|
lrcInstance_->selectConversation(convInfo.uid);
|
|
}
|
|
}
|
|
showNotification(accountId, convInfo.uid);
|
|
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
|
|
if (isCallSelected) {
|
|
// current conversation, only update
|
|
Q_EMIT lrcInstance_->conversationUpdated(convInfo.uid, accountId);
|
|
return;
|
|
}
|
|
|
|
// pop a notification if the current conversation has an in-progress call
|
|
const auto& currentConvInfo = lrcInstance_->getConversationFromConvUid(currentConvId);
|
|
auto currentConvHasCall = accInfo.callModel->hasCall(currentConvInfo.callId);
|
|
if (currentConvHasCall) {
|
|
auto currentCall = accInfo.callModel->getCall(currentConvInfo.callId);
|
|
if ((currentCall.status == call::Status::CONNECTED
|
|
|| currentCall.status == call::Status::IN_PROGRESS)
|
|
&& !accountProperties.autoAnswer) {
|
|
showNotification(accountId, convInfo.uid);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// finally, in this case, the conversation isn't selected yet
|
|
// and there are no other special conditions, so just select the conversation
|
|
lrcInstance_->selectConversation(convInfo.uid, accountId);
|
|
}
|
|
|
|
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);
|
|
|
|
connect(accInfo.callModel.get(),
|
|
&NewCallModel::callAddedToConference,
|
|
this,
|
|
&CallAdapter::onCallAddedToConference,
|
|
Qt::UniqueConnection);
|
|
|
|
connect(accInfo.callModel.get(),
|
|
&NewCallModel::callInfosChanged,
|
|
this,
|
|
QOverload<const QString&, const QString&>::of(&CallAdapter::onCallInfosChanged));
|
|
}
|
|
|
|
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();
|
|
bool isConferenceCall = !convInfo.confId.isEmpty()
|
|
|| (convInfo.confId.isEmpty() && call->participantsInfos.size() != 0);
|
|
bool isGrid = call->layout == lrc::api::call::Layout::GRID;
|
|
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,
|
|
isConferenceCall,
|
|
isGrid,
|
|
bestName);
|
|
}
|
|
|
|
void
|
|
CallAdapter::saveConferenceSubcalls()
|
|
{
|
|
const auto& currentConvId = lrcInstance_->get_selectedConvUid();
|
|
const auto& convInfo = lrcInstance_->getConversationFromConvUid(currentConvId);
|
|
if (!convInfo.confId.isEmpty()) {
|
|
currentConfSubcalls_ = lrcInstance_->getConferenceSubcalls(convInfo.confId);
|
|
}
|
|
}
|
|
|
|
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::showGridConferenceLayout()
|
|
{
|
|
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;
|
|
|
|
callModel->setConferenceLayout(confId, lrc::api::call::Layout::GRID);
|
|
}
|
|
|
|
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->requestMediaChange(callId, "audio_0");
|
|
}
|
|
}
|
|
|
|
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->requestMediaChange(callId, "video_0");
|
|
// media label should come from qml
|
|
// also thi function can me emrged with "muteThisCallToggle"
|
|
}
|
|
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();
|
|
}
|
|
};
|