diff --git a/Linphone/core/App.cpp b/Linphone/core/App.cpp index acb3f93e1..df54ca05d 100644 --- a/Linphone/core/App.cpp +++ b/Linphone/core/App.cpp @@ -267,7 +267,7 @@ void App::initCore() { mEngine->addImageProvider(WindowIconProvider::ProviderId, new WindowIconProvider()); // Enable notifications. - mNotifier = new Notifier(mEngine); + mNotifier = new Notifier(mEngine, settings); mSettings = settings; settings.reset(); diff --git a/Linphone/core/notifier/Notifier.cpp b/Linphone/core/notifier/Notifier.cpp index 8017420a6..6894877d1 100644 --- a/Linphone/core/notifier/Notifier.cpp +++ b/Linphone/core/notifier/Notifier.cpp @@ -33,10 +33,10 @@ #include "core/App.hpp" #include "core/call/CallGui.hpp" +#include "model/tool/ToolModel.hpp" #include "tool/LinphoneEnums.hpp" #include "tool/providers/AvatarProvider.hpp" #include "tool/providers/ImageProvider.hpp" -#include "model/tool/ToolModel.hpp" DEFINE_ABSTRACT_OBJECT(Notifier) @@ -95,7 +95,7 @@ const QHash Notifier::Notifications = { // ----------------------------------------------------------------------------- -Notifier::Notifier(QObject *parent) : QObject(parent) { +Notifier::Notifier(QObject *parent, QSharedPointer settings) : QObject(parent) { mustBeInMainThread(getClassName()); const int nComponents = Notifications.size(); mComponents = new QQmlComponent *[nComponents]; @@ -113,6 +113,7 @@ Notifier::Notifier(QObject *parent) : QObject(parent) { } mMutex = new QMutex(); + mSettings = settings; } Notifier::~Notifier() { @@ -267,6 +268,7 @@ void Notifier::deleteNotification(QVariant notification) { // ============================================================================= #define CREATE_NOTIFICATION(TYPE, DATA) \ + if (mSettings->dndEnabled()) return; \ QObject *notification = createNotification(TYPE, DATA); \ if (!notification) return; \ const int timeout = Notifications[TYPE].getTimeout() * 1000; \ @@ -283,11 +285,12 @@ void Notifier::notifyReceivedCall(const shared_ptr &call) { auto accountModel = Utils::makeQObject_ptr(account); accountModel->setSelf(accountModel); if (!accountModel->getNotificationsAllowed()) { - qInfo() << "Notifications have been disabled for this account - not creating a notification for incoming call"; + qInfo() + << "Notifications have been disabled for this account - not creating a notification for incoming call"; return; } } - + auto model = CallCore::create(call); auto gui = new CallGui(model); gui->moveToThread(App::getInstance()->thread()); diff --git a/Linphone/core/notifier/Notifier.hpp b/Linphone/core/notifier/Notifier.hpp index 0916a6d2c..607c01fe4 100644 --- a/Linphone/core/notifier/Notifier.hpp +++ b/Linphone/core/notifier/Notifier.hpp @@ -23,6 +23,7 @@ #include +#include "core/setting/SettingsCore.hpp" #include "tool/AbstractObject.hpp" #include #include @@ -36,7 +37,7 @@ class Notifier : public QObject, public AbstractObject { Q_OBJECT public: - Notifier(QObject *parent = Q_NULLPTR); + Notifier(QObject *parent = Q_NULLPTR, QSharedPointer settings = Q_NULLPTR); ~Notifier(); enum NotificationType { @@ -97,6 +98,7 @@ private: QQmlComponent **mComponents = nullptr; static const QHash Notifications; + QSharedPointer mSettings; DECLARE_ABSTRACT_OBJECT }; diff --git a/Linphone/core/setting/SettingsCore.cpp b/Linphone/core/setting/SettingsCore.cpp index 58ee7ef4a..66fa5537f 100644 --- a/Linphone/core/setting/SettingsCore.cpp +++ b/Linphone/core/setting/SettingsCore.cpp @@ -69,6 +69,9 @@ Settings::Settings(QObject *parent) : QObject(parent) { mLogsFolder = mSettingsModel->getLogsFolder(); mLogsEmail = mSettingsModel->getLogsEmail(); + // DND + mDndEnabled = mSettingsModel->dndEnabled(); + // Ui INIT_CORE_MEMBER(DisableChatFeature, mSettingsModel) INIT_CORE_MEMBER(DisableMeetingsFeature, mSettingsModel) @@ -254,6 +257,17 @@ void Settings::setSelf(QSharedPointer me) { }); }); + // DND + mSettingsModelConnection->makeConnectToCore(&Settings::lEnableDnd, [this](const bool value) { + mSettingsModelConnection->invokeToModel([this, value]() { mSettingsModel->enableDnd(value); }); + }); + mSettingsModelConnection->makeConnectToModel(&SettingsModel::dndChanged, [this](const bool value) { + mSettingsModelConnection->invokeToCore([this, value]() { + mDndEnabled = value; + emit dndChanged(); + }); + }); + DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, Settings, SettingsModel, mSettingsModel, bool, disableChatFeature, DisableChatFeature) DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, Settings, SettingsModel, mSettingsModel, bool, @@ -426,3 +440,7 @@ QString Settings::getLogsEmail() const { QString Settings::getLogsFolder() const { return mLogsFolder; } + +bool Settings::dndEnabled() const { + return mDndEnabled; +} diff --git a/Linphone/core/setting/SettingsCore.hpp b/Linphone/core/setting/SettingsCore.hpp index 72893a3cb..50530525f 100644 --- a/Linphone/core/setting/SettingsCore.hpp +++ b/Linphone/core/setting/SettingsCore.hpp @@ -65,6 +65,7 @@ class Settings : public QObject, public AbstractObject { Q_PROPERTY(bool fullLogsEnabled READ getFullLogsEnabled WRITE setFullLogsEnabled NOTIFY fullLogsEnabledChanged) Q_PROPERTY(QString logsEmail READ getLogsEmail) Q_PROPERTY(QString logsFolder READ getLogsFolder) + Q_PROPERTY(bool dnd READ dndEnabled WRITE lEnableDnd NOTIFY dndChanged) public: static QSharedPointer create(); @@ -136,6 +137,8 @@ public: QString getLogsEmail() const; QString getLogsFolder() const; + bool dndEnabled() const; + DECLARE_CORE_GETSET(bool, disableChatFeature, DisableChatFeature) DECLARE_CORE_GETSET(bool, disableMeetingsFeature, DisableMeetingsFeature) DECLARE_CORE_GETSET(bool, disableBroadcastFeature, DisableBroadcastFeature) @@ -204,6 +207,9 @@ signals: void lastActiveTabIndexChanged(); + void dndChanged(); + void lEnableDnd(bool value); + private: std::shared_ptr mSettingsModel; @@ -239,6 +245,9 @@ private: QString mLogsFolder; QString mLogsEmail; + // DND + bool mDndEnabled; + QSettings mAppSettings; QSharedPointer> mSettingsModelConnection; diff --git a/Linphone/data/config/linphonerc-factory b/Linphone/data/config/linphonerc-factory index 77fa78ae3..f92bca5e4 100644 --- a/Linphone/data/config/linphonerc-factory +++ b/Linphone/data/config/linphonerc-factory @@ -49,7 +49,6 @@ use_cpim=1 zrtp_key_agreements_suites=MS_ZRTP_KEY_AGREEMENT_K255_KYB512 [sound] -disable_ringing=1 #remove this property for any application that is not Linphone public version itself ec_calibrator_cool_tones=1 ec_filter=MSWebRTCAEC diff --git a/Linphone/data/image/bell-dnd.svg b/Linphone/data/image/bell-dnd.svg new file mode 100644 index 000000000..249f53324 --- /dev/null +++ b/Linphone/data/image/bell-dnd.svg @@ -0,0 +1,3 @@ + + + diff --git a/Linphone/model/account/AccountModel.cpp b/Linphone/model/account/AccountModel.cpp index 5bae46662..9615e6057 100644 --- a/Linphone/model/account/AccountModel.cpp +++ b/Linphone/model/account/AccountModel.cpp @@ -207,7 +207,6 @@ void AccountModel::setStunServer(QString value) { params->setNatPolicy(policy); mMonitor->setParams(params); emit stunServerChanged(value); - qWarning() << "cdes stun server set to" << value; } void AccountModel::setIceEnabled(bool value) { diff --git a/Linphone/model/core/CoreModel.cpp b/Linphone/model/core/CoreModel.cpp index 2bcfc755b..36c045a38 100644 --- a/Linphone/model/core/CoreModel.cpp +++ b/Linphone/model/core/CoreModel.cpp @@ -268,6 +268,10 @@ void CoreModel::onCallStateChanged(const std::shared_ptr &core, if (state == linphone::Call::State::IncomingReceived) { App::getInstance()->getNotifier()->notifyReceivedCall(call); } + if (state == linphone::Call::State::End && SettingsModel::dndEnabled(core->getConfig()) && + core->getCallsNb() == 0) { // Disable tones in DND mode if no more calls are running. + SettingsModel::enableTones(core->getConfig(), false); + } emit callStateChanged(core, call, state, message); } void CoreModel::onCallStatsUpdated(const std::shared_ptr &core, diff --git a/Linphone/model/setting/SettingsModel.cpp b/Linphone/model/setting/SettingsModel.cpp index c622bc970..850a22765 100644 --- a/Linphone/model/setting/SettingsModel.cpp +++ b/Linphone/model/setting/SettingsModel.cpp @@ -37,6 +37,9 @@ SettingsModel::SettingsModel(QObject *parent) : QObject(parent) { auto core = CoreModel::getInstance()->getCore(); mConfig = core->getConfig(); CoreModel::getInstance()->getLogger()->applyConfig(mConfig); + if (mConfig->hasEntry(UiSection, "do_not_disturb") == 1) { + enableDnd(dndEnabled()); + } } SettingsModel::~SettingsModel() { @@ -416,6 +419,39 @@ QString SettingsModel::getLogsEmail() const { return Utils::coreStringToAppString(mConfig->getString(UiSection, "logs_email", Constants::DefaultLogsEmail)); } +// ============================================================================= +// Do not disturb +// ============================================================================= + +bool SettingsModel::dndEnabled(const shared_ptr &config) { + mustBeInLinphoneThread(sLog().arg(Q_FUNC_INFO)); + return config ? config->getInt(UiSection, "do_not_disturb", false) : false; +} + +bool SettingsModel::dndEnabled() const { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + return dndEnabled(mConfig); +} + +void SettingsModel::enableTones(const shared_ptr &config, bool enable) { + mustBeInLinphoneThread(sLog().arg(Q_FUNC_INFO)); + config->setInt("sound", "tone_indications", enable); // General tones + config->setInt("misc", "tone_indications", enable); // Call tones +} + +void SettingsModel::enableRinging(bool enable) { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + mConfig->setInt("sound", "disable_ringing", !enable); // Ringing +} + +void SettingsModel::enableDnd(bool enableDnd) { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + enableTones(mConfig, !enableDnd); + enableRinging(!enableDnd); + mConfig->setInt(UiSection, "do_not_disturb", enableDnd); + emit dndChanged(enableDnd); +} + // ============================================================================= // Ui. // ============================================================================= diff --git a/Linphone/model/setting/SettingsModel.hpp b/Linphone/model/setting/SettingsModel.hpp index b81db2c96..0756766de 100644 --- a/Linphone/model/setting/SettingsModel.hpp +++ b/Linphone/model/setting/SettingsModel.hpp @@ -88,9 +88,6 @@ public: QString getPlaybackDevice() const; void setPlaybackDevice(const QString &device); - QString getRingPath() const; - void setRingPath(const QString &path); - void startEchoCancellerCalibration(); int getEchoCancellationCalibration() const; @@ -118,6 +115,12 @@ public: void cleanLogs() const; void sendLogs() const; + bool dndEnabled() const; + static bool dndEnabled(const std::shared_ptr &config); + void enableDnd(bool value); + static void enableTones(const std::shared_ptr &config, bool enable); + void enableRinging(bool enable); + QString getLogsEmail() const; // UI @@ -155,8 +158,6 @@ signals: void captureDeviceChanged(const QString &device); void playbackDeviceChanged(const QString &device); - void ringPathChanged(const QString &path); - void showAudioCodecsChanged(bool status); void videoDevicesChanged(const QStringList &devices); @@ -167,6 +168,8 @@ signals: void logsEnabledChanged(bool status); void fullLogsEnabledChanged(bool status); + void dndChanged(bool value); + private: MediastreamerUtils::SimpleCaptureGraph *mSimpleCaptureGraph = nullptr; int mCaptureGraphListenerCount = 0; diff --git a/Linphone/model/tool/ToolModel.cpp b/Linphone/model/tool/ToolModel.cpp index cbbd5624d..ff4efcac1 100644 --- a/Linphone/model/tool/ToolModel.cpp +++ b/Linphone/model/tool/ToolModel.cpp @@ -136,6 +136,11 @@ bool ToolModel::createCall(const QString &sipAddress, } } + if (SettingsModel::dndEnabled( + core->getConfig())) { // Force tones for outgoing calls when in DND mode (ringback, dtmf, etc ... ) disabled + // again when no more calls are running. + SettingsModel::enableTones(core->getConfig(), true); + } std::shared_ptr params = core->createCallParams(nullptr); CallModel::activateLocalVideo(params, nullptr, localVideoEnabled); diff --git a/Linphone/view/App/Layout/MainLayout.qml b/Linphone/view/App/Layout/MainLayout.qml index 1e16fb90b..48ad4660f 100644 --- a/Linphone/view/App/Layout/MainLayout.qml +++ b/Linphone/view/App/Layout/MainLayout.qml @@ -349,6 +349,36 @@ Item { } RowLayout { spacing: 10 * DefaultStyle.dp + PopupButton { + id: deactivateDndButton + Layout.preferredWidth: 32 * DefaultStyle.dp + Layout.preferredHeight: 32 * DefaultStyle.dp + popup.padding: 14 * DefaultStyle.dp + visible: SettingsCpp.dnd + contentItem: EffectImage { + imageSource: AppIcons.bellDnd + width: 32 * DefaultStyle.dp + height: 32 * DefaultStyle.dp + Layout.preferredWidth: 32 * DefaultStyle.dp + Layout.preferredHeight: 32 * DefaultStyle.dp + fillMode: Image.PreserveAspectFit + colorizationColor: DefaultStyle.main1_500_main + } + popup.contentItem: ColumnLayout { + IconLabelButton { + Layout.preferredHeight: 32 * DefaultStyle.dp + Layout.fillWidth: true + focus: visible + iconSize: 32 * DefaultStyle.dp + text: qsTr("Désactiver ne pas déranger") + iconSource: AppIcons.bellDnd + onClicked: { + deactivateDndButton.popup.close() + SettingsCpp.dnd = false + } + } + } + } PopupButton { id: avatarButton Layout.preferredWidth: 54 * DefaultStyle.dp @@ -419,6 +449,21 @@ Item { KeyNavigation.up: visibleChildren.length != 0 ? settingsButtons.getPreviousItem(0) : null KeyNavigation.down: visibleChildren.length != 0 ? settingsButtons.getNextItem(0) : null } + IconLabelButton { + id: dndButton + Layout.preferredHeight: 32 * DefaultStyle.dp + Layout.fillWidth: true + focus: visible + iconSize: 32 * DefaultStyle.dp + text: SettingsCpp.dnd ? qsTr("Désactiver ne pas déranger") : qsTr("Activer ne pas déranger") + iconSource: AppIcons.bellDnd + onClicked: { + settingsMenuButton.popup.close() + SettingsCpp.dnd = !SettingsCpp.dnd + } + KeyNavigation.up: visibleChildren.length != 0 ? settingsButtons.getPreviousItem(0) : null + KeyNavigation.down: visibleChildren.length != 0 ? settingsButtons.getNextItem(0) : null + } IconLabelButton { id: settingsButton Layout.preferredHeight: 32 * DefaultStyle.dp diff --git a/Linphone/view/Style/AppIcons.qml b/Linphone/view/Style/AppIcons.qml index 62275fc62..da8586703 100644 --- a/Linphone/view/Style/AppIcons.qml +++ b/Linphone/view/Style/AppIcons.qml @@ -112,4 +112,5 @@ QtObject { property string mobile: "image://internal/mobile.svg" property string desktop: "image://internal/desktop.svg" property string calendar: "image://internal/calendar.svg" + property string bellDnd: "image://internal/bell-dnd.svg" }