diff --git a/CMakeLists.txt b/CMakeLists.txt index 277dd7e987118894bfd4b9e8b053cfd26585a6b5..b640c2c792cf69d204c780eb6ffa326337baf2d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -221,7 +221,7 @@ set(COMMON_SOURCES ${APP_SRC_DIR}/callparticipantsmodel.cpp ${APP_SRC_DIR}/tipsmodel.cpp ${APP_SRC_DIR}/positioning.cpp -) + ${APP_SRC_DIR}/currentcall.cpp) set(COMMON_HEADERS ${APP_SRC_DIR}/avatarimageprovider.h @@ -280,7 +280,8 @@ set(COMMON_HEADERS ${APP_SRC_DIR}/videoprovider.h ${APP_SRC_DIR}/callparticipantsmodel.h ${APP_SRC_DIR}/tipsmodel.h - ${APP_SRC_DIR}/positioning.h) + ${APP_SRC_DIR}/positioning.h + ${APP_SRC_DIR}/currentcall.h) if(WITH_WEBENGINE) list(APPEND COMMON_SOURCES diff --git a/extras/packaging/update/sparkle/Sparkle b/extras/packaging/update/sparkle/Sparkle deleted file mode 160000 index 2195ee0883efc92828a0cf33b830f43f9bd7e01b..0000000000000000000000000000000000000000 --- a/extras/packaging/update/sparkle/Sparkle +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2195ee0883efc92828a0cf33b830f43f9bd7e01b diff --git a/src/app/calladapter.cpp b/src/app/calladapter.cpp index be21ec2d8eb0b88ed5bd0f043f6028fbbdc28d8d..fe6f7d4cc2567f3db7739608f7848cea91f20539 100644 --- a/src/app/calladapter.cpp +++ b/src/app/calladapter.cpp @@ -238,7 +238,6 @@ CallAdapter::onCallStatusChanged(const QString& callId, int code) 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) { @@ -330,25 +329,11 @@ CallAdapter::onCallInfosChanged(const QString& accountId, const QString& callId) } Q_EMIT callInfosChanged(call.isAudioOnly, accountId, convInfo.uid); participantsModel_->setConferenceLayout(static_cast<int>(call.layout), callId); - 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(lrcInstance_->get_selectedConvUid(), accountId_); - if (callId == currentCallId) - updateRecordingPeers(); -} - void 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_->setConferenceLayout(static_cast<int>(call->layout), call->id); } @@ -569,7 +552,6 @@ CallAdapter::getConferencesInfos() const .getAccountInfo(accountId_) .callModel.get() ->getParticipantsInfos(callId); - int index = 0; for (int index = 0; index < participantsModel.getParticipants().size(); index++) { auto participant = participantsModel.toQJsonObject(index); fillParticipantData(participant); @@ -642,12 +624,6 @@ CallAdapter::connectCallModel(const QString& accountId) QOverload<const QString&, int>::of(&CallAdapter::onCallStatusChanged), Qt::UniqueConnection); - connect(accInfo.callModel.get(), - &CallModel::remoteRecordingChanged, - this, - &CallAdapter::onRemoteRecordingChanged, - Qt::UniqueConnection); - connect(accInfo.callModel.get(), &CallModel::callAddedToConference, this, @@ -660,32 +636,6 @@ CallAdapter::connectCallModel(const QString& accountId) 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 CallAdapter::sipInputPanelPlayDTMF(const QString& key) { @@ -698,49 +648,6 @@ CallAdapter::sipInputPanelPlayDTMF(const QString& 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 CallAdapter::saveConferenceSubcalls() { @@ -1075,7 +982,6 @@ CallAdapter::holdThisCallToggle() if (callModel->hasCall(callId)) { callModel->togglePause(callId); } - Q_EMIT showOnHoldLabel(true); } void diff --git a/src/app/calladapter.h b/src/app/calladapter.h index 88e375cb14d0839218f3be37b92b687adc61f484..cf6d3eec91f8950981df30bda97af1100b1b3f56 100644 --- a/src/app/calladapter.h +++ b/src/app/calladapter.h @@ -100,15 +100,6 @@ Q_SIGNALS: // For Call Overlay 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: void onShowIncomingCallView(const QString& accountId, const QString& convUid); @@ -117,18 +108,15 @@ public Q_SLOTS: void onCallStatusChanged(const QString& accountId, const QString& callId); void onCallInfosChanged(const QString& accountId, const QString& callId); 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 onParticipantAdded(const QString& callId, int index); void onParticipantRemoved(const QString& callId, int index); void onParticipantUpdated(const QString& callId, int index); private: - void updateRecordingPeers(bool eraseLabelOnEmpty = false); void showNotification(const QString& accountId, const QString& convUid); void fillParticipantData(QJsonObject& participant) const; void preventScreenSaver(bool state); - void updateCallOverlay(const lrc::api::conversation::Info& convInfo); void saveConferenceSubcalls(); QString accountId_; diff --git a/src/app/currentcall.cpp b/src/app/currentcall.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4c3ddb64deead4011b5e893339ad2a02aba44f78 --- /dev/null +++ b/src/app/currentcall.cpp @@ -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); +} diff --git a/src/app/currentcall.h b/src/app/currentcall.h new file mode 100644 index 0000000000000000000000000000000000000000..c04a65fc8414268875813c7e126559860e5c4487 --- /dev/null +++ b/src/app/currentcall.h @@ -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_; +}; diff --git a/src/app/mainview/MainView.qml b/src/app/mainview/MainView.qml index 944788b46a2f167491171db214e95caf8379ef8d..4a19aea658163451f947702ec4504c8f9a077302 100644 --- a/src/app/mainview/MainView.qml +++ b/src/app/mainview/MainView.qml @@ -186,7 +186,6 @@ Rectangle { callStackView.setLinkedWebview(chatView) callStackView.responsibleAccountId = LRCInstance.currentAccountId callStackView.responsibleConvUid = convId - callStackView.isAudioOnly = item.isAudioOnly currentConvUID = convId if (item.callState === Call.Status.IN_PROGRESS || diff --git a/src/app/mainview/components/CallActionBar.qml b/src/app/mainview/components/CallActionBar.qml index 717981b95564a78b38ff9d8cbec1c801a9e8ef61..04c8a49b8762d8e7ad2df8ce0236e9bc1a1b773f 100644 --- a/src/app/mainview/components/CallActionBar.qml +++ b/src/app/mainview/components/CallActionBar.qml @@ -162,7 +162,7 @@ Control { layoutModel.get(index).ActiveSetting = layoutManager.isCallFullscreen break case JamiStrings.mosaic: - if (!isGrid) + if (!CurrentCall.isGrid) CallAdapter.showGridConferenceLayout() break case JamiStrings.participantsSide: @@ -189,10 +189,10 @@ Control { } onTriggered: { layoutModel.clear() - if (isConference) { + if (CurrentCall.isConference) { layoutModel.append({"Name": JamiStrings.mosaic, "IconSource": JamiResources.mosaic_black_24dp_svg, - "ActiveSetting": isGrid, + "ActiveSetting": CurrentCall.isGrid, "TopMargin": true, "BottomMargin": true, "SectionEnd": true}) @@ -223,8 +223,8 @@ Control { "ActiveSetting": layoutManager.isCallFullscreen, "TopMargin": true, "BottomMargin": true, - "SectionEnd": isConference}) - if (isConference) { + "SectionEnd": CurrentCall.isConference}) + if (CurrentCall.isConference) { layoutModel.append({"Name": JamiStrings.hideSpectators, "IconSource": JamiResources.videocam_off_24dp_svg, "ActiveSetting": UtilsAdapter.getAppValue(Settings.HideSpectators), @@ -263,6 +263,7 @@ Control { JamiResources.micro_black_24dp_svg icon.color: checked ? "red" : "white" text: !checked ? JamiStrings.mute : JamiStrings.unmute + checked: CurrentCall.isAudioMuted property var menuAction: audioInputMenuAction }, Action { @@ -282,6 +283,7 @@ Control { JamiResources.videocam_24dp_svg icon.color: checked ? "red" : "white" text: !checked ? JamiStrings.muteCamera : JamiStrings.unmuteCamera + checked: !CurrentCall.isCapturing property var menuAction: videoInputMenuAction } ] @@ -314,11 +316,13 @@ Control { Action { id: resumePauseCallAction onTriggered: root.resumePauseCallClicked() - icon.source: isPaused ? + icon.source: CurrentCall.isPaused ? JamiResources.play_circle_outline_24dp_svg : JamiResources.pause_circle_outline_24dp_svg icon.color: "white" - text: isPaused ? JamiStrings.resumeCall : JamiStrings.pauseCall + text: CurrentCall.isPaused ? + JamiStrings.resumeCall : + JamiStrings.pauseCall }, Action { id: inputPanelSIPAction @@ -337,17 +341,17 @@ Control { Action { id: shareAction onTriggered: { - if (sharingActive) + if (CurrentCall.isSharing) root.stopSharingClicked() else root.shareScreenClicked() } - icon.source: sharingActive ? + icon.source: CurrentCall.isSharing ? JamiResources.share_stop_black_24dp_svg : JamiResources.share_screen_black_24dp_svg - icon.color: sharingActive ? + icon.color: CurrentCall.isSharing ? "red" : "white" - text: sharingActive ? + text: CurrentCall.isSharing ? JamiStrings.stopSharing : JamiStrings.shareScreen property real size: 34 @@ -362,12 +366,13 @@ Control { text: checked ? JamiStrings.lowerHand : JamiStrings.raiseHand + checked: CurrentCall.isHandRaised property real size: 34 }, Action { id: layoutAction onTriggered: { - if (!isGrid) + if (!CurrentCall.isGrid) CallAdapter.showGridConferenceLayout() } checkable: true @@ -386,6 +391,7 @@ Control { text: !checked ? JamiStrings.startRec : JamiStrings.stopRec property bool blinksWhenChecked: true property real size: 28 + checked: CurrentCall.isRecordingLocally onCheckedChanged: function(checked) { CallOverlayModel.setUrgentCount(recordAction, checked ? -1 : 0) @@ -397,27 +403,29 @@ Control { icon.source: JamiResources.plugins_24dp_svg icon.color: "white" text: JamiStrings.viewPlugin - enabled: PluginAdapter.isEnabled && PluginAdapter.callMediaHandlersListCount + enabled: PluginAdapter.isEnabled + && PluginAdapter.callMediaHandlersListCount } ] property var overflowItemCount Connections { - target: callOverlay + target: CurrentCall - function onIsAudioOnlyChanged() { Qt.callLater(reset) } - function onIsSIPChanged() { Qt.callLater(reset) } + function onIsActiveChanged() { if (CurrentCall.isActive) reset() } + function onIsRecordingLocallyChanged() { Qt.callLater(reset) } + function onIsHandRaisedChanged() { Qt.callLater(reset) } + function onIsConferenceChanged() { Qt.callLater(reset) } function onIsModeratorChanged() { Qt.callLater(reset) } + function onIsSIPChanged() { Qt.callLater(reset) } + function onIsAudioOnlyChanged() { Qt.callLater(reset) } function onIsAudioMutedChanged() { Qt.callLater(reset) } function onIsVideoMutedChanged() { Qt.callLater(reset) } - function onIsRecordingChanged() { Qt.callLater(reset) } - function onLocalHandRaisedChanged() { Qt.callLater(reset) } - function onIsConferenceChanged() { Qt.callLater(reset) } } + Connections { target: CurrentAccount - function onVideoEnabledVideoChanged() { reset() } } @@ -433,29 +441,24 @@ Control { // overflow controls CallOverlayModel.addSecondaryControl(audioOutputAction) - if (isConference) { + if (CurrentCall.isConference) { CallOverlayModel.addSecondaryControl(raiseHandAction) - raiseHandAction.checked = CallAdapter.isHandRaised() } - if (isModerator && !isSIP) + if (CurrentCall.isModerator && !CurrentCall.isSIP) CallOverlayModel.addSecondaryControl(addPersonAction) - if (isSIP) { + if (CurrentCall.isSIP) { CallOverlayModel.addSecondaryControl(resumePauseCallAction) CallOverlayModel.addSecondaryControl(inputPanelSIPAction) CallOverlayModel.addSecondaryControl(callTransferAction) } CallOverlayModel.addSecondaryControl(chatAction) - if (CurrentAccount.videoEnabled_Video) + if (CurrentAccount.videoEnabled_Video && !CurrentCall.isSIP) CallOverlayModel.addSecondaryControl(shareAction) CallOverlayModel.addSecondaryControl(layoutAction) CallOverlayModel.addSecondaryControl(recordAction) if (pluginsAction.enabled) CallOverlayModel.addSecondaryControl(pluginsAction) overflowItemCount = CallOverlayModel.secondaryModel().rowCount() - - muteAudioAction.checked = isAudioMuted - recordAction.checked = CallAdapter.isRecordingThisCall() - muteVideoAction.checked = isAudioOnly ? true : isVideoMuted } Item { diff --git a/src/app/mainview/components/CallButtonDelegate.qml b/src/app/mainview/components/CallButtonDelegate.qml index d6650188649957799e63b63bb3b4b25e4559131c..2e72489084a747e210904194349feb39e8a48acc 100644 --- a/src/app/mainview/components/CallButtonDelegate.qml +++ b/src/app/mainview/components/CallButtonDelegate.qml @@ -39,6 +39,7 @@ ItemDelegate { action: ItemAction checkable: ItemAction.checkable + hoverEnabled: ItemAction.enabled // hide the action's visual elements like the blurry looking icon icon.source: "" @@ -122,7 +123,11 @@ ItemDelegate { anchors.centerIn: parent 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 { loops: Animation.Infinite @@ -172,7 +177,8 @@ ItemDelegate { indicator: null - visible: menuAction !== undefined && !UrgentCount && menuAction.enabled + visible: ItemAction.enabled + && menuAction !== undefined && !UrgentCount && menuAction.enabled y: isVertical ? 0 : -4 x: isVertical ? -4 : 0 diff --git a/src/app/mainview/components/CallOverlay.qml b/src/app/mainview/components/CallOverlay.qml index ad4fab6dda30d31dc423b1139464caf3a12012f3..1a869aa110a3763db25c60c25206725d49dd5ffd 100644 --- a/src/app/mainview/components/CallOverlay.qml +++ b/src/app/mainview/components/CallOverlay.qml @@ -35,48 +35,11 @@ import "../../commoncomponents" Item { 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 localHandRaised - property bool sharingActive: AvAdapter.isSharing() - property string callId: "" signal chatButtonClicked 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() { ContactPickerCreation.closeContactPicker() sipInputPanel.close() @@ -94,29 +57,6 @@ Item { 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 { anchors.fill: parent onDropped: function(drop) { @@ -158,7 +98,7 @@ Item { width: 200 height: 200 - visible: false + visible: CurrentCall.isPaused source: JamiResources.ic_pause_white_100px_svg } @@ -199,17 +139,10 @@ Item { PluginHandlerPickerCreation.openPluginHandlerPicker() } - function recordClicked() { - CallAdapter.recordThisCallToggle() - updateUI() - } - MainOverlay { id: mainOverlay anchors.fill: parent - isRecording: root.isRecording - remoteRecording: root.remoteRecording Connections { target: mainOverlay.callActionBar @@ -222,7 +155,7 @@ Item { function onShareWindowClicked() { openShareWindow() } function onStopSharingClicked() { AvAdapter.stopSharing() } function onShareScreenAreaClicked() { openShareScreenArea() } - function onRecordCallClicked() { recordClicked() } + function onRecordCallClicked() { CallAdapter.recordThisCallToggle() } function onShareFileClicked() { jamiFileDialog.open() } function onPluginsClicked() { openPluginsMenu() } function onFullScreenClicked() { root.fullScreenClicked() } @@ -232,13 +165,9 @@ Item { CallViewContextMenu { id: callViewContextMenu - isSIP: root.isSIP - isPaused: root.isPaused - isRecording: root.isRecording - onTransferCallButtonClicked: openContactPicker(ContactList.TRANSFER) onPluginItemClicked: openPluginsMenu() - onRecordCallClicked: root.recordClicked() + onRecordCallClicked: CallAdapter.recordThisCallToggle() onOpenSelectionWindow: { SelectScreenWindowCreation.createSelectScreenWindowObject(appWindow) SelectScreenWindowCreation.showSelectScreenWindow(callPreviewId, windowSelection) diff --git a/src/app/mainview/components/CallStackView.qml b/src/app/mainview/components/CallStackView.qml index 98d58627d83d80c3552b78d2994401801ebeb129..b4e74bbf1683938bbb60b98ec0d8bf07591ae898 100644 --- a/src/app/mainview/components/CallStackView.qml +++ b/src/app/mainview/components/CallStackView.qml @@ -28,7 +28,6 @@ import "../../commoncomponents" Rectangle { id: root - property bool isAudioOnly: false property var sipKeys: [ "1", "2", "3", "A", "4", "5", "6", "B", @@ -123,13 +122,6 @@ Rectangle { Connections { 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) { if (callStackMainView.currentItem.stackNumber === CallStackView.InitialPageStack && responsibleConvUid === convUid && responsibleAccountId === accountId) { @@ -143,8 +135,6 @@ Rectangle { property int stackNumber: CallStackView.OngoingPageStack - isAudioOnly: root.isAudioOnly - visible: callStackMainView.currentItem.stackNumber === stackNumber } @@ -153,8 +143,6 @@ Rectangle { property int stackNumber: CallStackView.InitialPageStack - isAudioOnly: root.isAudioOnly - onCallAccepted: { CallAdapter.acceptACall(responsibleAccountId, responsibleConvUid) mainViewSidePanel.selectTab(SidePanelTabBar.Conversations) diff --git a/src/app/mainview/components/CallViewContextMenu.qml b/src/app/mainview/components/CallViewContextMenu.qml index 1b8e86c42ccb6c5f0890aab2cfc938c65fec1d3d..733178729a6be6c27cbd0346576af50f6dfc9bac 100644 --- a/src/app/mainview/components/CallViewContextMenu.qml +++ b/src/app/mainview/components/CallViewContextMenu.qml @@ -31,11 +31,6 @@ import "../js/screenrubberbandcreation.js" as ScreenRubberBandCreation ContextMenuAutoLoader { id: root - property bool isSIP: false - property bool isPaused: false - property bool isVideoMuted: false - property bool isRecording: false - property bool windowSelection: false signal pluginItemClicked @@ -47,9 +42,11 @@ ContextMenuAutoLoader { GeneralMenuItem { id: resumePauseCall - canTrigger: isSIP - itemName: isPaused ? JamiStrings.resumeCall : JamiStrings.pauseCall - iconSource: isPaused ? + canTrigger: CurrentCall.isSIP + itemName: CurrentCall.isPaused ? + JamiStrings.resumeCall : + JamiStrings.pauseCall + iconSource: CurrentCall.isPaused ? JamiResources.play_circle_outline_24dp_svg : JamiResources.pause_circle_outline_24dp_svg onClicked: { @@ -59,7 +56,7 @@ ContextMenuAutoLoader { GeneralMenuItem { id: inputPanelSIP - canTrigger: isSIP + canTrigger: CurrentCall.isSIP itemName: JamiStrings.sipInputPanel iconSource: JamiResources.ic_keypad_svg onClicked: { @@ -69,10 +66,10 @@ ContextMenuAutoLoader { GeneralMenuItem { id: callTransfer - canTrigger: isSIP + canTrigger: CurrentCall.isSIP itemName: JamiStrings.transferCall iconSource: JamiResources.phone_forwarded_24dp_svg - addMenuSeparatorAfter: isSIP + addMenuSeparatorAfter: CurrentCall.isSIP onClicked: { root.transferCallButtonClicked() } @@ -80,7 +77,9 @@ ContextMenuAutoLoader { GeneralMenuItem { id: localRecord - itemName: root.isRecording ? JamiStrings.stopRec : JamiStrings.startRec + itemName: CurrentCall.isRecordingLocally ? + JamiStrings.stopRec : + JamiStrings.startRec iconSource: JamiResources.fiber_manual_record_24dp_svg iconColor: JamiTheme.recordIconColor onClicked: { @@ -103,8 +102,9 @@ ContextMenuAutoLoader { GeneralMenuItem { id: stopSharing - canTrigger: AvAdapter.isSharing() - && !isSIP && !isVideoMuted + canTrigger: CurrentCall.isSharing + && !CurrentCall.isSIP + && !CurrentCall.isVideoMuted itemName: JamiStrings.stopSharing iconSource: JamiResources.share_stop_black_24dp_svg iconColor: JamiTheme.redColor @@ -113,8 +113,9 @@ ContextMenuAutoLoader { GeneralMenuItem { id: shareScreen - canTrigger: CurrentAccount.videoEnabled_Video && AvAdapter.currentRenderingDeviceType !== Video.DeviceType.DISPLAY - && !isSIP + canTrigger: CurrentAccount.videoEnabled_Video + && AvAdapter.currentRenderingDeviceType !== Video.DeviceType.DISPLAY + && !CurrentCall.isSIP itemName: JamiStrings.shareScreen iconSource: JamiResources.laptop_black_24dp_svg onClicked: { @@ -129,8 +130,10 @@ ContextMenuAutoLoader { GeneralMenuItem { id: shareWindow - canTrigger: Qt.platform.os === "linux" && CurrentAccount.videoEnabled_Video && AvAdapter.currentRenderingDeviceType !== Video.DeviceType.DISPLAY - && !isSIP + canTrigger: Qt.platform.os === "linux" + && CurrentAccount.videoEnabled_Video + && AvAdapter.currentRenderingDeviceType !== Video.DeviceType.DISPLAY + && !CurrentCall.isSIP itemName: JamiStrings.shareWindow iconSource: JamiResources.window_black_24dp_svg onClicked: { @@ -144,8 +147,9 @@ ContextMenuAutoLoader { GeneralMenuItem { id: shareScreenArea - canTrigger: CurrentAccount.videoEnabled_Video && AvAdapter.currentRenderingDeviceType !== Video.DeviceType.DISPLAY - && !isSIP + canTrigger: CurrentAccount.videoEnabled_Video + && AvAdapter.currentRenderingDeviceType !== Video.DeviceType.DISPLAY + && !CurrentCall.isSIP itemName: JamiStrings.shareScreenArea iconSource: JamiResources.share_area_black_24dp_svg onClicked: { @@ -160,7 +164,8 @@ ContextMenuAutoLoader { GeneralMenuItem { id: shareFile - canTrigger: CurrentAccount.videoEnabled_Video && !isSIP + canTrigger: CurrentAccount.videoEnabled_Video + && !CurrentCall.isSIP itemName: JamiStrings.shareFile iconSource: JamiResources.file_black_24dp_svg onClicked: { @@ -170,7 +175,8 @@ ContextMenuAutoLoader { GeneralMenuItem { id: viewPlugin - canTrigger: PluginAdapter.isEnabled && PluginAdapter.callMediaHandlersListCount + canTrigger: PluginAdapter.isEnabled && + PluginAdapter.callMediaHandlersListCount itemName: JamiStrings.viewPlugin iconSource: JamiResources.extension_24dp_svg onClicked: { diff --git a/src/app/mainview/components/InitialCallPage.qml b/src/app/mainview/components/InitialCallPage.qml index cf8dd9b0dbd6e0f4ccb93b1ee315be49e6d0c0e2..ca5eca44cf67307bd1419fc46889b5b1a3337a1a 100644 --- a/src/app/mainview/components/InitialCallPage.qml +++ b/src/app/mainview/components/InitialCallPage.qml @@ -31,7 +31,7 @@ Rectangle { id: root property bool isIncoming: false - property bool isAudioOnly: false + property bool isAudioOnly: CurrentCall.isAudioOnly property int callStatus: 0 signal callCanceled diff --git a/src/app/mainview/components/MainOverlay.qml b/src/app/mainview/components/MainOverlay.qml index 7ecedde7c0c6f2626ff678c7d21f85e301b1b04d..78129d9af22f8ba7eaf9d1a0ecd3ad9a27034ec1 100644 --- a/src/app/mainview/components/MainOverlay.qml +++ b/src/app/mainview/components/MainOverlay.qml @@ -32,7 +32,21 @@ Item { id: root 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 @@ -43,8 +57,6 @@ Item { property string muteAlertMessage: "" property bool muteAlertActive: false - property bool remoteRecording: false - property bool isRecording: false onMuteAlertActiveChanged: { if (muteAlertActive) { @@ -117,7 +129,7 @@ Item { root.timeText = CallAdapter.getCallDurationTime( LRCInstance.currentAccountId, LRCInstance.selectedConvUid) - if (root.opacity === 0 && !root.remoteRecording) + if (root.opacity === 0 && !CurrentCall.isRecordingRemotely) root.remoteRecordingLabel = "" } } @@ -149,11 +161,11 @@ Item { font.pointSize: JamiTheme.textFontSize text: { - if (!root.isAudioOnly) { - if (remoteRecordingLabel === "") { + if (!CurrentCall.isAudioOnly) { + if (root.remoteRecordingLabel === "") { return CurrentConversation.title } else { - return remoteRecordingLabel + return root.remoteRecordingLabel } } return "" @@ -180,7 +192,7 @@ Item { Rectangle { id: recordingRect - visible: root.isRecording || root.remoteRecording + visible: CurrentCall.isRecordingLocally || CurrentCall.isRecordingRemotely Layout.alignment: Qt.AlignVCenter | Qt.AlignRight Layout.rightMargin: JamiTheme.preferredMarginSize diff --git a/src/app/mainview/components/OngoingCallPage.qml b/src/app/mainview/components/OngoingCallPage.qml index 93e0b2434646bf048705b3a1fe233d824018e65c..a1adc306b8671238f369bed67b9527f89c84cf16 100644 --- a/src/app/mainview/components/OngoingCallPage.qml +++ b/src/app/mainview/components/OngoingCallPage.qml @@ -42,9 +42,8 @@ Rectangle { property int previewMarginYBottom: previewMargin + 84 property int previewToX: 0 property int previewToY: 0 - property bool isAudioOnly: false property var linkedWebview: null - property string callPreviewId: "" + property string callPreviewId onCallPreviewIdChanged: { controlPreview.start() @@ -56,8 +55,6 @@ Rectangle { if (accountPeerPair[0] === "" || accountPeerPair[1] === "") return contactImage.imageId = accountPeerPair[1] - distantRenderer.rendererId = UtilsAdapter.getCallId(accountPeerPair[0], - accountPeerPair[1]) } function setLinkedWebview(webViewId) { @@ -68,7 +65,6 @@ Rectangle { closeInCallConversation) } - Connections { target: UtilsAdapter @@ -178,11 +174,12 @@ Rectangle { VideoView { id: distantRenderer + rendererId: CurrentCall.id anchors.centerIn: parent anchors.fill: parent z: -1 - visible: participantsLayer.count === 0 && !root.isAudioOnly + visible: !CurrentCall.isConference && !CurrentCall.isAudioOnly } ParticipantsLayer { @@ -190,17 +187,16 @@ Rectangle { anchors.fill: parent anchors.centerIn: parent anchors.margins: 1 - visible: participantsLayer.count !== 0 + visible: CurrentCall.isConference participantsSide: callOverlay.participantsSide - - onCountChanged: { - callOverlay.isConference = participantsLayer.count > 0 - } } LocalVideo { id: previewRenderer + visible: (CurrentCall.isSharing || !CurrentCall.isVideoMuted) + && !CurrentCall.isConference + height: width * invAspectRatio width: Math.max(callPageMainRect.width / 5, JamiTheme.minimumPreviewWidth) x: callPageMainRect.width - previewRenderer.width - previewMargin @@ -217,6 +213,7 @@ Rectangle { previewRenderer.startWithId(rendId) } } + onVisibleChanged: { controlPreview.stop() if (visible) { @@ -297,7 +294,6 @@ Rectangle { id: callOverlay anchors.fill: parent - isConference: participantsLayer.count > 0 function toggleConversation() { if (inCallMessageWebViewStack.visible) @@ -307,35 +303,19 @@ Rectangle { } Connections { - target: CallAdapter - - function onUpdateOverlay(isPaused, isAudioOnly, isAudioMuted, - isSIP, isGrid, previewId) { - root.callPreviewId = previewId - callOverlay.showOnHoldImage(isPaused) - root.isAudioOnly = isAudioOnly - 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 - } - - function onShowOnHoldLabel(isPaused) { - callOverlay.showOnHoldImage(isPaused) - audioCallPageRectCentralRect.visible = !isPaused && root.isAudioOnly && participantsLayer.count === 0 - } - - function onRemoteRecordingChanged(label, state) { - callOverlay.showRemoteRecording(label, state) - } - - function onEraseRemoteRecording() { - callOverlay.resetRemoteRecording() + target: CurrentCall + + function onPreviewIdChanged() { + if (CurrentCall.previewId !== "") { + if (root.callPreviewId !== "" && + root.callPreviewId !== CurrentCall.previewId) { + VideoDevices.stopDevice(root.callPreviewId) + } + VideoDevices.startDevice(CurrentCall.previewId) + } else { + VideoDevices.stopDevice(root.callPreviewId) + } + root.callPreviewId = CurrentCall.previewId } } @@ -367,7 +347,9 @@ Rectangle { anchors.left: parent.left anchors.right: parent.right - visible: root.isAudioOnly + visible: !CurrentCall.isPaused && + CurrentCall.isAudioOnly && + !CurrentCall.isConference ConversationAvatar { id: contactImage diff --git a/src/app/mainview/components/PluginHandlerPicker.qml b/src/app/mainview/components/PluginHandlerPicker.qml index 0270d8fd92e8305c553cc6e42d36e5134d0c4205..b5f5c0170040d3256c365f5543928d2abf38bcad 100644 --- a/src/app/mainview/components/PluginHandlerPicker.qml +++ b/src/app/mainview/components/PluginHandlerPicker.qml @@ -58,7 +58,7 @@ Popup { function onAboutToShow(visible) { // Reset the model on each show. if (isCall) { - pluginhandlerPickerListView.model = PluginAdapter.getMediaHandlerSelectableModel(CurrentConversation.callId) + pluginhandlerPickerListView.model = PluginAdapter.getMediaHandlerSelectableModel(CurrentCall.id) } else { var peerId = CurrentConversation.isSwarm ? CurrentConversation.id : CurrentConversation.uris[0] pluginhandlerPickerListView.model = PluginAdapter.getChatHandlerSelectableModel(LRCInstance.currentAccountId, peerId) @@ -68,8 +68,8 @@ Popup { function toggleHandlerSlot(handlerId, isLoaded) { if (isCall) { - PluginModel.toggleCallMediaHandler(handlerId, CurrentConversation.callId, !isLoaded) - pluginhandlerPickerListView.model = PluginAdapter.getMediaHandlerSelectableModel(CurrentConversation.callId) + PluginModel.toggleCallMediaHandler(handlerId, CurrentCall.id, !isLoaded) + pluginhandlerPickerListView.model = PluginAdapter.getMediaHandlerSelectableModel(CurrentCall.id) } else { var accountId = LRCInstance.currentAccountId var peerId = CurrentConversation.isSwarm ? CurrentConversation.id : CurrentConversation.uris[0] @@ -125,7 +125,7 @@ Popup { model: { if (isCall) { - return PluginAdapter.getMediaHandlerSelectableModel(CurrentConversation.callId) + return PluginAdapter.getMediaHandlerSelectableModel(CurrentCall.id) } else { var peerId = CurrentConversation.isSwarm ? CurrentConversation.id : CurrentConversation.uris[0] return PluginAdapter.getChatHandlerSelectableModel(LRCInstance.currentAccountId, peerId) diff --git a/src/app/qmlregister.cpp b/src/app/qmlregister.cpp index 6a57fe61c88cc043ae55d2e7256f9b02a2ffd589..2359c8014effef0f3ecbe264506eac2950db02bc 100644 --- a/src/app/qmlregister.cpp +++ b/src/app/qmlregister.cpp @@ -29,6 +29,7 @@ #include "previewengine.h" #include "utilsadapter.h" #include "conversationsadapter.h" +#include "currentcall.h" #include "currentconversation.h" #include "currentaccount.h" #include "videodevices.h" @@ -118,6 +119,7 @@ registerTypes(QQmlEngine* engine, auto accountAdapter = new AccountAdapter(settingsManager, systemTray, lrcInstance, parent); auto utilsAdapter = new UtilsAdapter(settingsManager, systemTray, lrcInstance, parent); auto pluginAdapter = new PluginAdapter(lrcInstance, parent); + auto currentCall = new CurrentCall(lrcInstance, parent); auto currentConversation = new CurrentConversation(lrcInstance, parent); auto currentAccount = new CurrentAccount(lrcInstance, 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, utilsAdapter, "UtilsAdapter"); 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, currentAccount, "CurrentAccount"); QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, videoDevices, "VideoDevices"); diff --git a/src/libclient/api/call.h b/src/libclient/api/call.h index 6f117a42d2f8e27d28c9b89b4ac2eec9b4426a86..99196736f398ab478cb62dfa9e1cf4ffc6c24c40 100644 --- a/src/libclient/api/call.h +++ b/src/libclient/api/call.h @@ -139,7 +139,7 @@ struct Info bool isAudioOnly = false; Layout layout = Layout::GRID; VectorMapStringString mediaList = {}; - QSet<QString> peerRec {}; + QStringList recordingPeers {}; bool hasMediaWithType(const QString& type, const QString& mediaType) const { diff --git a/src/libclient/api/callmodel.h b/src/libclient/api/callmodel.h index e635617e56542f9584af447227c0894779d4ec2d..2103a81611d49fddf2b56cf643a917fe16d4c1d2 100644 --- a/src/libclient/api/callmodel.h +++ b/src/libclient/api/callmodel.h @@ -429,7 +429,7 @@ Q_SIGNALS: * Emitted when the rendered image changed * @param confId */ - void onParticipantsChanged(const QString& confId) const; + void participantsChanged(const QString& confId) const; /** * Emitted when a call starts * @param callId @@ -469,15 +469,19 @@ Q_SIGNALS: 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 contactId - * @param peerName - * @param state the new state + * @param recorders */ - void remoteRecordingChanged(const QString& callId, - const QSet<QString>& peerRec, - bool state) const; + void remoteRecordersChanged(const QString& callId, const QStringList& recorders) 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 @@ -504,6 +508,11 @@ Q_SIGNALS: */ void callInfosChanged(const QString& accountId, const QString& callId) const; + /** + * Emit currentCallChanged + */ + void currentCallChanged(const QString& callId) const; + private: std::unique_ptr<CallModelPimpl> pimpl_; }; diff --git a/src/libclient/callbackshandler.cpp b/src/libclient/callbackshandler.cpp index 5ef8c3e9547b90c7f6c43103a301d822abde8153..b7b29606b0d3512cee498b023e4958de701871b1 100644 --- a/src/libclient/callbackshandler.cpp +++ b/src/libclient/callbackshandler.cpp @@ -201,6 +201,12 @@ CallbacksHandler::CallbacksHandler(const Lrc& parent) &CallbacksHandler::slotConferenceChanged, Qt::QueuedConnection); + connect(&CallManager::instance(), + &CallManagerInterface::recordingStateChanged, + this, + &CallbacksHandler::recordingStateChanged, + Qt::QueuedConnection); + connect(&CallManager::instance(), &CallManagerInterface::incomingMessage, this, @@ -572,6 +578,12 @@ CallbacksHandler::slotConferenceCreated(const QString& accountId, const QString& Q_EMIT conferenceCreated(accountId, callId); } +void +CallbacksHandler::slotConferenceRemoved(const QString& accountId, const QString& callId) +{ + Q_EMIT conferenceRemoved(accountId, callId); +} + void CallbacksHandler::slotConferenceChanged(const QString& accountId, const QString& callId, @@ -581,12 +593,6 @@ CallbacksHandler::slotConferenceChanged(const QString& accountId, slotCallStateChanged(accountId, callId, state, 0); } -void -CallbacksHandler::slotConferenceRemoved(const QString& accountId, const QString& callId) -{ - Q_EMIT conferenceRemoved(accountId, callId); -} - void CallbacksHandler::slotAccountMessageStatusChanged(const QString& accountId, const QString& conversationId, diff --git a/src/libclient/callbackshandler.h b/src/libclient/callbackshandler.h index cc6cdf3ab65478f583a2a3448fd58922c74f7fb8..72eec60b243b7e786c7a74d02ab39f676dc20908 100644 --- a/src/libclient/callbackshandler.h +++ b/src/libclient/callbackshandler.h @@ -377,6 +377,7 @@ Q_SIGNALS: void conversationPreferencesUpdated(const QString& accountId, const QString& conversationId, const MapStringString& preferences); + void recordingStateChanged(const QString& callId, bool state); /** * Emitted when a conversation receives a new position diff --git a/src/libclient/callmodel.cpp b/src/libclient/callmodel.cpp index 8e2de9284e4539b9971b7de3403de4b0ee846b0a..c4e454ca403fea6c4760e772e1a6abae638057ee 100644 --- a/src/libclient/callmodel.cpp +++ b/src/libclient/callmodel.cpp @@ -259,9 +259,16 @@ public Q_SLOTS: /** * Listen from CallbacksHandler when the peer start recording * @param callId + * @param peerUri * @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, @@ -382,7 +389,7 @@ CallModel::createCall(const QString& uri, bool isAudioOnly, VectorMapStringStrin } #ifdef ENABLE_LIBWRAP auto callId = CallManager::instance().placeCallWithMedia(owner.id, uri, mediaList); -#else // dbus +#else // dbus // do not use auto here (QDBusPendingReply<QString>) QString callId = CallManager::instance().placeCallWithMedia(owner.id, uri, mediaList); #endif // ENABLE_LIBWRAP @@ -975,7 +982,11 @@ CallModelPimpl::CallModelPimpl(const CallModel& linked, connect(&callbacksHandler, &CallbacksHandler::remoteRecordingChanged, this, - &CallModelPimpl::remoteRecordingChanged); + &CallModelPimpl::onRemoteRecordingChanged); + connect(&callbacksHandler, + &CallbacksHandler::recordingStateChanged, + this, + &CallModelPimpl::onRecordingStateChanged); #ifndef ENABLE_LIBWRAP // Only necessary with dbus since the daemon runs separately @@ -1140,7 +1151,7 @@ CallModel::setCurrentCall(const QString& callId) const } if (!lrc::api::Lrc::holdConferences) { - return; + continue; } // If the account is the host and it is attached to the conference, // then we should hold it. @@ -1157,6 +1168,8 @@ CallModel::setCurrentCall(const QString& callId) const } } } + + Q_EMIT currentCallChanged(callId); } void @@ -1599,7 +1612,7 @@ CallModelPimpl::slotOnConferenceInfosUpdated(const QString& confId, } } Q_EMIT linked.callInfosChanged(linked.owner.id, confId); - Q_EMIT linked.onParticipantsChanged(confId); + Q_EMIT linked.participantsChanged(confId); } bool @@ -1686,13 +1699,14 @@ CallModelPimpl::sendProfile(const QString& callId) } 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); - if (it == calls.end() or not it->second) + if (it == calls.end() or !it->second) { return; + } - auto uri = peerNumber; + auto uri = peerUri; if (uri.contains("ring:")) uri.remove("ring:"); @@ -1701,15 +1715,19 @@ CallModelPimpl::remoteRecordingChanged(const QString& callId, const QString& pee if (uri.contains("@ring.dht")) uri.remove("@ring.dht"); - // Add peer to peerRec set - if (state && not it->second->peerRec.contains(uri)) - it->second->peerRec.insert(uri); + // Add/remove peer to recordingPeers, preventing duplicates. + if (state && !it->second->recordingPeers.contains(uri)) + it->second->recordingPeers.append(uri); + else if (!state && it->second->recordingPeers.contains(uri)) + it->second->recordingPeers.removeAll(uri); - // remove peer from peerRec set - if (!state && it->second->peerRec.contains(uri)) - it->second->peerRec.remove(uri); + Q_EMIT linked.remoteRecordersChanged(callId, it->second->recordingPeers); +} - 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