Merge pull request #356 from trilene/call-dialogs-qml
Port call dialogs to Qml
This commit is contained in:
commit
49abcb24b5
@ -245,7 +245,6 @@ configure_file(cmake/nheko.h config/nheko.h)
|
||||
#
|
||||
set(SRC_FILES
|
||||
# Dialogs
|
||||
src/dialogs/AcceptCall.cpp
|
||||
src/dialogs/CreateRoom.cpp
|
||||
src/dialogs/FallbackAuth.cpp
|
||||
src/dialogs/ImageOverlay.cpp
|
||||
@ -254,7 +253,6 @@ set(SRC_FILES
|
||||
src/dialogs/LeaveRoom.cpp
|
||||
src/dialogs/Logout.cpp
|
||||
src/dialogs/MemberList.cpp
|
||||
src/dialogs/PlaceCall.cpp
|
||||
src/dialogs/PreviewUploadOverlay.cpp
|
||||
src/dialogs/ReCaptcha.cpp
|
||||
src/dialogs/ReadReceipts.cpp
|
||||
@ -464,7 +462,6 @@ feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAG
|
||||
|
||||
qt5_wrap_cpp(MOC_HEADERS
|
||||
# Dialogs
|
||||
src/dialogs/AcceptCall.h
|
||||
src/dialogs/CreateRoom.h
|
||||
src/dialogs/FallbackAuth.h
|
||||
src/dialogs/ImageOverlay.h
|
||||
@ -473,7 +470,6 @@ qt5_wrap_cpp(MOC_HEADERS
|
||||
src/dialogs/LeaveRoom.h
|
||||
src/dialogs/Logout.h
|
||||
src/dialogs/MemberList.h
|
||||
src/dialogs/PlaceCall.h
|
||||
src/dialogs/PreviewUploadOverlay.h
|
||||
src/dialogs/RawMessage.h
|
||||
src/dialogs/ReCaptcha.h
|
||||
|
@ -1,3 +1,4 @@
|
||||
import "./voip"
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Layouts 1.2
|
||||
@ -10,6 +11,13 @@ Rectangle {
|
||||
Layout.preferredHeight: textInput.height
|
||||
Layout.minimumHeight: 40
|
||||
|
||||
Component {
|
||||
id: placeCallDialog
|
||||
|
||||
PlaceCall {
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: inputBar
|
||||
|
||||
@ -17,18 +25,33 @@ Rectangle {
|
||||
spacing: 16
|
||||
|
||||
ImageButton {
|
||||
visible: TimelineManager.callsSupported
|
||||
visible: CallManager.callsSupported
|
||||
opacity: CallManager.haveCallInvite ? 0.3 : 1.0
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
hoverEnabled: true
|
||||
width: 22
|
||||
height: 22
|
||||
image: TimelineManager.isOnCall ? ":/icons/icons/ui/end-call.png" : ":/icons/icons/ui/place-call.png"
|
||||
image: CallManager.isOnCall ? ":/icons/icons/ui/end-call.png" : ":/icons/icons/ui/place-call.png"
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: TimelineManager.isOnCall ? qsTr("Hang up") : qsTr("Place a call")
|
||||
ToolTip.text: CallManager.isOnCall ? qsTr("Hang up") : qsTr("Place a call")
|
||||
Layout.topMargin: 8
|
||||
Layout.bottomMargin: 8
|
||||
Layout.leftMargin: 16
|
||||
onClicked: TimelineManager.timeline.input.callButton()
|
||||
onClicked: {
|
||||
if (TimelineManager.timeline) {
|
||||
if (CallManager.haveCallInvite) {
|
||||
return;
|
||||
}
|
||||
else if (CallManager.isOnCall) {
|
||||
CallManager.hangUp();
|
||||
}
|
||||
else {
|
||||
CallManager.refreshDevices();
|
||||
var dialog = placeCallDialog.createObject(timelineRoot);
|
||||
dialog.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImageButton {
|
||||
@ -39,7 +62,7 @@ Rectangle {
|
||||
image: ":/icons/icons/ui/paper-clip-outline.png"
|
||||
Layout.topMargin: 8
|
||||
Layout.bottomMargin: 8
|
||||
Layout.leftMargin: TimelineManager.callsSupported ? 0 : 16
|
||||
Layout.leftMargin: CallManager.callsSupported ? 0 : 16
|
||||
onClicked: TimelineManager.timeline.input.openFileSelection()
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: qsTr("Send a file")
|
||||
|
@ -1,6 +1,7 @@
|
||||
import "./delegates"
|
||||
import "./device-verification"
|
||||
import "./emoji"
|
||||
import "./voip"
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.3
|
||||
@ -210,7 +211,7 @@ Page {
|
||||
}
|
||||
|
||||
Loader {
|
||||
source: TimelineManager.onVideoCall ? "VideoCall.qml" : ""
|
||||
source: CallManager.isOnCall && CallManager.isVideo ? "voip/VideoCall.qml" : ""
|
||||
onLoaded: TimelineManager.setVideoCallItem()
|
||||
}
|
||||
|
||||
@ -223,6 +224,12 @@ Page {
|
||||
|
||||
}
|
||||
|
||||
CallInviteBar {
|
||||
id: callInviteBar
|
||||
Layout.fillWidth: true
|
||||
z: 3
|
||||
}
|
||||
|
||||
ActiveCallBar {
|
||||
Layout.fillWidth: true
|
||||
z: 3
|
||||
|
@ -1,19 +1,19 @@
|
||||
import "../"
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Layouts 1.2
|
||||
import im.nheko 1.0
|
||||
|
||||
Rectangle {
|
||||
id: activeCallBar
|
||||
|
||||
visible: TimelineManager.callState != WebRTCState.DISCONNECTED
|
||||
color: "#2ECC71"
|
||||
visible: CallManager.isOnCall
|
||||
color: callInviteBar.color
|
||||
implicitHeight: visible ? rowLayout.height + 8 : 0
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
if (TimelineManager.onVideoCall)
|
||||
if (CallManager.isVideo)
|
||||
stackLayout.currentIndex = stackLayout.currentIndex ? 0 : 1;
|
||||
|
||||
}
|
||||
@ -30,63 +30,66 @@ Rectangle {
|
||||
Avatar {
|
||||
width: avatarSize
|
||||
height: avatarSize
|
||||
url: TimelineManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/")
|
||||
displayName: TimelineManager.callPartyName
|
||||
url: CallManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/")
|
||||
displayName: CallManager.callParty
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.leftMargin: 8
|
||||
font.pointSize: fontMetrics.font.pointSize * 1.1
|
||||
text: " " + TimelineManager.callPartyName + " "
|
||||
text: CallManager.callParty
|
||||
color: "#000000"
|
||||
}
|
||||
|
||||
Image {
|
||||
Layout.leftMargin: 4
|
||||
Layout.preferredWidth: 24
|
||||
Layout.preferredHeight: 24
|
||||
source: TimelineManager.onVideoCall ? "qrc:/icons/icons/ui/video-call.png" : "qrc:/icons/icons/ui/place-call.png"
|
||||
source: CallManager.isVideo ? "qrc:/icons/icons/ui/video-call.png" : "qrc:/icons/icons/ui/place-call.png"
|
||||
}
|
||||
|
||||
Label {
|
||||
id: callStateLabel
|
||||
|
||||
font.pointSize: fontMetrics.font.pointSize * 1.1
|
||||
color: "#000000"
|
||||
}
|
||||
|
||||
Item {
|
||||
state: TimelineManager.callState
|
||||
states: [
|
||||
State {
|
||||
name: "OFFERSENT"
|
||||
when: state == WebRTCState.OFFERSENT
|
||||
when: CallManager.callState == WebRTCState.OFFERSENT
|
||||
|
||||
PropertyChanges {
|
||||
target: callStateLabel
|
||||
text: "Calling..."
|
||||
text: qsTr("Calling...")
|
||||
}
|
||||
|
||||
},
|
||||
State {
|
||||
name: "CONNECTING"
|
||||
when: state == WebRTCState.CONNECTING
|
||||
when: CallManager.callState == WebRTCState.CONNECTING
|
||||
|
||||
PropertyChanges {
|
||||
target: callStateLabel
|
||||
text: "Connecting..."
|
||||
text: qsTr("Connecting...")
|
||||
}
|
||||
|
||||
},
|
||||
State {
|
||||
name: "ANSWERSENT"
|
||||
when: state == WebRTCState.ANSWERSENT
|
||||
when: CallManager.callState == WebRTCState.ANSWERSENT
|
||||
|
||||
PropertyChanges {
|
||||
target: callStateLabel
|
||||
text: "Connecting..."
|
||||
text: qsTr("Connecting...")
|
||||
}
|
||||
|
||||
},
|
||||
State {
|
||||
name: "CONNECTED"
|
||||
when: state == WebRTCState.CONNECTED
|
||||
when: CallManager.callState == WebRTCState.CONNECTED
|
||||
|
||||
PropertyChanges {
|
||||
target: callStateLabel
|
||||
@ -100,13 +103,13 @@ Rectangle {
|
||||
|
||||
PropertyChanges {
|
||||
target: stackLayout
|
||||
currentIndex: TimelineManager.onVideoCall ? 1 : 0
|
||||
currentIndex: CallManager.isVideo ? 1 : 0
|
||||
}
|
||||
|
||||
},
|
||||
State {
|
||||
name: "DISCONNECTED"
|
||||
when: state == WebRTCState.DISCONNECTED
|
||||
when: CallManager.callState == WebRTCState.DISCONNECTED
|
||||
|
||||
PropertyChanges {
|
||||
target: callStateLabel
|
||||
@ -132,7 +135,7 @@ Rectangle {
|
||||
}
|
||||
|
||||
interval: 1000
|
||||
running: TimelineManager.callState == WebRTCState.CONNECTED
|
||||
running: CallManager.callState == WebRTCState.CONNECTED
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
var d = new Date();
|
||||
@ -149,36 +152,28 @@ Rectangle {
|
||||
}
|
||||
|
||||
ImageButton {
|
||||
visible: TimelineManager.onVideoCall
|
||||
visible: CallManager.haveLocalVideo
|
||||
width: 24
|
||||
height: 24
|
||||
buttonTextColor: "#000000"
|
||||
image: ":/icons/icons/ui/toggle-camera-view.png"
|
||||
hoverEnabled: true
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: "Toggle camera view"
|
||||
onClicked: TimelineManager.toggleCameraView()
|
||||
}
|
||||
|
||||
Item {
|
||||
implicitWidth: 8
|
||||
ToolTip.text: qsTr("Toggle camera view")
|
||||
onClicked: CallManager.toggleCameraView()
|
||||
}
|
||||
|
||||
ImageButton {
|
||||
Layout.leftMargin: 8
|
||||
Layout.rightMargin: 16
|
||||
width: 24
|
||||
height: 24
|
||||
buttonTextColor: "#000000"
|
||||
image: TimelineManager.isMicMuted ? ":/icons/icons/ui/microphone-unmute.png" : ":/icons/icons/ui/microphone-mute.png"
|
||||
image: CallManager.isMicMuted ? ":/icons/icons/ui/microphone-unmute.png" : ":/icons/icons/ui/microphone-mute.png"
|
||||
hoverEnabled: true
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: TimelineManager.isMicMuted ? qsTr("Unmute Mic") : qsTr("Mute Mic")
|
||||
onClicked: TimelineManager.toggleMicMute()
|
||||
ToolTip.text: CallManager.isMicMuted ? qsTr("Unmute Mic") : qsTr("Mute Mic")
|
||||
onClicked: CallManager.toggleMicMute()
|
||||
}
|
||||
|
||||
Item {
|
||||
implicitWidth: 16
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
79
resources/qml/voip/CallDevices.qml
Normal file
79
resources/qml/voip/CallDevices.qml
Normal file
@ -0,0 +1,79 @@
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Layouts 1.2
|
||||
import im.nheko 1.0
|
||||
|
||||
Popup {
|
||||
|
||||
modal: true
|
||||
anchors.centerIn: parent
|
||||
background: Rectangle {
|
||||
color: colors.window
|
||||
border.color: colors.windowText
|
||||
}
|
||||
|
||||
palette: colors
|
||||
|
||||
ColumnLayout {
|
||||
|
||||
spacing: 16
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 8
|
||||
|
||||
Layout.topMargin: 8
|
||||
Layout.leftMargin: 8
|
||||
Layout.rightMargin: 8
|
||||
|
||||
RowLayout {
|
||||
|
||||
Image {
|
||||
Layout.preferredWidth: 22
|
||||
Layout.preferredHeight: 22
|
||||
source: "image://colorimage/:/icons/icons/ui/microphone-unmute.png?" + colors.windowText
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: micCombo
|
||||
Layout.fillWidth: true
|
||||
model: CallManager.mics
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
|
||||
visible: CallManager.isVideo && CallManager.cameras.length > 0
|
||||
|
||||
Image {
|
||||
Layout.preferredWidth: 22
|
||||
Layout.preferredHeight: 22
|
||||
source: "image://colorimage/:/icons/icons/ui/video-call.png?" + colors.windowText
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: cameraCombo
|
||||
Layout.fillWidth: true
|
||||
model: CallManager.cameras
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DialogButtonBox {
|
||||
|
||||
Layout.leftMargin: 128
|
||||
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
|
||||
|
||||
onAccepted: {
|
||||
Settings.microphone = micCombo.currentText
|
||||
if (cameraCombo.visible) {
|
||||
Settings.camera = cameraCombo.currentText
|
||||
}
|
||||
close();
|
||||
}
|
||||
|
||||
onRejected: {
|
||||
close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
126
resources/qml/voip/CallInviteBar.qml
Normal file
126
resources/qml/voip/CallInviteBar.qml
Normal file
@ -0,0 +1,126 @@
|
||||
import "../"
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Layouts 1.2
|
||||
import im.nheko 1.0
|
||||
|
||||
Rectangle {
|
||||
|
||||
visible: CallManager.haveCallInvite
|
||||
color: "#2ECC71"
|
||||
implicitHeight: visible ? rowLayout.height + 8 : 0
|
||||
|
||||
Component {
|
||||
id: devicesDialog
|
||||
CallDevices {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: deviceError
|
||||
DeviceError {
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: rowLayout
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 8
|
||||
|
||||
Avatar {
|
||||
width: avatarSize
|
||||
height: avatarSize
|
||||
url: CallManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/")
|
||||
displayName: CallManager.callParty
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.leftMargin: 8
|
||||
font.pointSize: fontMetrics.font.pointSize * 1.1
|
||||
text: CallManager.callParty
|
||||
color: "#000000"
|
||||
}
|
||||
|
||||
Image {
|
||||
Layout.leftMargin: 4
|
||||
Layout.preferredWidth: 24
|
||||
Layout.preferredHeight: 24
|
||||
source: CallManager.isVideo ? "qrc:/icons/icons/ui/video-call.png" : "qrc:/icons/icons/ui/place-call.png"
|
||||
}
|
||||
|
||||
Label {
|
||||
font.pointSize: fontMetrics.font.pointSize * 1.1
|
||||
text: CallManager.isVideo ? qsTr("Video Call") : qsTr("Voice Call")
|
||||
color: "#000000"
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
ImageButton {
|
||||
Layout.rightMargin: 16
|
||||
width: 20
|
||||
height: 20
|
||||
buttonTextColor: "#000000"
|
||||
image: ":/icons/icons/ui/settings.png"
|
||||
hoverEnabled: true
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: qsTr("Devices")
|
||||
onClicked: {
|
||||
CallManager.refreshDevices();
|
||||
var dialog = devicesDialog.createObject(timelineRoot);
|
||||
dialog.open();
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.rightMargin: 4
|
||||
icon.source: CallManager.isVideo ? "qrc:/icons/icons/ui/video-call.png" : "qrc:/icons/icons/ui/place-call.png"
|
||||
text: qsTr(" Accept ")
|
||||
palette: colors
|
||||
|
||||
onClicked: {
|
||||
if (CallManager.mics.length == 0) {
|
||||
var dialog = deviceError.createObject(timelineRoot, {
|
||||
"errorString": qsTr("No microphone found."),
|
||||
"image": ":/icons/icons/ui/place-call.png"
|
||||
});
|
||||
dialog.open();
|
||||
return;
|
||||
}
|
||||
else if (!CallManager.mics.includes(Settings.microphone)) {
|
||||
var dialog = deviceError.createObject(timelineRoot, {
|
||||
"errorString": qsTr("Unknown microphone: ") + Settings.microphone,
|
||||
"image": ":/icons/icons/ui/place-call.png"
|
||||
});
|
||||
dialog.open();
|
||||
return;
|
||||
}
|
||||
if (CallManager.isVideo && CallManager.cameras.length > 0 && !CallManager.cameras.includes(Settings.camera)) {
|
||||
var dialog = deviceError.createObject(timelineRoot, {
|
||||
"errorString": qsTr("Unknown camera: ") + Settings.camera,
|
||||
"image": ":/icons/icons/ui/video-call.png"
|
||||
});
|
||||
dialog.open();
|
||||
return;
|
||||
}
|
||||
CallManager.acceptInvite();
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.rightMargin: 16
|
||||
icon.source: "qrc:/icons/icons/ui/end-call.png"
|
||||
text: qsTr(" Decline ")
|
||||
palette: colors
|
||||
|
||||
onClicked: {
|
||||
CallManager.hangUp();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
31
resources/qml/voip/DeviceError.qml
Normal file
31
resources/qml/voip/DeviceError.qml
Normal file
@ -0,0 +1,31 @@
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Layouts 1.2
|
||||
import im.nheko 1.0
|
||||
|
||||
Popup {
|
||||
|
||||
property string errorString
|
||||
property var image
|
||||
|
||||
modal: true
|
||||
anchors.centerIn: parent
|
||||
background: Rectangle {
|
||||
color: colors.window
|
||||
border.color: colors.windowText
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
|
||||
Image {
|
||||
Layout.preferredWidth: 16
|
||||
Layout.preferredHeight: 16
|
||||
source: "image://colorimage/" + image + "?" + colors.windowText
|
||||
}
|
||||
|
||||
Label {
|
||||
text: errorString
|
||||
color: colors.windowText
|
||||
}
|
||||
}
|
||||
}
|
146
resources/qml/voip/PlaceCall.qml
Normal file
146
resources/qml/voip/PlaceCall.qml
Normal file
@ -0,0 +1,146 @@
|
||||
import "../"
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Layouts 1.2
|
||||
import im.nheko 1.0
|
||||
|
||||
Popup {
|
||||
|
||||
modal: true
|
||||
anchors.centerIn: parent
|
||||
palette: colors
|
||||
background: Rectangle {
|
||||
color: colors.window
|
||||
border.color: colors.windowText
|
||||
}
|
||||
|
||||
Component {
|
||||
id: deviceError
|
||||
DeviceError {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: columnLayout
|
||||
|
||||
spacing: 16
|
||||
|
||||
RowLayout {
|
||||
|
||||
Layout.topMargin: 8
|
||||
Layout.leftMargin: 8
|
||||
|
||||
Label {
|
||||
text: qsTr("Place a call to ") + TimelineManager.timeline.roomName + "?"
|
||||
color: colors.windowText
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: buttonLayout
|
||||
|
||||
Layout.leftMargin: 8
|
||||
Layout.rightMargin: 8
|
||||
|
||||
function validateMic() {
|
||||
if (CallManager.mics.length == 0) {
|
||||
var dialog = deviceError.createObject(timelineRoot, {
|
||||
"errorString": qsTr("No microphone found."),
|
||||
"image": ":/icons/icons/ui/place-call.png"
|
||||
});
|
||||
dialog.open();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Avatar {
|
||||
Layout.rightMargin: cameraCombo.visible ? 16 : 64
|
||||
width: avatarSize
|
||||
height: avatarSize
|
||||
url: TimelineManager.timeline.roomAvatarUrl.replace("mxc://", "image://MxcImage/")
|
||||
displayName: TimelineManager.timeline.roomName
|
||||
}
|
||||
|
||||
Button {
|
||||
text: qsTr(" Voice ")
|
||||
icon.source: "qrc:/icons/icons/ui/place-call.png"
|
||||
onClicked: {
|
||||
if (buttonLayout.validateMic()) {
|
||||
Settings.microphone = micCombo.currentText
|
||||
CallManager.sendInvite(TimelineManager.timeline.roomId(), false);
|
||||
close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
visible: CallManager.cameras.length > 0
|
||||
text: qsTr(" Video ")
|
||||
icon.source: "qrc:/icons/icons/ui/video-call.png"
|
||||
onClicked: {
|
||||
if (buttonLayout.validateMic()) {
|
||||
Settings.microphone = micCombo.currentText
|
||||
Settings.camera = cameraCombo.currentText
|
||||
CallManager.sendInvite(TimelineManager.timeline.roomId(), true);
|
||||
close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: qsTr("Cancel")
|
||||
onClicked: {
|
||||
close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 8
|
||||
|
||||
RowLayout {
|
||||
|
||||
Layout.leftMargin: 8
|
||||
Layout.rightMargin: 8
|
||||
Layout.bottomMargin: cameraCombo.visible ? 0 : 8
|
||||
|
||||
Image {
|
||||
Layout.preferredWidth: 22
|
||||
Layout.preferredHeight: 22
|
||||
source: "image://colorimage/:/icons/icons/ui/microphone-unmute.png?" + colors.windowText
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: micCombo
|
||||
Layout.fillWidth: true
|
||||
model: CallManager.mics
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
|
||||
visible: CallManager.cameras.length > 0
|
||||
Layout.leftMargin: 8
|
||||
Layout.rightMargin: 8
|
||||
Layout.bottomMargin: 8
|
||||
|
||||
Image {
|
||||
Layout.preferredWidth: 22
|
||||
Layout.preferredHeight: 22
|
||||
source: "image://colorimage/:/icons/icons/ui/video-call.png?" + colors.windowText
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: cameraCombo
|
||||
Layout.fillWidth: true
|
||||
model: CallManager.cameras
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -123,7 +123,6 @@
|
||||
<file>qtquickcontrols2.conf</file>
|
||||
|
||||
<file>qml/TimelineView.qml</file>
|
||||
<file>qml/ActiveCallBar.qml</file>
|
||||
<file>qml/Avatar.qml</file>
|
||||
<file>qml/Completer.qml</file>
|
||||
<file>qml/EncryptionIndicator.qml</file>
|
||||
@ -139,7 +138,6 @@
|
||||
<file>qml/TimelineRow.qml</file>
|
||||
<file>qml/TopBar.qml</file>
|
||||
<file>qml/TypingIndicator.qml</file>
|
||||
<file>qml/VideoCall.qml</file>
|
||||
<file>qml/emoji/EmojiButton.qml</file>
|
||||
<file>qml/emoji/EmojiPicker.qml</file>
|
||||
<file>qml/UserProfile.qml</file>
|
||||
@ -159,6 +157,12 @@
|
||||
<file>qml/device-verification/NewVerificationRequest.qml</file>
|
||||
<file>qml/device-verification/Failed.qml</file>
|
||||
<file>qml/device-verification/Success.qml</file>
|
||||
<file>qml/voip/ActiveCallBar.qml</file>
|
||||
<file>qml/voip/CallDevices.qml</file>
|
||||
<file>qml/voip/CallInviteBar.qml</file>
|
||||
<file>qml/voip/DeviceError.qml</file>
|
||||
<file>qml/voip/PlaceCall.qml</file>
|
||||
<file>qml/voip/VideoCall.qml</file>
|
||||
</qresource>
|
||||
<qresource prefix="/media">
|
||||
<file>media/ring.ogg</file>
|
||||
|
@ -10,11 +10,9 @@
|
||||
#include "CallManager.h"
|
||||
#include "ChatPage.h"
|
||||
#include "Logging.h"
|
||||
#include "MainWindow.h"
|
||||
#include "MatrixClient.h"
|
||||
#include "UserSettingsPage.h"
|
||||
#include "Utils.h"
|
||||
#include "WebRTCSession.h"
|
||||
#include "dialogs/AcceptCall.h"
|
||||
|
||||
#include "mtx/responses/turn_server.hpp"
|
||||
|
||||
@ -112,6 +110,23 @@ CallManager::CallManager(QObject *parent)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
emit newCallState();
|
||||
});
|
||||
|
||||
connect(&session_, &WebRTCSession::devicesChanged, this, [this]() {
|
||||
if (ChatPage::instance()->userSettings()->microphone().isEmpty()) {
|
||||
auto mics = session_.getDeviceNames(false, std::string());
|
||||
if (!mics.empty())
|
||||
ChatPage::instance()->userSettings()->setMicrophone(
|
||||
QString::fromStdString(mics.front()));
|
||||
}
|
||||
if (ChatPage::instance()->userSettings()->camera().isEmpty()) {
|
||||
auto cameras = session_.getDeviceNames(true, std::string());
|
||||
if (!cameras.empty())
|
||||
ChatPage::instance()->userSettings()->setCamera(
|
||||
QString::fromStdString(cameras.front()));
|
||||
}
|
||||
emit devicesChanged();
|
||||
});
|
||||
|
||||
connect(&player_,
|
||||
@ -144,7 +159,7 @@ CallManager::CallManager(QObject *parent)
|
||||
void
|
||||
CallManager::sendInvite(const QString &roomid, bool isVideo)
|
||||
{
|
||||
if (onActiveCall())
|
||||
if (isOnCall())
|
||||
return;
|
||||
|
||||
auto roomInfo = cache::singleRoomInfo(roomid.toStdString());
|
||||
@ -160,7 +175,8 @@ CallManager::sendInvite(const QString &roomid, bool isVideo)
|
||||
return;
|
||||
}
|
||||
|
||||
roomid_ = roomid;
|
||||
isVideo_ = isVideo;
|
||||
roomid_ = roomid;
|
||||
session_.setTurnServers(turnURIs_);
|
||||
generateCallID();
|
||||
nhlog::ui()->debug(
|
||||
@ -168,16 +184,14 @@ CallManager::sendInvite(const QString &roomid, bool isVideo)
|
||||
std::vector<RoomMember> members(cache::getMembers(roomid.toStdString()));
|
||||
const RoomMember &callee =
|
||||
members.front().user_id == utils::localUser() ? members.back() : members.front();
|
||||
callPartyName_ = callee.display_name.isEmpty() ? callee.user_id : callee.display_name;
|
||||
callParty_ = callee.display_name.isEmpty() ? callee.user_id : callee.display_name;
|
||||
callPartyAvatarUrl_ = QString::fromStdString(roomInfo.avatar_url);
|
||||
emit newCallParty();
|
||||
emit newInviteState();
|
||||
playRingtone(QUrl("qrc:/media/media/ringback.ogg"), true);
|
||||
if (!session_.createOffer(isVideo)) {
|
||||
emit ChatPage::instance()->showNotification("Problem setting up call.");
|
||||
endCall();
|
||||
}
|
||||
if (isVideo)
|
||||
emit newVideoCallState();
|
||||
}
|
||||
|
||||
namespace {
|
||||
@ -206,12 +220,6 @@ CallManager::hangUp(CallHangUp::Reason reason)
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
CallManager::onActiveCall() const
|
||||
{
|
||||
return session_.state() != webrtc::State::DISCONNECTED;
|
||||
}
|
||||
|
||||
void
|
||||
CallManager::syncEvent(const mtx::events::collections::TimelineEvents &event)
|
||||
{
|
||||
@ -257,7 +265,7 @@ CallManager::handleEvent(const RoomEvent<CallInvite> &callInviteEvent)
|
||||
return;
|
||||
|
||||
auto roomInfo = cache::singleRoomInfo(callInviteEvent.room_id);
|
||||
if (onActiveCall() || roomInfo.member_count != 2) {
|
||||
if (isOnCall() || roomInfo.member_count != 2) {
|
||||
emit newMessage(QString::fromStdString(callInviteEvent.room_id),
|
||||
CallHangUp{callInviteEvent.content.call_id,
|
||||
0,
|
||||
@ -277,48 +285,41 @@ CallManager::handleEvent(const RoomEvent<CallInvite> &callInviteEvent)
|
||||
std::vector<RoomMember> members(cache::getMembers(callInviteEvent.room_id));
|
||||
const RoomMember &caller =
|
||||
members.front().user_id == utils::localUser() ? members.back() : members.front();
|
||||
callPartyName_ = caller.display_name.isEmpty() ? caller.user_id : caller.display_name;
|
||||
callParty_ = caller.display_name.isEmpty() ? caller.user_id : caller.display_name;
|
||||
callPartyAvatarUrl_ = QString::fromStdString(roomInfo.avatar_url);
|
||||
emit newCallParty();
|
||||
auto dialog = new dialogs::AcceptCall(caller.user_id,
|
||||
caller.display_name,
|
||||
QString::fromStdString(roomInfo.name),
|
||||
QString::fromStdString(roomInfo.avatar_url),
|
||||
isVideo,
|
||||
MainWindow::instance());
|
||||
connect(dialog, &dialogs::AcceptCall::accept, this, [this, callInviteEvent, isVideo]() {
|
||||
MainWindow::instance()->hideOverlay();
|
||||
answerInvite(callInviteEvent.content, isVideo);
|
||||
});
|
||||
connect(dialog, &dialogs::AcceptCall::reject, this, [this]() {
|
||||
MainWindow::instance()->hideOverlay();
|
||||
hangUp();
|
||||
});
|
||||
MainWindow::instance()->showSolidOverlayModal(dialog);
|
||||
|
||||
haveCallInvite_ = true;
|
||||
isVideo_ = isVideo;
|
||||
inviteSDP_ = callInviteEvent.content.sdp;
|
||||
session_.refreshDevices();
|
||||
emit newInviteState();
|
||||
}
|
||||
|
||||
void
|
||||
CallManager::answerInvite(const CallInvite &invite, bool isVideo)
|
||||
CallManager::acceptInvite()
|
||||
{
|
||||
if (!haveCallInvite_)
|
||||
return;
|
||||
|
||||
stopRingtone();
|
||||
std::string errorMessage;
|
||||
if (!session_.havePlugins(false, &errorMessage) ||
|
||||
(isVideo && !session_.havePlugins(true, &errorMessage))) {
|
||||
(isVideo_ && !session_.havePlugins(true, &errorMessage))) {
|
||||
emit ChatPage::instance()->showNotification(QString::fromStdString(errorMessage));
|
||||
hangUp();
|
||||
return;
|
||||
}
|
||||
|
||||
session_.setTurnServers(turnURIs_);
|
||||
if (!session_.acceptOffer(invite.sdp)) {
|
||||
if (!session_.acceptOffer(inviteSDP_)) {
|
||||
emit ChatPage::instance()->showNotification("Problem setting up call.");
|
||||
hangUp();
|
||||
return;
|
||||
}
|
||||
session_.acceptICECandidates(remoteICECandidates_);
|
||||
remoteICECandidates_.clear();
|
||||
if (isVideo)
|
||||
emit newVideoCallState();
|
||||
haveCallInvite_ = false;
|
||||
emit newInviteState();
|
||||
}
|
||||
|
||||
void
|
||||
@ -332,7 +333,7 @@ CallManager::handleEvent(const RoomEvent<CallCandidates> &callCandidatesEvent)
|
||||
callCandidatesEvent.sender);
|
||||
|
||||
if (callid_ == callCandidatesEvent.content.call_id) {
|
||||
if (onActiveCall())
|
||||
if (isOnCall())
|
||||
session_.acceptICECandidates(callCandidatesEvent.content.candidates);
|
||||
else {
|
||||
// CallInvite has been received and we're awaiting localUser to accept or
|
||||
@ -350,15 +351,19 @@ CallManager::handleEvent(const RoomEvent<CallAnswer> &callAnswerEvent)
|
||||
callAnswerEvent.content.call_id,
|
||||
callAnswerEvent.sender);
|
||||
|
||||
if (!onActiveCall() && callAnswerEvent.sender == utils::localUser().toStdString() &&
|
||||
if (callAnswerEvent.sender == utils::localUser().toStdString() &&
|
||||
callid_ == callAnswerEvent.content.call_id) {
|
||||
emit ChatPage::instance()->showNotification("Call answered on another device.");
|
||||
stopRingtone();
|
||||
MainWindow::instance()->hideOverlay();
|
||||
if (!isOnCall()) {
|
||||
emit ChatPage::instance()->showNotification(
|
||||
"Call answered on another device.");
|
||||
stopRingtone();
|
||||
haveCallInvite_ = false;
|
||||
emit newInviteState();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (onActiveCall() && callid_ == callAnswerEvent.content.call_id) {
|
||||
if (isOnCall() && callid_ == callAnswerEvent.content.call_id) {
|
||||
stopRingtone();
|
||||
if (!session_.acceptAnswer(callAnswerEvent.content.sdp)) {
|
||||
emit ChatPage::instance()->showNotification("Problem setting up call.");
|
||||
@ -375,10 +380,42 @@ CallManager::handleEvent(const RoomEvent<CallHangUp> &callHangUpEvent)
|
||||
callHangUpReasonString(callHangUpEvent.content.reason),
|
||||
callHangUpEvent.sender);
|
||||
|
||||
if (callid_ == callHangUpEvent.content.call_id) {
|
||||
MainWindow::instance()->hideOverlay();
|
||||
if (callid_ == callHangUpEvent.content.call_id)
|
||||
endCall();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CallManager::toggleMicMute()
|
||||
{
|
||||
session_.toggleMicMute();
|
||||
emit micMuteChanged();
|
||||
}
|
||||
|
||||
bool
|
||||
CallManager::callsSupported() const
|
||||
{
|
||||
#ifdef GSTREAMER_AVAILABLE
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
QStringList
|
||||
CallManager::devices(bool isVideo) const
|
||||
{
|
||||
QStringList ret;
|
||||
const QString &defaultDevice = isVideo ? ChatPage::instance()->userSettings()->camera()
|
||||
: ChatPage::instance()->userSettings()->microphone();
|
||||
std::vector<std::string> devices =
|
||||
session_.getDeviceNames(isVideo, defaultDevice.toStdString());
|
||||
ret.reserve(devices.size());
|
||||
std::transform(devices.cbegin(),
|
||||
devices.cend(),
|
||||
std::back_inserter(ret),
|
||||
[](const auto &d) { return QString::fromStdString(d); });
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
@ -393,9 +430,13 @@ void
|
||||
CallManager::clear()
|
||||
{
|
||||
roomid_.clear();
|
||||
callPartyName_.clear();
|
||||
callParty_.clear();
|
||||
callPartyAvatarUrl_.clear();
|
||||
callid_.clear();
|
||||
isVideo_ = false;
|
||||
haveCallInvite_ = false;
|
||||
emit newInviteState();
|
||||
inviteSDP_.clear();
|
||||
remoteICECandidates_.clear();
|
||||
}
|
||||
|
||||
@ -403,11 +444,8 @@ void
|
||||
CallManager::endCall()
|
||||
{
|
||||
stopRingtone();
|
||||
clear();
|
||||
bool isVideo = session_.isVideo();
|
||||
session_.end();
|
||||
if (isVideo)
|
||||
emit newVideoCallState();
|
||||
clear();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <QString>
|
||||
#include <QTimer>
|
||||
|
||||
#include "WebRTCSession.h"
|
||||
#include "mtx/events/collections.hpp"
|
||||
#include "mtx/events/voip.hpp"
|
||||
|
||||
@ -15,34 +16,59 @@ namespace mtx::responses {
|
||||
struct TurnServer;
|
||||
}
|
||||
|
||||
class QStringList;
|
||||
class QUrl;
|
||||
class WebRTCSession;
|
||||
|
||||
class CallManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool haveCallInvite READ haveCallInvite NOTIFY newInviteState)
|
||||
Q_PROPERTY(bool isOnCall READ isOnCall NOTIFY newCallState)
|
||||
Q_PROPERTY(bool isVideo READ isVideo NOTIFY newInviteState)
|
||||
Q_PROPERTY(bool haveLocalVideo READ haveLocalVideo NOTIFY newCallState)
|
||||
Q_PROPERTY(webrtc::State callState READ callState NOTIFY newCallState)
|
||||
Q_PROPERTY(QString callParty READ callParty NOTIFY newInviteState)
|
||||
Q_PROPERTY(QString callPartyAvatarUrl READ callPartyAvatarUrl NOTIFY newInviteState)
|
||||
Q_PROPERTY(bool isMicMuted READ isMicMuted NOTIFY micMuteChanged)
|
||||
Q_PROPERTY(bool callsSupported READ callsSupported CONSTANT)
|
||||
Q_PROPERTY(QStringList mics READ mics NOTIFY devicesChanged)
|
||||
Q_PROPERTY(QStringList cameras READ cameras NOTIFY devicesChanged)
|
||||
|
||||
public:
|
||||
CallManager(QObject *);
|
||||
|
||||
void sendInvite(const QString &roomid, bool isVideo);
|
||||
void hangUp(
|
||||
mtx::events::msg::CallHangUp::Reason = mtx::events::msg::CallHangUp::Reason::User);
|
||||
bool onActiveCall() const;
|
||||
QString callPartyName() const { return callPartyName_; }
|
||||
bool haveCallInvite() const { return haveCallInvite_; }
|
||||
bool isOnCall() const { return session_.state() != webrtc::State::DISCONNECTED; }
|
||||
bool isVideo() const { return isVideo_; }
|
||||
bool haveLocalVideo() const { return session_.haveLocalVideo(); }
|
||||
webrtc::State callState() const { return session_.state(); }
|
||||
QString callParty() const { return callParty_; }
|
||||
QString callPartyAvatarUrl() const { return callPartyAvatarUrl_; }
|
||||
bool isMicMuted() const { return session_.isMicMuted(); }
|
||||
bool callsSupported() const;
|
||||
QStringList mics() const { return devices(false); }
|
||||
QStringList cameras() const { return devices(true); }
|
||||
void refreshTurnServer();
|
||||
|
||||
public slots:
|
||||
void sendInvite(const QString &roomid, bool isVideo);
|
||||
void syncEvent(const mtx::events::collections::TimelineEvents &event);
|
||||
void refreshDevices() { session_.refreshDevices(); }
|
||||
void toggleMicMute();
|
||||
void toggleCameraView() { session_.toggleCameraView(); }
|
||||
void acceptInvite();
|
||||
void hangUp(
|
||||
mtx::events::msg::CallHangUp::Reason = mtx::events::msg::CallHangUp::Reason::User);
|
||||
|
||||
signals:
|
||||
void newMessage(const QString &roomid, const mtx::events::msg::CallInvite &);
|
||||
void newMessage(const QString &roomid, const mtx::events::msg::CallCandidates &);
|
||||
void newMessage(const QString &roomid, const mtx::events::msg::CallAnswer &);
|
||||
void newMessage(const QString &roomid, const mtx::events::msg::CallHangUp &);
|
||||
void newCallParty();
|
||||
void newVideoCallState();
|
||||
void newInviteState();
|
||||
void newCallState();
|
||||
void micMuteChanged();
|
||||
void devicesChanged();
|
||||
void turnServerRetrieved(const mtx::responses::TurnServer &);
|
||||
|
||||
private slots:
|
||||
@ -51,10 +77,13 @@ private slots:
|
||||
private:
|
||||
WebRTCSession &session_;
|
||||
QString roomid_;
|
||||
QString callPartyName_;
|
||||
QString callParty_;
|
||||
QString callPartyAvatarUrl_;
|
||||
std::string callid_;
|
||||
const uint32_t timeoutms_ = 120000;
|
||||
bool isVideo_ = false;
|
||||
bool haveCallInvite_ = false;
|
||||
std::string inviteSDP_;
|
||||
std::vector<mtx::events::msg::CallCandidates::Candidate> remoteICECandidates_;
|
||||
std::vector<std::string> turnURIs_;
|
||||
QTimer turnServerTimer_;
|
||||
@ -68,6 +97,7 @@ private:
|
||||
void handleEvent(const mtx::events::RoomEvent<mtx::events::msg::CallHangUp> &);
|
||||
void answerInvite(const mtx::events::msg::CallInvite &, bool isVideo);
|
||||
void generateCallID();
|
||||
QStringList devices(bool isVideo) const;
|
||||
void clear();
|
||||
void endCall();
|
||||
void playRingtone(const QUrl &ringtone, bool repeat);
|
||||
|
@ -47,7 +47,6 @@
|
||||
|
||||
#include "notifications/Manager.h"
|
||||
|
||||
#include "dialogs/PlaceCall.h"
|
||||
#include "dialogs/ReadReceipts.h"
|
||||
#include "popups/UserMentions.h"
|
||||
#include "timeline/TimelineViewManager.h"
|
||||
|
@ -464,7 +464,7 @@ UserSettings::applyTheme()
|
||||
stylefile.setFileName(":/styles/styles/nheko.qss");
|
||||
QPalette lightActive(
|
||||
/*windowText*/ QColor("#333"),
|
||||
/*button*/ QColor("#333"),
|
||||
/*button*/ QColor("white"),
|
||||
/*light*/ QColor(0xef, 0xef, 0xef),
|
||||
/*dark*/ QColor(110, 110, 110),
|
||||
/*mid*/ QColor(220, 220, 220),
|
||||
@ -477,7 +477,7 @@ UserSettings::applyTheme()
|
||||
lightActive.setColor(QPalette::ToolTipBase, lightActive.base().color());
|
||||
lightActive.setColor(QPalette::ToolTipText, lightActive.text().color());
|
||||
lightActive.setColor(QPalette::Link, QColor("#0077b5"));
|
||||
lightActive.setColor(QPalette::ButtonText, QColor("#495057"));
|
||||
lightActive.setColor(QPalette::ButtonText, QColor("#333"));
|
||||
QApplication::setPalette(lightActive);
|
||||
} else if (this->theme() == "dark") {
|
||||
stylefile.setFileName(":/styles/styles/nheko-dark.qss");
|
||||
|
@ -242,12 +242,14 @@ newBusMessage(GstBus *bus G_GNUC_UNUSED, GstMessage *msg, gpointer user_data)
|
||||
GstDevice *device;
|
||||
gst_message_parse_device_added(msg, &device);
|
||||
addDevice(device);
|
||||
emit WebRTCSession::instance().devicesChanged();
|
||||
break;
|
||||
}
|
||||
case GST_MESSAGE_DEVICE_REMOVED: {
|
||||
GstDevice *device;
|
||||
gst_message_parse_device_removed(msg, &device);
|
||||
removeDevice(device, false);
|
||||
emit WebRTCSession::instance().devicesChanged();
|
||||
break;
|
||||
}
|
||||
case GST_MESSAGE_DEVICE_CHANGED: {
|
||||
@ -553,7 +555,10 @@ getResolution(GstPad *pad)
|
||||
void
|
||||
addCameraView(GstElement *pipe, const std::pair<int, int> &videoCallSize)
|
||||
{
|
||||
GstElement *tee = gst_bin_get_by_name(GST_BIN(pipe), "videosrctee");
|
||||
GstElement *tee = gst_bin_get_by_name(GST_BIN(pipe), "videosrctee");
|
||||
if (!tee)
|
||||
return;
|
||||
|
||||
GstElement *queue = gst_element_factory_make("queue", nullptr);
|
||||
GstElement *videorate = gst_element_factory_make("videorate", nullptr);
|
||||
gst_bin_add_many(GST_BIN(pipe), queue, videorate, nullptr);
|
||||
@ -1150,6 +1155,19 @@ WebRTCSession::addVideoPipeline(int vp8PayloadType)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
WebRTCSession::haveLocalVideo() const
|
||||
{
|
||||
if (isVideo_ && state_ >= State::INITIATED) {
|
||||
GstElement *tee = gst_bin_get_by_name(GST_BIN(pipe_), "videosrctee");
|
||||
if (tee) {
|
||||
gst_object_unref(tee);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
WebRTCSession::isMicMuted() const
|
||||
{
|
||||
@ -1274,6 +1292,7 @@ WebRTCSession::refreshDevices()
|
||||
addDevice(GST_DEVICE_CAST(l->data));
|
||||
g_list_free(devices);
|
||||
}
|
||||
emit devicesChanged();
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -1324,6 +1343,12 @@ WebRTCSession::havePlugins(bool, std::string *)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
WebRTCSession::haveLocalVideo() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
WebRTCSession::createOffer(bool)
|
||||
{
|
||||
|
@ -43,6 +43,7 @@ public:
|
||||
bool havePlugins(bool isVideo, std::string *errorMessage = nullptr);
|
||||
webrtc::State state() const { return state_; }
|
||||
bool isVideo() const { return isVideo_; }
|
||||
bool haveLocalVideo() const;
|
||||
bool isOffering() const { return isOffering_; }
|
||||
bool isRemoteVideoRecvOnly() const { return isRemoteVideoRecvOnly_; }
|
||||
|
||||
@ -75,6 +76,7 @@ signals:
|
||||
const std::vector<mtx::events::msg::CallCandidates::Candidate> &);
|
||||
void newICECandidate(const mtx::events::msg::CallCandidates::Candidate &);
|
||||
void stateChanged(webrtc::State);
|
||||
void devicesChanged();
|
||||
|
||||
private slots:
|
||||
void setState(webrtc::State state) { state_ = state; }
|
||||
|
@ -1,152 +0,0 @@
|
||||
#include <QComboBox>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QString>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "ChatPage.h"
|
||||
#include "Config.h"
|
||||
#include "UserSettingsPage.h"
|
||||
#include "Utils.h"
|
||||
#include "WebRTCSession.h"
|
||||
#include "dialogs/AcceptCall.h"
|
||||
#include "ui/Avatar.h"
|
||||
|
||||
namespace dialogs {
|
||||
|
||||
AcceptCall::AcceptCall(const QString &caller,
|
||||
const QString &displayName,
|
||||
const QString &roomName,
|
||||
const QString &avatarUrl,
|
||||
bool isVideo,
|
||||
QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
std::string errorMessage;
|
||||
WebRTCSession *session = &WebRTCSession::instance();
|
||||
if (!session->havePlugins(false, &errorMessage)) {
|
||||
emit ChatPage::instance()->showNotification(QString::fromStdString(errorMessage));
|
||||
emit close();
|
||||
return;
|
||||
}
|
||||
if (isVideo && !session->havePlugins(true, &errorMessage)) {
|
||||
emit ChatPage::instance()->showNotification(QString::fromStdString(errorMessage));
|
||||
emit close();
|
||||
return;
|
||||
}
|
||||
|
||||
session->refreshDevices();
|
||||
microphones_ = session->getDeviceNames(
|
||||
false, ChatPage::instance()->userSettings()->microphone().toStdString());
|
||||
if (microphones_.empty()) {
|
||||
emit ChatPage::instance()->showNotification(
|
||||
tr("Incoming call: No microphone found."));
|
||||
emit close();
|
||||
return;
|
||||
}
|
||||
if (isVideo)
|
||||
cameras_ = session->getDeviceNames(
|
||||
true, ChatPage::instance()->userSettings()->camera().toStdString());
|
||||
|
||||
setAutoFillBackground(true);
|
||||
setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
|
||||
setWindowModality(Qt::WindowModal);
|
||||
setAttribute(Qt::WA_DeleteOnClose, true);
|
||||
|
||||
setMinimumWidth(conf::modals::MIN_WIDGET_WIDTH);
|
||||
setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
|
||||
|
||||
auto layout = new QVBoxLayout(this);
|
||||
layout->setSpacing(conf::modals::WIDGET_SPACING);
|
||||
layout->setMargin(conf::modals::WIDGET_MARGIN);
|
||||
|
||||
QFont f;
|
||||
f.setPointSizeF(f.pointSizeF());
|
||||
|
||||
QFont labelFont;
|
||||
labelFont.setWeight(QFont::Medium);
|
||||
|
||||
QLabel *displayNameLabel = nullptr;
|
||||
if (!displayName.isEmpty() && displayName != caller) {
|
||||
displayNameLabel = new QLabel(displayName, this);
|
||||
labelFont.setPointSizeF(f.pointSizeF() * 2);
|
||||
displayNameLabel->setFont(labelFont);
|
||||
displayNameLabel->setAlignment(Qt::AlignCenter);
|
||||
}
|
||||
|
||||
QLabel *callerLabel = new QLabel(caller, this);
|
||||
labelFont.setPointSizeF(f.pointSizeF() * 1.2);
|
||||
callerLabel->setFont(labelFont);
|
||||
callerLabel->setAlignment(Qt::AlignCenter);
|
||||
|
||||
auto avatar = new Avatar(this, QFontMetrics(f).height() * 6);
|
||||
if (!avatarUrl.isEmpty())
|
||||
avatar->setImage(avatarUrl);
|
||||
else
|
||||
avatar->setLetter(utils::firstChar(roomName));
|
||||
|
||||
const int iconSize = 22;
|
||||
QLabel *callTypeIndicator = new QLabel(this);
|
||||
callTypeIndicator->setPixmap(
|
||||
QIcon(isVideo ? ":/icons/icons/ui/video-call.png" : ":/icons/icons/ui/place-call.png")
|
||||
.pixmap(QSize(iconSize * 2, iconSize * 2)));
|
||||
|
||||
QLabel *callTypeLabel = new QLabel(isVideo ? tr("Video Call") : tr("Voice Call"), this);
|
||||
labelFont.setPointSizeF(f.pointSizeF() * 1.1);
|
||||
callTypeLabel->setFont(labelFont);
|
||||
callTypeLabel->setAlignment(Qt::AlignCenter);
|
||||
|
||||
auto buttonLayout = new QHBoxLayout;
|
||||
buttonLayout->setSpacing(18);
|
||||
acceptBtn_ = new QPushButton(tr("Accept"), this);
|
||||
acceptBtn_->setDefault(true);
|
||||
acceptBtn_->setIcon(
|
||||
QIcon(isVideo ? ":/icons/icons/ui/video-call.png" : ":/icons/icons/ui/place-call.png"));
|
||||
acceptBtn_->setIconSize(QSize(iconSize, iconSize));
|
||||
|
||||
rejectBtn_ = new QPushButton(tr("Reject"), this);
|
||||
rejectBtn_->setIcon(QIcon(":/icons/icons/ui/end-call.png"));
|
||||
rejectBtn_->setIconSize(QSize(iconSize, iconSize));
|
||||
buttonLayout->addWidget(acceptBtn_);
|
||||
buttonLayout->addWidget(rejectBtn_);
|
||||
|
||||
microphoneCombo_ = new QComboBox(this);
|
||||
for (const auto &m : microphones_)
|
||||
microphoneCombo_->addItem(QIcon(":/icons/icons/ui/microphone-unmute.png"),
|
||||
QString::fromStdString(m));
|
||||
|
||||
if (!cameras_.empty()) {
|
||||
cameraCombo_ = new QComboBox(this);
|
||||
for (const auto &c : cameras_)
|
||||
cameraCombo_->addItem(QIcon(":/icons/icons/ui/video-call.png"),
|
||||
QString::fromStdString(c));
|
||||
}
|
||||
|
||||
if (displayNameLabel)
|
||||
layout->addWidget(displayNameLabel, 0, Qt::AlignCenter);
|
||||
layout->addWidget(callerLabel, 0, Qt::AlignCenter);
|
||||
layout->addWidget(avatar, 0, Qt::AlignCenter);
|
||||
layout->addWidget(callTypeIndicator, 0, Qt::AlignCenter);
|
||||
layout->addWidget(callTypeLabel, 0, Qt::AlignCenter);
|
||||
layout->addLayout(buttonLayout);
|
||||
layout->addWidget(microphoneCombo_);
|
||||
if (cameraCombo_)
|
||||
layout->addWidget(cameraCombo_);
|
||||
|
||||
connect(acceptBtn_, &QPushButton::clicked, this, [this]() {
|
||||
ChatPage::instance()->userSettings()->setMicrophone(
|
||||
QString::fromStdString(microphones_[microphoneCombo_->currentIndex()]));
|
||||
if (cameraCombo_) {
|
||||
ChatPage::instance()->userSettings()->setCamera(
|
||||
QString::fromStdString(cameras_[cameraCombo_->currentIndex()]));
|
||||
}
|
||||
emit accept();
|
||||
emit close();
|
||||
});
|
||||
connect(rejectBtn_, &QPushButton::clicked, this, [this]() {
|
||||
emit reject();
|
||||
emit close();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class QComboBox;
|
||||
class QPushButton;
|
||||
class QString;
|
||||
|
||||
namespace dialogs {
|
||||
|
||||
class AcceptCall : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AcceptCall(const QString &caller,
|
||||
const QString &displayName,
|
||||
const QString &roomName,
|
||||
const QString &avatarUrl,
|
||||
bool isVideo,
|
||||
QWidget *parent = nullptr);
|
||||
|
||||
signals:
|
||||
void accept();
|
||||
void reject();
|
||||
|
||||
private:
|
||||
QPushButton *acceptBtn_ = nullptr;
|
||||
QPushButton *rejectBtn_ = nullptr;
|
||||
QComboBox *microphoneCombo_ = nullptr;
|
||||
QComboBox *cameraCombo_ = nullptr;
|
||||
std::vector<std::string> microphones_;
|
||||
std::vector<std::string> cameras_;
|
||||
};
|
||||
|
||||
}
|
@ -1,131 +0,0 @@
|
||||
#include <QComboBox>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QString>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "ChatPage.h"
|
||||
#include "Config.h"
|
||||
#include "UserSettingsPage.h"
|
||||
#include "Utils.h"
|
||||
#include "WebRTCSession.h"
|
||||
#include "dialogs/PlaceCall.h"
|
||||
#include "ui/Avatar.h"
|
||||
|
||||
namespace dialogs {
|
||||
|
||||
PlaceCall::PlaceCall(const QString &callee,
|
||||
const QString &displayName,
|
||||
const QString &roomName,
|
||||
const QString &avatarUrl,
|
||||
QSharedPointer<UserSettings> settings,
|
||||
QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
std::string errorMessage;
|
||||
WebRTCSession *session = &WebRTCSession::instance();
|
||||
if (!session->havePlugins(false, &errorMessage)) {
|
||||
emit ChatPage::instance()->showNotification(QString::fromStdString(errorMessage));
|
||||
emit close();
|
||||
return;
|
||||
}
|
||||
session->refreshDevices();
|
||||
microphones_ = session->getDeviceNames(false, settings->microphone().toStdString());
|
||||
if (microphones_.empty()) {
|
||||
emit ChatPage::instance()->showNotification(tr("No microphone found."));
|
||||
emit close();
|
||||
return;
|
||||
}
|
||||
cameras_ = session->getDeviceNames(true, settings->camera().toStdString());
|
||||
|
||||
setAutoFillBackground(true);
|
||||
setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
|
||||
setWindowModality(Qt::WindowModal);
|
||||
setAttribute(Qt::WA_DeleteOnClose, true);
|
||||
|
||||
auto layout = new QVBoxLayout(this);
|
||||
layout->setSpacing(conf::modals::WIDGET_SPACING);
|
||||
layout->setMargin(conf::modals::WIDGET_MARGIN);
|
||||
|
||||
auto buttonLayout = new QHBoxLayout;
|
||||
buttonLayout->setSpacing(15);
|
||||
buttonLayout->setMargin(0);
|
||||
|
||||
QFont f;
|
||||
f.setPointSizeF(f.pointSizeF());
|
||||
auto avatar = new Avatar(this, QFontMetrics(f).height() * 3);
|
||||
if (!avatarUrl.isEmpty())
|
||||
avatar->setImage(avatarUrl);
|
||||
else
|
||||
avatar->setLetter(utils::firstChar(roomName));
|
||||
|
||||
voiceBtn_ = new QPushButton(tr("Voice"), this);
|
||||
voiceBtn_->setIcon(QIcon(":/icons/icons/ui/place-call.png"));
|
||||
voiceBtn_->setIconSize(QSize(iconSize_, iconSize_));
|
||||
voiceBtn_->setDefault(true);
|
||||
|
||||
if (!cameras_.empty()) {
|
||||
videoBtn_ = new QPushButton(tr("Video"), this);
|
||||
videoBtn_->setIcon(QIcon(":/icons/icons/ui/video-call.png"));
|
||||
videoBtn_->setIconSize(QSize(iconSize_, iconSize_));
|
||||
}
|
||||
cancelBtn_ = new QPushButton(tr("Cancel"), this);
|
||||
|
||||
buttonLayout->addWidget(avatar);
|
||||
buttonLayout->addStretch();
|
||||
buttonLayout->addWidget(voiceBtn_);
|
||||
if (videoBtn_)
|
||||
buttonLayout->addWidget(videoBtn_);
|
||||
buttonLayout->addWidget(cancelBtn_);
|
||||
|
||||
QString name = displayName.isEmpty() ? callee : displayName;
|
||||
QLabel *label = new QLabel(tr("Place a call to ") + name + "?", this);
|
||||
|
||||
microphoneCombo_ = new QComboBox(this);
|
||||
for (const auto &m : microphones_)
|
||||
microphoneCombo_->addItem(QIcon(":/icons/icons/ui/microphone-unmute.png"),
|
||||
QString::fromStdString(m));
|
||||
|
||||
if (videoBtn_) {
|
||||
cameraCombo_ = new QComboBox(this);
|
||||
for (const auto &c : cameras_)
|
||||
cameraCombo_->addItem(QIcon(":/icons/icons/ui/video-call.png"),
|
||||
QString::fromStdString(c));
|
||||
}
|
||||
|
||||
layout->addWidget(label);
|
||||
layout->addLayout(buttonLayout);
|
||||
layout->addStretch();
|
||||
layout->addWidget(microphoneCombo_);
|
||||
if (videoBtn_)
|
||||
layout->addWidget(cameraCombo_);
|
||||
|
||||
connect(voiceBtn_, &QPushButton::clicked, this, [this, settings]() {
|
||||
settings->setMicrophone(
|
||||
QString::fromStdString(microphones_[microphoneCombo_->currentIndex()]));
|
||||
emit voice();
|
||||
emit close();
|
||||
});
|
||||
if (videoBtn_)
|
||||
connect(videoBtn_, &QPushButton::clicked, this, [this, settings, session]() {
|
||||
std::string error;
|
||||
if (!session->havePlugins(true, &error)) {
|
||||
emit ChatPage::instance()->showNotification(
|
||||
QString::fromStdString(error));
|
||||
emit close();
|
||||
return;
|
||||
}
|
||||
settings->setMicrophone(
|
||||
QString::fromStdString(microphones_[microphoneCombo_->currentIndex()]));
|
||||
settings->setCamera(
|
||||
QString::fromStdString(cameras_[cameraCombo_->currentIndex()]));
|
||||
emit video();
|
||||
emit close();
|
||||
});
|
||||
connect(cancelBtn_, &QPushButton::clicked, this, [this]() {
|
||||
emit cancel();
|
||||
emit close();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <QSharedPointer>
|
||||
#include <QWidget>
|
||||
|
||||
class QComboBox;
|
||||
class QPushButton;
|
||||
class QString;
|
||||
class UserSettings;
|
||||
|
||||
namespace dialogs {
|
||||
|
||||
class PlaceCall : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PlaceCall(const QString &callee,
|
||||
const QString &displayName,
|
||||
const QString &roomName,
|
||||
const QString &avatarUrl,
|
||||
QSharedPointer<UserSettings> settings,
|
||||
QWidget *parent = nullptr);
|
||||
|
||||
signals:
|
||||
void voice();
|
||||
void video();
|
||||
void cancel();
|
||||
|
||||
private:
|
||||
const int iconSize_ = 18;
|
||||
QPushButton *voiceBtn_ = nullptr;
|
||||
QPushButton *videoBtn_ = nullptr;
|
||||
QPushButton *cancelBtn_ = nullptr;
|
||||
QComboBox *microphoneCombo_ = nullptr;
|
||||
QComboBox *cameraCombo_ = nullptr;
|
||||
std::vector<std::string> microphones_;
|
||||
std::vector<std::string> cameras_;
|
||||
};
|
||||
|
||||
}
|
@ -13,7 +13,6 @@
|
||||
#include <mtx/responses/media.hpp>
|
||||
|
||||
#include "Cache.h"
|
||||
#include "CallManager.h"
|
||||
#include "ChatPage.h"
|
||||
#include "CompletionProxyModel.h"
|
||||
#include "Logging.h"
|
||||
@ -25,7 +24,6 @@
|
||||
#include "UserSettingsPage.h"
|
||||
#include "UsersModel.h"
|
||||
#include "Utils.h"
|
||||
#include "dialogs/PlaceCall.h"
|
||||
#include "dialogs/PreviewUploadOverlay.h"
|
||||
#include "emoji/EmojiModel.h"
|
||||
|
||||
@ -593,48 +591,6 @@ InputBar::showPreview(const QMimeData &source, QString path, const QStringList &
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
InputBar::callButton()
|
||||
{
|
||||
auto callManager_ = ChatPage::instance()->callManager();
|
||||
if (callManager_->onActiveCall()) {
|
||||
callManager_->hangUp();
|
||||
} else {
|
||||
auto current_room_ = room->roomId();
|
||||
if (auto roomInfo = cache::singleRoomInfo(current_room_.toStdString());
|
||||
roomInfo.member_count != 2) {
|
||||
ChatPage::instance()->showNotification("Calls are limited to 1:1 rooms.");
|
||||
} else {
|
||||
std::vector<RoomMember> members(
|
||||
cache::getMembers(current_room_.toStdString()));
|
||||
const RoomMember &callee = members.front().user_id == utils::localUser()
|
||||
? members.back()
|
||||
: members.front();
|
||||
auto dialog =
|
||||
new dialogs::PlaceCall(callee.user_id,
|
||||
callee.display_name,
|
||||
QString::fromStdString(roomInfo.name),
|
||||
QString::fromStdString(roomInfo.avatar_url),
|
||||
ChatPage::instance()->userSettings(),
|
||||
MainWindow::instance());
|
||||
connect(dialog,
|
||||
&dialogs::PlaceCall::voice,
|
||||
callManager_,
|
||||
[callManager_, current_room_]() {
|
||||
callManager_->sendInvite(current_room_, false);
|
||||
});
|
||||
connect(dialog,
|
||||
&dialogs::PlaceCall::video,
|
||||
callManager_,
|
||||
[callManager_, current_room_]() {
|
||||
callManager_->sendInvite(current_room_, true);
|
||||
});
|
||||
utils::centerWidget(dialog, MainWindow::instance());
|
||||
dialog->show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InputBar::startTyping()
|
||||
{
|
||||
|
@ -41,7 +41,6 @@ public slots:
|
||||
void updateState(int selectionStart, int selectionEnd, int cursorPosition, QString text);
|
||||
void openFileSelection();
|
||||
bool uploading() const { return uploading_; }
|
||||
void callButton();
|
||||
void message(QString body);
|
||||
|
||||
QObject *completerFor(QString completerName);
|
||||
|
@ -613,8 +613,13 @@ TimelineModel::addEvents(const mtx::responses::Timeline &timeline)
|
||||
std::visit(
|
||||
[this](auto &event) {
|
||||
event.room_id = room_id_.toStdString();
|
||||
if (event.sender != http::client()->user_id().to_string())
|
||||
if constexpr (std::is_same_v<std::decay_t<decltype(event)>,
|
||||
RoomEvent<msg::CallAnswer>>)
|
||||
emit newCallEvent(event);
|
||||
else {
|
||||
if (event.sender != http::client()->user_id().to_string())
|
||||
emit newCallEvent(event);
|
||||
}
|
||||
},
|
||||
e);
|
||||
else if (std::holds_alternative<StateEvent<state::Avatar>>(e))
|
||||
|
@ -136,6 +136,10 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
|
||||
"im.nheko", 1, 0, "Settings", [](QQmlEngine *, QJSEngine *) -> QObject * {
|
||||
return ChatPage::instance()->userSettings().data();
|
||||
});
|
||||
qmlRegisterSingletonType<CallManager>(
|
||||
"im.nheko", 1, 0, "CallManager", [](QQmlEngine *, QJSEngine *) -> QObject * {
|
||||
return ChatPage::instance()->callManager();
|
||||
});
|
||||
|
||||
qRegisterMetaType<mtx::events::collections::TimelineEvents>();
|
||||
qRegisterMetaType<std::vector<DeviceInfo>>();
|
||||
@ -237,36 +241,6 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
|
||||
isInitialSync_ = true;
|
||||
emit initialSyncChanged(true);
|
||||
});
|
||||
connect(&WebRTCSession::instance(),
|
||||
&WebRTCSession::stateChanged,
|
||||
this,
|
||||
&TimelineViewManager::callStateChanged);
|
||||
connect(
|
||||
callManager_, &CallManager::newCallParty, this, &TimelineViewManager::callPartyChanged);
|
||||
connect(callManager_,
|
||||
&CallManager::newVideoCallState,
|
||||
this,
|
||||
&TimelineViewManager::videoCallChanged);
|
||||
|
||||
connect(&WebRTCSession::instance(),
|
||||
&WebRTCSession::stateChanged,
|
||||
this,
|
||||
&TimelineViewManager::onCallChanged);
|
||||
}
|
||||
|
||||
bool
|
||||
TimelineViewManager::isOnCall() const
|
||||
{
|
||||
return callManager_->onActiveCall();
|
||||
}
|
||||
bool
|
||||
TimelineViewManager::callsSupported() const
|
||||
{
|
||||
#ifdef GSTREAMER_AVAILABLE
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
@ -354,19 +328,6 @@ TimelineViewManager::escapeEmoji(QString str) const
|
||||
return utils::replaceEmoji(str);
|
||||
}
|
||||
|
||||
void
|
||||
TimelineViewManager::toggleMicMute()
|
||||
{
|
||||
WebRTCSession::instance().toggleMicMute();
|
||||
emit micMuteChanged();
|
||||
}
|
||||
|
||||
void
|
||||
TimelineViewManager::toggleCameraView()
|
||||
{
|
||||
WebRTCSession::instance().toggleCameraView();
|
||||
}
|
||||
|
||||
void
|
||||
TimelineViewManager::openImageOverlay(QString mxcUrl, QString eventId) const
|
||||
{
|
||||
|
@ -36,13 +36,6 @@ class TimelineViewManager : public QObject
|
||||
bool isInitialSync MEMBER isInitialSync_ READ isInitialSync NOTIFY initialSyncChanged)
|
||||
Q_PROPERTY(
|
||||
bool isNarrowView MEMBER isNarrowView_ READ isNarrowView NOTIFY narrowViewChanged)
|
||||
Q_PROPERTY(webrtc::State callState READ callState NOTIFY callStateChanged)
|
||||
Q_PROPERTY(bool onVideoCall READ onVideoCall NOTIFY videoCallChanged)
|
||||
Q_PROPERTY(QString callPartyName READ callPartyName NOTIFY callPartyChanged)
|
||||
Q_PROPERTY(QString callPartyAvatarUrl READ callPartyAvatarUrl NOTIFY callPartyChanged)
|
||||
Q_PROPERTY(bool isMicMuted READ isMicMuted NOTIFY micMuteChanged)
|
||||
Q_PROPERTY(bool isOnCall READ isOnCall NOTIFY onCallChanged)
|
||||
Q_PROPERTY(bool callsSupported READ callsSupported CONSTANT)
|
||||
|
||||
public:
|
||||
TimelineViewManager(CallManager *callManager, ChatPage *parent = nullptr);
|
||||
@ -61,14 +54,6 @@ public:
|
||||
Q_INVOKABLE TimelineModel *activeTimeline() const { return timeline_; }
|
||||
Q_INVOKABLE bool isInitialSync() const { return isInitialSync_; }
|
||||
bool isNarrowView() const { return isNarrowView_; }
|
||||
webrtc::State callState() const { return WebRTCSession::instance().state(); }
|
||||
bool onVideoCall() const { return WebRTCSession::instance().isVideo(); }
|
||||
Q_INVOKABLE void setVideoCallItem();
|
||||
QString callPartyName() const { return callManager_->callPartyName(); }
|
||||
QString callPartyAvatarUrl() const { return callManager_->callPartyAvatarUrl(); }
|
||||
bool isMicMuted() const { return WebRTCSession::instance().isMicMuted(); }
|
||||
Q_INVOKABLE void toggleMicMute();
|
||||
Q_INVOKABLE void toggleCameraView();
|
||||
Q_INVOKABLE void openImageOverlay(QString mxcUrl, QString eventId) const;
|
||||
Q_INVOKABLE QColor userColor(QString id, QColor background);
|
||||
Q_INVOKABLE QString escapeEmoji(QString str) const;
|
||||
@ -98,11 +83,6 @@ signals:
|
||||
void inviteUsers(QStringList users);
|
||||
void showRoomList();
|
||||
void narrowViewChanged();
|
||||
void callStateChanged(webrtc::State);
|
||||
void videoCallChanged();
|
||||
void callPartyChanged();
|
||||
void micMuteChanged();
|
||||
void onCallChanged();
|
||||
|
||||
public slots:
|
||||
void updateReadReceipts(const QString &room_id, const std::vector<QString> &event_ids);
|
||||
@ -130,8 +110,7 @@ public slots:
|
||||
void queueCallMessage(const QString &roomid, const mtx::events::msg::CallHangUp &);
|
||||
|
||||
void updateEncryptedDescriptions();
|
||||
bool isOnCall() const;
|
||||
bool callsSupported() const;
|
||||
void setVideoCallItem();
|
||||
|
||||
void enableBackButton()
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user