From 01da004adfdcc9ce18659067f8beda6a1186588d Mon Sep 17 00:00:00 2001 From: Ming Rui Zhang <mingrui.zhang@savoirfairelinux.com> Date: Wed, 7 Aug 2019 13:02:06 -0400 Subject: [PATCH] transferCall: add blind/attended transfer call support Change-Id: I7a159ac51cb7527ce8fc1010c2c6caebd7251c0e --- callwidget.cpp | 13 +++--- contactpicker.cpp | 24 +++++++--- contactpicker.h | 7 +-- contactpicker.ui | 12 +++-- contactpickeritemdelegate.cpp | 64 ++++++++++++++++++++++++++- contactpickeritemdelegate.h | 2 +- stylesheet.css | 6 +-- videooverlay.cpp | 82 +++++++++++++++++++++++++++++++++++ videooverlay.h | 4 ++ videoview.cpp | 11 ++++- videoview.h | 4 +- 11 files changed, 199 insertions(+), 30 deletions(-) diff --git a/callwidget.cpp b/callwidget.cpp index 4f28dd4..2840691 100644 --- a/callwidget.cpp +++ b/callwidget.cpp @@ -642,11 +642,13 @@ void CallWidget::slotShowCallView(const std::string& accountId, const lrc::api::conversation::Info& convInfo) { Q_UNUSED(accountId); - Q_UNUSED(convInfo); qDebug() << "slotShowCallView"; - setCallPanelVisibility(true); - + // find out the best name for current callee, remove the search result from smart list auto callModel = LRCInstance::getCurrentCallModel(); + auto convModel = LRCInstance::getCurrentConversationModel(); + auto bestName = QString::fromStdString(Utils::bestNameForConversation(convInfo, *convModel)); + ui->videoWidget->setCurrentCalleeName(bestName); + setCallPanelVisibility(true); if (callModel->hasCall(convInfo.callId)) { auto call = callModel->getCall(convInfo.callId); @@ -659,7 +661,7 @@ void CallWidget::slotShowCallView(const std::string& accountId, } ui->callStackWidget->setCurrentWidget(ui->videoPage); hideMiniSpinner(); - ui->videoWidget->pushRenderer(convInfo.callId); + ui->videoWidget->pushRenderer(convInfo.callId, LRCInstance::accountModel().getAccountInfo(accountId).profileInfo.type == lrc::api::profile::Type::SIP); } void CallWidget::slotShowIncomingCallView(const std::string& accountId, @@ -683,6 +685,7 @@ void CallWidget::slotShowIncomingCallView(const std::string& accountId, auto call = callModel->getCall(convInfo.callId); auto isCallSelected = LRCInstance::getSelectedConvUid() == convInfo.uid; ui->callingStatusLabel->setText(QString::fromStdString(lrc::api::call::to_string(call.status))); + ui->videoWidget->setCurrentCalleeName(bestName); connect(callModel, &lrc::api::NewCallModel::callStatusChanged, ui->incomingCallPage, [this, accountId](const std::string& callId) { @@ -726,7 +729,7 @@ void CallWidget::slotShowIncomingCallView(const std::string& accountId, ui->messagesWidget->show(); } - ui->videoWidget->pushRenderer(convInfo.callId); + ui->videoWidget->pushRenderer(convInfo.callId, LRCInstance::accountModel().getAccountInfo(accountId).profileInfo.type == lrc::api::profile::Type::SIP); QFontMetrics primaryCallLabelFontMetrics(ui->callingBestNameLabel->font()); QFontMetrics sencondaryCallLabelFontMetrics(ui->callingBestIdLabel->font()); diff --git a/contactpicker.cpp b/contactpicker.cpp index 70ccd7f..4830882 100644 --- a/contactpicker.cpp +++ b/contactpicker.cpp @@ -73,9 +73,8 @@ ContactPicker::accept() case Type::CONFERENCE: emit contactWillJoinConference(thisCallId, contactUri); break; - case Type::BLIND_TRANSFER: - case Type::ATTENDED_TRANSFER: - emit contactWillDoBlindTransfer(thisCallId, contactUri); + case Type::TRANSFER: + emit contactWillDoTransfer(thisCallId, contactUri); break; default: break; @@ -96,7 +95,6 @@ ContactPicker::mousePressEvent(QMouseEvent *event) { auto contactPickerWidgetRect = ui->contactPickerWidget->rect(); if (!contactPickerWidgetRect.contains(event->pos())) { - //close(); emit willClose(event); } } @@ -131,11 +129,17 @@ ContactPicker::setType(const Type& type) !index.parent().isValid(); }); break; - case Type::BLIND_TRANSFER: - case Type::ATTENDED_TRANSFER: + case Type::TRANSFER: selectableProxyModel_->setPredicate( [this](const QModelIndex& index, const QRegExp& regexp) { - return true; + // Regex to remove current callee + QRegExp matchExcept= QRegExp(QString("\\b(?!" + CalleeDisplayName_ + "\\b)\\w+")); + bool match = false; + bool match_non_self = matchExcept.indexIn(index.data(Qt::DisplayRole).toString()) != -1; + if (match_non_self) { + match = regexp.indexIn(index.data(Qt::DisplayRole).toString()) != -1; + } + return match && !index.parent().isValid(); }); break; default: @@ -143,3 +147,9 @@ ContactPicker::setType(const Type& type) } selectableProxyModel_->invalidate(); } + +void +ContactPicker::setCurrentCalleeDisplayName(const QString& CalleeDisplayName) +{ + CalleeDisplayName_ = CalleeDisplayName; +} diff --git a/contactpicker.h b/contactpicker.h index b9e9b4a..c491b5f 100644 --- a/contactpicker.h +++ b/contactpicker.h @@ -63,8 +63,7 @@ class ContactPicker : public QDialog public: enum class Type { CONFERENCE, - BLIND_TRANSFER, - ATTENDED_TRANSFER, + TRANSFER, COUNT__ }; @@ -72,13 +71,14 @@ public: ~ContactPicker(); void setTitle(const std::string& title); void setType(const Type& type); + void setCurrentCalleeDisplayName(const QString& CalleeDisplayName); protected: void mousePressEvent(QMouseEvent *event); signals: void contactWillJoinConference(const std::string& callId, const std::string& contactUri); - void contactWillDoBlindTransfer(const std::string& callId, const std::string& contactUri); + void contactWillDoTransfer(const std::string& callId, const std::string& contactUri); void willClose(QMouseEvent *event); protected slots: @@ -94,5 +94,6 @@ private: std::unique_ptr<SmartListModel> smartListModel_; SelectableProxyModel* selectableProxyModel_; Type type_; + QString CalleeDisplayName_; }; diff --git a/contactpicker.ui b/contactpicker.ui index a943292..e42fe84 100644 --- a/contactpicker.ui +++ b/contactpicker.ui @@ -190,7 +190,7 @@ <cursorShape>IBeamCursor</cursorShape> </property> <property name="toolTip"> - <string>Search contact text input</string> + <string>Search contact</string> </property> <property name="maxLength"> <number>100</number> @@ -251,10 +251,10 @@ <attribute name="headerVisible"> <bool>false</bool> </attribute> - <attribute name="headerDefaultSectionSize"> + <attribute name="headerMinimumSectionSize"> <number>0</number> </attribute> - <attribute name="headerMinimumSectionSize"> + <attribute name="headerDefaultSectionSize"> <number>0</number> </attribute> </widget> @@ -294,7 +294,7 @@ <string/> </property> <property name="pixmap"> - <pixmap resource="ressources.qrc">:/images/spike.png</pixmap> + <pixmap>:/images/spike.png</pixmap> </property> </widget> </item> @@ -312,8 +312,6 @@ <header>smartlistview.h</header> </customwidget> </customwidgets> - <resources> - <include location="ressources.qrc"/> - </resources> + <resources/> <connections/> </ui> diff --git a/contactpickeritemdelegate.cpp b/contactpickeritemdelegate.cpp index 476ebf6..b86bf5b 100644 --- a/contactpickeritemdelegate.cpp +++ b/contactpickeritemdelegate.cpp @@ -104,6 +104,7 @@ ContactPickerItemDelegate::paint(QPainter* painter paintRingContactItem(painter, option, rect, index); break; case profile::Type::SIP: + paintSIPContactItem(painter, option, rect, index); break; default: paintRingContactItem(painter, option, rect, index); @@ -183,9 +184,68 @@ ContactPickerItemDelegate::paintRingContactItem(QPainter* painter, void ContactPickerItemDelegate::paintSIPContactItem(QPainter* painter, const QStyleOptionViewItem& option, + const QRect& rect, const QModelIndex& index) const { - Q_UNUSED(painter); Q_UNUSED(option); - Q_UNUSED(index); + QRect rectName1; + QRect rectName2; + QFont font(painter->font()); + QPen pen(painter->pen()); + painter->setPen(pen); + + auto scalingRatio = MainWindow::instance().getCurrentScalingRatio(); + if (scalingRatio > 1.0) { + font.setPointSize(fontSize_ - 2); + } else { + font.setPointSize(fontSize_); + } + + auto leftMargin = dx_ + sizeImage_ + dx_; + auto rightMargin = dx_; + auto topMargin = 4; + auto bottomMargin = 8; + + if (!rect.isEmpty()) { + + rectName1 = QRect(rect.left() + leftMargin, + rect.top() + topMargin, + rect.width() - leftMargin * 2, + rect.height() / 2 - 2); + + rectName2 = QRect(rectName1.left(), + rectName1.top() + rectName1.height(), + rectName1.width(), + rectName1.height() - bottomMargin); + } + + QFontMetrics fontMetrics(font); + + // The name is displayed at the avatar's right + QString nameStr = index.data(static_cast<int>(SmartListModel::Role::DisplayName)).value<QString>(); + if (!nameStr.isNull()) { + font.setItalic(false); + font.setBold(false); + pen.setColor(RingTheme::lightBlack_); + painter->setPen(pen); + painter->setFont(font); + if (!rectName1.isEmpty()) { + QString elidedNameStr = fontMetrics.elidedText(nameStr, Qt::ElideRight, rectName1.width()); + painter->drawText(rectName1, Qt::AlignVCenter | Qt::AlignLeft, elidedNameStr); + } + } + + // Display the ID under the name + QString idStr = index.data(static_cast<int>(SmartListModel::Role::DisplayID)).value<QString>(); + if (idStr != nameStr && !idStr.isNull()) { + font.setItalic(false); + font.setBold(false); + pen.setColor(RingTheme::grey_); + painter->setPen(pen); + painter->setFont(font); + if (!rectName2.isEmpty()) { + idStr = fontMetrics.elidedText(idStr, Qt::ElideRight, rectName2.width()); + painter->drawText(rectName2, Qt::AlignVCenter | Qt::AlignLeft, idStr); + } + } } diff --git a/contactpickeritemdelegate.h b/contactpickeritemdelegate.h index 6cdc225..e6def29 100644 --- a/contactpickeritemdelegate.h +++ b/contactpickeritemdelegate.h @@ -35,7 +35,7 @@ protected: private: void paintRingContactItem(QPainter* painter, const QStyleOptionViewItem& option, const QRect& rect, const QModelIndex& index) const; - void paintSIPContactItem(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; + void paintSIPContactItem(QPainter* painter, const QStyleOptionViewItem& option, const QRect& rect, const QModelIndex& index) const; constexpr static int sizeImage_ = 48; constexpr static int cellHeight_ = 60; diff --git a/stylesheet.css b/stylesheet.css index f80075c..d2e012f 100644 --- a/stylesheet.css +++ b/stylesheet.css @@ -177,7 +177,7 @@ QWidget#messageViewLayoutWidget, QWidget#welcomePage { QPushButton#holdButton, QPushButton#chatButton, QPushButton#noMicButton, QPushButton#noVideoButton, QPushButton#transferButton, QPushButton#addPersonButton, QPushButton#joinButton, -QPushButton#qualityButton, QPushButton#recButton { +QPushButton#qualityButton, QPushButton#recButton, QPushButton#transferCallButton { background-color: rgba(0, 0, 0, 140); border-radius: 18px; border: solid 1px; @@ -186,7 +186,7 @@ QPushButton#qualityButton, QPushButton#recButton { QPushButton#holdButton:hover, QPushButton#chatButton:hover, QPushButton#noMicButton:hover, QPushButton#noVideoButton:hover, QPushButton#transferButton:hover, QPushButton#addPersonButton:hover, QPushButton#joinButton:hover, QPushButton#qualityButton:hover, QPushButton#addToContactButton:hover, -QPushButton#recButton:hover { +QPushButton#recButton:hover, QPushButton#transferCallButton:hover { background-color: rgba(0, 192, 213, 0.6); } @@ -199,7 +199,7 @@ QPushButton#recButton:pressed { QPushButton#holdButton:checked, QPushButton#noMicButton:checked, QPushButton#noVideoButton:checked, QPushButton#recButton:checked, -QPushButton#chatButton:checked{ +QPushButton#chatButton:checked, QPushButton#transferCallButton:checked { background-color: rgba(0, 192, 213, 0.8); } diff --git a/videooverlay.cpp b/videooverlay.cpp index 17d2d26..820065c 100644 --- a/videooverlay.cpp +++ b/videooverlay.cpp @@ -44,6 +44,15 @@ VideoOverlay::VideoOverlay(QWidget* parent) : ui->onHoldLabel->setVisible(false); ui->recButton->setVisible(false); + + ui->transferCallButton->setVisible(false); + ui->transferCallButton->setCheckable(true); + + contactPicker_->setVisible(false); + contactPicker_->setTitle("Select Peer to Tranfer"); + + connect(ui->transferCallButton, &QPushButton::toggled, this, &VideoOverlay::on_transferButton_toggled); + connect(contactPicker_, &ContactPicker::contactWillDoTransfer, this, &VideoOverlay::on_transferCall_requested); } VideoOverlay::~VideoOverlay() @@ -176,6 +185,79 @@ VideoOverlay::on_recButton_clicked() } } +void +VideoOverlay::setTransferCallAvailability(bool visible) +{ + ui->transferCallButton->setVisible(visible); +} + +void +VideoOverlay::on_transferButton_toggled(bool checked) +{ + if (callId_.empty() || !checked) { + return; + } + contactPicker_->setType(ContactPicker::Type::TRANSFER); + + QPoint globalPos_button = mapToGlobal(ui->transferCallButton->pos()); + QPoint globalPos_bottomButtons = mapToGlobal(ui->bottomButtons->pos()); + + contactPicker_->move(globalPos_button.x(), globalPos_bottomButtons.y() - contactPicker_->height()); + + // receive the signal that ensure the button checked status is correct and contactpicker + // is properly hidden + Utils::oneShotConnect(contactPicker_, &ContactPicker::willClose, [this](QMouseEvent *event) { + contactPicker_->hide(); + // check if current mouse position is on button + auto relativeCursorPos = ui->transferCallButton->mapFromGlobal(event->pos()); + if (!ui->transferCallButton->rect().contains(relativeCursorPos)) { + ui->transferCallButton->setChecked(false); + } + }); + + // for esc key, receive reject signal + Utils::oneShotConnect(contactPicker_, &QDialog::rejected, + [this] { + ui->transferCallButton->setChecked(false); + }); + + contactPicker_->show(); +} + +void +VideoOverlay::on_transferCall_requested(const std::string& callId, const std::string& contactUri) +{ + auto callModel = LRCInstance::getCurrentCallModel(); + contactPicker_->hide(); + ui->transferCallButton->setChecked(false); + std::string destCallId; + + try { + //check if the call exist - (check non-finished calls) + auto callInfo = callModel->getCallFromURI(contactUri, true); + destCallId = callInfo.id; + } catch(std::exception& e) { + qDebug().noquote() << e.what(); + destCallId = ""; + } + // if no second call -> blind transfer + // if there is a second call -> attended transfer + if (destCallId.size() == 0) { + callModel->transfer(callId, "sip:" + contactUri); + callModel->hangUp(callId); + }else{ + callModel->transferToCall(callId, destCallId); + callModel->hangUp(callId); + callModel->hangUp(destCallId); + } +} + +void +VideoOverlay::setCurrentSelectedCalleeDisplayName(const QString& CalleeDisplayName) +{ + contactPicker_->setCurrentCalleeDisplayName(CalleeDisplayName); +} + void VideoOverlay::resetOverlay(bool isAudioMuted, bool isVideoMuted, bool isRecording, bool isHolding) { diff --git a/videooverlay.h b/videooverlay.h index 640c1eb..c7b1c67 100644 --- a/videooverlay.h +++ b/videooverlay.h @@ -45,6 +45,8 @@ public: bool shouldShowOverlay(); void simulateShowChatview(bool checked); bool getShowChatView(); + void setTransferCallAvailability(bool visible); + void setCurrentSelectedCalleeDisplayName(const QString& CalleeDisplayName); void resetOverlay(bool isAudioMuted, bool isVideoMuted, bool isRecording, bool isHolding); //UI SLOTS @@ -56,6 +58,8 @@ private slots: void on_noMicButton_toggled(bool checked); void on_noVideoButton_toggled(bool checked); void on_recButton_clicked(); + void on_transferButton_toggled(bool checked); + void on_transferCall_requested(const std::string& callId, const std::string& contactUri); private: Ui::VideoOverlay* ui; diff --git a/videoview.cpp b/videoview.cpp index 61e54e3..f15af67 100644 --- a/videoview.cpp +++ b/videoview.cpp @@ -333,7 +333,7 @@ VideoView::showContextMenu(const QPoint& pos) } void -VideoView::pushRenderer(const std::string& callId) { +VideoView::pushRenderer(const std::string& callId, bool isSIP) { auto callModel = LRCInstance::getCurrentCallModel(); QObject::disconnect(ui->videoWidget); @@ -349,6 +349,8 @@ VideoView::pushRenderer(const std::string& callId) { return; } + // transfer call will only happen in SIP calls + this->overlay_->setTransferCallAvailability(isSIP); this->overlay_->callStarted(callId); this->overlay_->setVideoMuteVisibility(!LRCInstance::getCurrentCallModel()->getCall(callId).isAudioOnly); @@ -426,6 +428,13 @@ VideoView::mouseMoveEvent(QMouseEvent* event) and geometry().contains(event->pos())) previewRect.setBottomRight(event->pos()); } + +void +VideoView::setCurrentCalleeName(const QString& CalleeDisplayName) +{ + overlay_->setCurrentSelectedCalleeDisplayName(CalleeDisplayName); +} + void VideoView::resetVideoOverlay(bool isAudioMuted, bool isVideoMuted, bool isRecording, bool isHolding) { diff --git a/videoview.h b/videoview.h index f07bef2..c73ac01 100644 --- a/videoview.h +++ b/videoview.h @@ -36,8 +36,10 @@ class VideoView : public QWidget public: explicit VideoView(QWidget* parent = 0); ~VideoView(); - void pushRenderer(const std::string& callUid); + void pushRenderer(const std::string& callUid, bool isSIP); + void showChatviewIfToggled(); void simulateShowChatview(bool checked); + void setCurrentCalleeName(const QString& CalleeDisplayName); void resetVideoOverlay(bool isAudioMuted, bool isVideoMuted, bool isRecording, bool isHolding); protected: -- GitLab