summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authortrilene <trilene@runbox.com>2020-12-17 11:25:32 -0500
committerNicolas Werner <nicolas.werner@hotmail.de>2021-01-07 14:04:21 +0100
commit7124024977ccb237547b88b6a96a50ac6838c354 (patch)
treef5a6ca7b69444413b3c806667e72584d40e36689 /src
parentMove call-related properties to CallManager (diff)
downloadnheko-7124024977ccb237547b88b6a96a50ac6838c354.tar.xz
Make call invites less intrusive
Diffstat (limited to 'src')
-rw-r--r--src/CallManager.cpp100
-rw-r--r--src/CallManager.h32
-rw-r--r--src/WebRTCSession.cpp2
-rw-r--r--src/WebRTCSession.h1
-rw-r--r--src/dialogs/AcceptCall.cpp152
-rw-r--r--src/dialogs/AcceptCall.h39
-rw-r--r--src/timeline/InputBar.cpp4
7 files changed, 90 insertions, 240 deletions
diff --git a/src/CallManager.cpp b/src/CallManager.cpp
index cb523bc2..9864b203 100644
--- a/src/CallManager.cpp
+++ b/src/CallManager.cpp
@@ -10,10 +10,9 @@
 #include "CallManager.h"
 #include "ChatPage.h"
 #include "Logging.h"
-#include "MainWindow.h"
 #include "MatrixClient.h"
+#include "UserSettingsPage.h"
 #include "Utils.h"
-#include "dialogs/AcceptCall.h"
 
 #include "mtx/responses/turn_server.hpp"
 
@@ -114,6 +113,22 @@ CallManager::CallManager(QObject *parent)
                 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_,
                 &QMediaPlayer::mediaStatusChanged,
                 this,
@@ -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 {
@@ -271,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
@@ -348,7 +355,8 @@ CallManager::handleEvent(const RoomEvent<CallAnswer> &callAnswerEvent)
             callid_ == callAnswerEvent.content.call_id) {
                 emit ChatPage::instance()->showNotification("Call answered on another device.");
                 stopRingtone();
-                MainWindow::instance()->hideOverlay();
+                haveCallInvite_ = false;
+                emit newInviteState();
                 return;
         }
 
@@ -369,10 +377,8 @@ 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
@@ -392,6 +398,23 @@ CallManager::callsSupported() const
 #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
 CallManager::generateCallID()
 {
@@ -404,9 +427,13 @@ void
 CallManager::clear()
 {
         roomid_.clear();
-        callPartyName_.clear();
+        callParty_.clear();
         callPartyAvatarUrl_.clear();
         callid_.clear();
+        isVideo_        = false;
+        haveCallInvite_ = false;
+        emit newInviteState();
+        inviteSDP_.clear();
         remoteICECandidates_.clear();
 }
 
@@ -414,11 +441,8 @@ void
 CallManager::endCall()
 {
         stopRingtone();
-        clear();
-        bool isVideo = session_.isVideo();
         session_.end();
-        if (isVideo)
-                emit newVideoCallState();
+        clear();
 }
 
 void
diff --git a/src/CallManager.h b/src/CallManager.h
index d59a6249..e5571c88 100644
--- a/src/CallManager.h
+++ b/src/CallManager.h
@@ -16,48 +16,56 @@ namespace mtx::responses {
 struct TurnServer;
 }
 
+class QStringList;
 class QUrl;
 
 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 isOnVideoCall READ isOnVideoCall NOTIFY newVideoCallState)
+        Q_PROPERTY(bool isVideo READ isVideo NOTIFY newInviteState)
         Q_PROPERTY(webrtc::State callState READ callState NOTIFY newCallState)
-        Q_PROPERTY(QString callPartyName READ callPartyName NOTIFY newCallParty)
-        Q_PROPERTY(QString callPartyAvatarUrl READ callPartyAvatarUrl NOTIFY newCallParty)
+        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 haveCallInvite() const { return haveCallInvite_; }
         bool isOnCall() const { return session_.state() != webrtc::State::DISCONNECTED; }
-        bool isOnVideoCall() const { return session_.isVideo(); }
+        bool isVideo() const { return isVideo_; }
         webrtc::State callState() const { return session_.state(); }
-        QString callPartyName() const { return callPartyName_; }
+        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 syncEvent(const mtx::events::collections::TimelineEvents &event);
         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 newInviteState();
         void newCallState();
-        void newVideoCallState();
-        void newCallParty();
         void micMuteChanged();
+        void devicesChanged();
         void turnServerRetrieved(const mtx::responses::TurnServer &);
 
 private slots:
@@ -66,10 +74,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_;
@@ -83,6 +94,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);
diff --git a/src/WebRTCSession.cpp b/src/WebRTCSession.cpp
index 0770a439..90a693a4 100644
--- a/src/WebRTCSession.cpp
+++ b/src/WebRTCSession.cpp
@@ -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: {
diff --git a/src/WebRTCSession.h b/src/WebRTCSession.h
index 57002f8f..fe82725f 100644
--- a/src/WebRTCSession.h
+++ b/src/WebRTCSession.h
@@ -75,6 +75,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; }
diff --git a/src/dialogs/AcceptCall.cpp b/src/dialogs/AcceptCall.cpp
deleted file mode 100644
index 3d25ad82..00000000
--- a/src/dialogs/AcceptCall.cpp
+++ /dev/null
@@ -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();
-        });
-}
-
-}
diff --git a/src/dialogs/AcceptCall.h b/src/dialogs/AcceptCall.h
deleted file mode 100644
index 76ca7ae1..00000000
--- a/src/dialogs/AcceptCall.h
+++ /dev/null
@@ -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_;
-};
-
-}
diff --git a/src/timeline/InputBar.cpp b/src/timeline/InputBar.cpp
index 2f50a7cc..78c8c6a3 100644
--- a/src/timeline/InputBar.cpp
+++ b/src/timeline/InputBar.cpp
@@ -597,7 +597,9 @@ void
 InputBar::callButton()
 {
         auto callManager_ = ChatPage::instance()->callManager();
-        if (callManager_->isOnCall()) {
+        if (callManager_->haveCallInvite()) {
+                return;
+        } else if (callManager_->isOnCall()) {
                 callManager_->hangUp();
         } else {
                 auto current_room_ = room->roomId();