1
0
Fork 0
mirror of https://git.jami.net/savoirfairelinux/jami-client-qt.git synced 2025-08-29 19:23:40 +02:00

callview: implement an observable object to detail the current call

Change-Id: Ic7025f66e472457e32149911053c8ff32e06bf21
This commit is contained in:
Aline Gondim Santos 2022-12-16 11:35:12 -03:00
parent fc91cdc23c
commit ce6c7dc02b
22 changed files with 572 additions and 333 deletions

View file

@ -221,7 +221,7 @@ set(COMMON_SOURCES
${APP_SRC_DIR}/callparticipantsmodel.cpp ${APP_SRC_DIR}/callparticipantsmodel.cpp
${APP_SRC_DIR}/tipsmodel.cpp ${APP_SRC_DIR}/tipsmodel.cpp
${APP_SRC_DIR}/positioning.cpp ${APP_SRC_DIR}/positioning.cpp
) ${APP_SRC_DIR}/currentcall.cpp)
set(COMMON_HEADERS set(COMMON_HEADERS
${APP_SRC_DIR}/avatarimageprovider.h ${APP_SRC_DIR}/avatarimageprovider.h
@ -280,7 +280,8 @@ set(COMMON_HEADERS
${APP_SRC_DIR}/videoprovider.h ${APP_SRC_DIR}/videoprovider.h
${APP_SRC_DIR}/callparticipantsmodel.h ${APP_SRC_DIR}/callparticipantsmodel.h
${APP_SRC_DIR}/tipsmodel.h ${APP_SRC_DIR}/tipsmodel.h
${APP_SRC_DIR}/positioning.h) ${APP_SRC_DIR}/positioning.h
${APP_SRC_DIR}/currentcall.h)
if(WITH_WEBENGINE) if(WITH_WEBENGINE)
list(APPEND COMMON_SOURCES list(APPEND COMMON_SOURCES

@ -1 +0,0 @@
Subproject commit 2195ee0883efc92828a0cf33b830f43f9bd7e01b

View file

@ -238,7 +238,6 @@ CallAdapter::onCallStatusChanged(const QString& callId, int code)
const auto& convInfo = lrcInstance_->getConversationFromCallId(callId); const auto& convInfo = lrcInstance_->getConversationFromCallId(callId);
if (!convInfo.uid.isEmpty()) { if (!convInfo.uid.isEmpty()) {
Q_EMIT callStatusChanged(static_cast<int>(call.status), accountId_, convInfo.uid); Q_EMIT callStatusChanged(static_cast<int>(call.status), accountId_, convInfo.uid);
updateCallOverlay(convInfo);
} }
switch (call.status) { switch (call.status) {
@ -330,25 +329,11 @@ CallAdapter::onCallInfosChanged(const QString& accountId, const QString& callId)
} }
Q_EMIT callInfosChanged(call.isAudioOnly, accountId, convInfo.uid); Q_EMIT callInfosChanged(call.isAudioOnly, accountId, convInfo.uid);
participantsModel_->setConferenceLayout(static_cast<int>(call.layout), callId); participantsModel_->setConferenceLayout(static_cast<int>(call.layout), callId);
updateCallOverlay(convInfo);
} }
} catch (...) { } catch (...) {
} }
} }
void
CallAdapter::onRemoteRecordingChanged(const QString& callId,
const QSet<QString>& peerRec,
bool state)
{
Q_UNUSED(peerRec)
Q_UNUSED(state)
const auto currentCallId
= lrcInstance_->getCallIdForConversationUid(lrcInstance_->get_selectedConvUid(), accountId_);
if (callId == currentCallId)
updateRecordingPeers();
}
void void
CallAdapter::onCallAddedToConference(const QString& callId, const QString& confId) CallAdapter::onCallAddedToConference(const QString& callId, const QString& confId)
{ {
@ -511,8 +496,6 @@ CallAdapter::updateCall(const QString& convUid, const QString& accountId, bool f
} }
} }
updateCallOverlay(convInfo);
updateRecordingPeers(true);
participantsModel_->setParticipants(call->id, getConferencesInfos()); participantsModel_->setParticipants(call->id, getConferencesInfos());
participantsModel_->setConferenceLayout(static_cast<int>(call->layout), call->id); participantsModel_->setConferenceLayout(static_cast<int>(call->layout), call->id);
} }
@ -569,7 +552,6 @@ CallAdapter::getConferencesInfos() const
.getAccountInfo(accountId_) .getAccountInfo(accountId_)
.callModel.get() .callModel.get()
->getParticipantsInfos(callId); ->getParticipantsInfos(callId);
int index = 0;
for (int index = 0; index < participantsModel.getParticipants().size(); index++) { for (int index = 0; index < participantsModel.getParticipants().size(); index++) {
auto participant = participantsModel.toQJsonObject(index); auto participant = participantsModel.toQJsonObject(index);
fillParticipantData(participant); fillParticipantData(participant);
@ -642,12 +624,6 @@ CallAdapter::connectCallModel(const QString& accountId)
QOverload<const QString&, int>::of(&CallAdapter::onCallStatusChanged), QOverload<const QString&, int>::of(&CallAdapter::onCallStatusChanged),
Qt::UniqueConnection); Qt::UniqueConnection);
connect(accInfo.callModel.get(),
&CallModel::remoteRecordingChanged,
this,
&CallAdapter::onRemoteRecordingChanged,
Qt::UniqueConnection);
connect(accInfo.callModel.get(), connect(accInfo.callModel.get(),
&CallModel::callAddedToConference, &CallModel::callAddedToConference,
this, this,
@ -660,32 +636,6 @@ CallAdapter::connectCallModel(const QString& accountId)
QOverload<const QString&, const QString&>::of(&CallAdapter::onCallInfosChanged)); QOverload<const QString&, const QString&>::of(&CallAdapter::onCallInfosChanged));
} }
void
CallAdapter::updateRecordingPeers(bool eraseLabelOnEmpty)
{
const auto& convInfo = lrcInstance_->getConversationFromConvUid(
lrcInstance_->get_selectedConvUid());
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 void
CallAdapter::sipInputPanelPlayDTMF(const QString& key) CallAdapter::sipInputPanelPlayDTMF(const QString& key)
{ {
@ -698,49 +648,6 @@ CallAdapter::sipInputPanelPlayDTMF(const QString& key)
lrcInstance_->getCurrentCallModel()->playDTMF(callId, key); lrcInstance_->getCurrentCallModel()->playDTMF(callId, key);
} }
/*
* For Call Overlay
*/
void
CallAdapter::updateCallOverlay(const lrc::api::conversation::Info& convInfo)
{
auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId_);
auto* callModel = accInfo.callModel.get();
const auto* callInfo = lrcInstance_->getCallInfoForConversation(convInfo);
const auto currentCallId = lrcInstance_->getCurrentCallId();
if (!callInfo || callInfo->id != currentCallId)
return;
bool isPaused = callInfo->status == lrc::api::call::Status::PAUSED;
bool isAudioOnly = callInfo->isAudioOnly && !isPaused;
bool isAudioMuted = callInfo->status == lrc::api::call::Status::PAUSED;
bool isGrid = callInfo->layout == lrc::api::call::Layout::GRID;
QString previewId {};
if (callInfo->status != lrc::api::call::Status::ENDED) {
for (const auto& media : callInfo->mediaList) {
if (media[libjami::Media::MediaAttributeKey::MEDIA_TYPE]
== libjami::Media::Details::MEDIA_TYPE_VIDEO) {
if (media[libjami::Media::MediaAttributeKey::ENABLED] == TRUE_STR
&& media[libjami::Media::MediaAttributeKey::MUTED] == FALSE_STR) {
if (previewId.isEmpty()) {
previewId = media[libjami::Media::MediaAttributeKey::SOURCE];
}
}
} else if (media[libjami::Media::MediaAttributeKey::LABEL] == "audio_0") {
isAudioMuted |= media[libjami::Media::MediaAttributeKey::MUTED] == TRUE_STR;
}
}
}
Q_EMIT updateOverlay(isPaused,
isAudioOnly,
isAudioMuted,
accInfo.profileInfo.type == lrc::api::profile::Type::SIP,
isGrid,
previewId);
}
void void
CallAdapter::saveConferenceSubcalls() CallAdapter::saveConferenceSubcalls()
{ {
@ -1075,7 +982,6 @@ CallAdapter::holdThisCallToggle()
if (callModel->hasCall(callId)) { if (callModel->hasCall(callId)) {
callModel->togglePause(callId); callModel->togglePause(callId);
} }
Q_EMIT showOnHoldLabel(true);
} }
void void

View file

@ -100,15 +100,6 @@ Q_SIGNALS:
// For Call Overlay // For Call Overlay
void updateTimeText(const QString& time); void updateTimeText(const QString& time);
void showOnHoldLabel(bool isPaused);
void updateOverlay(bool isPaused,
bool isAudioOnly,
bool isAudioMuted,
bool isSIP,
bool isGrid,
const QString& previewId);
void remoteRecordingChanged(const QStringList& peers, bool state);
void eraseRemoteRecording();
public Q_SLOTS: public Q_SLOTS:
void onShowIncomingCallView(const QString& accountId, const QString& convUid); void onShowIncomingCallView(const QString& accountId, const QString& convUid);
@ -117,18 +108,15 @@ public Q_SLOTS:
void onCallStatusChanged(const QString& accountId, const QString& callId); void onCallStatusChanged(const QString& accountId, const QString& callId);
void onCallInfosChanged(const QString& accountId, const QString& callId); void onCallInfosChanged(const QString& accountId, const QString& callId);
void onCallStatusChanged(const QString& callId, int code); void onCallStatusChanged(const QString& callId, int code);
void onRemoteRecordingChanged(const QString& callId, const QSet<QString>& peerRec, bool state);
void onCallAddedToConference(const QString& callId, const QString& confId); void onCallAddedToConference(const QString& callId, const QString& confId);
void onParticipantAdded(const QString& callId, int index); void onParticipantAdded(const QString& callId, int index);
void onParticipantRemoved(const QString& callId, int index); void onParticipantRemoved(const QString& callId, int index);
void onParticipantUpdated(const QString& callId, int index); void onParticipantUpdated(const QString& callId, int index);
private: private:
void updateRecordingPeers(bool eraseLabelOnEmpty = false);
void showNotification(const QString& accountId, const QString& convUid); void showNotification(const QString& accountId, const QString& convUid);
void fillParticipantData(QJsonObject& participant) const; void fillParticipantData(QJsonObject& participant) const;
void preventScreenSaver(bool state); void preventScreenSaver(bool state);
void updateCallOverlay(const lrc::api::conversation::Info& convInfo);
void saveConferenceSubcalls(); void saveConferenceSubcalls();
QString accountId_; QString accountId_;

308
src/app/currentcall.cpp Normal file
View file

@ -0,0 +1,308 @@
/*
* Copyright (C) 2022 Savoir-faire Linux Inc.
*
* 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 <https://www.gnu.org/licenses/>.
*/
#include "currentcall.h"
#include <api/callparticipantsmodel.h>
CurrentCall::CurrentCall(LRCInstance* lrcInstance, QObject* parent)
: QObject(parent)
, lrcInstance_(lrcInstance)
{
connect(lrcInstance_,
&LRCInstance::currentAccountIdChanged,
this,
&CurrentCall::onCurrentAccountIdChanged);
connect(lrcInstance_,
&LRCInstance::selectedConvUidChanged,
this,
&CurrentCall::onCurrentConvIdChanged);
connectModel();
}
void
CurrentCall::updateId(QString callId)
{
auto convId = lrcInstance_->get_selectedConvUid();
auto optConv = lrcInstance_->getCurrentConversationModel()->getConversationForUid(convId);
if (!optConv.has_value()) {
return;
}
// If the optional parameter callId is empty, then we've just
// changed conversation selection and need to check the current
// conv's callId for an existing call.
// Otherwise, return if callId doesn't belong to this conversation.
if (callId.isEmpty()) {
callId = optConv->get().getCallId();
} else if (optConv->get().getCallId() != callId) {
return;
}
// Set the current id_ if there is a call.
auto& accInfo = lrcInstance_->getCurrentAccountInfo();
if (accInfo.callModel->hasCall(callId)) {
set_id(callId);
}
}
void
CurrentCall::updateCallStatus()
{
call::Status status {};
auto callModel = lrcInstance_->getCurrentCallModel();
if (callModel->hasCall(id_)) {
auto callInfo = callModel->getCall(id_);
status = callInfo.status;
}
set_status(status);
set_isActive(status_ == call::Status::CONNECTED || status_ == call::Status::IN_PROGRESS
|| status_ == call::Status::PAUSED);
set_isPaused(status_ == call::Status::PAUSED);
}
void
CurrentCall::updateParticipants()
{
auto callModel = lrcInstance_->getCurrentCallModel();
QStringList uris;
auto& participantsModel = callModel->getParticipantsInfos(id_);
for (int index = 0; index < participantsModel.getParticipants().size(); index++) {
auto participantInfo = participantsModel.toQJsonObject(index);
uris.append(participantInfo[ParticipantsInfosStrings::URI].toString());
}
set_uris(uris);
set_isConference(uris.size());
}
void
CurrentCall::updateCallInfo()
{
auto callModel = lrcInstance_->getCurrentCallModel();
if (!callModel->hasCall(id_)) {
return;
}
auto callInfo = callModel->getCall(id_);
set_isGrid(callInfo.layout == call::Layout::GRID);
set_isAudioOnly(callInfo.isAudioOnly);
bool isAudioMuted {};
bool isVideoMuted {};
bool isSharing {};
bool isCapturing {};
QString previewId {};
using namespace libjami::Media;
if (callInfo.status != lrc::api::call::Status::ENDED) {
for (const auto& media : callInfo.mediaList) {
if (media[MediaAttributeKey::MEDIA_TYPE] == Details::MEDIA_TYPE_VIDEO) {
if (media[MediaAttributeKey::SOURCE].startsWith(VideoProtocolPrefix::DISPLAY)
|| media[MediaAttributeKey::SOURCE].startsWith(VideoProtocolPrefix::FILE)) {
isSharing = true;
}
if (media[MediaAttributeKey::ENABLED] == TRUE_STR
&& media[MediaAttributeKey::MUTED] == FALSE_STR && previewId.isEmpty()) {
previewId = media[libjami::Media::MediaAttributeKey::SOURCE];
}
if (media[libjami::Media::MediaAttributeKey::SOURCE].startsWith(
libjami::Media::VideoProtocolPrefix::CAMERA)) {
isVideoMuted |= media[MediaAttributeKey::MUTED] == TRUE_STR;
isCapturing = media[MediaAttributeKey::MUTED] == FALSE_STR;
}
} else if (media[MediaAttributeKey::MEDIA_TYPE] == Details::MEDIA_TYPE_AUDIO) {
if (media[MediaAttributeKey::LABEL] == "audio_0") {
isAudioMuted |= media[libjami::Media::MediaAttributeKey::MUTED] == TRUE_STR;
}
}
}
}
set_previewId(previewId);
set_isAudioMuted(isAudioMuted);
set_isVideoMuted(isVideoMuted);
set_isSharing(isSharing);
set_isCapturing(isCapturing);
set_isHandRaised(callModel->isHandRaised(id_));
set_isModerator(callModel->isModerator(id_));
}
void
CurrentCall::updateRemoteRecorders(const QStringList& recorders)
{
auto& accInfo = lrcInstance_->getCurrentAccountInfo();
remoteRecorderNameList_.clear();
Q_FOREACH (const auto& uri, recorders) {
auto bestName = accInfo.contactModel->bestNameForContact(uri);
if (!bestName.isEmpty()) {
remoteRecorderNameList_.append(bestName);
}
}
// Convenience flag.
set_isRecordingRemotely(!remoteRecorderNameList_.isEmpty());
Q_EMIT remoteRecorderNameListChanged();
}
void
CurrentCall::updateRecordingState(bool state)
{
set_isRecordingLocally(state);
}
void
CurrentCall::connectModel()
{
try {
auto& accInfo = lrcInstance_->getCurrentAccountInfo();
connect(accInfo.callModel.get(),
&CallModel::callStatusChanged,
this,
&CurrentCall::onCallStatusChanged,
Qt::UniqueConnection);
connect(accInfo.callModel.get(),
&CallModel::callInfosChanged,
this,
&CurrentCall::onCallInfosChanged,
Qt::UniqueConnection);
connect(accInfo.callModel.get(),
&CallModel::currentCallChanged,
this,
&CurrentCall::onCurrentCallChanged,
Qt::UniqueConnection);
connect(accInfo.callModel.get(),
&CallModel::participantsChanged,
this,
&CurrentCall::onParticipantsChanged,
Qt::UniqueConnection);
connect(accInfo.callModel.get(),
&CallModel::remoteRecordersChanged,
this,
&CurrentCall::onRemoteRecordersChanged,
Qt::UniqueConnection);
connect(accInfo.callModel.get(),
&CallModel::recordingStateChanged,
this,
&CurrentCall::onRecordingStateChanged,
Qt::UniqueConnection);
} catch (const std::exception& e) {
qWarning() << "Exception getting account info." << e.what();
}
}
void
CurrentCall::onCurrentConvIdChanged()
{
updateId();
updateCallStatus();
updateParticipants();
updateCallInfo();
auto callModel = lrcInstance_->getCurrentCallModel();
QStringList recorders {};
if (callModel->hasCall(id_)) {
auto callInfo = callModel->getCall(id_);
recorders = callInfo.recordingPeers;
}
updateRecordingState(callModel->isRecording(id_));
updateRemoteRecorders(recorders);
}
void
CurrentCall::onCurrentAccountIdChanged()
{
try {
auto& accInfo = lrcInstance_->getCurrentAccountInfo();
set_isSIP(accInfo.profileInfo.type == profile::Type::SIP);
} catch (const std::exception& e) {
qWarning() << "Can't update current call type" << e.what();
}
connectModel();
}
void
CurrentCall::onCallStatusChanged(const QString& callId, int code)
{
Q_UNUSED(code)
if (id_ != callId) {
return;
}
updateCallStatus();
}
void
CurrentCall::onCallInfosChanged(const QString& accountId, const QString& callId)
{
if (id_ != callId) {
return;
}
updateCallInfo();
}
void
CurrentCall::onCurrentCallChanged(const QString& callId)
{
// If this status change's callId is not the current, it's possible that
// the current value of id_ is stale, and needs to be updated after checking
// the current conversation's getCallId(). Other slots need not do this, as the
// id_ is updated here.
if (id_ == callId) {
return;
}
updateId(callId);
updateCallStatus();
updateParticipants();
updateCallInfo();
}
void
CurrentCall::onParticipantsChanged(const QString& callId)
{
if (id_ != callId) {
return;
}
updateParticipants();
}
void
CurrentCall::onRemoteRecordersChanged(const QString& callId, const QStringList& recorders)
{
if (id_ != callId) {
return;
}
updateRemoteRecorders(recorders);
}
void
CurrentCall::onRecordingStateChanged(const QString& callId, bool state)
{
if (id_ != callId) {
return;
}
updateRecordingState(state);
}

75
src/app/currentcall.h Normal file
View file

@ -0,0 +1,75 @@
/*
* Copyright (C) 2022 Savoir-faire Linux Inc.
*
* 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 <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "lrcinstance.h"
#include "qtutils.h"
#include <QObject>
#include <QString>
class CurrentCall final : public QObject
{
Q_OBJECT
QML_RO_PROPERTY(QString, id)
QML_RO_PROPERTY(QStringList, uris)
QML_RO_PROPERTY(bool, isAudioOnly)
QML_RO_PROPERTY(bool, isSIP)
QML_RO_PROPERTY(bool, isGrid)
QML_RO_PROPERTY(call::Status, status)
QML_RO_PROPERTY(bool, isActive)
QML_RO_PROPERTY(bool, isPaused)
QML_RO_PROPERTY(bool, isAudioMuted)
QML_RO_PROPERTY(bool, isCapturing)
QML_RO_PROPERTY(bool, isVideoMuted)
QML_RO_PROPERTY(QString, previewId)
QML_RO_PROPERTY(bool, isRecordingLocally)
QML_RO_PROPERTY(bool, isRecordingRemotely)
QML_RO_PROPERTY(QStringList, remoteRecorderNameList)
QML_RO_PROPERTY(bool, isSharing)
QML_RO_PROPERTY(bool, isHandRaised)
QML_RO_PROPERTY(bool, isConference)
QML_RO_PROPERTY(bool, isModerator)
public:
explicit CurrentCall(LRCInstance* lrcInstance, QObject* parent = nullptr);
~CurrentCall() = default;
private:
void updateId(QString callId = {});
void updateCallStatus();
void updateParticipants();
void updateCallInfo();
void updateRemoteRecorders(const QStringList& recorders);
void updateRecordingState(bool state);
void connectModel();
private Q_SLOTS:
void onCurrentConvIdChanged();
void onCurrentAccountIdChanged();
void onCallStatusChanged(const QString& callId, int code);
void onCallInfosChanged(const QString& accountId, const QString& callId);
void onCurrentCallChanged(const QString& callId);
void onParticipantsChanged(const QString& callId);
void onRemoteRecordersChanged(const QString& callId, const QStringList& recorders);
void onRecordingStateChanged(const QString& callId, bool state);
private:
LRCInstance* lrcInstance_;
};

View file

@ -186,7 +186,6 @@ Rectangle {
callStackView.setLinkedWebview(chatView) callStackView.setLinkedWebview(chatView)
callStackView.responsibleAccountId = LRCInstance.currentAccountId callStackView.responsibleAccountId = LRCInstance.currentAccountId
callStackView.responsibleConvUid = convId callStackView.responsibleConvUid = convId
callStackView.isAudioOnly = item.isAudioOnly
currentConvUID = convId currentConvUID = convId
if (item.callState === Call.Status.IN_PROGRESS || if (item.callState === Call.Status.IN_PROGRESS ||

View file

@ -162,7 +162,7 @@ Control {
layoutModel.get(index).ActiveSetting = layoutManager.isCallFullscreen layoutModel.get(index).ActiveSetting = layoutManager.isCallFullscreen
break break
case JamiStrings.mosaic: case JamiStrings.mosaic:
if (!isGrid) if (!CurrentCall.isGrid)
CallAdapter.showGridConferenceLayout() CallAdapter.showGridConferenceLayout()
break break
case JamiStrings.participantsSide: case JamiStrings.participantsSide:
@ -189,10 +189,10 @@ Control {
} }
onTriggered: { onTriggered: {
layoutModel.clear() layoutModel.clear()
if (isConference) { if (CurrentCall.isConference) {
layoutModel.append({"Name": JamiStrings.mosaic, layoutModel.append({"Name": JamiStrings.mosaic,
"IconSource": JamiResources.mosaic_black_24dp_svg, "IconSource": JamiResources.mosaic_black_24dp_svg,
"ActiveSetting": isGrid, "ActiveSetting": CurrentCall.isGrid,
"TopMargin": true, "TopMargin": true,
"BottomMargin": true, "BottomMargin": true,
"SectionEnd": true}) "SectionEnd": true})
@ -223,8 +223,8 @@ Control {
"ActiveSetting": layoutManager.isCallFullscreen, "ActiveSetting": layoutManager.isCallFullscreen,
"TopMargin": true, "TopMargin": true,
"BottomMargin": true, "BottomMargin": true,
"SectionEnd": isConference}) "SectionEnd": CurrentCall.isConference})
if (isConference) { if (CurrentCall.isConference) {
layoutModel.append({"Name": JamiStrings.hideSpectators, layoutModel.append({"Name": JamiStrings.hideSpectators,
"IconSource": JamiResources.videocam_off_24dp_svg, "IconSource": JamiResources.videocam_off_24dp_svg,
"ActiveSetting": UtilsAdapter.getAppValue(Settings.HideSpectators), "ActiveSetting": UtilsAdapter.getAppValue(Settings.HideSpectators),
@ -263,6 +263,7 @@ Control {
JamiResources.micro_black_24dp_svg JamiResources.micro_black_24dp_svg
icon.color: checked ? "red" : "white" icon.color: checked ? "red" : "white"
text: !checked ? JamiStrings.mute : JamiStrings.unmute text: !checked ? JamiStrings.mute : JamiStrings.unmute
checked: CurrentCall.isAudioMuted
property var menuAction: audioInputMenuAction property var menuAction: audioInputMenuAction
}, },
Action { Action {
@ -282,6 +283,7 @@ Control {
JamiResources.videocam_24dp_svg JamiResources.videocam_24dp_svg
icon.color: checked ? "red" : "white" icon.color: checked ? "red" : "white"
text: !checked ? JamiStrings.muteCamera : JamiStrings.unmuteCamera text: !checked ? JamiStrings.muteCamera : JamiStrings.unmuteCamera
checked: !CurrentCall.isCapturing
property var menuAction: videoInputMenuAction property var menuAction: videoInputMenuAction
} }
] ]
@ -314,11 +316,13 @@ Control {
Action { Action {
id: resumePauseCallAction id: resumePauseCallAction
onTriggered: root.resumePauseCallClicked() onTriggered: root.resumePauseCallClicked()
icon.source: isPaused ? icon.source: CurrentCall.isPaused ?
JamiResources.play_circle_outline_24dp_svg : JamiResources.play_circle_outline_24dp_svg :
JamiResources.pause_circle_outline_24dp_svg JamiResources.pause_circle_outline_24dp_svg
icon.color: "white" icon.color: "white"
text: isPaused ? JamiStrings.resumeCall : JamiStrings.pauseCall text: CurrentCall.isPaused ?
JamiStrings.resumeCall :
JamiStrings.pauseCall
}, },
Action { Action {
id: inputPanelSIPAction id: inputPanelSIPAction
@ -337,17 +341,17 @@ Control {
Action { Action {
id: shareAction id: shareAction
onTriggered: { onTriggered: {
if (sharingActive) if (CurrentCall.isSharing)
root.stopSharingClicked() root.stopSharingClicked()
else else
root.shareScreenClicked() root.shareScreenClicked()
} }
icon.source: sharingActive ? icon.source: CurrentCall.isSharing ?
JamiResources.share_stop_black_24dp_svg : JamiResources.share_stop_black_24dp_svg :
JamiResources.share_screen_black_24dp_svg JamiResources.share_screen_black_24dp_svg
icon.color: sharingActive ? icon.color: CurrentCall.isSharing ?
"red" : "white" "red" : "white"
text: sharingActive ? text: CurrentCall.isSharing ?
JamiStrings.stopSharing : JamiStrings.stopSharing :
JamiStrings.shareScreen JamiStrings.shareScreen
property real size: 34 property real size: 34
@ -362,12 +366,13 @@ Control {
text: checked ? text: checked ?
JamiStrings.lowerHand : JamiStrings.lowerHand :
JamiStrings.raiseHand JamiStrings.raiseHand
checked: CurrentCall.isHandRaised
property real size: 34 property real size: 34
}, },
Action { Action {
id: layoutAction id: layoutAction
onTriggered: { onTriggered: {
if (!isGrid) if (!CurrentCall.isGrid)
CallAdapter.showGridConferenceLayout() CallAdapter.showGridConferenceLayout()
} }
checkable: true checkable: true
@ -386,6 +391,7 @@ Control {
text: !checked ? JamiStrings.startRec : JamiStrings.stopRec text: !checked ? JamiStrings.startRec : JamiStrings.stopRec
property bool blinksWhenChecked: true property bool blinksWhenChecked: true
property real size: 28 property real size: 28
checked: CurrentCall.isRecordingLocally
onCheckedChanged: function(checked) { onCheckedChanged: function(checked) {
CallOverlayModel.setUrgentCount(recordAction, CallOverlayModel.setUrgentCount(recordAction,
checked ? -1 : 0) checked ? -1 : 0)
@ -397,27 +403,29 @@ Control {
icon.source: JamiResources.plugins_24dp_svg icon.source: JamiResources.plugins_24dp_svg
icon.color: "white" icon.color: "white"
text: JamiStrings.viewPlugin text: JamiStrings.viewPlugin
enabled: PluginAdapter.isEnabled && PluginAdapter.callMediaHandlersListCount enabled: PluginAdapter.isEnabled
&& PluginAdapter.callMediaHandlersListCount
} }
] ]
property var overflowItemCount property var overflowItemCount
Connections { Connections {
target: callOverlay target: CurrentCall
function onIsAudioOnlyChanged() { Qt.callLater(reset) } function onIsActiveChanged() { if (CurrentCall.isActive) reset() }
function onIsSIPChanged() { Qt.callLater(reset) } function onIsRecordingLocallyChanged() { Qt.callLater(reset) }
function onIsHandRaisedChanged() { Qt.callLater(reset) }
function onIsConferenceChanged() { Qt.callLater(reset) }
function onIsModeratorChanged() { Qt.callLater(reset) } function onIsModeratorChanged() { Qt.callLater(reset) }
function onIsSIPChanged() { Qt.callLater(reset) }
function onIsAudioOnlyChanged() { Qt.callLater(reset) }
function onIsAudioMutedChanged() { Qt.callLater(reset) } function onIsAudioMutedChanged() { Qt.callLater(reset) }
function onIsVideoMutedChanged() { Qt.callLater(reset) } function onIsVideoMutedChanged() { Qt.callLater(reset) }
function onIsRecordingChanged() { Qt.callLater(reset) }
function onLocalHandRaisedChanged() { Qt.callLater(reset) }
function onIsConferenceChanged() { Qt.callLater(reset) }
} }
Connections { Connections {
target: CurrentAccount target: CurrentAccount
function onVideoEnabledVideoChanged() { reset() } function onVideoEnabledVideoChanged() { reset() }
} }
@ -433,29 +441,24 @@ Control {
// overflow controls // overflow controls
CallOverlayModel.addSecondaryControl(audioOutputAction) CallOverlayModel.addSecondaryControl(audioOutputAction)
if (isConference) { if (CurrentCall.isConference) {
CallOverlayModel.addSecondaryControl(raiseHandAction) CallOverlayModel.addSecondaryControl(raiseHandAction)
raiseHandAction.checked = CallAdapter.isHandRaised()
} }
if (isModerator && !isSIP) if (CurrentCall.isModerator && !CurrentCall.isSIP)
CallOverlayModel.addSecondaryControl(addPersonAction) CallOverlayModel.addSecondaryControl(addPersonAction)
if (isSIP) { if (CurrentCall.isSIP) {
CallOverlayModel.addSecondaryControl(resumePauseCallAction) CallOverlayModel.addSecondaryControl(resumePauseCallAction)
CallOverlayModel.addSecondaryControl(inputPanelSIPAction) CallOverlayModel.addSecondaryControl(inputPanelSIPAction)
CallOverlayModel.addSecondaryControl(callTransferAction) CallOverlayModel.addSecondaryControl(callTransferAction)
} }
CallOverlayModel.addSecondaryControl(chatAction) CallOverlayModel.addSecondaryControl(chatAction)
if (CurrentAccount.videoEnabled_Video) if (CurrentAccount.videoEnabled_Video && !CurrentCall.isSIP)
CallOverlayModel.addSecondaryControl(shareAction) CallOverlayModel.addSecondaryControl(shareAction)
CallOverlayModel.addSecondaryControl(layoutAction) CallOverlayModel.addSecondaryControl(layoutAction)
CallOverlayModel.addSecondaryControl(recordAction) CallOverlayModel.addSecondaryControl(recordAction)
if (pluginsAction.enabled) if (pluginsAction.enabled)
CallOverlayModel.addSecondaryControl(pluginsAction) CallOverlayModel.addSecondaryControl(pluginsAction)
overflowItemCount = CallOverlayModel.secondaryModel().rowCount() overflowItemCount = CallOverlayModel.secondaryModel().rowCount()
muteAudioAction.checked = isAudioMuted
recordAction.checked = CallAdapter.isRecordingThisCall()
muteVideoAction.checked = isAudioOnly ? true : isVideoMuted
} }
Item { Item {

View file

@ -39,6 +39,7 @@ ItemDelegate {
action: ItemAction action: ItemAction
checkable: ItemAction.checkable checkable: ItemAction.checkable
hoverEnabled: ItemAction.enabled
// hide the action's visual elements like the blurry looking icon // hide the action's visual elements like the blurry looking icon
icon.source: "" icon.source: ""
@ -122,7 +123,11 @@ ItemDelegate {
anchors.centerIn: parent anchors.centerIn: parent
source: ItemAction ? ItemAction.icon.source : "" source: ItemAction ? ItemAction.icon.source : ""
color: ItemAction ? ItemAction.icon.color : null color: ItemAction ?
(ItemAction.enabled ?
ItemAction.icon.color :
Qt.lighter(ItemAction.icon.color)) :
null
SequentialAnimation on opacity { SequentialAnimation on opacity {
loops: Animation.Infinite loops: Animation.Infinite
@ -172,7 +177,8 @@ ItemDelegate {
indicator: null indicator: null
visible: menuAction !== undefined && !UrgentCount && menuAction.enabled visible: ItemAction.enabled
&& menuAction !== undefined && !UrgentCount && menuAction.enabled
y: isVertical ? 0 : -4 y: isVertical ? 0 : -4
x: isVertical ? -4 : 0 x: isVertical ? -4 : 0

View file

@ -35,48 +35,11 @@ import "../../commoncomponents"
Item { Item {
id: root id: root
property bool isPaused
property bool isAudioOnly
property bool isAudioMuted
property bool isVideoMuted
property bool isRecording
property bool remoteRecording
property bool isSIP
property bool isModerator
property bool isConference
property bool isGrid
property bool participantsSide: UtilsAdapter.getAppValue(Settings.ParticipantsSide) property bool participantsSide: UtilsAdapter.getAppValue(Settings.ParticipantsSide)
property bool localHandRaised
property bool sharingActive: AvAdapter.isSharing()
property string callId: ""
signal chatButtonClicked signal chatButtonClicked
signal fullScreenClicked signal fullScreenClicked
function setRecording(localIsRecording) {
callViewContextMenu.localIsRecording = localIsRecording
mainOverlay.recordingVisible = localIsRecording
|| callViewContextMenu.peerIsRecording
}
function updateUI(isPaused, isAudioOnly, isAudioMuted, isSIP, isGrid) {
if (isPaused !== undefined) {
root.isPaused = isPaused
root.isAudioOnly = isAudioOnly
root.isAudioMuted = isAudioMuted
callViewContextMenu.isVideoMuted = root.isVideoMuted
root.isSIP = isSIP
root.isGrid = isGrid
root.localHandRaised = CallAdapter.isHandRaised()
}
root.isRecording = CallAdapter.isRecordingThisCall()
root.isModerator = CallAdapter.isModerator()
}
function showOnHoldImage(visible) {
onHoldImage.visible = visible
}
function closeContextMenuAndRelatedWindows() { function closeContextMenuAndRelatedWindows() {
ContactPickerCreation.closeContactPicker() ContactPickerCreation.closeContactPicker()
sipInputPanel.close() sipInputPanel.close()
@ -94,29 +57,6 @@ Item {
callViewContextMenu.openMenu() callViewContextMenu.openMenu()
} }
function showRemoteRecording(peers, state) {
var label = ""
var i = 0
if (state) {
for (var p in peers) {
label += peers[p]
if (i !== (peers.length - 1))
label += ", "
i += 1
}
label += " " + ((peers.length > 1) ? JamiStrings.areRecording : JamiStrings.isRecording)
}
mainOverlay.remoteRecordingLabel = state ? label : JamiStrings.peerStoppedRecording
root.remoteRecording = state
callOverlayRectMouseArea.entered()
}
function resetRemoteRecording() {
mainOverlay.remoteRecordingLabel = ""
root.remoteRecording = false
}
DropArea { DropArea {
anchors.fill: parent anchors.fill: parent
onDropped: function(drop) { onDropped: function(drop) {
@ -158,7 +98,7 @@ Item {
width: 200 width: 200
height: 200 height: 200
visible: false visible: CurrentCall.isPaused
source: JamiResources.ic_pause_white_100px_svg source: JamiResources.ic_pause_white_100px_svg
} }
@ -199,17 +139,10 @@ Item {
PluginHandlerPickerCreation.openPluginHandlerPicker() PluginHandlerPickerCreation.openPluginHandlerPicker()
} }
function recordClicked() {
CallAdapter.recordThisCallToggle()
updateUI()
}
MainOverlay { MainOverlay {
id: mainOverlay id: mainOverlay
anchors.fill: parent anchors.fill: parent
isRecording: root.isRecording
remoteRecording: root.remoteRecording
Connections { Connections {
target: mainOverlay.callActionBar target: mainOverlay.callActionBar
@ -222,7 +155,7 @@ Item {
function onShareWindowClicked() { openShareWindow() } function onShareWindowClicked() { openShareWindow() }
function onStopSharingClicked() { AvAdapter.stopSharing() } function onStopSharingClicked() { AvAdapter.stopSharing() }
function onShareScreenAreaClicked() { openShareScreenArea() } function onShareScreenAreaClicked() { openShareScreenArea() }
function onRecordCallClicked() { recordClicked() } function onRecordCallClicked() { CallAdapter.recordThisCallToggle() }
function onShareFileClicked() { jamiFileDialog.open() } function onShareFileClicked() { jamiFileDialog.open() }
function onPluginsClicked() { openPluginsMenu() } function onPluginsClicked() { openPluginsMenu() }
function onFullScreenClicked() { root.fullScreenClicked() } function onFullScreenClicked() { root.fullScreenClicked() }
@ -232,13 +165,9 @@ Item {
CallViewContextMenu { CallViewContextMenu {
id: callViewContextMenu id: callViewContextMenu
isSIP: root.isSIP
isPaused: root.isPaused
isRecording: root.isRecording
onTransferCallButtonClicked: openContactPicker(ContactList.TRANSFER) onTransferCallButtonClicked: openContactPicker(ContactList.TRANSFER)
onPluginItemClicked: openPluginsMenu() onPluginItemClicked: openPluginsMenu()
onRecordCallClicked: root.recordClicked() onRecordCallClicked: CallAdapter.recordThisCallToggle()
onOpenSelectionWindow: { onOpenSelectionWindow: {
SelectScreenWindowCreation.createSelectScreenWindowObject(appWindow) SelectScreenWindowCreation.createSelectScreenWindowObject(appWindow)
SelectScreenWindowCreation.showSelectScreenWindow(callPreviewId, windowSelection) SelectScreenWindowCreation.showSelectScreenWindow(callPreviewId, windowSelection)

View file

@ -28,7 +28,6 @@ import "../../commoncomponents"
Rectangle { Rectangle {
id: root id: root
property bool isAudioOnly: false
property var sipKeys: [ property var sipKeys: [
"1", "2", "3", "A", "1", "2", "3", "A",
"4", "5", "6", "B", "4", "5", "6", "B",
@ -123,13 +122,6 @@ Rectangle {
Connections { Connections {
target: CallAdapter target: CallAdapter
function onCallInfosChanged(audioOnly, accountId, convUid) {
if (callStackMainView.currentItem.stackNumber === CallStackView.OngoingPageStack
&& responsibleConvUid === convUid && responsibleAccountId === accountId) {
ongoingCallPage.isAudioOnly = audioOnly
}
}
function onCallStatusChanged(status, accountId, convUid) { function onCallStatusChanged(status, accountId, convUid) {
if (callStackMainView.currentItem.stackNumber === CallStackView.InitialPageStack if (callStackMainView.currentItem.stackNumber === CallStackView.InitialPageStack
&& responsibleConvUid === convUid && responsibleAccountId === accountId) { && responsibleConvUid === convUid && responsibleAccountId === accountId) {
@ -143,8 +135,6 @@ Rectangle {
property int stackNumber: CallStackView.OngoingPageStack property int stackNumber: CallStackView.OngoingPageStack
isAudioOnly: root.isAudioOnly
visible: callStackMainView.currentItem.stackNumber === stackNumber visible: callStackMainView.currentItem.stackNumber === stackNumber
} }
@ -153,8 +143,6 @@ Rectangle {
property int stackNumber: CallStackView.InitialPageStack property int stackNumber: CallStackView.InitialPageStack
isAudioOnly: root.isAudioOnly
onCallAccepted: { onCallAccepted: {
CallAdapter.acceptACall(responsibleAccountId, responsibleConvUid) CallAdapter.acceptACall(responsibleAccountId, responsibleConvUid)
mainViewSidePanel.selectTab(SidePanelTabBar.Conversations) mainViewSidePanel.selectTab(SidePanelTabBar.Conversations)

View file

@ -31,11 +31,6 @@ import "../js/screenrubberbandcreation.js" as ScreenRubberBandCreation
ContextMenuAutoLoader { ContextMenuAutoLoader {
id: root id: root
property bool isSIP: false
property bool isPaused: false
property bool isVideoMuted: false
property bool isRecording: false
property bool windowSelection: false property bool windowSelection: false
signal pluginItemClicked signal pluginItemClicked
@ -47,9 +42,11 @@ ContextMenuAutoLoader {
GeneralMenuItem { GeneralMenuItem {
id: resumePauseCall id: resumePauseCall
canTrigger: isSIP canTrigger: CurrentCall.isSIP
itemName: isPaused ? JamiStrings.resumeCall : JamiStrings.pauseCall itemName: CurrentCall.isPaused ?
iconSource: isPaused ? JamiStrings.resumeCall :
JamiStrings.pauseCall
iconSource: CurrentCall.isPaused ?
JamiResources.play_circle_outline_24dp_svg : JamiResources.play_circle_outline_24dp_svg :
JamiResources.pause_circle_outline_24dp_svg JamiResources.pause_circle_outline_24dp_svg
onClicked: { onClicked: {
@ -59,7 +56,7 @@ ContextMenuAutoLoader {
GeneralMenuItem { GeneralMenuItem {
id: inputPanelSIP id: inputPanelSIP
canTrigger: isSIP canTrigger: CurrentCall.isSIP
itemName: JamiStrings.sipInputPanel itemName: JamiStrings.sipInputPanel
iconSource: JamiResources.ic_keypad_svg iconSource: JamiResources.ic_keypad_svg
onClicked: { onClicked: {
@ -69,10 +66,10 @@ ContextMenuAutoLoader {
GeneralMenuItem { GeneralMenuItem {
id: callTransfer id: callTransfer
canTrigger: isSIP canTrigger: CurrentCall.isSIP
itemName: JamiStrings.transferCall itemName: JamiStrings.transferCall
iconSource: JamiResources.phone_forwarded_24dp_svg iconSource: JamiResources.phone_forwarded_24dp_svg
addMenuSeparatorAfter: isSIP addMenuSeparatorAfter: CurrentCall.isSIP
onClicked: { onClicked: {
root.transferCallButtonClicked() root.transferCallButtonClicked()
} }
@ -80,7 +77,9 @@ ContextMenuAutoLoader {
GeneralMenuItem { GeneralMenuItem {
id: localRecord id: localRecord
itemName: root.isRecording ? JamiStrings.stopRec : JamiStrings.startRec itemName: CurrentCall.isRecordingLocally ?
JamiStrings.stopRec :
JamiStrings.startRec
iconSource: JamiResources.fiber_manual_record_24dp_svg iconSource: JamiResources.fiber_manual_record_24dp_svg
iconColor: JamiTheme.recordIconColor iconColor: JamiTheme.recordIconColor
onClicked: { onClicked: {
@ -103,8 +102,9 @@ ContextMenuAutoLoader {
GeneralMenuItem { GeneralMenuItem {
id: stopSharing id: stopSharing
canTrigger: AvAdapter.isSharing() canTrigger: CurrentCall.isSharing
&& !isSIP && !isVideoMuted && !CurrentCall.isSIP
&& !CurrentCall.isVideoMuted
itemName: JamiStrings.stopSharing itemName: JamiStrings.stopSharing
iconSource: JamiResources.share_stop_black_24dp_svg iconSource: JamiResources.share_stop_black_24dp_svg
iconColor: JamiTheme.redColor iconColor: JamiTheme.redColor
@ -113,8 +113,9 @@ ContextMenuAutoLoader {
GeneralMenuItem { GeneralMenuItem {
id: shareScreen id: shareScreen
canTrigger: CurrentAccount.videoEnabled_Video && AvAdapter.currentRenderingDeviceType !== Video.DeviceType.DISPLAY canTrigger: CurrentAccount.videoEnabled_Video
&& !isSIP && AvAdapter.currentRenderingDeviceType !== Video.DeviceType.DISPLAY
&& !CurrentCall.isSIP
itemName: JamiStrings.shareScreen itemName: JamiStrings.shareScreen
iconSource: JamiResources.laptop_black_24dp_svg iconSource: JamiResources.laptop_black_24dp_svg
onClicked: { onClicked: {
@ -129,8 +130,10 @@ ContextMenuAutoLoader {
GeneralMenuItem { GeneralMenuItem {
id: shareWindow id: shareWindow
canTrigger: Qt.platform.os === "linux" && CurrentAccount.videoEnabled_Video && AvAdapter.currentRenderingDeviceType !== Video.DeviceType.DISPLAY canTrigger: Qt.platform.os === "linux"
&& !isSIP && CurrentAccount.videoEnabled_Video
&& AvAdapter.currentRenderingDeviceType !== Video.DeviceType.DISPLAY
&& !CurrentCall.isSIP
itemName: JamiStrings.shareWindow itemName: JamiStrings.shareWindow
iconSource: JamiResources.window_black_24dp_svg iconSource: JamiResources.window_black_24dp_svg
onClicked: { onClicked: {
@ -144,8 +147,9 @@ ContextMenuAutoLoader {
GeneralMenuItem { GeneralMenuItem {
id: shareScreenArea id: shareScreenArea
canTrigger: CurrentAccount.videoEnabled_Video && AvAdapter.currentRenderingDeviceType !== Video.DeviceType.DISPLAY canTrigger: CurrentAccount.videoEnabled_Video
&& !isSIP && AvAdapter.currentRenderingDeviceType !== Video.DeviceType.DISPLAY
&& !CurrentCall.isSIP
itemName: JamiStrings.shareScreenArea itemName: JamiStrings.shareScreenArea
iconSource: JamiResources.share_area_black_24dp_svg iconSource: JamiResources.share_area_black_24dp_svg
onClicked: { onClicked: {
@ -160,7 +164,8 @@ ContextMenuAutoLoader {
GeneralMenuItem { GeneralMenuItem {
id: shareFile id: shareFile
canTrigger: CurrentAccount.videoEnabled_Video && !isSIP canTrigger: CurrentAccount.videoEnabled_Video
&& !CurrentCall.isSIP
itemName: JamiStrings.shareFile itemName: JamiStrings.shareFile
iconSource: JamiResources.file_black_24dp_svg iconSource: JamiResources.file_black_24dp_svg
onClicked: { onClicked: {
@ -170,7 +175,8 @@ ContextMenuAutoLoader {
GeneralMenuItem { GeneralMenuItem {
id: viewPlugin id: viewPlugin
canTrigger: PluginAdapter.isEnabled && PluginAdapter.callMediaHandlersListCount canTrigger: PluginAdapter.isEnabled &&
PluginAdapter.callMediaHandlersListCount
itemName: JamiStrings.viewPlugin itemName: JamiStrings.viewPlugin
iconSource: JamiResources.extension_24dp_svg iconSource: JamiResources.extension_24dp_svg
onClicked: { onClicked: {

View file

@ -31,7 +31,7 @@ Rectangle {
id: root id: root
property bool isIncoming: false property bool isIncoming: false
property bool isAudioOnly: false property bool isAudioOnly: CurrentCall.isAudioOnly
property int callStatus: 0 property int callStatus: 0
signal callCanceled signal callCanceled

View file

@ -32,7 +32,21 @@ Item {
id: root id: root
property string timeText: "00:00" property string timeText: "00:00"
property string remoteRecordingLabel: "" property string remoteRecordingLabel
Connections {
target: CurrentCall
function onIsRecordingRemotelyChanged() {
var label = ""
if (CurrentCall.isRecordingRemotely) {
label = CurrentCall.remoteRecorderNameList.join(", ") + " "
label += (CurrentCall.remoteRecorderNameList.length > 1) ?
JamiStrings.areRecording : JamiStrings.isRecording
}
root.remoteRecordingLabel = label
}
}
property alias callActionBar: __callActionBar property alias callActionBar: __callActionBar
@ -43,8 +57,6 @@ Item {
property string muteAlertMessage: "" property string muteAlertMessage: ""
property bool muteAlertActive: false property bool muteAlertActive: false
property bool remoteRecording: false
property bool isRecording: false
onMuteAlertActiveChanged: { onMuteAlertActiveChanged: {
if (muteAlertActive) { if (muteAlertActive) {
@ -117,7 +129,7 @@ Item {
root.timeText = CallAdapter.getCallDurationTime( root.timeText = CallAdapter.getCallDurationTime(
LRCInstance.currentAccountId, LRCInstance.currentAccountId,
LRCInstance.selectedConvUid) LRCInstance.selectedConvUid)
if (root.opacity === 0 && !root.remoteRecording) if (root.opacity === 0 && !CurrentCall.isRecordingRemotely)
root.remoteRecordingLabel = "" root.remoteRecordingLabel = ""
} }
} }
@ -149,11 +161,11 @@ Item {
font.pointSize: JamiTheme.textFontSize font.pointSize: JamiTheme.textFontSize
text: { text: {
if (!root.isAudioOnly) { if (!CurrentCall.isAudioOnly) {
if (remoteRecordingLabel === "") { if (root.remoteRecordingLabel === "") {
return CurrentConversation.title return CurrentConversation.title
} else { } else {
return remoteRecordingLabel return root.remoteRecordingLabel
} }
} }
return "" return ""
@ -180,7 +192,7 @@ Item {
Rectangle { Rectangle {
id: recordingRect id: recordingRect
visible: root.isRecording || root.remoteRecording visible: CurrentCall.isRecordingLocally || CurrentCall.isRecordingRemotely
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
Layout.rightMargin: JamiTheme.preferredMarginSize Layout.rightMargin: JamiTheme.preferredMarginSize

View file

@ -42,9 +42,8 @@ Rectangle {
property int previewMarginYBottom: previewMargin + 84 property int previewMarginYBottom: previewMargin + 84
property int previewToX: 0 property int previewToX: 0
property int previewToY: 0 property int previewToY: 0
property bool isAudioOnly: false
property var linkedWebview: null property var linkedWebview: null
property string callPreviewId: "" property string callPreviewId
onCallPreviewIdChanged: { onCallPreviewIdChanged: {
controlPreview.start() controlPreview.start()
@ -56,8 +55,6 @@ Rectangle {
if (accountPeerPair[0] === "" || accountPeerPair[1] === "") if (accountPeerPair[0] === "" || accountPeerPair[1] === "")
return return
contactImage.imageId = accountPeerPair[1] contactImage.imageId = accountPeerPair[1]
distantRenderer.rendererId = UtilsAdapter.getCallId(accountPeerPair[0],
accountPeerPair[1])
} }
function setLinkedWebview(webViewId) { function setLinkedWebview(webViewId) {
@ -68,7 +65,6 @@ Rectangle {
closeInCallConversation) closeInCallConversation)
} }
Connections { Connections {
target: UtilsAdapter target: UtilsAdapter
@ -178,11 +174,12 @@ Rectangle {
VideoView { VideoView {
id: distantRenderer id: distantRenderer
rendererId: CurrentCall.id
anchors.centerIn: parent anchors.centerIn: parent
anchors.fill: parent anchors.fill: parent
z: -1 z: -1
visible: participantsLayer.count === 0 && !root.isAudioOnly visible: !CurrentCall.isConference && !CurrentCall.isAudioOnly
} }
ParticipantsLayer { ParticipantsLayer {
@ -190,17 +187,16 @@ Rectangle {
anchors.fill: parent anchors.fill: parent
anchors.centerIn: parent anchors.centerIn: parent
anchors.margins: 1 anchors.margins: 1
visible: participantsLayer.count !== 0 visible: CurrentCall.isConference
participantsSide: callOverlay.participantsSide participantsSide: callOverlay.participantsSide
onCountChanged: {
callOverlay.isConference = participantsLayer.count > 0
}
} }
LocalVideo { LocalVideo {
id: previewRenderer id: previewRenderer
visible: (CurrentCall.isSharing || !CurrentCall.isVideoMuted)
&& !CurrentCall.isConference
height: width * invAspectRatio height: width * invAspectRatio
width: Math.max(callPageMainRect.width / 5, JamiTheme.minimumPreviewWidth) width: Math.max(callPageMainRect.width / 5, JamiTheme.minimumPreviewWidth)
x: callPageMainRect.width - previewRenderer.width - previewMargin x: callPageMainRect.width - previewRenderer.width - previewMargin
@ -217,6 +213,7 @@ Rectangle {
previewRenderer.startWithId(rendId) previewRenderer.startWithId(rendId)
} }
} }
onVisibleChanged: { onVisibleChanged: {
controlPreview.stop() controlPreview.stop()
if (visible) { if (visible) {
@ -297,7 +294,6 @@ Rectangle {
id: callOverlay id: callOverlay
anchors.fill: parent anchors.fill: parent
isConference: participantsLayer.count > 0
function toggleConversation() { function toggleConversation() {
if (inCallMessageWebViewStack.visible) if (inCallMessageWebViewStack.visible)
@ -307,35 +303,19 @@ Rectangle {
} }
Connections { Connections {
target: CallAdapter target: CurrentCall
function onUpdateOverlay(isPaused, isAudioOnly, isAudioMuted, function onPreviewIdChanged() {
isSIP, isGrid, previewId) { if (CurrentCall.previewId !== "") {
root.callPreviewId = previewId if (root.callPreviewId !== "" &&
callOverlay.showOnHoldImage(isPaused) root.callPreviewId !== CurrentCall.previewId) {
root.isAudioOnly = isAudioOnly VideoDevices.stopDevice(root.callPreviewId)
callOverlay.showOnHoldImage(isPaused)
audioCallPageRectCentralRect.visible = !isPaused && root.isAudioOnly && participantsLayer.count === 0
callOverlay.updateUI(isPaused, isAudioOnly,
isAudioMuted,
isSIP,
isGrid)
callOverlay.isVideoMuted = !AvAdapter.isCapturing()
callOverlay.sharingActive = AvAdapter.isSharing()
previewRenderer.visible = (AvAdapter.isSharing() || AvAdapter.isCapturing()) && participantsLayer.count == 0
} }
VideoDevices.startDevice(CurrentCall.previewId)
function onShowOnHoldLabel(isPaused) { } else {
callOverlay.showOnHoldImage(isPaused) VideoDevices.stopDevice(root.callPreviewId)
audioCallPageRectCentralRect.visible = !isPaused && root.isAudioOnly && participantsLayer.count === 0
} }
root.callPreviewId = CurrentCall.previewId
function onRemoteRecordingChanged(label, state) {
callOverlay.showRemoteRecording(label, state)
}
function onEraseRemoteRecording() {
callOverlay.resetRemoteRecording()
} }
} }
@ -367,7 +347,9 @@ Rectangle {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
visible: root.isAudioOnly visible: !CurrentCall.isPaused &&
CurrentCall.isAudioOnly &&
!CurrentCall.isConference
ConversationAvatar { ConversationAvatar {
id: contactImage id: contactImage

View file

@ -58,7 +58,7 @@ Popup {
function onAboutToShow(visible) { function onAboutToShow(visible) {
// Reset the model on each show. // Reset the model on each show.
if (isCall) { if (isCall) {
pluginhandlerPickerListView.model = PluginAdapter.getMediaHandlerSelectableModel(CurrentConversation.callId) pluginhandlerPickerListView.model = PluginAdapter.getMediaHandlerSelectableModel(CurrentCall.id)
} else { } else {
var peerId = CurrentConversation.isSwarm ? CurrentConversation.id : CurrentConversation.uris[0] var peerId = CurrentConversation.isSwarm ? CurrentConversation.id : CurrentConversation.uris[0]
pluginhandlerPickerListView.model = PluginAdapter.getChatHandlerSelectableModel(LRCInstance.currentAccountId, peerId) pluginhandlerPickerListView.model = PluginAdapter.getChatHandlerSelectableModel(LRCInstance.currentAccountId, peerId)
@ -68,8 +68,8 @@ Popup {
function toggleHandlerSlot(handlerId, isLoaded) { function toggleHandlerSlot(handlerId, isLoaded) {
if (isCall) { if (isCall) {
PluginModel.toggleCallMediaHandler(handlerId, CurrentConversation.callId, !isLoaded) PluginModel.toggleCallMediaHandler(handlerId, CurrentCall.id, !isLoaded)
pluginhandlerPickerListView.model = PluginAdapter.getMediaHandlerSelectableModel(CurrentConversation.callId) pluginhandlerPickerListView.model = PluginAdapter.getMediaHandlerSelectableModel(CurrentCall.id)
} else { } else {
var accountId = LRCInstance.currentAccountId var accountId = LRCInstance.currentAccountId
var peerId = CurrentConversation.isSwarm ? CurrentConversation.id : CurrentConversation.uris[0] var peerId = CurrentConversation.isSwarm ? CurrentConversation.id : CurrentConversation.uris[0]
@ -125,7 +125,7 @@ Popup {
model: { model: {
if (isCall) { if (isCall) {
return PluginAdapter.getMediaHandlerSelectableModel(CurrentConversation.callId) return PluginAdapter.getMediaHandlerSelectableModel(CurrentCall.id)
} else { } else {
var peerId = CurrentConversation.isSwarm ? CurrentConversation.id : CurrentConversation.uris[0] var peerId = CurrentConversation.isSwarm ? CurrentConversation.id : CurrentConversation.uris[0]
return PluginAdapter.getChatHandlerSelectableModel(LRCInstance.currentAccountId, peerId) return PluginAdapter.getChatHandlerSelectableModel(LRCInstance.currentAccountId, peerId)

View file

@ -29,6 +29,7 @@
#include "previewengine.h" #include "previewengine.h"
#include "utilsadapter.h" #include "utilsadapter.h"
#include "conversationsadapter.h" #include "conversationsadapter.h"
#include "currentcall.h"
#include "currentconversation.h" #include "currentconversation.h"
#include "currentaccount.h" #include "currentaccount.h"
#include "videodevices.h" #include "videodevices.h"
@ -118,6 +119,7 @@ registerTypes(QQmlEngine* engine,
auto accountAdapter = new AccountAdapter(settingsManager, systemTray, lrcInstance, parent); auto accountAdapter = new AccountAdapter(settingsManager, systemTray, lrcInstance, parent);
auto utilsAdapter = new UtilsAdapter(settingsManager, systemTray, lrcInstance, parent); auto utilsAdapter = new UtilsAdapter(settingsManager, systemTray, lrcInstance, parent);
auto pluginAdapter = new PluginAdapter(lrcInstance, parent); auto pluginAdapter = new PluginAdapter(lrcInstance, parent);
auto currentCall = new CurrentCall(lrcInstance, parent);
auto currentConversation = new CurrentConversation(lrcInstance, parent); auto currentConversation = new CurrentConversation(lrcInstance, parent);
auto currentAccount = new CurrentAccount(lrcInstance, settingsManager, parent); auto currentAccount = new CurrentAccount(lrcInstance, settingsManager, parent);
auto tipsModel = new TipsModel(settingsManager, parent); auto tipsModel = new TipsModel(settingsManager, parent);
@ -135,6 +137,7 @@ registerTypes(QQmlEngine* engine,
QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, accountAdapter, "AccountAdapter"); QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, accountAdapter, "AccountAdapter");
QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, utilsAdapter, "UtilsAdapter"); QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, utilsAdapter, "UtilsAdapter");
QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, pluginAdapter, "PluginAdapter"); QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, pluginAdapter, "PluginAdapter");
QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, currentCall, "CurrentCall");
QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, currentConversation, "CurrentConversation"); QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, currentConversation, "CurrentConversation");
QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, currentAccount, "CurrentAccount"); QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, currentAccount, "CurrentAccount");
QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, videoDevices, "VideoDevices"); QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, videoDevices, "VideoDevices");

View file

@ -139,7 +139,7 @@ struct Info
bool isAudioOnly = false; bool isAudioOnly = false;
Layout layout = Layout::GRID; Layout layout = Layout::GRID;
VectorMapStringString mediaList = {}; VectorMapStringString mediaList = {};
QSet<QString> peerRec {}; QStringList recordingPeers {};
bool hasMediaWithType(const QString& type, const QString& mediaType) const bool hasMediaWithType(const QString& type, const QString& mediaType) const
{ {

View file

@ -429,7 +429,7 @@ Q_SIGNALS:
* Emitted when the rendered image changed * Emitted when the rendered image changed
* @param confId * @param confId
*/ */
void onParticipantsChanged(const QString& confId) const; void participantsChanged(const QString& confId) const;
/** /**
* Emitted when a call starts * Emitted when a call starts
* @param callId * @param callId
@ -469,15 +469,19 @@ Q_SIGNALS:
int urgentCount) const; int urgentCount) const;
/** /**
* Listen from CallbacksHandler when the peer start recording * Provides notification of a new set of recording peers once a change has occured,
* in the form of a list, as QSet<QString> is not directly QML compatible.
* @param callId * @param callId
* @param contactId * @param recorders
* @param peerName
* @param state the new state
*/ */
void remoteRecordingChanged(const QString& callId, void remoteRecordersChanged(const QString& callId, const QStringList& recorders) const;
const QSet<QString>& peerRec,
bool state) const; /**
* Provides notification of change in the local call recording state.,
* @param callId
* @param state
*/
void recordingStateChanged(const QString& callId, bool state) const;
/*! /*!
* Emitted before new pending conferences are inserted into the underlying list * Emitted before new pending conferences are inserted into the underlying list
@ -504,6 +508,11 @@ Q_SIGNALS:
*/ */
void callInfosChanged(const QString& accountId, const QString& callId) const; void callInfosChanged(const QString& accountId, const QString& callId) const;
/**
* Emit currentCallChanged
*/
void currentCallChanged(const QString& callId) const;
private: private:
std::unique_ptr<CallModelPimpl> pimpl_; std::unique_ptr<CallModelPimpl> pimpl_;
}; };

View file

@ -201,6 +201,12 @@ CallbacksHandler::CallbacksHandler(const Lrc& parent)
&CallbacksHandler::slotConferenceChanged, &CallbacksHandler::slotConferenceChanged,
Qt::QueuedConnection); Qt::QueuedConnection);
connect(&CallManager::instance(),
&CallManagerInterface::recordingStateChanged,
this,
&CallbacksHandler::recordingStateChanged,
Qt::QueuedConnection);
connect(&CallManager::instance(), connect(&CallManager::instance(),
&CallManagerInterface::incomingMessage, &CallManagerInterface::incomingMessage,
this, this,
@ -572,6 +578,12 @@ CallbacksHandler::slotConferenceCreated(const QString& accountId, const QString&
Q_EMIT conferenceCreated(accountId, callId); Q_EMIT conferenceCreated(accountId, callId);
} }
void
CallbacksHandler::slotConferenceRemoved(const QString& accountId, const QString& callId)
{
Q_EMIT conferenceRemoved(accountId, callId);
}
void void
CallbacksHandler::slotConferenceChanged(const QString& accountId, CallbacksHandler::slotConferenceChanged(const QString& accountId,
const QString& callId, const QString& callId,
@ -581,12 +593,6 @@ CallbacksHandler::slotConferenceChanged(const QString& accountId,
slotCallStateChanged(accountId, callId, state, 0); slotCallStateChanged(accountId, callId, state, 0);
} }
void
CallbacksHandler::slotConferenceRemoved(const QString& accountId, const QString& callId)
{
Q_EMIT conferenceRemoved(accountId, callId);
}
void void
CallbacksHandler::slotAccountMessageStatusChanged(const QString& accountId, CallbacksHandler::slotAccountMessageStatusChanged(const QString& accountId,
const QString& conversationId, const QString& conversationId,

View file

@ -377,6 +377,7 @@ Q_SIGNALS:
void conversationPreferencesUpdated(const QString& accountId, void conversationPreferencesUpdated(const QString& accountId,
const QString& conversationId, const QString& conversationId,
const MapStringString& preferences); const MapStringString& preferences);
void recordingStateChanged(const QString& callId, bool state);
/** /**
* Emitted when a conversation receives a new position * Emitted when a conversation receives a new position

View file

@ -259,9 +259,16 @@ public Q_SLOTS:
/** /**
* Listen from CallbacksHandler when the peer start recording * Listen from CallbacksHandler when the peer start recording
* @param callId * @param callId
* @param peerUri
* @param state the new state * @param state the new state
*/ */
void remoteRecordingChanged(const QString& callId, const QString& peerNumber, bool state); void onRemoteRecordingChanged(const QString& callId, const QString& peerUri, bool state);
/**
* Listen from CallbacksHandler when we start/stop recording
* @param callId
* @param state the new state
*/
void onRecordingStateChanged(const QString& callId, bool state);
}; };
CallModel::CallModel(const account::Info& owner, CallModel::CallModel(const account::Info& owner,
@ -975,7 +982,11 @@ CallModelPimpl::CallModelPimpl(const CallModel& linked,
connect(&callbacksHandler, connect(&callbacksHandler,
&CallbacksHandler::remoteRecordingChanged, &CallbacksHandler::remoteRecordingChanged,
this, this,
&CallModelPimpl::remoteRecordingChanged); &CallModelPimpl::onRemoteRecordingChanged);
connect(&callbacksHandler,
&CallbacksHandler::recordingStateChanged,
this,
&CallModelPimpl::onRecordingStateChanged);
#ifndef ENABLE_LIBWRAP #ifndef ENABLE_LIBWRAP
// Only necessary with dbus since the daemon runs separately // Only necessary with dbus since the daemon runs separately
@ -1140,7 +1151,7 @@ CallModel::setCurrentCall(const QString& callId) const
} }
if (!lrc::api::Lrc::holdConferences) { if (!lrc::api::Lrc::holdConferences) {
return; continue;
} }
// If the account is the host and it is attached to the conference, // If the account is the host and it is attached to the conference,
// then we should hold it. // then we should hold it.
@ -1157,6 +1168,8 @@ CallModel::setCurrentCall(const QString& callId) const
} }
} }
} }
Q_EMIT currentCallChanged(callId);
} }
void void
@ -1599,7 +1612,7 @@ CallModelPimpl::slotOnConferenceInfosUpdated(const QString& confId,
} }
} }
Q_EMIT linked.callInfosChanged(linked.owner.id, confId); Q_EMIT linked.callInfosChanged(linked.owner.id, confId);
Q_EMIT linked.onParticipantsChanged(confId); Q_EMIT linked.participantsChanged(confId);
} }
bool bool
@ -1686,13 +1699,14 @@ CallModelPimpl::sendProfile(const QString& callId)
} }
void void
CallModelPimpl::remoteRecordingChanged(const QString& callId, const QString& peerNumber, bool state) CallModelPimpl::onRemoteRecordingChanged(const QString& callId, const QString& peerUri, bool state)
{ {
auto it = calls.find(callId); auto it = calls.find(callId);
if (it == calls.end() or not it->second) if (it == calls.end() or !it->second) {
return; return;
}
auto uri = peerNumber; auto uri = peerUri;
if (uri.contains("ring:")) if (uri.contains("ring:"))
uri.remove("ring:"); uri.remove("ring:");
@ -1701,15 +1715,19 @@ CallModelPimpl::remoteRecordingChanged(const QString& callId, const QString& pee
if (uri.contains("@ring.dht")) if (uri.contains("@ring.dht"))
uri.remove("@ring.dht"); uri.remove("@ring.dht");
// Add peer to peerRec set // Add/remove peer to recordingPeers, preventing duplicates.
if (state && not it->second->peerRec.contains(uri)) if (state && !it->second->recordingPeers.contains(uri))
it->second->peerRec.insert(uri); it->second->recordingPeers.append(uri);
else if (!state && it->second->recordingPeers.contains(uri))
it->second->recordingPeers.removeAll(uri);
// remove peer from peerRec set Q_EMIT linked.remoteRecordersChanged(callId, it->second->recordingPeers);
if (!state && it->second->peerRec.contains(uri)) }
it->second->peerRec.remove(uri);
Q_EMIT linked.remoteRecordingChanged(callId, it->second->peerRec, state); void
CallModelPimpl::onRecordingStateChanged(const QString& callId, bool state)
{
Q_EMIT linked.recordingStateChanged(callId, state);
} }
} // namespace lrc } // namespace lrc