diff --git a/src/app/calladapter.cpp b/src/app/calladapter.cpp index 0f710b17509468007ccb799e6d347b9351d66347..783c5ab9b7695e471ef0b465100f3f00e98fd0fe 100644 --- a/src/app/calladapter.cpp +++ b/src/app/calladapter.cpp @@ -740,18 +740,36 @@ CallAdapter::maximizeParticipant(const QString& uri) try { const auto call = callModel->getCall(confId); auto participants = getConferencesInfos(); + std::vector<QJsonObject> activeParticipants = {}; + bool removeActive = false; for (auto part : participants) { auto participant = part.toJsonObject(); - if (participant[lrc::api::ParticipantsInfosStrings::URI].toString() == uri) { - auto active = participant[lrc::api::ParticipantsInfosStrings::ACTIVE].toBool(); + auto isParticipant = participant[lrc::api::ParticipantsInfosStrings::URI].toString() + == uri; + auto active = participant[lrc::api::ParticipantsInfosStrings::ACTIVE].toBool(); + if (active && !isParticipant) { + activeParticipants.push_back(participant); + } + if (isParticipant) { + auto deviceId = participant[lrc::api::ParticipantsInfosStrings::DEVICE].toString(); + auto streamId = participant[lrc::api::ParticipantsInfosStrings::STREAMID].toString(); // Else, continue. if (!active) { - callModel->setActiveParticipant(confId, uri); + callModel->setActiveStream(confId, uri, deviceId, streamId, true); callModel->setConferenceLayout(confId, lrc::api::call::Layout::ONE_WITH_SMALL); } else if (call.layout == lrc::api::call::Layout::ONE_WITH_SMALL) { + removeActive = true; callModel->setConferenceLayout(confId, lrc::api::call::Layout::ONE); } - return; + } + } + if (removeActive) { + // If in Big, we can remove other actives + for (const auto& p : activeParticipants) { + auto puri = p[lrc::api::ParticipantsInfosStrings::URI].toString(); + auto deviceId = p[lrc::api::ParticipantsInfosStrings::DEVICE].toString(); + auto streamId = p[lrc::api::ParticipantsInfosStrings::STREAMID].toString(); + callModel->setActiveStream(confId, puri, deviceId, streamId, false); } } } catch (...) { @@ -771,21 +789,29 @@ CallAdapter::minimizeParticipant(const QString& uri) try { const auto call = callModel->getCall(confId); auto participants = getConferencesInfos(); + auto activeParticipants = 0; for (auto& part : participants) { auto participant = part.toJsonObject(); - if (participant[lrc::api::ParticipantsInfosStrings::URI].toString() == uri) { - auto active = participant[lrc::api::ParticipantsInfosStrings::ACTIVE].toBool(); - if (active) { - if (call.layout == lrc::api::call::Layout::ONE) { - callModel->setConferenceLayout(confId, - lrc::api::call::Layout::ONE_WITH_SMALL); - } else { - callModel->setConferenceLayout(confId, lrc::api::call::Layout::GRID); - } + if (participant[lrc::api::ParticipantsInfosStrings::ACTIVE].toBool()) { + activeParticipants += 1; + if (participant[lrc::api::ParticipantsInfosStrings::URI].toString() == uri + && call.layout == lrc::api::call::Layout::ONE_WITH_SMALL) { + auto deviceId = participant[lrc::api::ParticipantsInfosStrings::DEVICE] + .toString(); + auto streamId = participant[lrc::api::ParticipantsInfosStrings::STREAMID].toString(); + callModel->setActiveStream(confId, uri, deviceId, streamId, false); } - return; } } + if (activeParticipants == 1) { + // only one active left, we can change the layout. + if (call.layout == lrc::api::call::Layout::ONE) { + callModel->setConferenceLayout(confId, lrc::api::call::Layout::ONE_WITH_SMALL); + } else { + callModel->setConferenceLayout(confId, lrc::api::call::Layout::GRID); + } + } + } catch (...) { } } @@ -904,7 +930,7 @@ CallAdapter::isHandRaised(const QString& uri) const } void -CallAdapter::setHandRaised(const QString& uri, bool state) +CallAdapter::raiseHand(const QString& uri, const QString& deviceId, bool state) { auto* callModel = lrcInstance_->getAccountInfo(accountId_).callModel.get(); const auto& convInfo = lrcInstance_->getConversationFromConvUid( @@ -913,7 +939,7 @@ CallAdapter::setHandRaised(const QString& uri, bool state) if (confId.isEmpty()) confId = convInfo.callId; try { - callModel->setHandRaised(accountId_, confId, uri, state); + callModel->raiseHand(confId, uri, deviceId, state); } catch (...) { } } @@ -934,7 +960,10 @@ CallAdapter::setModerator(const QString& uri, const bool state) } void -CallAdapter::muteParticipant(const QString& uri, const bool state) +CallAdapter::muteParticipant(const QString& accountUri, + const QString& deviceId, + const QString& streamId, + const bool state) { auto* callModel = lrcInstance_->getAccountInfo(accountId_).callModel.get(); const auto& convInfo = lrcInstance_->getConversationFromConvUid( @@ -945,7 +974,7 @@ CallAdapter::muteParticipant(const QString& uri, const bool state) confId = convInfo.callId; try { const auto call = callModel->getCall(confId); - callModel->muteParticipant(confId, uri, state); + callModel->muteStream(confId, accountUri, deviceId, streamId, state); } catch (...) { } } @@ -984,7 +1013,7 @@ CallAdapter::getMuteState(const QString& uri) const } void -CallAdapter::hangupParticipant(const QString& uri) +CallAdapter::hangupParticipant(const QString& uri, const QString& deviceId) { auto* callModel = lrcInstance_->getAccountInfo(accountId_).callModel.get(); const auto& convInfo = lrcInstance_->getConversationFromConvUid( @@ -995,7 +1024,7 @@ CallAdapter::hangupParticipant(const QString& uri) confId = convInfo.callId; try { const auto call = callModel->getCall(confId); - callModel->hangupParticipant(confId, uri); + callModel->hangupParticipant(confId, uri, deviceId); } catch (...) { } } diff --git a/src/app/calladapter.h b/src/app/calladapter.h index 89dabc0df660a5c199e17ac6f3fa524afddc5f01..09b0e45ea40c0021adb49478be93f7d32502a9a4 100644 --- a/src/app/calladapter.h +++ b/src/app/calladapter.h @@ -69,16 +69,16 @@ public: Q_INVOKABLE void setModerator(const QString& uri, const bool state); Q_INVOKABLE bool isModerator(const QString& uri = {}) const; Q_INVOKABLE bool isHandRaised(const QString& uri = {}) const; - Q_INVOKABLE void setHandRaised(const QString& uri, bool state); + Q_INVOKABLE void raiseHand(const QString& uri, const QString& deviceId, bool state); Q_INVOKABLE void holdThisCallToggle(); Q_INVOKABLE void muteThisCallToggle(bool mute); Q_INVOKABLE void recordThisCallToggle(); Q_INVOKABLE void videoPauseThisCallToggle(bool mute); Q_INVOKABLE bool isRecordingThisCall(); Q_INVOKABLE QVariantList getConferencesInfos() const; - Q_INVOKABLE void muteParticipant(const QString& uri, const bool state); + Q_INVOKABLE void muteParticipant(const QString& accountUri, const QString& deviceId, const QString& sinkId, const bool state); Q_INVOKABLE MuteStates getMuteState(const QString& uri) const; - Q_INVOKABLE void hangupParticipant(const QString& uri); + Q_INVOKABLE void hangupParticipant(const QString& uri, const QString& deviceId); Q_INVOKABLE void updateCall(const QString& convUid = {}, const QString& accountId = {}, bool forceCallOnly = false); diff --git a/src/app/callparticipantsmodel.cpp b/src/app/callparticipantsmodel.cpp index 21ddc8ce58ada844555851fa33977f9a83e4f78e..2a02a98e7ccb816e1e3de8a4b6a3b007874c22ad 100644 --- a/src/app/callparticipantsmodel.cpp +++ b/src/app/callparticipantsmodel.cpp @@ -59,6 +59,9 @@ CallParticipantsModel::data(const QModelIndex& index, int role) const case Role::BestName: return QVariant::fromValue( participant.item.value(lrc::api::ParticipantsInfosStrings::BESTNAME)); + case Role::Device: + return QVariant::fromValue( + participant.item.value(lrc::api::ParticipantsInfosStrings::DEVICE)); case Role::Active: return QVariant::fromValue( participant.item.value(lrc::api::ParticipantsInfosStrings::ACTIVE)); @@ -85,7 +88,7 @@ CallParticipantsModel::data(const QModelIndex& index, int role) const participant.item.value(lrc::api::ParticipantsInfosStrings::AVATAR)); case Role::SinkId: return QVariant::fromValue( - participant.item.value(lrc::api::ParticipantsInfosStrings::SINKID)); + participant.item.value(lrc::api::ParticipantsInfosStrings::STREAMID)); case Role::Height: return QVariant::fromValue( participant.item.value(lrc::api::ParticipantsInfosStrings::HEIGHT)); diff --git a/src/app/mainview/components/CallActionBar.qml b/src/app/mainview/components/CallActionBar.qml index 9f062447921345f419871b546d2fa3b41fe8044e..abb748704657c7d0f97bed2b1ac555344207616e 100644 --- a/src/app/mainview/components/CallActionBar.qml +++ b/src/app/mainview/components/CallActionBar.qml @@ -284,7 +284,7 @@ Control { }, Action { id: raiseHandAction - onTriggered: CallAdapter.setHandRaised("", !CallAdapter.isHandRaised()) + onTriggered: CallAdapter.raiseHand("", "", !CallAdapter.isHandRaised()) checkable: true icon.source: JamiResources.hand_black_24dp_svg icon.color: checked ? JamiTheme.raiseHandColor : "white" diff --git a/src/app/mainview/components/ParticipantControlLayout.qml b/src/app/mainview/components/ParticipantControlLayout.qml index b80bc6c4d9080c5bdd54350841ea53b05771bf4c..057207e7e3df22285b6b58cdf113443a9d00d607 100644 --- a/src/app/mainview/components/ParticipantControlLayout.qml +++ b/src/app/mainview/components/ParticipantControlLayout.qml @@ -68,7 +68,7 @@ RowLayout { muteAlertMessage = JamiStrings.participantMicIsStillMuted muteAlertActive = true } - CallAdapter.muteParticipant(uri, showModeratorMute) + CallAdapter.muteParticipant(uri, deviceId, sinkId, showModeratorMute) } toolTipText: { if (!checkable && participantIsModeratorMuted) @@ -115,7 +115,7 @@ RowLayout { Layout.preferredWidth: buttonPreferredSize Layout.alignment: Qt.AlignVCenter source: JamiResources.ic_hangup_participant_24dp_svg - onClicked: CallAdapter.hangupParticipant(uri) + onClicked: CallAdapter.hangupParticipant(uri, deviceId) toolTipText: JamiStrings.hangupParticipant } } diff --git a/src/app/mainview/components/ParticipantOverlay.qml b/src/app/mainview/components/ParticipantOverlay.qml index b1a03e825aee03bb6937dccef68e3ae7d5df811d..4ce7c5087d05c9e6810c705a7d94b81f5e5afe72 100644 --- a/src/app/mainview/components/ParticipantOverlay.qml +++ b/src/app/mainview/components/ParticipantOverlay.qml @@ -43,6 +43,7 @@ Item { .arg(shapeWidth) property string uri: "" + property string deviceId: "" property string bestName: "" property string sinkId: "" property bool participantIsActive: false @@ -320,7 +321,7 @@ Item { normalColor: JamiTheme.raiseHandColor z: participantRect.z + 1 toolTipText: root.meModerator ? JamiStrings.lowerHand : "" - onClicked: CallAdapter.setHandRaised(uri, false) + onClicked: CallAdapter.raiseHand(uri, deviceId, false) radius: 5 } diff --git a/src/app/mainview/components/ParticipantsLayer.qml b/src/app/mainview/components/ParticipantsLayer.qml index 790f4f0c6e9fbd600d58b3826648ba667819f089..ccd278fd9a07a97655ed50c3a9ee50f9cee24580 100644 --- a/src/app/mainview/components/ParticipantsLayer.qml +++ b/src/app/mainview/components/ParticipantsLayer.qml @@ -41,6 +41,7 @@ Item { sinkId: sinkId_ uri: uri_ + deviceId: deviceId_ isMe: isLocal_ participantIsModerator: isModerator_ bestName: { @@ -237,6 +238,7 @@ Item { } property string uri_: Uri + property string deviceId_: Device property string bestName_: BestName property string avatar_: Avatar ? Avatar : "" property string sinkId_: SinkId ? SinkId : "" @@ -311,6 +313,8 @@ Item { property string bestName_: BestName property string avatar_: Avatar ? Avatar : "" property string sinkId_: SinkId ? SinkId : "" + property string deviceId_: Device + property int leftMargin_: 0 property bool isLocal_: IsLocal property bool active_: Active property bool videoMuted_: VideoMuted diff --git a/src/libclient/api/callparticipantsmodel.h b/src/libclient/api/callparticipantsmodel.h index a699beb8ce15a8a2c3f7e8153894fb6c0979d1d8..37c8a8b9f2785b3cdba0d92da60f91c15e6a90ab 100644 --- a/src/libclient/api/callparticipantsmodel.h +++ b/src/libclient/api/callparticipantsmodel.h @@ -52,7 +52,7 @@ const QString AUDIOLOCALMUTED = "audioLocalMuted"; const QString AUDIOMODERATORMUTED = "audioModeratorMuted"; const QString ISMODERATOR = "isModerator"; const QString HANDRAISED = "handRaised"; -const QString SINKID = "sinkId"; +const QString STREAMID = "sinkId"; // TODO update const QString BESTNAME = "bestName"; const QString ISLOCAL = "isLocal"; const QString ISCONTACT = "isContact"; @@ -82,10 +82,10 @@ struct ParticipantInfos isModerator = infos[ParticipantsInfosStrings::ISMODERATOR] == "true"; handRaised = infos[ParticipantsInfosStrings::HANDRAISED] == "true"; - if (infos[ParticipantsInfosStrings::SINKID].isEmpty()) + if (infos[ParticipantsInfosStrings::STREAMID].isEmpty()) sinkId = callId + uri + device; else - sinkId = infos[ParticipantsInfosStrings::SINKID]; + sinkId = infos[ParticipantsInfosStrings::STREAMID]; bestName = ""; } diff --git a/src/libclient/api/newcallmodel.h b/src/libclient/api/newcallmodel.h index 363bdc5f224e27cd7f30c68884b97ed68a044b1a..2c6edc8b3643f9b9f0b3960a92bd124c99b5b7a7 100644 --- a/src/libclient/api/newcallmodel.h +++ b/src/libclient/api/newcallmodel.h @@ -276,9 +276,16 @@ public: /** * Set the shown participant * @param confId The call to change - * @param participant Use contact URI + * @param accountUri Peer's uri + * @param deviceId Peer's device + * @param sinkId Peer's sinkId + * @param state new state wanted */ - void setActiveParticipant(const QString& confId, const QString& participant); + void setActiveStream(const QString& confId, + const QString& accountUri, + const QString& deviceId, + const QString& sinkId, + bool state); /** * Check if a participant is a moderator or not @@ -307,28 +314,38 @@ public: /** * Set/unset a moderator * @param confId The conference to change - * @param peerId Uri of the participant to change + * @param accountUri Uri of the participant to change + * @param deviceId DeviceId of the participant to change * @param state State of the change (true set hand raised / false unset hand raised) */ - void setHandRaised(const QString& accountId, - const QString& confId, - const QString& peerId, - bool state); + void raiseHand(const QString& confId, + const QString& accountUri, + const QString& deviceId, + bool state); /** - * Mute/unmute participant + * Mute/unmute sink * @param confId The conference to change - * @param peerId Uri of the participant to mute + * @param accountUri Uri of the participant to mute + * @param deviceId Device Id of the participant to mute + * @param streamId Stream to mute * @param state State of the change (true mute participant / false unmute participant) */ - void muteParticipant(const QString& confId, const QString& peerId, const bool& state); + void muteStream(const QString& confId, + const QString& accountUri, + const QString& deviceId, + const QString& streamId, + const bool& state); /** - * Hangup participant + * Hangup participant from the conference * @param confId The call to change - * @param participant Use contact URI + * @param accountUri Uri of the participant to mute + * @param deviceId Device Id of the participant to mute */ - void hangupParticipant(const QString& confId, const QString& participant); + void hangupParticipant(const QString& confId, + const QString& accountUri, + const QString& deviceId); /** * Check if a call is a conference or not diff --git a/src/libclient/callparticipantsmodel.cpp b/src/libclient/callparticipantsmodel.cpp index 691d28597595885400fc7145f41d7410952750c3..b4e7cbeea6ef5d3f7356e48f32c7e36487eba18e 100644 --- a/src/libclient/callparticipantsmodel.cpp +++ b/src/libclient/callparticipantsmodel.cpp @@ -84,7 +84,6 @@ CallParticipants::verifyLayout() [](const lrc::api::ParticipantInfos& participant) -> bool { return participant.active; }); - auto newLayout = call::Layout::GRID; if (it != participants_.end()) if (participants_.size() == 1) @@ -93,7 +92,6 @@ CallParticipants::verifyLayout() newLayout = call::Layout::ONE_WITH_SMALL; else newLayout = call::Layout::GRID; - if (newLayout != hostLayout_) hostLayout_ = newLayout; } @@ -137,6 +135,8 @@ CallParticipants::filterCandidates(const VectorMapStringString& infos) std::lock_guard<std::mutex> lk(participantsMtx_); candidates_.clear(); for (const auto& candidate : infos) { + if (!candidate.contains(ParticipantsInfosStrings::URI)) + continue; auto peerId = candidate[ParticipantsInfosStrings::URI]; peerId.truncate(peerId.lastIndexOf("@")); if (peerId.isEmpty()) { @@ -182,7 +182,7 @@ CallParticipants::toQJsonObject(uint index) const ret[ParticipantsInfosStrings::URI] = participant->uri; ret[ParticipantsInfosStrings::DEVICE] = participant->device; - ret[ParticipantsInfosStrings::SINKID] = participant->sinkId; + ret[ParticipantsInfosStrings::STREAMID] = participant->sinkId; ret[ParticipantsInfosStrings::BESTNAME] = participant->bestName; ret[ParticipantsInfosStrings::AVATAR] = participant->avatar; ret[ParticipantsInfosStrings::ACTIVE] = participant->active; diff --git a/src/libclient/newcallmodel.cpp b/src/libclient/newcallmodel.cpp index 5fbcc9b2f6e8406cc1a191184ae59b5555fcbc03..97f7dc3f2d99f7a5aa6b39e07b418a10a6786678 100644 --- a/src/libclient/newcallmodel.cpp +++ b/src/libclient/newcallmodel.cpp @@ -1112,9 +1112,13 @@ NewCallModel::setConferenceLayout(const QString& confId, const call::Layout& lay } void -NewCallModel::setActiveParticipant(const QString& confId, const QString& participant) +NewCallModel::setActiveStream(const QString& confId, + const QString& accountUri, + const QString& deviceId, + const QString& streamId, + bool state) { - CallManager::instance().setActiveParticipant(owner.id, confId, participant); + CallManager::instance().setActiveStream(owner.id, confId, accountUri, deviceId, streamId, state); } bool @@ -1177,29 +1181,30 @@ NewCallModel::isHandRaised(const QString& confId, const QString& uri) noexcept } void -NewCallModel::setHandRaised(const QString& accountId, - const QString& confId, - const QString& peerId, - bool state) +NewCallModel::raiseHand(const QString& confId, + const QString& accountUri, + const QString& deviceId, + bool state) { - auto ownerUri = owner.profileInfo.uri; - auto uriToCheck = peerId; - if (uriToCheck.isEmpty()) { - uriToCheck = ownerUri; - } - CallManager::instance().raiseParticipantHand(accountId, confId, uriToCheck, state); + CallManager::instance().raiseHand(owner.id, confId, accountUri, deviceId, state); } void -NewCallModel::muteParticipant(const QString& confId, const QString& peerId, const bool& state) +NewCallModel::muteStream(const QString& confId, + const QString& accountUri, + const QString& deviceId, + const QString& streamId, + const bool& state) { - CallManager::instance().muteParticipant(owner.id, confId, peerId, state); + CallManager::instance().muteStream(owner.id, confId, accountUri, deviceId, streamId, state); } void -NewCallModel::hangupParticipant(const QString& confId, const QString& participant) +NewCallModel::hangupParticipant(const QString& confId, + const QString& accountUri, + const QString& deviceId) { - CallManager::instance().hangupParticipant(owner.id, confId, participant); + CallManager::instance().hangupParticipant(owner.id, confId, accountUri, deviceId); } void diff --git a/src/libclient/qtwrapper/callmanager_wrap.h b/src/libclient/qtwrapper/callmanager_wrap.h index a65a4588bcb38efe784952d926ad71c3054345a2..ce327a7af785fa79cc58419c0903f284fadff74a 100644 --- a/src/libclient/qtwrapper/callmanager_wrap.h +++ b/src/libclient/qtwrapper/callmanager_wrap.h @@ -501,6 +501,21 @@ public Q_SLOTS: // METHODS DRing::setConferenceLayout(accountId.toStdString(), confId.toStdString(), layout); } + void setActiveStream(const QString& accountId, + const QString& confId, + const QString& accountUri, + const QString& deviceId, + const QString& streamId, + bool state) + { + DRing::setActiveStream(accountId.toStdString(), + confId.toStdString(), + accountUri.toStdString(), + deviceId.toStdString(), + streamId.toStdString(), + state); + } + void setActiveParticipant(const QString& accountId, const QString& confId, const QString& callId) { DRing::setActiveParticipant(accountId.toStdString(), @@ -533,35 +548,43 @@ public Q_SLOTS: // METHODS state); } - void muteParticipant(const QString& accountId, - const QString& confId, - const QString& peerId, - const bool& state) + void muteStream(const QString& accountId, + const QString& confId, + const QString& accountUri, + const QString& deviceId, + const QString& streamId, + bool state) { - DRing::muteParticipant(accountId.toStdString(), - confId.toStdString(), - peerId.toStdString(), - state); + DRing::muteStream(accountId.toStdString(), + confId.toStdString(), + accountUri.toStdString(), + deviceId.toStdString(), + streamId.toStdString(), + state); } void hangupParticipant(const QString& accountId, const QString& confId, - const QString& participant) + const QString& accountUri, + const QString& deviceId) { DRing::hangupParticipant(accountId.toStdString(), confId.toStdString(), - participant.toStdString()); + accountUri.toStdString(), + deviceId.toStdString()); } - void raiseParticipantHand(const QString& accountId, - const QString& confId, - const QString& peerId, - const bool& state) + void raiseHand(const QString& accountId, + const QString& confId, + const QString& accountUri, + const QString& deviceId, + const bool& state) { - DRing::raiseParticipantHand(accountId.toStdString(), - confId.toStdString(), - peerId.toStdString(), - state); + DRing::raiseHand(accountId.toStdString(), + confId.toStdString(), + accountUri.toStdString(), + deviceId.toStdString(), + state); } Q_SIGNALS: // SIGNALS