From ac48c332867e773e0e0eb9ad0139b7b625e26851 Mon Sep 17 00:00:00 2001 From: Rohit Sutradhar Date: Fri, 14 Oct 2022 19:19:05 +0530 Subject: [PATCH] VoIP v1 implementation (#1161) * Initial commit for VoIP v1 implementation * Added draft of event handlers for voip methods * Added event handlers for VoIP events, added rejectCall, added version tracking for call version for V0 and V1 compatibility * Added call events to the general message pipeline. Modified Call Reject mechanism * Added message delegates for new events. Modified hidden events. Updated handle events. * Updated implementation to keep track of calls on other devices * Fixed linting * Fixed code warnings * Fixed minor bugs * fixed ci * Added acceptNegotiation method definition when missing gstreamer * Fixed warnings * Fixed linting --- resources/qml/MessageInput.qml | 10 +- resources/qml/delegates/MessageDelegate.qml | 42 ++ resources/qml/dialogs/LeaveRoomDialog.qml | 12 +- resources/qml/voip/CallInvite.qml | 2 +- resources/qml/voip/CallInviteBar.qml | 2 +- src/Cache.cpp | 17 + src/ChatPage.cpp | 3 + src/PowerlevelsEditModels.cpp | 2 + src/Utils.cpp | 3 + src/Utils.h | 7 + src/timeline/TimelineModel.cpp | 65 +++- src/timeline/TimelineModel.h | 6 + src/timeline/TimelineViewManager.cpp | 22 ++ src/timeline/TimelineViewManager.h | 3 + src/ui/HiddenEvents.cpp | 11 + src/voip/CallManager.cpp | 402 +++++++++++++++++--- src/voip/CallManager.h | 37 +- src/voip/WebRTCSession.cpp | 15 + src/voip/WebRTCSession.h | 1 + 19 files changed, 583 insertions(+), 79 deletions(-) diff --git a/resources/qml/MessageInput.qml b/resources/qml/MessageInput.qml index 6174a6c0..37d9614a 100644 --- a/resources/qml/MessageInput.qml +++ b/resources/qml/MessageInput.qml @@ -46,14 +46,14 @@ Rectangle { ImageButton { visible: CallManager.callsSupported && showAllButtons - opacity: CallManager.haveCallInvite ? 0.3 : 1 + opacity: (CallManager.haveCallInvite || CallManager.isOnCallOnOtherDevice) ? 0.3 : 1 Layout.alignment: Qt.AlignBottom hoverEnabled: true width: 22 height: 22 image: CallManager.isOnCall ? ":/icons/icons/ui/end-call.svg" : ":/icons/icons/ui/place-call.svg" ToolTip.visible: hovered - ToolTip.text: CallManager.isOnCall ? qsTr("Hang up") : qsTr("Place a call") + ToolTip.text: CallManager.isOnCall ? qsTr("Hang up") : (CallManager.isOnCallOnOtherDevice ? qsTr("Already on a call") : qsTr("Place a call")) Layout.margins: 8 onClicked: { if (room) { @@ -61,7 +61,11 @@ Rectangle { return ; } else if (CallManager.isOnCall) { CallManager.hangUp(); - } else { + } + else if(CallManager.isOnCallOnOtherDevice) { + return; + } + else { var dialog = placeCallDialog.createObject(timelineRoot); dialog.open(); timelineRoot.destroyOnClose(dialog); diff --git a/resources/qml/delegates/MessageDelegate.qml b/resources/qml/delegates/MessageDelegate.qml index 200c34d4..a2a44cb2 100644 --- a/resources/qml/delegates/MessageDelegate.qml +++ b/resources/qml/delegates/MessageDelegate.qml @@ -378,6 +378,34 @@ Item { } + DelegateChoice { + roleValue: MtxEvent.CallReject + + NoticeMessage { + body: formatted + isOnlyEmoji: false + isReply: d.isReply + keepFullText: d.keepFullText + isStateEvent: d.isStateEvent + formatted: qsTr("%1 rejected the call.").arg(d.userName) + } + + } + + DelegateChoice { + roleValue: MtxEvent.CallSelectAnswer + + NoticeMessage { + body: formatted + isOnlyEmoji: false + isReply: d.isReply + keepFullText: d.keepFullText + isStateEvent: d.isStateEvent + formatted: qsTr("%1 select answer").arg(d.userName) + // formatted: qsTr("Call answered elsewhere") + } + } + DelegateChoice { roleValue: MtxEvent.CallHangUp @@ -406,6 +434,20 @@ Item { } + DelegateChoice { + roleValue: MtxEvent.CallNegotiate + + NoticeMessage { + body: formatted + isOnlyEmoji: false + isReply: d.isReply + keepFullText: d.keepFullText + isStateEvent: d.isStateEvent + formatted: qsTr("%1 is negotiating the call...").arg(d.userName) + } + + } + DelegateChoice { roleValue: MtxEvent.PowerLevels diff --git a/resources/qml/dialogs/LeaveRoomDialog.qml b/resources/qml/dialogs/LeaveRoomDialog.qml index d64b2d31..cb15a74d 100644 --- a/resources/qml/dialogs/LeaveRoomDialog.qml +++ b/resources/qml/dialogs/LeaveRoomDialog.qml @@ -7,6 +7,7 @@ import Qt.labs.platform 1.1 as P import QtQuick 2.15 import QtQuick.Controls 2.15 import im.nheko 1.0 +import "../voip" P.MessageDialog { id: leaveRoomRoot @@ -18,5 +19,14 @@ P.MessageDialog { text: qsTr("Are you sure you want to leave?") modality: Qt.ApplicationModal buttons: P.MessageDialog.Ok | P.MessageDialog.Cancel - onAccepted: Rooms.leave(roomId, reason) + onAccepted: { + + if (CallManager.haveCallInvite) { + callManager.rejectInvite(); + } else if (CallManager.isOnCall) { + CallManager.hangUp(); + } + Rooms.leave(roomId, reason) + } + } diff --git a/resources/qml/voip/CallInvite.qml b/resources/qml/voip/CallInvite.qml index 7bab3616..beed2e51 100644 --- a/resources/qml/voip/CallInvite.qml +++ b/resources/qml/voip/CallInvite.qml @@ -153,7 +153,7 @@ Popup { implicitWidth: buttonLayout.buttonSize implicitHeight: buttonLayout.buttonSize onClicked: { - CallManager.hangUp(); + CallManager.rejectInvite(); close(); } diff --git a/resources/qml/voip/CallInviteBar.qml b/resources/qml/voip/CallInviteBar.qml index ab377ca8..a622ca27 100644 --- a/resources/qml/voip/CallInviteBar.qml +++ b/resources/qml/voip/CallInviteBar.qml @@ -129,7 +129,7 @@ Rectangle { text: qsTr("Decline") palette: Nheko.colors onClicked: { - CallManager.hangUp(); + CallManager.rejectInvite(); } } diff --git a/src/Cache.cpp b/src/Cache.cpp index 863f0683..a83b73f7 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -184,8 +184,18 @@ Cache::isHiddenEvent(lmdb::txn &txn, hiddenEvents.hidden_event_types = std::vector{ EventType::Reaction, EventType::CallCandidates, + EventType::CallNegotiate, EventType::Unsupported, }; + // check if selected answer is from to local user + /* + * localUser accepts/rejects the call and it is selected by caller - No message + * Another User accepts/rejects the call and it is selected by caller - "Call answered/rejected + * elsewhere" + */ + bool callLocalUser_ = true; + if (callLocalUser_) + hiddenEvents.hidden_event_types->push_back(EventType::CallSelectAnswer); if (auto temp = getAccountData(txn, mtx::events::EventType::NhekoHiddenEvents, "")) { auto h = std::get< @@ -1661,11 +1671,18 @@ isMessage(const mtx::events::RoomEvent &) { return true; } + auto isMessage(const mtx::events::RoomEvent &) { return true; } + +// auto +// isMessage(const mtx::events::RoomEvent &) +// { +// return true; +// } } void diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index 3736ec6b..756ef425 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -363,6 +363,9 @@ ChatPage::ChatPage(QSharedPointer userSettings, QObject *parent) connectCallMessage(); connectCallMessage(); connectCallMessage(); + connectCallMessage(); + connectCallMessage(); + connectCallMessage(); } void diff --git a/src/PowerlevelsEditModels.cpp b/src/PowerlevelsEditModels.cpp index 8cc2dcc0..2c2d4a7f 100644 --- a/src/PowerlevelsEditModels.cpp +++ b/src/PowerlevelsEditModels.cpp @@ -222,6 +222,8 @@ PowerlevelsTypeListModel::data(const QModelIndex &index, int role) const return tr("Answer a call"); else if (type.type == "m.call.hangup") return tr("Hang up a call"); + else if (type.type == "m.call.reject") + return tr("Reject a call"); else if (type.type == "im.ponies.room_emotes") return tr("Change the room emotes"); return QString::fromStdString(type.type); diff --git a/src/Utils.cpp b/src/Utils.cpp index cedf537a..b92c6cce 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -219,6 +219,7 @@ utils::getMessageDescription(const TimelineEvent &event, using CallInvite = mtx::events::RoomEvent; using CallAnswer = mtx::events::RoomEvent; using CallHangUp = mtx::events::RoomEvent; + using CallReject = mtx::events::RoomEvent; using Encrypted = mtx::events::EncryptedEvent; if (std::holds_alternative