diff --git a/images/icons/ic_keypad.svg b/images/icons/ic_keypad.svg new file mode 100644 index 0000000000000000000000000000000000000000..c55cdf4fa7e5efbbf4ad08b01cea3a7655e64285 --- /dev/null +++ b/images/icons/ic_keypad.svg @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve"> +<g> + <g> + <g> + <path d="M64,0C28.715,0,0,28.715,0,64s28.715,64,64,64s64-28.715,64-64S99.285,0,64,0z"/> + <path d="M64,192c-35.285,0-64,28.715-64,64s28.715,64,64,64s64-28.715,64-64S99.285,192,64,192z"/> + <path d="M64,384c-35.285,0-64,28.715-64,64c0,35.285,28.715,64,64,64s64-28.715,64-64C128,412.715,99.285,384,64,384z"/> + <path d="M256,0c-35.285,0-64,28.715-64,64s28.715,64,64,64s64-28.715,64-64S291.285,0,256,0z"/> + <path d="M256,192c-35.285,0-64,28.715-64,64s28.715,64,64,64s64-28.715,64-64S291.285,192,256,192z"/> + <path d="M256,384c-35.285,0-64,28.715-64,64c0,35.285,28.715,64,64,64s64-28.715,64-64C320,412.715,291.285,384,256,384z"/> + <path d="M448,128c35.285,0,64-28.715,64-64S483.285,0,448,0c-35.285,0-64,28.715-64,64S412.715,128,448,128z"/> + <path d="M448,192c-35.285,0-64,28.715-64,64s28.715,64,64,64c35.285,0,64-28.715,64-64S483.285,192,448,192z"/> + <path d="M448,384c-35.285,0-64,28.715-64,64c0,35.285,28.715,64,64,64c35.285,0,64-28.715,64-64C512,412.715,483.285,384,448,384 + z"/> + </g> + </g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +</svg> diff --git a/images/icons/icon-keypad-24-2x.png b/images/icons/icon-keypad-24-2x.png deleted file mode 100644 index 2b84863e648cf02bb13cbbdbe2e53168897fc5cd..0000000000000000000000000000000000000000 Binary files a/images/icons/icon-keypad-24-2x.png and /dev/null differ diff --git a/images/icons/icon-keypad-24.png b/images/icons/icon-keypad-24.png deleted file mode 100644 index 2abc9be80cb8393a2357c49383663951b5a4b88f..0000000000000000000000000000000000000000 Binary files a/images/icons/icon-keypad-24.png and /dev/null differ diff --git a/qml.qrc b/qml.qrc index 5d20bbbedae736dd96a6b80461b8bc591b2d6610..d9e8085f772712c1c18f0f242e5b79c54fdf9ee0 100644 --- a/qml.qrc +++ b/qml.qrc @@ -102,5 +102,6 @@ <file>src/commoncomponents/MaterialButton.qml</file> <file>src/mainview/components/RecordBox.qml</file> <file>src/commoncomponents/ElidedTextLabel.qml</file> + <file>src/mainview/components/SipInputPanel.qml</file> </qresource> </RCC> diff --git a/ressources.qrc b/ressources.qrc index f64f592a1d92eeb432ee264846f5155b7afaa636..357fe0bf761a5ed4f63333036d0ecc84221c1cd4 100644 --- a/ressources.qrc +++ b/ressources.qrc @@ -44,6 +44,7 @@ <file>images/icons/ic_content_copy.svg</file> <file>images/icons/ic_delete_black_18dp_2x.png</file> <file>images/icons/ic_done_white_24dp.png</file> + <file>images/icons/ic_keypad.svg</file> <file>images/icons/open_in_full-24px.svg</file> <file>images/icons/close_fullscreen-24px.svg</file> <file>images/icons/ic_group_add_white_24dp.png</file> @@ -97,9 +98,7 @@ <file>images/icons/screen_share-24px.svg</file> <file>images/icons/round-add_a_photo-24px.svg</file> <file>images/icons/ic_mic_white_24dp.png</file> - <file>images/icons/icon-keypad-24.png</file> <file>images/icons/ic_play_white_24dp.png</file> - <file>images/icons/icon-keypad-24-2x.png</file> <file>images/icons/ic_voicemail_black_24dp_2x_.png</file> <file>images/icons/av_icons/delete-24px.svg</file> <file>images/icons/av_icons/fiber_manual_record-24px.svg</file> diff --git a/src/calladapter.cpp b/src/calladapter.cpp index efc74102d9d4fa29c575d19f81edfda98df0e4da..c4badc91d9379d96bda348fb51b26c90b1631886 100644 --- a/src/calladapter.cpp +++ b/src/calladapter.cpp @@ -27,7 +27,7 @@ #include "globalsystemtray.h" #include "utils.h" -CallAdapter::CallAdapter(QObject *parent) +CallAdapter::CallAdapter(QObject* parent) : QmlAdapterBase(parent) , oneSecondTimer_(new QTimer(this)) {} @@ -78,7 +78,7 @@ CallAdapter::placeCall() } void -CallAdapter::hangUpACall(const QString &accountId, const QString &convUid) +CallAdapter::hangUpACall(const QString& accountId, const QString& convUid) { auto* convModel = LRCInstance::getCurrentConversationModel(); const auto convInfo = convModel->getConversationForUID(convUid); @@ -88,7 +88,7 @@ CallAdapter::hangUpACall(const QString &accountId, const QString &convUid) } void -CallAdapter::refuseACall(const QString &accountId, const QString &convUid) +CallAdapter::refuseACall(const QString& accountId, const QString& convUid) { auto* convModel = LRCInstance::getCurrentConversationModel(); const auto convInfo = convModel->getConversationForUID(convUid); @@ -98,20 +98,20 @@ CallAdapter::refuseACall(const QString &accountId, const QString &convUid) } void -CallAdapter::acceptACall(const QString &accountId, const QString &convUid) +CallAdapter::acceptACall(const QString& accountId, const QString& convUid) { emit incomingCallNeedToSetupMainView(accountId, convUid); auto* convModel = LRCInstance::getCurrentConversationModel(); const auto convInfo = convModel->getConversationForUID(convUid); if (!convInfo.uid.isEmpty()) { LRCInstance::getAccountInfo(accountId).callModel->accept(convInfo.callId); - auto &accInfo = LRCInstance::getAccountInfo(convInfo.accountId); + auto& accInfo = LRCInstance::getAccountInfo(convInfo.accountId); accInfo.callModel->setCurrentCall(convInfo.callId); } } void -CallAdapter::slotShowIncomingCallView(const QString &accountId, const conversation::Info &convInfo) +CallAdapter::slotShowIncomingCallView(const QString& accountId, const conversation::Info& convInfo) { auto* callModel = LRCInstance::getCurrentCallModel(); @@ -119,17 +119,17 @@ CallAdapter::slotShowIncomingCallView(const QString &accountId, const conversati /* * Connection to close potential incoming call page when it is not current account. */ - auto &accInfo = LRCInstance::accountModel().getAccountInfo(accountId); + auto& accInfo = LRCInstance::accountModel().getAccountInfo(accountId); QObject::disconnect(closeIncomingCallPageConnection_); closeIncomingCallPageConnection_ = QObject::connect(accInfo.callModel.get(), &lrc::api::NewCallModel::callStatusChanged, - [this, accountId, uid = convInfo.uid](const QString &callId) { - auto &accInfo = LRCInstance::accountModel().getAccountInfo( + [this, accountId, uid = convInfo.uid](const QString& callId) { + auto& accInfo = LRCInstance::accountModel().getAccountInfo( accountId); - auto &callModel = accInfo.callModel; + auto& callModel = accInfo.callModel; auto call = callModel->getCall(callId); switch (call.status) { @@ -182,18 +182,18 @@ CallAdapter::slotShowIncomingCallView(const QString &accountId, const conversati } void -CallAdapter::slotShowCallView(const QString &accountId, const lrc::api::conversation::Info &convInfo) +CallAdapter::slotShowCallView(const QString& accountId, const lrc::api::conversation::Info& convInfo) { updateCall(convInfo.uid, accountId); } void -CallAdapter::updateCall(const QString &convUid, const QString &accountId, bool forceCallOnly) +CallAdapter::updateCall(const QString& convUid, const QString& accountId, bool forceCallOnly) { accountId_ = accountId.isEmpty() ? accountId_ : accountId; convUid_ = convUid.isEmpty() ? convUid_ : convUid; - auto *convModel = LRCInstance::getCurrentConversationModel(); + auto* convModel = LRCInstance::getCurrentConversationModel(); const auto convInfo = convModel->getConversationForUID(convUid_); if (convInfo.uid.isEmpty()) { return; @@ -223,7 +223,7 @@ CallAdapter::updateCall(const QString &convUid, const QString &accountId, bool f bool CallAdapter::shouldShowPreview(bool force) { - bool shouldShowPreview{false}; + bool shouldShowPreview {false}; auto* convModel = LRCInstance::getCurrentConversationModel(); const auto convInfo = convModel->getConversationForUID(convUid_); if (convInfo.uid.isEmpty()) { @@ -245,11 +245,11 @@ CallAdapter::getConferencesInfos() const auto convInfo = convModel->getConversationForUID(convUid_); if (convInfo.uid.isEmpty()) return map; - auto callId = convInfo.confId.isEmpty()? convInfo.callId : convInfo.confId; + auto callId = convInfo.confId.isEmpty() ? convInfo.callId : convInfo.confId; if (!callId.isEmpty()) { try { auto call = LRCInstance::getCurrentCallModel()->getCall(callId); - for (const auto& participant: call.participantsInfos) { + for (const auto& participant : call.participantsInfos) { QJsonObject data; data["x"] = participant["x"].toInt(); data["y"] = participant["y"].toInt(); @@ -257,31 +257,34 @@ CallAdapter::getConferencesInfos() data["h"] = participant["h"].toInt(); data["active"] = participant["active"] == "true"; auto bestName = participant["uri"]; - auto &accInfo = LRCInstance::accountModel().getAccountInfo(accountId_); + auto& accInfo = LRCInstance::accountModel().getAccountInfo(accountId_); data["isLocal"] = false; if (bestName == accInfo.profileInfo.uri) { bestName = tr("me"); data["isLocal"] = true; } else { try { - auto &contact = LRCInstance::getCurrentAccountInfo().contactModel->getContact(participant["uri"]); + auto& contact = LRCInstance::getCurrentAccountInfo() + .contactModel->getContact(participant["uri"]); bestName = Utils::bestNameForContact(contact); - } catch (...) {} + } catch (...) { + } } data["bestName"] = bestName; map.push_back(QVariant(data)); } return map; - } catch (...) {} + } catch (...) { + } } return map; } void -CallAdapter::connectCallModel(const QString &accountId) +CallAdapter::connectCallModel(const QString& accountId) { - auto &accInfo = LRCInstance::accountModel().getAccountInfo(accountId); + auto& accInfo = LRCInstance::accountModel().getAccountInfo(accountId); QObject::disconnect(callStatusChangedConnection_); QObject::disconnect(onParticipantsChangedConnection_); @@ -289,15 +292,15 @@ CallAdapter::connectCallModel(const QString &accountId) onParticipantsChangedConnection_ = QObject::connect( accInfo.callModel.get(), &lrc::api::NewCallModel::onParticipantsChanged, - [this, accountId](const QString &confId) { - auto &accInfo = LRCInstance::accountModel().getAccountInfo(accountId); - auto &callModel = accInfo.callModel; + [this, accountId](const QString& confId) { + auto& accInfo = LRCInstance::accountModel().getAccountInfo(accountId); + auto& callModel = accInfo.callModel; auto call = callModel->getCall(confId); const auto convInfo = LRCInstance::getConversationFromCallId(confId); if (!convInfo.uid.isEmpty()) { // Convert to QML QVariantList map; - for (const auto& participant: call.participantsInfos) { + for (const auto& participant : call.participantsInfos) { QJsonObject data; data["x"] = participant["x"].toInt(); data["y"] = participant["y"].toInt(); @@ -307,29 +310,31 @@ CallAdapter::connectCallModel(const QString &accountId) data["active"] = participant["active"] == "true"; auto bestName = participant["uri"]; data["isLocal"] = false; - auto &accInfo = LRCInstance::accountModel().getAccountInfo(accountId_); + auto& accInfo = LRCInstance::accountModel().getAccountInfo(accountId_); if (bestName == accInfo.profileInfo.uri) { bestName = tr("me"); data["isLocal"] = true; } else { try { - auto &contact = LRCInstance::getCurrentAccountInfo().contactModel->getContact(participant["uri"]); + auto& contact = LRCInstance::getCurrentAccountInfo() + .contactModel->getContact(participant["uri"]); bestName = Utils::bestNameForContact(contact); - } catch (...) {} + } catch (...) { + } } data["bestName"] = bestName; map.push_back(QVariant(data)); } emit updateParticipantsInfos(map, accountId, confId); } - }); + }); callStatusChangedConnection_ = QObject::connect( accInfo.callModel.get(), &lrc::api::NewCallModel::callStatusChanged, - [this, accountId](const QString &callId) { - auto &accInfo = LRCInstance::accountModel().getAccountInfo(accountId); - auto &callModel = accInfo.callModel; + [this, accountId](const QString& callId) { + auto& accInfo = LRCInstance::accountModel().getAccountInfo(accountId); + auto& callModel = accInfo.callModel; const auto call = callModel->getCall(callId); /* @@ -357,7 +362,7 @@ CallAdapter::connectCallModel(const QString &accountId) * If it's a conference, change the smartlist index * to the next remaining participant. */ - bool forceCallOnly{false}; + bool forceCallOnly {false}; if (!convInfo.confId.isEmpty()) { auto callList = LRCInstance::getAPI().getConferenceSubcalls(convInfo.confId); if (callList.empty()) { @@ -366,7 +371,7 @@ CallAdapter::connectCallModel(const QString &accountId) callList.append(lastConferencee); forceCallOnly = true; } - for (const auto &callId : callList) { + for (const auto& callId : callList) { if (!callModel->hasCall(callId)) { continue; } @@ -408,17 +413,28 @@ CallAdapter::connectCallModel(const QString &accountId) }); } +void +CallAdapter::sipInputPanelPlayDTMF(const QString& key) +{ + auto callId = LRCInstance::getCallIdForConversationUid(convUid_, accountId_); + if (callId.isEmpty() || !LRCInstance::getCurrentCallModel()->hasCall(callId)) { + return; + } + + LRCInstance::getCurrentCallModel()->playDTMF(callId, key); +} + /* * For Call Overlay */ void -CallAdapter::updateCallOverlay(const lrc::api::conversation::Info &convInfo) +CallAdapter::updateCallOverlay(const lrc::api::conversation::Info& convInfo) { setTime(accountId_, convUid_); QObject::disconnect(oneSecondTimer_); QObject::connect(oneSecondTimer_, &QTimer::timeout, [this] { setTime(accountId_, convUid_); }); oneSecondTimer_->start(20); - auto &accInfo = LRCInstance::accountModel().getAccountInfo(accountId_); + auto& accInfo = LRCInstance::accountModel().getAccountInfo(accountId_); auto call = LRCInstance::getCallInfoForConversation(convInfo); if (!call) { @@ -450,13 +466,13 @@ CallAdapter::hangupCall(const QString& uri) auto callModel = LRCInstance::getAccountInfo(accountId_).callModel.get(); if (callModel->hasCall(convInfo.callId)) { /* - * Store the last remaining participant of the conference, - * so we can switch the smartlist index after termination. - */ + * Store the last remaining participant of the conference, + * so we can switch the smartlist index after termination. + */ if (!convInfo.confId.isEmpty()) { auto callList = LRCInstance::getAPI().getConferenceSubcalls(convInfo.confId); if (callList.size() == 2) { - for (const auto &cId : callList) { + for (const auto& cId : callList) { if (cId != convInfo.callId) { LRCInstance::instance().pushLastConferencee(convInfo.confId, cId); } @@ -487,21 +503,23 @@ CallAdapter::maximizeParticipant(const QString& uri, bool isActive) try { const auto call = callModel->getCall(confId); switch (call.layout) { - case lrc::api::call::Layout::GRID: - callModel->setActiveParticipant(confId, callId); - callModel->setConferenceLayout(confId, lrc::api::call::Layout::ONE_WITH_SMALL); - break; - case lrc::api::call::Layout::ONE_WITH_SMALL: - callModel->setActiveParticipant(confId, callId); - callModel->setConferenceLayout(confId, - isActive? lrc::api::call::Layout::ONE : lrc::api::call::Layout::ONE_WITH_SMALL); - break; - case lrc::api::call::Layout::ONE: - callModel->setActiveParticipant(confId, callId); - callModel->setConferenceLayout(confId, lrc::api::call::Layout::GRID); - break; + case lrc::api::call::Layout::GRID: + callModel->setActiveParticipant(confId, callId); + callModel->setConferenceLayout(confId, lrc::api::call::Layout::ONE_WITH_SMALL); + break; + case lrc::api::call::Layout::ONE_WITH_SMALL: + callModel->setActiveParticipant(confId, callId); + callModel->setConferenceLayout(confId, + isActive ? lrc::api::call::Layout::ONE + : lrc::api::call::Layout::ONE_WITH_SMALL); + break; + case lrc::api::call::Layout::ONE: + callModel->setActiveParticipant(confId, callId); + callModel->setConferenceLayout(confId, lrc::api::call::Layout::GRID); + break; }; - } catch (...) {} + } catch (...) { + } } void @@ -514,16 +532,17 @@ CallAdapter::minimizeParticipant() try { auto call = callModel->getCall(confId); switch (call.layout) { - case lrc::api::call::Layout::GRID: - break; - case lrc::api::call::Layout::ONE_WITH_SMALL: - callModel->setConferenceLayout(confId, lrc::api::call::Layout::GRID); - break; - case lrc::api::call::Layout::ONE: - callModel->setConferenceLayout(confId, lrc::api::call::Layout::ONE_WITH_SMALL); - break; + case lrc::api::call::Layout::GRID: + break; + case lrc::api::call::Layout::ONE_WITH_SMALL: + callModel->setConferenceLayout(confId, lrc::api::call::Layout::GRID); + break; + case lrc::api::call::Layout::ONE: + callModel->setConferenceLayout(confId, lrc::api::call::Layout::ONE_WITH_SMALL); + break; }; - } catch (...) {} + } catch (...) { + } } void @@ -544,11 +563,11 @@ CallAdapter::hangUpThisCall() bool CallAdapter::isRecordingThisCall() { - auto &accInfo = LRCInstance::accountModel().getAccountInfo(accountId_); - auto &convModel = accInfo.conversationModel; + auto& accInfo = LRCInstance::accountModel().getAccountInfo(accountId_); + auto& convModel = accInfo.conversationModel; const auto convInfo = convModel->getConversationForUID(convUid_); return accInfo.callModel->isRecording(convInfo.confId) - || accInfo.callModel->isRecording(convInfo.callId); + || accInfo.callModel->isRecording(convInfo.callId); } bool @@ -565,7 +584,8 @@ CallAdapter::isCurrentMaster() const auto call = callModel->getCall(convInfo.callId); return call.participantsInfos.size() == 0; } - } catch (...) {} + } catch (...) { + } } return true; } @@ -580,7 +600,8 @@ CallAdapter::getCurrentLayoutType() const try { auto call = callModel->getCall(convInfo.confId); return Utils::toUnderlyingValue(call.layout); - } catch (...) {} + } catch (...) { + } } return -1; } @@ -640,7 +661,7 @@ CallAdapter::videoPauseThisCallToggle() } void -CallAdapter::setTime(const QString &accountId, const QString &convUid) +CallAdapter::setTime(const QString& accountId, const QString& convUid) { const auto callId = LRCInstance::getCallIdForConversationUid(convUid, accountId); if (callId.isEmpty() || !LRCInstance::getCurrentCallModel()->hasCall(callId)) { @@ -652,4 +673,4 @@ CallAdapter::setTime(const QString &accountId, const QString &convUid) auto timeString = LRCInstance::getCurrentCallModel()->getFormattedCallDuration(callId); emit updateTimeText(timeString); } -} +} \ No newline at end of file diff --git a/src/calladapter.h b/src/calladapter.h index ce439731b9e4b296320a073b5d7574b50d0f789d..fbf5ffc4d0be44620218c80f010a63ffdeb75f89 100644 --- a/src/calladapter.h +++ b/src/calladapter.h @@ -31,7 +31,7 @@ class CallAdapter : public QmlAdapterBase Q_OBJECT public: - explicit CallAdapter(QObject *parent = nullptr); + explicit CallAdapter(QObject* parent = nullptr); ~CallAdapter(); /* @@ -41,11 +41,12 @@ public: Q_INVOKABLE void placeAudioOnlyCall(); Q_INVOKABLE void placeCall(); - Q_INVOKABLE void hangUpACall(const QString &accountId, const QString &convUid); - Q_INVOKABLE void refuseACall(const QString &accountId, const QString &convUid); - Q_INVOKABLE void acceptACall(const QString &accountId, const QString &convUid); + Q_INVOKABLE void hangUpACall(const QString& accountId, const QString& convUid); + Q_INVOKABLE void refuseACall(const QString& accountId, const QString& convUid); + Q_INVOKABLE void acceptACall(const QString& accountId, const QString& convUid); - Q_INVOKABLE void connectCallModel(const QString &accountId); + Q_INVOKABLE void connectCallModel(const QString& accountId); + Q_INVOKABLE void sipInputPanelPlayDTMF(const QString& key); /* * For Call Overlay @@ -55,7 +56,7 @@ public: Q_INVOKABLE void minimizeParticipant(); Q_INVOKABLE void hangUpThisCall(); Q_INVOKABLE bool isCurrentMaster() const; - Q_INVOKABLE int getCurrentLayoutType() const; + Q_INVOKABLE int getCurrentLayoutType() const; Q_INVOKABLE void holdThisCallToggle(); Q_INVOKABLE void muteThisCallToggle(); Q_INVOKABLE void recordThisCallToggle(); @@ -64,24 +65,26 @@ public: Q_INVOKABLE QVariantList getConferencesInfos(); signals: - void showOutgoingCallPage(const QString &accountId, const QString &convUid); - void showIncomingCallPage(const QString &accountId, const QString &convUid); - void showAudioCallPage(const QString &accountId, const QString &convUid); - void showVideoCallPage(const QString &accountId, const QString &convUid, const QString &callId); - void showCallStack(const QString &accountId, const QString &convUid, bool forceReset = false); - void closeCallStack(const QString &accountId, const QString &convUid); - void closePotentialIncomingCallPageWindow(const QString &accountId, const QString &convUid); - void callStatusChanged(const QString &status, const QString &accountId, const QString &convUid); + void showOutgoingCallPage(const QString& accountId, const QString& convUid); + void showIncomingCallPage(const QString& accountId, const QString& convUid); + void showAudioCallPage(const QString& accountId, const QString& convUid); + void showVideoCallPage(const QString& accountId, const QString& convUid, const QString& callId); + void showCallStack(const QString& accountId, const QString& convUid, bool forceReset = false); + void closeCallStack(const QString& accountId, const QString& convUid); + void closePotentialIncomingCallPageWindow(const QString& accountId, const QString& convUid); + void callStatusChanged(const QString& status, const QString& accountId, const QString& convUid); void updateConversationSmartList(); - void updateParticipantsInfos(const QVariantList& infos, const QString &accountId, const QString &callId); + void updateParticipantsInfos(const QVariantList& infos, + const QString& accountId, + const QString& callId); - void incomingCallNeedToSetupMainView(const QString &accountId, const QString &convUid); + void incomingCallNeedToSetupMainView(const QString& accountId, const QString& convUid); void previewVisibilityNeedToChange(bool visible); /* * For Call Overlay */ - void updateTimeText(const QString &time); + void updateTimeText(const QString& time); void showOnHoldLabel(bool isPaused); void updateOverlay(bool isPaused, bool isAudioOnly, @@ -90,17 +93,17 @@ signals: bool isRecording, bool isSIP, bool isConferenceCall, - const QString &bestName); + const QString& bestName); public slots: - void slotShowIncomingCallView(const QString &accountId, - const lrc::api::conversation::Info &convInfo); - void slotShowCallView(const QString &accountId, const lrc::api::conversation::Info &convInfo); + void slotShowIncomingCallView(const QString& accountId, + const lrc::api::conversation::Info& convInfo); + void slotShowCallView(const QString& accountId, const lrc::api::conversation::Info& convInfo); void slotAccountChanged(); private: - void updateCall(const QString &convUid = {}, - const QString &accountId = {}, + void updateCall(const QString& convUid = {}, + const QString& accountId = {}, bool forceCallOnly = false); bool shouldShowPreview(bool force); @@ -117,7 +120,7 @@ private: /* * For Call Overlay */ - void updateCallOverlay(const lrc::api::conversation::Info &convInfo); - void setTime(const QString &accountId, const QString &convUid); - QTimer *oneSecondTimer_; + void updateCallOverlay(const lrc::api::conversation::Info& convInfo); + void setTime(const QString& accountId, const QString& convUid); + QTimer* oneSecondTimer_; }; diff --git a/src/commoncomponents/HoverableButton.qml b/src/commoncomponents/HoverableButton.qml index 4e96677d29d3773fccbc58e9f6f8ed04f7cc1608..0668487b9e55effe96ab7b86e9dc45f2d2b20747 100644 --- a/src/commoncomponents/HoverableButton.qml +++ b/src/commoncomponents/HoverableButton.qml @@ -28,6 +28,8 @@ import QtGraphicalEffects 1.15 * 2. Radius control (rounded) * 3. Text content or image content * 4. Can use OnClicked slot to implement some click logic + * + * Note: if use text property directly, buttonTextColor will not work. */ Button { id: hoverableButton @@ -39,6 +41,9 @@ Button { property int buttonImageHeight: hoverableButtonBackground.height - 10 property int buttonImageWidth: hoverableButtonBackground.width - 10 + property string buttonText: "" + property string buttonTextColor: "black" + property string backgroundColor: JamiTheme.releaseColor property string onPressColor: JamiTheme.pressColor property string onReleaseColor: JamiTheme.releaseColor @@ -58,6 +63,8 @@ Button { hoverEnabled: true + text: "<font color=" + "'" + buttonTextColor + "'>" + buttonText + "</font>" + ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval ToolTip.visible: hovered && (toolTipText.length > 0) ToolTip.text: toolTipText @@ -79,7 +86,12 @@ Button { mipmap: true asynchronous: true - source: hoverableButton.checked && checkedImage? checkedImage : baseImage + source: { + if (checkable && checkedImage) + return hoverableButton.checked ? checkedImage : baseImage + else + return "" + } layer { enabled: true diff --git a/src/constant/JamiTheme.qml b/src/constant/JamiTheme.qml index a29330de120609ce43783d2b102f212c8fe92e05..d9086c69ce843ff5dcb1e95f5d9916ddfee5aece 100644 --- a/src/constant/JamiTheme.qml +++ b/src/constant/JamiTheme.qml @@ -68,6 +68,9 @@ Item { property string draftRed: "#cf5300" + property string sipInputButtonBackgroundColor: "#336699" + property string sipInputButtonHoverColor: "#4477aa" + property string sipInputButtonPressColor: "#5588bb" /* * Font. diff --git a/src/mainview/components/CallOverlay.qml b/src/mainview/components/CallOverlay.qml index 75774d34f1aa8aa61465ba1fc0ff6eae44070346..1f2ed4edea58c9b53fa4eaea90b3effcb17bafe1 100644 --- a/src/mainview/components/CallOverlay.qml +++ b/src/mainview/components/CallOverlay.qml @@ -110,6 +110,12 @@ Rectangle { anchors.fill: parent + SipInputPanel { + id: sipInputPanel + + x: callOverlayRect.width / 2 - sipInputPanel.width / 2 + y: callOverlayRect.height / 2 - sipInputPanel.height / 2 + } /* * Timer to decide when overlay fade out. diff --git a/src/mainview/components/CallOverlayButtonGroup.qml b/src/mainview/components/CallOverlayButtonGroup.qml index c360a7cdaae3edab6be8508ac45081d580b02d27..107ab9e25b1365cfa7c3f233729d745316899712 100644 --- a/src/mainview/components/CallOverlayButtonGroup.qml +++ b/src/mainview/components/CallOverlayButtonGroup.qml @@ -50,8 +50,6 @@ Rectangle { root.isSip = isSIP noVideoButton.visible = !isAudioOnly addToConferenceButton.visible = !isSIP && isMaster - transferCallButton.visible = isSIP - sipInputPanelButton.visible = isSIP noMicButton.checked = isAudioMuted noVideoButton.checked = isVideoMuted diff --git a/src/mainview/components/CallViewContextMenu.qml b/src/mainview/components/CallViewContextMenu.qml index 23da3fd700184a619072fef6b35d1437f70e9a46..2deeeaac2a440f582891946b01de78226b1aa831 100644 --- a/src/mainview/components/CallViewContextMenu.qml +++ b/src/mainview/components/CallViewContextMenu.qml @@ -112,6 +112,23 @@ Menu { } } + GeneralMenuItem { + id: showSipInputPanelButton + + visible: isSIP + height: isSIP? undefined : 0 + + itemName: qsTr("Sip Input Panel") + iconSource: "qrc:/images/icons/ic_keypad.svg" + leftBorderWidth: commonBorderWidth + rightBorderWidth: commonBorderWidth + + onClicked: { + root.close() + sipInputPanel.open() + } + } + GeneralMenuItem { id: transferCallButton diff --git a/src/mainview/components/SipInputPanel.qml b/src/mainview/components/SipInputPanel.qml new file mode 100644 index 0000000000000000000000000000000000000000..8d547a26a4356b8d8ef52a4db191868f688ebee0 --- /dev/null +++ b/src/mainview/components/SipInputPanel.qml @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import QtQuick.Controls.Universal 2.12 +import net.jami.Models 1.0 + +import "../../commoncomponents" + +/* + * SipInputPanel is a key pad that is designed to be + * used in sip calls. + */ +Popup { + id: sipInputPanelPopUp + + /* + * Space between sipInputPanelRect and grid layout + */ + property int sipPanelPadding: 20 + + contentWidth: sipInputPanelRectGridLayout.implicitWidth + 20 + contentHeight: sipInputPanelRectGridLayout.implicitHeight + 20 + + padding: 0 + + modal: true + + contentItem: Rectangle { + id: sipInputPanelRect + + radius: 10 + + GridLayout { + id: sipInputPanelRectGridLayout + + anchors.centerIn: parent + + columns: 4 + + Repeater { + id: sipInputPanelRectGridLayoutRepeater + model: ["1", "2", "3", "A", "4", "5", "6", "B", "7", + "8", "9", "C", "*", "0", "#", "D"] + + HoverableButton { + id: sipInputPanelButton + + Layout.preferredWidth: 30 + Layout.preferredHeight: 30 + + radius: 30 + buttonText: modelData + buttonTextColor: "white" + checkable: false + backgroundColor: JamiTheme.sipInputButtonBackgroundColor + onEnterColor: JamiTheme.sipInputButtonHoverColor + onExitColor: JamiTheme.sipInputButtonBackgroundColor + onPressColor: JamiTheme.sipInputButtonPressColor + onReleaseColor: JamiTheme.sipInputButtonHoverColor + + toolTipText: modelData + + onClicked: { + CallAdapter.sipInputPanelPlayDTMF(modelData) + } + } + } + } + } + + background: Rectangle { + color: "transparent" + } +}