From 6f7ebb1f9fca43b47b77d6b931be8720d0c003dd Mon Sep 17 00:00:00 2001 From: Christophe Deschamps Date: Wed, 4 Sep 2024 09:04:17 +0200 Subject: [PATCH] MWI --- Linphone/core/account/AccountCore.cpp | 10 +++--- Linphone/core/account/AccountCore.hpp | 1 + Linphone/data/image/voicemail.svg | 3 ++ Linphone/model/account/AccountModel.cpp | 44 ++++++++++++++++++++++-- Linphone/model/account/AccountModel.hpp | 22 ++++++++++++ Linphone/tool/AbstractObject.hpp | 13 +++++++ Linphone/view/App/Layout/MainLayout.qml | 24 +++++++++++++ Linphone/view/CMakeLists.txt | 1 + Linphone/view/Item/Contact/Contact.qml | 15 +++++++- Linphone/view/Item/Contact/Voicemail.qml | 38 ++++++++++++++++++++ Linphone/view/Style/AppIcons.qml | 1 + 11 files changed, 165 insertions(+), 7 deletions(-) create mode 100644 Linphone/data/image/voicemail.svg create mode 100644 Linphone/view/Item/Contact/Voicemail.qml diff --git a/Linphone/core/account/AccountCore.cpp b/Linphone/core/account/AccountCore.cpp index baa52a071..2a95f53cc 100644 --- a/Linphone/core/account/AccountCore.cpp +++ b/Linphone/core/account/AccountCore.cpp @@ -52,10 +52,7 @@ AccountCore::AccountCore(const std::shared_ptr &account) : QO mRegisterEnabled = params->registerEnabled(); mMwiServerAddress = params->getMwiServerAddress() ? Utils::coreStringToAppString(params->getMwiServerAddress()->asString()) : ""; - mTransports << "TCP" - << "UDP" - << "TLS" - << "DTLS"; + mTransports << "TCP" << "UDP" << "TLS" << "DTLS"; mTransport = LinphoneEnums::toString(LinphoneEnums::fromLinphone(params->getTransport())); mServerAddress = params->getServerAddress() ? Utils::coreStringToAppString(params->getServerAddress()->asString()) : ""; @@ -87,6 +84,8 @@ AccountCore::AccountCore(const std::shared_ptr &account) : QO mDialPlan = mAccountModel->dialPlanAsString(dialPlan); } } + + INIT_CORE_MEMBER(VoicemailCount, mAccountModel) } AccountCore::~AccountCore() { @@ -255,6 +254,9 @@ void AccountCore::setSelf(QSharedPointer me) { mAccountModelConnection->makeConnectToCore(&AccountCore::lSetLimeServerUrl, [this](QString value) { mAccountModelConnection->invokeToModel([this, value]() { mAccountModel->setLimeServerUrl(value); }); }); + + DEFINE_CORE_GET_CONNECT(mAccountModelConnection, AccountCore, AccountModel, mAccountModel, int, voicemailCount, + VoicemailCount) } const std::shared_ptr &AccountCore::getModel() const { diff --git a/Linphone/core/account/AccountCore.hpp b/Linphone/core/account/AccountCore.hpp index fd4fc74d8..00f1a9600 100644 --- a/Linphone/core/account/AccountCore.hpp +++ b/Linphone/core/account/AccountCore.hpp @@ -71,6 +71,7 @@ class AccountCore : public QObject, public AbstractObject { Q_PROPERTY(QString audioVideoConferenceFactoryAddress READ getAudioVideoConferenceFactoryAddress WRITE lSetAudioVideoConferenceFactoryAddress NOTIFY audioVideoConferenceFactoryAddressChanged) Q_PROPERTY(QString limeServerUrl READ getLimeServerUrl WRITE lSetLimeServerUrl NOTIFY limeServerUrlChanged) + DECLARE_CORE_GET(int, voicemailCount, VoicemailCount) public: static QSharedPointer create(const std::shared_ptr &account); diff --git a/Linphone/data/image/voicemail.svg b/Linphone/data/image/voicemail.svg new file mode 100644 index 000000000..6facd31ac --- /dev/null +++ b/Linphone/data/image/voicemail.svg @@ -0,0 +1,3 @@ + + + diff --git a/Linphone/model/account/AccountModel.cpp b/Linphone/model/account/AccountModel.cpp index f1635ac3c..871e5a7e2 100644 --- a/Linphone/model/account/AccountModel.cpp +++ b/Linphone/model/account/AccountModel.cpp @@ -55,6 +55,20 @@ void AccountModel::onRegistrationStateChanged(const std::shared_ptr &account, + const std::shared_ptr &mwi) { + for (auto summary : mwi->getSummaries()) { + qInfo() << "[MWI] new" << summary->getNbNew() << "new+urgent" << summary->getNbNewUrgent() << "old" + << summary->getNbOld() << "old+urgent" << summary->getNbOldUrgent(); + auto userData = getUserData(account); + if (!userData) userData = std::make_shared(); + userData->voicemailCount = summary->getNbNew(); + setUserData(account, userData); + emit voicemailCountChanged(summary->getNbNew()); + } +} + void AccountModel::setPictureUri(QString uri) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); auto params = mMonitor->getParams()->clone(); @@ -71,8 +85,8 @@ void AccountModel::setPictureUri(QString uri) { mMonitor->setParams(params); // Hack because Account doesn't provide callbacks on updated data // emit pictureUriChanged(uri); - emit CoreModel::getInstance()->defaultAccountChanged(CoreModel::getInstance()->getCore(), - CoreModel::getInstance()->getCore()->getDefaultAccount()); + auto core = CoreModel::getInstance()->getCore(); + emit CoreModel::getInstance()->defaultAccountChanged(core, core->getDefaultAccount()); } void AccountModel::onDefaultAccountChanged() { @@ -88,6 +102,7 @@ void AccountModel::setDefault() { void AccountModel::removeAccount() { CoreModel::getInstance()->getCore()->removeAccount(mMonitor); + removeUserData(mMonitor); emit removed(); } @@ -283,3 +298,28 @@ QString AccountModel::dialPlanAsString(const std::shared_ptr return Utils::coreStringToAppString(dialPlan->getFlag() + " " + dialPlan->getCountry() + " | +" + dialPlan->getCountryCallingCode()); } + +int AccountModel::getVoicemailCount() { + auto userData = getUserData(mMonitor); + if (userData) return userData->voicemailCount; + else return 0; +} + +// UserData (see hpp for explanations) + +static QMap, std::shared_ptr> userDataMap; + +void AccountModel::setUserData(const std::shared_ptr &account, + std::shared_ptr &data) { + mustBeInLinphoneThread(sLog().arg(Q_FUNC_INFO)); + userDataMap[account] = data; +} +std::shared_ptr AccountModel::getUserData(const std::shared_ptr &account) { + mustBeInLinphoneThread(sLog().arg(Q_FUNC_INFO)); + if (userDataMap.contains(account)) return userDataMap.value(account); + else return nullptr; +} +void AccountModel::removeUserData(const std::shared_ptr &account) { + mustBeInLinphoneThread(sLog().arg(Q_FUNC_INFO)); + userDataMap.remove(account); +} diff --git a/Linphone/model/account/AccountModel.hpp b/Linphone/model/account/AccountModel.hpp index c22a85c7d..0682ef55c 100644 --- a/Linphone/model/account/AccountModel.hpp +++ b/Linphone/model/account/AccountModel.hpp @@ -27,6 +27,8 @@ #include #include +struct AccountUserData; + class AccountModel : public ::Listener, public linphone::AccountListener, public AbstractObject { @@ -38,6 +40,10 @@ public: virtual void onRegistrationStateChanged(const std::shared_ptr &account, linphone::RegistrationState state, const std::string &message) override; + virtual void + onMessageWaitingIndicationChanged(const std::shared_ptr &account, + const std::shared_ptr &mwi) override; + void onDefaultAccountChanged(); std::string getConfigAccountUiSection(); @@ -67,6 +73,7 @@ public: void setAudioVideoConferenceFactoryAddress(QString value); void setLimeServerUrl(QString value); QString dialPlanAsString(const std::shared_ptr &dialPlan); + int getVoicemailCount(); signals: void registrationStateChanged(const std::shared_ptr &account, @@ -93,9 +100,24 @@ signals: void audioVideoConferenceFactoryAddressChanged(QString value); void limeServerUrlChanged(QString value); void removed(); + void voicemailCountChanged(int count); private: + // UserData + static void setUserData(const std::shared_ptr &account, std::shared_ptr &data); + static std::shared_ptr getUserData(const std::shared_ptr &account); + static void removeUserData(const std::shared_ptr &account); + DECLARE_ABSTRACT_OBJECT }; +// UserData : user data storage for linphone account information, that cannot be retrieved from Linphone account object +// through API, but received through listeners (example MWI/Voicemail count). Usually this is done using (s/g)etUserData +// on Linphone Objects on other wrappers, but not available in C++ Wrapper. + +struct AccountUserData { + int voicemailCount; + // .. +}; + #endif diff --git a/Linphone/tool/AbstractObject.hpp b/Linphone/tool/AbstractObject.hpp index 882ec9efc..a45227b8c 100644 --- a/Linphone/tool/AbstractObject.hpp +++ b/Linphone/tool/AbstractObject.hpp @@ -67,6 +67,11 @@ public: Q_SIGNAL void x##Changed(); \ type m##X; +#define DECLARE_CORE_GET(type, x, X) \ + Q_PROPERTY(type x MEMBER m##X NOTIFY x##Changed) \ + Q_SIGNAL void x##Changed(); \ + type m##X; + #define DECLARE_GETSET(type, x, X) \ type get##X() const; \ void set##X(type data); \ @@ -86,6 +91,14 @@ public: }); \ }); +#define DEFINE_CORE_GET_CONNECT(safe, CoreClass, ModelClass, model, type, x, X) \ + safe->makeConnectToModel(&ModelClass::x##Changed, [this](type data) { \ + safe->invokeToCore([this, data]() { \ + m##X = data; \ + emit x##Changed(); \ + }); \ + }); + #define DEFINE_GETSET_CONFIG(Class, type, Type, x, X, key, def) \ type Class::get##X() const { \ mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); \ diff --git a/Linphone/view/App/Layout/MainLayout.qml b/Linphone/view/App/Layout/MainLayout.qml index 9c54fbe5e..06d224f6b 100644 --- a/Linphone/view/App/Layout/MainLayout.qml +++ b/Linphone/view/App/Layout/MainLayout.qml @@ -406,6 +406,30 @@ Item { } } } + Voicemail { + id: voicemail + Layout.preferredWidth: 27 * DefaultStyle.dp + Layout.preferredHeight: 28 * DefaultStyle.dp + + function cumulatedVoicemailCount() { + var count = 0 + for (var i=0 ; i < accountProxy.count ; i++ ) + count += accountProxy.getAt(i).core.voicemailCount + return count + } + + voicemailCount: cumulatedVoicemailCount() + onClicked: { + if (accountProxy.count > 1) { + avatarButton.popup.open() + } else { + if (accountProxy.defaultAccount.core.mwiServerAddress.length > 0) + UtilsCpp.createCall(accountProxy.defaultAccount.core.mwiServerAddress) + else + UtilsCpp.showInformationPopup(qsTr("Erreur"), qsTr("L'adresse de la messagerie vocale n'est pas définie."), false) + } + } + } PopupButton { id: avatarButton Layout.preferredWidth: 54 * DefaultStyle.dp diff --git a/Linphone/view/CMakeLists.txt b/Linphone/view/CMakeLists.txt index 9801ddbc0..90ee508b7 100644 --- a/Linphone/view/CMakeLists.txt +++ b/Linphone/view/CMakeLists.txt @@ -52,6 +52,7 @@ list(APPEND _LINPHONEAPP_QML_FILES view/Item/Contact/ContactEdition.qml view/Item/Contact/ContactsList.qml view/Item/Contact/Sticker.qml + view/Item/Contact/Voicemail.qml view/Item/Meeting/MeetingList.qml view/Item/Meeting/MeetingSetUp.qml diff --git a/Linphone/view/Item/Contact/Contact.qml b/Linphone/view/Item/Contact/Contact.qml index 0ea496fcb..1ac25d752 100644 --- a/Linphone/view/Item/Contact/Contact.qml +++ b/Linphone/view/Item/Contact/Contact.qml @@ -106,7 +106,7 @@ Rectangle{ Layout.preferredHeight: 26 * DefaultStyle.dp Layout.fillHeight: true Layout.leftMargin: 40 * DefaultStyle.dp - visible: mainItem.account.core.unreadCallNotifications > 0 + visible: true // mainItem.account.core.unreadCallNotifications > 0 Rectangle{ id: unreadNotifications anchors.verticalCenter: parent.verticalCenter @@ -138,6 +138,19 @@ Rectangle{ shadowOpacity: 0.15 } } + Voicemail { + Layout.leftMargin: 18 * DefaultStyle.dp + Layout.rightMargin: 20 * DefaultStyle.dp + Layout.preferredWidth: 27 * DefaultStyle.dp + Layout.preferredHeight: 28 * DefaultStyle.dp + voicemailCount: mainItem.account.core.voicemailCount >= 100 ? '99+' : mainItem.account.core.voicemailCount + onClicked: { + if (mainItem.account.core.mwiServerAddress.length > 0) + UtilsCpp.createCall(mainItem.account.core.mwiServerAddress) + else + UtilsCpp.showInformationPopup(qsTr("Erreur"), qsTr("L'adresse de la messagerie vocale n'est pas définie."), false) + } + } Item{Layout.fillWidth: true} EffectImage { id: manageAccount diff --git a/Linphone/view/Item/Contact/Voicemail.qml b/Linphone/view/Item/Contact/Voicemail.qml new file mode 100644 index 000000000..8fec3c616 --- /dev/null +++ b/Linphone/view/Item/Contact/Voicemail.qml @@ -0,0 +1,38 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls + +import Linphone +import UtilsCpp +import SettingsCpp + +Rectangle{ + id: mainItem + property int voicemailCount: 0 + visible: voicemailCount > 0 + width: 27 * DefaultStyle.dp + height: 28 * DefaultStyle.dp + signal clicked() + Button { + anchors.bottom: parent.bottom + anchors.left: parent.left + icon.source: AppIcons.voicemail + width: 24 * DefaultStyle.dp + height: 24 * DefaultStyle.dp + background: Item { + anchors.fill: parent + } + onClicked: { + mainItem.clicked() + } + } + Text { + anchors.top: parent.top + anchors.right: parent.right + font.weight: 700 * DefaultStyle.dp + font.pixelSize: 10 * DefaultStyle.dp + color: DefaultStyle.danger_500main + text: voicemailCount + maximumLineCount: 1 + } +} diff --git a/Linphone/view/Style/AppIcons.qml b/Linphone/view/Style/AppIcons.qml index 9a215ddd9..dff6bd669 100644 --- a/Linphone/view/Style/AppIcons.qml +++ b/Linphone/view/Style/AppIcons.qml @@ -115,4 +115,5 @@ QtObject { property string desktop: "image://internal/desktop.svg" property string calendar: "image://internal/calendar.svg" property string bellDnd: "image://internal/bell-dnd.svg" + property string voicemail: "image://internal/voicemail.svg" }