diff --git a/Linphone/CMakeLists.txt b/Linphone/CMakeLists.txt index d49127d62..a81637fc9 100644 --- a/Linphone/CMakeLists.txt +++ b/Linphone/CMakeLists.txt @@ -5,7 +5,7 @@ project(Linphone VERSION 6.0 LANGUAGES CXX) ################################################################ # PACKAGES ################################################################ -set(LINPHONE_PACKAGES LinphoneCxx BCToolbox) +set(LINPHONE_PACKAGES Mediastreamer2 LinphoneCxx BCToolbox LibLinphone) list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/Modules") foreach(PACKAGE ${LINPHONE_PACKAGES}) message(STATUS "Trying to find ${PACKAGE}") @@ -17,7 +17,9 @@ endforeach() set(TARGET_NAME Linphone) -set(APP_TARGETS ${LinphoneCxx_TARGET}) +set(APP_TARGETS ${LinphoneCxx_TARGET} + ${Mediastreamer2_TARGET}#MediastreamerUtils + ${LibLinphone_TARGET})#MediastreamerUtils set(QT_DEFAULT_MAJOR_VERSION 6) set(QT_PACKAGES Core Quick Qml Widgets Svg Multimedia Test)# Search Core at first for initialize Qt scripts for next find_packages. diff --git a/Linphone/core/setting/SettingsCore.cpp b/Linphone/core/setting/SettingsCore.cpp index f1032e362..689b61464 100644 --- a/Linphone/core/setting/SettingsCore.cpp +++ b/Linphone/core/setting/SettingsCore.cpp @@ -41,23 +41,28 @@ QSharedPointer Settings::create() { Settings::Settings(QObject *parent) : QObject(parent) { mustBeInLinphoneThread(getClassName()); mSettingsModel = Utils::makeQObject_ptr(); - auto core = CoreModel::getInstance()->getCore(); - for (auto &device : core->getExtendedAudioDevices()) { - auto core = CoreModel::getInstance()->getCore(); - if (device->hasCapability(linphone::AudioDevice::Capabilities::CapabilityRecord)) { - mInputAudioDevices.append(Utils::coreStringToAppString(device->getId())); - // mInputAudioDevices.append(createDeviceVariant(Utils::coreStringToAppString(device->getId()), - // Utils::coreStringToAppString(device->getDeviceName()))); - } - if (device->hasCapability(linphone::AudioDevice::Capabilities::CapabilityPlay)) { - mOutputAudioDevices.append(Utils::coreStringToAppString(device->getId())); - // mOutputAudioDevices.append(createDeviceVariant(Utils::coreStringToAppString(device->getId()), - // Utils::coreStringToAppString(device->getDeviceName()))); - } - } - for (auto &device : core->getVideoDevicesList()) { - mVideoDevices.append(Utils::coreStringToAppString(device)); - } + + // Security + mVfsEnabled = mSettingsModel->getVfsEnabled(); + + // Call + mVideoEnabled = mSettingsModel->getVideoEnabled(); + mEchoCancellationEnabled = mSettingsModel->getEchoCancellationEnabled(); + mAutomaticallyRecordCallsEnabled = mSettingsModel->getAutomaticallyRecordCallsEnabled(); + + // Audio + mCaptureDevices = mSettingsModel->getCaptureDevices(); + mPlaybackDevices = mSettingsModel->getPlaybackDevices(); + mCaptureDevice = mSettingsModel->getCaptureDevice(); + mPlaybackDevice = mSettingsModel->getPlaybackDevice(); + + mCaptureGain = mSettingsModel->getCaptureGain(); + mPlaybackGain = mSettingsModel->getPlaybackGain(); + + // Video + mVideoDevice = mSettingsModel->getVideoDevice(); + mVideoDevices = mSettingsModel->getVideoDevices(); + } Settings::~Settings() { @@ -67,13 +72,162 @@ void Settings::setSelf(QSharedPointer me) { mustBeInLinphoneThread(getClassName()); mSettingsModelConnection = QSharedPointer>( new SafeConnection(me, mSettingsModel), &QObject::deleteLater); - mSettingsModelConnection->makeConnectToCore(&Settings::lSetVideoDevice, [this](const QString &id) { + + // VFS + mSettingsModelConnection->makeConnectToCore(&Settings::setVfsEnabled, [this](const bool enabled) { mSettingsModelConnection->invokeToModel( - [this, id]() { mSettingsModel->setVideoDevice(Utils::appStringToCoreString(id)); }); + [this, enabled]() { mSettingsModel->setVfsEnabled(enabled); } + ); }); - mSettingsModelConnection->makeConnectToModel(&SettingsModel::videoDeviceChanged, [this](const std::string &id) { + + mSettingsModelConnection->makeConnectToModel(&SettingsModel::vfsEnabledChanged, [this](const bool enabled) { + mSettingsModelConnection->invokeToCore( + [this, enabled]() { + mVfsEnabled = enabled; + emit vfsEnabledChanged(); + } + ); + }); + + // Video Calls + mSettingsModelConnection->makeConnectToCore(&Settings::setVideoEnabled, [this](const bool enabled) { mSettingsModelConnection->invokeToModel( - [this, id = Utils::coreStringToAppString(id)]() { setCurrentVideoDevice(id); }); + [this, enabled]() { mSettingsModel->setVideoEnabled(enabled); } + ); + }); + + mSettingsModelConnection->makeConnectToModel(&SettingsModel::videoEnabledChanged, [this](const bool enabled) { + mSettingsModelConnection->invokeToCore( + [this, enabled]() { + mVideoEnabled = enabled; + emit videoEnabledChanged(); + }); + }); + + // Echo cancelling + mSettingsModelConnection->makeConnectToCore(&Settings::setEchoCancellationEnabled, [this](const bool enabled) { + mSettingsModelConnection->invokeToModel( + [this, enabled]() { mSettingsModel->setEchoCancellationEnabled(enabled); } + ); + }); + + mSettingsModelConnection->makeConnectToModel(&SettingsModel::echoCancellationEnabledChanged, [this](const bool enabled) { + mSettingsModelConnection->invokeToCore( + [this, enabled]() { + mEchoCancellationEnabled = enabled; + emit echoCancellationEnabledChanged(); + }); + }); + + // Auto recording + mSettingsModelConnection->makeConnectToCore(&Settings::setAutomaticallyRecordCallsEnabled, [this](const bool enabled) { + mSettingsModelConnection->invokeToModel( + [this, enabled]() { mSettingsModel->setAutomaticallyRecordCallsEnabled(enabled); } + ); + }); + mSettingsModelConnection->makeConnectToModel(&SettingsModel::automaticallyRecordCallsEnabledChanged, [this](const bool enabled) { + mSettingsModelConnection->invokeToCore( + [this, enabled]() { + mAutomaticallyRecordCallsEnabled = enabled; + emit automaticallyRecordCallsEnabledChanged(); + }); + }); + + // Audio device(s) + mSettingsModelConnection->makeConnectToCore(&Settings::setCaptureDevice, [this](const QString id) { + mSettingsModelConnection->invokeToModel( + [this, id]() { mSettingsModel->setCaptureDevice(id); }); + }); + mSettingsModelConnection->makeConnectToModel(&SettingsModel::captureDeviceChanged, [this](const QString device) { + mSettingsModelConnection->invokeToCore( + [this, device]() { + mCaptureDevice = device; + emit captureDeviceChanged(device); + }); + }); + + mSettingsModelConnection->makeConnectToCore(&Settings::setPlaybackDevice, [this](const QString id) { + mSettingsModelConnection->invokeToModel( + [this, id]() { mSettingsModel->setPlaybackDevice(id); }); + }); + + mSettingsModelConnection->makeConnectToModel(&SettingsModel::playbackDeviceChanged, [this](const QString device) { + mSettingsModelConnection->invokeToCore( + [this, device]() { + mPlaybackDevice = device; + emit playbackDeviceChanged(device); + }); + }); + + mSettingsModelConnection->makeConnectToCore(&Settings::setPlaybackGain, [this](const float value) { + mSettingsModelConnection->invokeToModel( + [this, value]() { mSettingsModel->setPlaybackGain(value); }); + }); + + mSettingsModelConnection->makeConnectToModel(&SettingsModel::playbackGainChanged, [this](const float value) { + mSettingsModelConnection->invokeToCore( + [this, value]() { + mPlaybackGain = value; + emit playbackGainChanged(value); + }); + }); + + mSettingsModelConnection->makeConnectToCore(&Settings::setCaptureGain, [this](const float value) { + mSettingsModelConnection->invokeToModel( + [this, value]() { mSettingsModel->setCaptureGain(value); }); + }); + + mSettingsModelConnection->makeConnectToModel(&SettingsModel::captureGainChanged, [this](const float value) { + mSettingsModelConnection->invokeToCore( + [this, value]() { + mCaptureGain = value; + emit captureGainChanged(value); + }); + }); + + mSettingsModelConnection->makeConnectToModel(&SettingsModel::micVolumeChanged, [this](const float value) { + mSettingsModelConnection->invokeToCore( + [this, value]() { + emit micVolumeChanged(value); + }); + }); + + mSettingsModelConnection->makeConnectToModel(&SettingsModel::captureDevicesChanged, [this](const QStringList devices) { + mSettingsModelConnection->invokeToCore( + [this, devices]() { + mCaptureDevices = devices; + emit captureDevicesChanged(devices); + }); + }); + + mSettingsModelConnection->makeConnectToModel(&SettingsModel::playbackDevicesChanged, [this](const QStringList devices) { + mSettingsModelConnection->invokeToCore( + [this, devices]() { + mPlaybackDevices = devices; + emit playbackDevicesChanged(devices); + }); + }); + + // Video device(s) + mSettingsModelConnection->makeConnectToCore(&Settings::setVideoDevice, [this](const QString id) { + mSettingsModelConnection->invokeToModel( + [this, id]() { mSettingsModel->setVideoDevice(id); }); + }); + + mSettingsModelConnection->makeConnectToModel(&SettingsModel::videoDeviceChanged, [this](const QString device) { + mSettingsModelConnection->invokeToCore( + [this, device]() { + mVideoDevice = device; + emit videoDeviceChanged(); + }); + }); + + mSettingsModelConnection->makeConnectToModel(&SettingsModel::videoDevicesChanged, [this](const QStringList devices) { + mSettingsModelConnection->invokeToCore( + [this, devices]() { + mVideoDevices = devices; + emit videoDevicesChanged(); + }); }); } @@ -89,33 +243,44 @@ QString Settings::getConfigPath(const QCommandLineParser &parser) { return configPath; } -QStringList Settings::getInputAudioDevicesList() const { - return mInputAudioDevices; +QStringList Settings::getCaptureDevices() const { + return mCaptureDevices; } -QStringList Settings::getOutputAudioDevicesList() const { - return mOutputAudioDevices; +QStringList Settings::getPlaybackDevices() const { + return mPlaybackDevices; } -QStringList Settings::getVideoDevicesList() const { +QStringList Settings::getVideoDevices() const { return mVideoDevices; } -void Settings::setCurrentVideoDevice(const QString &id) { - if (mCurrentVideoDeviceId != id) { - mCurrentVideoDeviceId = id; - emit videoDeviceChanged(); - } +bool Settings::getCaptureGraphRunning() { + return mCaptureGraphRunning; } -int Settings::getCurrentVideoDeviceIndex() { - auto found = std::find_if(mVideoDevices.begin(), mVideoDevices.end(), - [this](const QString &device) { return mCurrentVideoDeviceId == device; }); - if (found != mVideoDevices.end()) { - auto index = std::distance(mVideoDevices.begin(), found); - return index; - } - return -1; +float Settings::getCaptureGain() const { + return mCaptureGain; +} + +float Settings::getPlaybackGain() const { + return mPlaybackGain; +} + +QString Settings::getRingerDevice() const { + return mRingerDevice; +} + +QString Settings::getCaptureDevice() const { + return mCaptureDevice; +} + +QString Settings::getPlaybackDevice() const { + return mPlaybackDevice; +} + +int Settings::getEchoCancellationCalibration() const { + return mEchoCancellationCalibration; } bool Settings::getFirstLaunch() const { @@ -129,4 +294,28 @@ void Settings::setFirstLaunch(bool first) { mAppSettings.setValue("firstLaunch", (int)first); mAppSettings.sync(); } -} \ No newline at end of file +} + +void Settings::startEchoCancellerCalibration() { + mSettingsModelConnection->invokeToModel([this]() { + mSettingsModel->startEchoCancellerCalibration(); + }); +} + + +void Settings::accessCallSettings() { + mSettingsModelConnection->invokeToModel( + [this]() { mSettingsModel->accessCallSettings(); } + ); +} +void Settings::closeCallSettings() { + mSettingsModelConnection->invokeToModel( + [this]() { mSettingsModel->closeCallSettings(); } + ); +} + +void Settings::updateMicVolume() const { + mSettingsModelConnection->invokeToModel( + [this]() { mSettingsModel->getMicVolume(); } + ); +} diff --git a/Linphone/core/setting/SettingsCore.hpp b/Linphone/core/setting/SettingsCore.hpp index 6a5a38963..81b84a501 100644 --- a/Linphone/core/setting/SettingsCore.hpp +++ b/Linphone/core/setting/SettingsCore.hpp @@ -31,10 +31,33 @@ class Settings : public QObject, public AbstractObject { Q_OBJECT - Q_PROPERTY(QStringList inputAudioDevicesList READ getInputAudioDevicesList NOTIFY inputAudioDeviceChanged) - Q_PROPERTY(QStringList outputAudioDevicesList READ getOutputAudioDevicesList NOTIFY outputAudioDeviceChanged) - Q_PROPERTY(QStringList videoDevicesList READ getVideoDevicesList NOTIFY videoDeviceChanged) - Q_PROPERTY(int currentVideoDeviceIndex READ getCurrentVideoDeviceIndex NOTIFY videoDeviceChanged) + + // Security + Q_PROPERTY(bool vfsEnabled READ getVfsEnabled WRITE setVfsEnabled NOTIFY vfsEnabledChanged) + + // Call + Q_PROPERTY(bool videoEnabled READ getVideoEnabled WRITE setVideoEnabled NOTIFY videoEnabledChanged) + Q_PROPERTY(bool echoCancellationEnabled READ getEchoCancellationEnabled WRITE setEchoCancellationEnabled NOTIFY echoCancellationEnabledChanged) + Q_PROPERTY(int echoCancellationCalibration READ getEchoCancellationCalibration NOTIFY echoCancellationCalibrationChanged) + Q_PROPERTY(bool automaticallyRecordCallsEnabled READ getAutomaticallyRecordCallsEnabled WRITE setAutomaticallyRecordCallsEnabled NOTIFY automaticallyRecordCallsEnabledChanged) + + Q_PROPERTY(bool captureGraphRunning READ getCaptureGraphRunning NOTIFY captureGraphRunningChanged) + + Q_PROPERTY(QStringList captureDevices READ getCaptureDevices NOTIFY captureDevicesChanged) + Q_PROPERTY(QStringList playbackDevices READ getPlaybackDevices NOTIFY playbackDevicesChanged) + + Q_PROPERTY(float playbackGain READ getPlaybackGain WRITE setPlaybackGain NOTIFY playbackGainChanged) + Q_PROPERTY(float captureGain READ getCaptureGain WRITE setCaptureGain NOTIFY captureGainChanged) + + Q_PROPERTY(QString captureDevice READ getCaptureDevice WRITE setCaptureDevice NOTIFY captureDeviceChanged) + Q_PROPERTY(QString playbackDevice READ getPlaybackDevice WRITE setPlaybackDevice NOTIFY playbackDeviceChanged) + Q_PROPERTY(QString ringerDevice READ getRingerDevice WRITE setRingerDevice NOTIFY ringerDeviceChanged) + + Q_PROPERTY(QStringList videoDevices READ getVideoDevices NOTIFY videoDevicesChanged) + Q_PROPERTY(QString videoDevice READ getVideoDevice WRITE setVideoDevice NOTIFY videoDeviceChanged) + + Q_PROPERTY(float micVolume MEMBER _dummy_int NOTIFY micVolumeChanged) + public: static QSharedPointer create(); Settings(QObject *parent = Q_NULLPTR); @@ -44,33 +67,116 @@ public: QString getConfigPath(const QCommandLineParser &parser = QCommandLineParser()); - QStringList getInputAudioDevicesList() const; - - QStringList getOutputAudioDevicesList() const; - - QStringList getVideoDevicesList() const; - - void setCurrentVideoDevice(const QString &id); - int getCurrentVideoDeviceIndex(); - Q_INVOKABLE void setFirstLaunch(bool first); Q_INVOKABLE bool getFirstLaunch() const; + + // Security. -------------------------------------------------------------------- + bool getVfsEnabled() { return mVfsEnabled; } + + // Call. -------------------------------------------------------------------- + + bool getVideoEnabled() { return mVideoEnabled; } + bool getEchoCancellationEnabled() { return mEchoCancellationEnabled; } + bool getAutomaticallyRecordCallsEnabled() { return mAutomaticallyRecordCallsEnabled; } + + float getPlaybackGain() const; + + float getCaptureGain() const; + + QStringList getCaptureDevices () const; + QStringList getPlaybackDevices () const; + + QString getCaptureDevice () const; + + QString getPlaybackDevice () const; + + QString getRingerDevice () const; + + QString getVideoDevice() { return mVideoDevice; } + QStringList getVideoDevices() const; + + bool getCaptureGraphRunning(); + Q_INVOKABLE void startEchoCancellerCalibration(); + int getEchoCancellationCalibration() const; + + Q_INVOKABLE void accessCallSettings(); + Q_INVOKABLE void closeCallSettings(); + Q_INVOKABLE void updateMicVolume() const; + signals: - void inputAudioDeviceChanged(const QString &id); - void outputAudioDeviceChanged(const QString &id); - void videoDeviceChanged(); + + // Security + void setVfsEnabled(const bool enabled); + void vfsEnabledChanged(); + + // Call + void setVideoEnabled(const bool enabled); + void videoEnabledChanged(); + + void setEchoCancellationEnabled(const bool enabled); + void echoCancellationEnabledChanged(); + + void setAutomaticallyRecordCallsEnabled(const bool enabled); + void automaticallyRecordCallsEnabledChanged(); + + void captureGraphRunningChanged(bool running); + + void playbackGainChanged(float gain); + void captureGainChanged(float gain); + + void captureDevicesChanged (const QStringList &devices); + void playbackDevicesChanged (const QStringList &devices); + + void setCaptureDevice (const QString &device); + void captureDeviceChanged (const QString &device); + + void setPlaybackDevice (const QString &device); + void playbackDeviceChanged (const QString &device); + void ringerDeviceChanged (const QString &device); + + void setVideoDevice(const QString &device); + void videoDeviceChanged(); + void videoDevicesChanged(); + + void setCaptureGain(float gain); + void setPlaybackGain(float gain); + void setRingerDevice (const QString &device); + + void echoCancellationCalibrationChanged(); + void micVolumeChanged(float volume); - void lSetInputAudioDevice(const QString &device); - void lSetOutputAudioDevice(const QString &device); - void lSetVideoDevice(const QString &device); private: std::shared_ptr mSettingsModel; - QStringList mInputAudioDevices; - QString mCurrentVideoDeviceId; - QStringList mOutputAudioDevices; + + // Dummy properties (for properties that use values from core received throuh signals) + int _dummy_int = 0; + + // Security + bool mVfsEnabled; + + // Call + bool mVideoEnabled; + bool mEchoCancellationEnabled; + bool mAutomaticallyRecordCallsEnabled; + + // Audio + QStringList mCaptureDevices; + QStringList mPlaybackDevices; + QString mCaptureDevice; + QString mPlaybackDevice; + QString mRingerDevice; + + // Video QStringList mVideoDevices; + QString mVideoDevice; + + bool mCaptureGraphRunning; + float mCaptureGain; + float mPlaybackGain; + int mEchoCancellationCalibration; + QSettings mAppSettings; QSharedPointer> mSettingsModelConnection; diff --git a/Linphone/data/image/switch-off.svg b/Linphone/data/image/switch-off.svg new file mode 100644 index 000000000..154f9b9d4 --- /dev/null +++ b/Linphone/data/image/switch-off.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Linphone/data/image/switch-on.svg b/Linphone/data/image/switch-on.svg new file mode 100644 index 000000000..c24932e2a --- /dev/null +++ b/Linphone/data/image/switch-on.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Linphone/model/CMakeLists.txt b/Linphone/model/CMakeLists.txt index 11638fcbd..e58ff6ed3 100644 --- a/Linphone/model/CMakeLists.txt +++ b/Linphone/model/CMakeLists.txt @@ -28,6 +28,8 @@ list(APPEND _LINPHONEAPP_SOURCES model/search/MagicSearchModel.cpp model/setting/SettingsModel.cpp + model/setting/MediastreamerUtils.cpp + model/tool/ToolModel.cpp diff --git a/Linphone/model/setting/MediastreamerUtils.cpp b/Linphone/model/setting/MediastreamerUtils.cpp new file mode 100644 index 000000000..fe6c8f721 --- /dev/null +++ b/Linphone/model/setting/MediastreamerUtils.cpp @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2010-2020 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include "MediastreamerUtils.hpp" + +#include + +using namespace MediastreamerUtils; + +SimpleCaptureGraph::SimpleCaptureGraph(const std::string &capture, const std::string &playback) + : captureCardId(capture), playbackCardId(playback) +{ + LinphoneCore *ccore = CoreModel::getInstance()->getCore()->cPtr(); + msFactory = linphone_core_get_ms_factory(ccore); + + playbackCard = ms_snd_card_manager_get_card(ms_factory_get_snd_card_manager(msFactory), playbackCardId.c_str()); + if (!playbackCard) + qWarning("Cannot get playback card from MSFactory with : %s", playbackCardId.c_str()); + captureCard = ms_snd_card_manager_get_card(ms_factory_get_snd_card_manager(msFactory), captureCardId.c_str()); + if (!captureCard) + qWarning("Cannot get capture card from MSFactory with : %s", captureCardId.c_str()); + + if(playbackCard && captureCard)// Assure to initialize when playback and capture are available + init(); +} + +SimpleCaptureGraph::~SimpleCaptureGraph() +{ + destroy(); +} +static void device_notify_cb(void *user_data, MSFilter *f, unsigned int event, void *eventdata) { + if (event == MS_FILTER_OUTPUT_FMT_CHANGED) { + SimpleCaptureGraph * graph = (SimpleCaptureGraph *)user_data; + int captureRate, playbackRate, captureChannels, playbackChannels; + ms_filter_call_method(graph->audioCapture,MS_FILTER_GET_SAMPLE_RATE,&captureRate); + ms_filter_call_method(graph->audioSink,MS_FILTER_GET_SAMPLE_RATE,&playbackRate); + ms_filter_call_method(graph->audioCapture,MS_FILTER_GET_NCHANNELS,&captureChannels); + ms_filter_call_method(graph->audioSink,MS_FILTER_GET_NCHANNELS,&playbackChannels); + + ms_filter_call_method(graph->resamplerFilter,MS_FILTER_SET_SAMPLE_RATE,&captureRate); + ms_filter_call_method(graph->resamplerFilter,MS_FILTER_SET_OUTPUT_SAMPLE_RATE,&playbackRate); + ms_filter_call_method(graph->resamplerFilter,MS_FILTER_SET_NCHANNELS,&captureChannels); + ms_filter_call_method(graph->resamplerFilter,MS_FILTER_SET_OUTPUT_NCHANNELS,&playbackChannels); + } +} + +void SimpleCaptureGraph::init() { + if (!audioCapture) { + audioCapture = ms_snd_card_create_reader(captureCard); + ms_filter_add_notify_callback(audioCapture, device_notify_cb,this,FALSE); + } + if (!audioSink) { + audioSink = ms_snd_card_create_writer(playbackCard); + ms_filter_add_notify_callback(audioSink, device_notify_cb,this,FALSE); + } + if (!captureVolumeFilter) { + captureVolumeFilter = ms_factory_create_filter(msFactory, MS_VOLUME_ID); + } + if (!playbackVolumeFilter) { + playbackVolumeFilter = ms_factory_create_filter(msFactory, MS_VOLUME_ID); + } + if(!resamplerFilter) + resamplerFilter = ms_factory_create_filter(msFactory, MS_RESAMPLE_ID); + int captureRate, playbackRate, captureChannels, playbackChannels; + ms_filter_call_method(audioCapture,MS_FILTER_GET_SAMPLE_RATE,&captureRate); + ms_filter_call_method(audioSink,MS_FILTER_GET_SAMPLE_RATE,&playbackRate); + ms_filter_call_method(audioCapture,MS_FILTER_GET_NCHANNELS,&captureChannels); + ms_filter_call_method(audioSink,MS_FILTER_GET_NCHANNELS,&playbackChannels); + + ms_filter_call_method(resamplerFilter,MS_FILTER_SET_SAMPLE_RATE,&captureRate); + ms_filter_call_method(resamplerFilter,MS_FILTER_SET_OUTPUT_SAMPLE_RATE,&playbackRate); + ms_filter_call_method(resamplerFilter,MS_FILTER_SET_NCHANNELS,&captureChannels); + ms_filter_call_method(resamplerFilter,MS_FILTER_SET_OUTPUT_NCHANNELS,&playbackChannels); + + ms_filter_link(audioCapture, 0, captureVolumeFilter, 0); + ms_filter_link(captureVolumeFilter, 0, resamplerFilter, 0); + ms_filter_link(resamplerFilter, 0, playbackVolumeFilter, 0); + ms_filter_link(playbackVolumeFilter, 0, audioSink, 0); + + //Mute playback + float muteGain = 0.0f; + ms_filter_call_method(playbackVolumeFilter, static_cast(MS_VOLUME_SET_GAIN), &muteGain); + ticker = ms_ticker_new(); + running = false; + +} + +void SimpleCaptureGraph::start() { + if (!running && audioCapture) { + running = true; + ms_ticker_attach(ticker, audioCapture); + + } +} + +void SimpleCaptureGraph::stop() { + if (running && audioCapture){ + ms_ticker_detach(ticker, audioCapture); + running = false; + } +} + +void SimpleCaptureGraph::destroy() { + if (running) { + stop(); + } + + if (audioSink) + ms_filter_unlink(playbackVolumeFilter, 0, audioSink, 0); + if (captureVolumeFilter && resamplerFilter) + ms_filter_unlink(captureVolumeFilter, 0, resamplerFilter, 0); + if (resamplerFilter && playbackVolumeFilter) + ms_filter_unlink(resamplerFilter, 0, playbackVolumeFilter, 0); + if (audioCapture) + ms_filter_unlink(audioCapture, 0, captureVolumeFilter, 0); + if (playbackVolumeFilter) + ms_filter_destroy(playbackVolumeFilter); + if (captureVolumeFilter) + ms_filter_destroy(captureVolumeFilter); + if (resamplerFilter) + ms_filter_destroy(resamplerFilter); + if (audioSink) + ms_filter_destroy(audioSink); + if (audioCapture) + ms_filter_destroy(audioCapture); + if (ticker) { + ms_ticker_destroy(ticker);// Destroy ticker at the end to avoid conflicts between attached filters + } + ticker = nullptr; + playbackVolumeFilter = nullptr; + captureVolumeFilter = nullptr; + resamplerFilter = nullptr; + audioSink = nullptr; + audioCapture = nullptr; +} + +float SimpleCaptureGraph::getCaptureGain() { + float gain = 0.0f; + + if (isRunning() && audioCapture) { + ms_filter_call_method(audioCapture, MS_AUDIO_CAPTURE_GET_VOLUME_GAIN, &gain); + } + return gain; +} + +void SimpleCaptureGraph::setCaptureGain(float gain) { + if (isRunning() && audioCapture) { + ms_filter_call_method(audioCapture, MS_AUDIO_CAPTURE_SET_VOLUME_GAIN, &gain); + } +} + +float SimpleCaptureGraph::getPlaybackGain() { + float gain = 0.0f; + if (isRunning() && audioSink) { + ms_filter_call_method(audioSink, MS_AUDIO_PLAYBACK_GET_VOLUME_GAIN, &gain); + } + return gain; +} + +void SimpleCaptureGraph::setPlaybackGain(float gain) { + if (isRunning() && audioSink) { + ms_filter_call_method(audioSink, MS_AUDIO_PLAYBACK_SET_VOLUME_GAIN, &gain); + } +} + +float SimpleCaptureGraph::getCaptureVolume() { + float vol = 0; + + if (captureVolumeFilter) { + ms_filter_call_method(captureVolumeFilter, MS_VOLUME_GET, &vol); + vol = MediastreamerUtils::dbToLinear(vol); + } + return vol; +} + +float MediastreamerUtils::linearToDb(float volume) { + if (qFuzzyIsNull(volume)) { + return MS_VOLUME_DB_LOWEST; + } + return static_cast(10.0 * log10(volume)); +} diff --git a/Linphone/model/setting/MediastreamerUtils.hpp b/Linphone/model/setting/MediastreamerUtils.hpp new file mode 100644 index 000000000..80d87347c --- /dev/null +++ b/Linphone/model/setting/MediastreamerUtils.hpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2010-2020 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * 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 . + */ + +#ifndef MEDIASTREAMER_UTILS_H_ +#define MEDIASTREAMER_UTILS_H_ + +#include + +#include + +#include + +// ============================================================================= + +struct _MSSndCard; +struct _MSFilter; +struct _MSTicker; +struct _MSFactory; + +namespace MediastreamerUtils { + + inline float computeVu (float volume) { + constexpr float VuMin = -20.f; + constexpr float VuMax = 4.f; + + if (volume < VuMin) + return 0.f; + if (volume > VuMax) + return 1.f; + + return (volume - VuMin) / (VuMax - VuMin); + } + + inline float dbToLinear(float volume) { + return static_cast(pow(10.0, volume / 10.0)); + } + + float linearToDb(float volume); + + //Simple mediastreamer audio capture graph + //Used to get current microphone volume in audio settings + class SimpleCaptureGraph { + public: + SimpleCaptureGraph(const std::string &captureCardId, const std::string &playbackCardId); + ~SimpleCaptureGraph(); + + void start(); + void stop(); + + float getCaptureVolume(); + + float getCaptureGain(); + float getPlaybackGain(); + void setCaptureGain(float volume); + void setPlaybackGain(float volume); + + bool isRunning() const { + return running; + } + + void init(); + void destroy(); + + bool running = false; + + std::string captureCardId; + std::string playbackCardId; + + _MSFilter *audioSink = nullptr; + _MSFilter *audioCapture = nullptr; + _MSFilter *captureVolumeFilter = nullptr; + _MSFilter *playbackVolumeFilter = nullptr; + _MSFilter *resamplerFilter = nullptr; + _MSTicker *ticker = nullptr; + _MSSndCard *playbackCard = nullptr; + _MSSndCard *captureCard = nullptr; + _MSFactory *msFactory = nullptr; + }; + +} + +#endif // ifndef MEDIASTREAMER_UTILS_H_ diff --git a/Linphone/model/setting/SettingsModel.cpp b/Linphone/model/setting/SettingsModel.cpp index 0f25bb049..dd557698b 100644 --- a/Linphone/model/setting/SettingsModel.cpp +++ b/Linphone/model/setting/SettingsModel.cpp @@ -20,15 +20,21 @@ #include "SettingsModel.hpp" #include "model/core/CoreModel.hpp" +#include "tool/Utils.hpp" +#include "model/tool/ToolModel.hpp" // ============================================================================= DEFINE_ABSTRACT_OBJECT(SettingsModel) +using namespace std; + const std::string SettingsModel::UiSection("ui"); SettingsModel::SettingsModel(QObject *parent) : QObject(parent) { mustBeInLinphoneThread(getClassName()); + auto core = CoreModel::getInstance()->getCore(); + mConfig = core->getConfig(); } SettingsModel::~SettingsModel() { @@ -44,19 +50,319 @@ std::string SettingsModel::getEntryFullName(const std::string §ion, const st return isReadOnly(section, name) ? name + "/readonly" : name; } -std::list SettingsModel::getVideoDevices() const { +QStringList SettingsModel::getVideoDevices() const { + mustBeInLinphoneThread(getClassName()); auto core = CoreModel::getInstance()->getCore(); - return core->getVideoDevicesList(); -} - -std::string SettingsModel::getVideoDevice() { - return CoreModel::getInstance()->getCore()->getVideoDevice(); -} - -void SettingsModel::setVideoDevice(const std::string &id) { - auto core = CoreModel::getInstance()->getCore(); - if (core->getVideoDevice() != id) { - CoreModel::getInstance()->getCore()->setVideoDevice(id); - emit videoDeviceChanged(id); + QStringList result; + for (auto &device : core->getVideoDevicesList()) { + result.append(Utils::coreStringToAppString(device)); } -} \ No newline at end of file + return result; +} + +QString SettingsModel::getVideoDevice () const { + mustBeInLinphoneThread(getClassName()); + return Utils::coreStringToAppString( + CoreModel::getInstance()->getCore()->getVideoDevice() + ); +} + +void SettingsModel::setVideoDevice (const QString &device) { + mustBeInLinphoneThread(getClassName()); + CoreModel::getInstance()->getCore()->setVideoDevice( + Utils::appStringToCoreString(device) + ); + emit videoDeviceChanged(device); +} + +// ============================================================================= +// Audio. +// ============================================================================= + +bool SettingsModel::getIsInCall() const { + mustBeInLinphoneThread(getClassName()); + return CoreModel::getInstance()->getCore()->getCallsNb() != 0; +} + +void SettingsModel::resetCaptureGraph() { + mustBeInLinphoneThread(getClassName()); + deleteCaptureGraph(); + createCaptureGraph(); +} +void SettingsModel::createCaptureGraph() { + mustBeInLinphoneThread(getClassName()); + mSimpleCaptureGraph = + new MediastreamerUtils::SimpleCaptureGraph(Utils::appStringToCoreString(getCaptureDevice()), Utils::appStringToCoreString(getPlaybackDevice())); + mSimpleCaptureGraph->start(); + emit captureGraphRunningChanged(getCaptureGraphRunning()); +} +void SettingsModel::startCaptureGraph() { + mustBeInLinphoneThread(getClassName()); + if (!getIsInCall()) { + if (!mSimpleCaptureGraph) { + qDebug() << "Starting capture graph [" << mCaptureGraphListenerCount << "]"; + createCaptureGraph(); + } + ++mCaptureGraphListenerCount; + } +} +void SettingsModel::stopCaptureGraph() { + mustBeInLinphoneThread(getClassName()); + if (mCaptureGraphListenerCount > 0) { + if (--mCaptureGraphListenerCount == 0) { + qDebug() << "Stopping capture graph [" << mCaptureGraphListenerCount << "]"; + deleteCaptureGraph(); + } + } +} +void SettingsModel::stopCaptureGraphs() { + mustBeInLinphoneThread(getClassName()); + if (mCaptureGraphListenerCount > 0) { + mCaptureGraphListenerCount = 0; + deleteCaptureGraph(); + } +} +void SettingsModel::deleteCaptureGraph() { + mustBeInLinphoneThread(getClassName()); + if (mSimpleCaptureGraph) { + if (mSimpleCaptureGraph->isRunning()) { + mSimpleCaptureGraph->stop(); + } + delete mSimpleCaptureGraph; + mSimpleCaptureGraph = nullptr; + } +} +//Force a call on the 'detect' method of all audio filters, updating new or removed devices +void SettingsModel::accessCallSettings() { + // Audio + mustBeInLinphoneThread(getClassName()); + CoreModel::getInstance()->getCore()->reloadSoundDevices(); + emit captureDevicesChanged(getCaptureDevices()); + emit playbackDevicesChanged(getPlaybackDevices()); + emit playbackDeviceChanged(getPlaybackDevice()); + emit captureDeviceChanged(getCaptureDevice()); + emit ringerDeviceChanged(getRingerDevice()); + emit playbackGainChanged(getPlaybackGain()); + emit captureGainChanged(getCaptureGain()); + + // Media cards must not be used twice (capture card + call) else we will get latencies issues and bad echo calibrations in call. + if (!getIsInCall()) { + qDebug() << "Starting capture graph from accessing audio panel"; + startCaptureGraph(); + } + // Video + CoreModel::getInstance()->getCore()->reloadVideoDevices(); + emit videoDevicesChanged(getVideoDevices()); +} + +void SettingsModel::closeCallSettings() { + mustBeInLinphoneThread(getClassName()); + stopCaptureGraph(); + emit captureGraphRunningChanged(getCaptureGraphRunning()); +} + +bool SettingsModel::getCaptureGraphRunning() { + mustBeInLinphoneThread(getClassName()); + return mSimpleCaptureGraph && mSimpleCaptureGraph->isRunning() && !getIsInCall(); +} + +float SettingsModel::getMicVolume() { + mustBeInLinphoneThread(getClassName()); + float v = 0.0; + + if (mSimpleCaptureGraph && mSimpleCaptureGraph->isRunning()) { + v = mSimpleCaptureGraph->getCaptureVolume(); + } + emit micVolumeChanged(v); + return v; +} + +float SettingsModel::getPlaybackGain() const { + mustBeInLinphoneThread(getClassName()); + float dbGain = CoreModel::getInstance()->getCore()->getPlaybackGainDb(); + return MediastreamerUtils::dbToLinear(dbGain); +} + +void SettingsModel::setPlaybackGain(float gain) { + mustBeInLinphoneThread(getClassName()); + float oldGain = getPlaybackGain(); + CoreModel::getInstance()->getCore()->setPlaybackGainDb(MediastreamerUtils::linearToDb(gain)); + if (mSimpleCaptureGraph && mSimpleCaptureGraph->isRunning()) { + mSimpleCaptureGraph->setPlaybackGain(gain); + } + if((int)(oldGain*1000) != (int)(gain*1000)) + emit playbackGainChanged(gain); +} + +float SettingsModel::getCaptureGain() const { + mustBeInLinphoneThread(getClassName()); + float dbGain = CoreModel::getInstance()->getCore()->getMicGainDb(); + return MediastreamerUtils::dbToLinear(dbGain); +} + +void SettingsModel::setCaptureGain(float gain) { + mustBeInLinphoneThread(getClassName()); + float oldGain = getCaptureGain(); + CoreModel::getInstance()->getCore()->setMicGainDb(MediastreamerUtils::linearToDb(gain)); + if (mSimpleCaptureGraph && mSimpleCaptureGraph->isRunning()) { + mSimpleCaptureGraph->setCaptureGain(gain); + } + if((int)(oldGain *1000) != (int)(gain *1000)) + emit captureGainChanged(gain); +} + +QStringList SettingsModel::getCaptureDevices () const { + mustBeInLinphoneThread(getClassName()); + shared_ptr core = CoreModel::getInstance()->getCore(); + QStringList list; + + for (const auto &device : core->getExtendedAudioDevices()) { + if (device->hasCapability(linphone::AudioDevice::Capabilities::CapabilityRecord)) + list << Utils::coreStringToAppString(device->getId()); + } + return list; +} + +QStringList SettingsModel::getPlaybackDevices () const { + mustBeInLinphoneThread(getClassName()); + shared_ptr core = CoreModel::getInstance()->getCore(); + QStringList list; + + for (const auto &device : core->getExtendedAudioDevices()) { + if (device->hasCapability(linphone::AudioDevice::Capabilities::CapabilityPlay)) + list << Utils::coreStringToAppString(device->getId()); + } + + return list; +} + +// ----------------------------------------------------------------------------- + +QString SettingsModel::getCaptureDevice () const { + mustBeInLinphoneThread(getClassName()); + auto audioDevice = CoreModel::getInstance()->getCore()->getInputAudioDevice(); + return Utils::coreStringToAppString(audioDevice? audioDevice->getId() : CoreModel::getInstance()->getCore()->getCaptureDevice()); +} + +void SettingsModel::setCaptureDevice (const QString &device) { + mustBeInLinphoneThread(getClassName()); + std::string devId = Utils::appStringToCoreString(device); + auto list = CoreModel::getInstance()->getCore()->getExtendedAudioDevices(); + auto audioDevice = find_if(list.cbegin(), list.cend(), [&] ( const std::shared_ptr & audioItem) { + return audioItem->getId() == devId; + }); + if(audioDevice != list.cend()){ + CoreModel::getInstance()->getCore()->setCaptureDevice(devId); + CoreModel::getInstance()->getCore()->setInputAudioDevice(*audioDevice); + emit captureDeviceChanged(device); + resetCaptureGraph(); + }else + qWarning() << "Cannot set Capture device. The ID cannot be matched with an existant device : " << device; +} + +// ----------------------------------------------------------------------------- + +QString SettingsModel::getPlaybackDevice () const { + mustBeInLinphoneThread(getClassName()); + auto audioDevice = CoreModel::getInstance()->getCore()->getOutputAudioDevice(); + return Utils::coreStringToAppString(audioDevice? audioDevice->getId() : CoreModel::getInstance()->getCore()->getPlaybackDevice()); +} + +void SettingsModel::setPlaybackDevice (const QString &device) { + mustBeInLinphoneThread(getClassName()); + std::string devId = Utils::appStringToCoreString(device); + + auto list = CoreModel::getInstance()->getCore()->getExtendedAudioDevices(); + auto audioDevice = find_if(list.cbegin(), list.cend(), [&] ( const std::shared_ptr & audioItem) { + return audioItem->getId() == devId; + }); + if(audioDevice != list.cend()){ + + CoreModel::getInstance()->getCore()->setPlaybackDevice(devId); + CoreModel::getInstance()->getCore()->setOutputAudioDevice(*audioDevice); + emit playbackDeviceChanged(device); + resetCaptureGraph(); + }else + qWarning() << "Cannot set Playback device. The ID cannot be matched with an existant device : " << device; +} + +// ----------------------------------------------------------------------------- + +QString SettingsModel::getRingerDevice () const { + mustBeInLinphoneThread(getClassName()); + return Utils::coreStringToAppString( + CoreModel::getInstance()->getCore()->getRingerDevice() + ); +} + +void SettingsModel::setRingerDevice (const QString &device) { + mustBeInLinphoneThread(getClassName()); + CoreModel::getInstance()->getCore()->setRingerDevice( + Utils::appStringToCoreString(device) + ); + emit ringerDeviceChanged(device); +} + +// ----------------------------------------------------------------------------- + +bool SettingsModel::getVideoEnabled() const { + mustBeInLinphoneThread(getClassName()); + return CoreModel::getInstance()->getCore()->videoEnabled(); +} + +void SettingsModel::setVideoEnabled(const bool enabled) { + mustBeInLinphoneThread(getClassName()); + auto core = CoreModel::getInstance()->getCore(); + core->enableVideoCapture(enabled); + core->enableVideoDisplay(enabled); + emit videoEnabledChanged(enabled); +} + +// ----------------------------------------------------------------------------- + +bool SettingsModel::getEchoCancellationEnabled () const { + mustBeInLinphoneThread(getClassName()); + return CoreModel::getInstance()->getCore()->echoCancellationEnabled(); +} + +void SettingsModel::setEchoCancellationEnabled (bool status) { + mustBeInLinphoneThread(getClassName()); + CoreModel::getInstance()->getCore()->enableEchoCancellation(status); + emit echoCancellationEnabledChanged(status); +} + +void SettingsModel::startEchoCancellerCalibration(){ + mustBeInLinphoneThread(getClassName()); + CoreModel::getInstance()->getCore()->startEchoCancellerCalibration(); +} + +int SettingsModel::getEchoCancellationCalibration()const { + mustBeInLinphoneThread(getClassName()); + return CoreModel::getInstance()->getCore()->getEchoCancellationCalibration(); +} + +bool SettingsModel::getAutomaticallyRecordCallsEnabled () const { + mustBeInLinphoneThread(getClassName()); + return !!mConfig->getInt(UiSection, "automatically_record_calls", 0); +} + +void SettingsModel::setAutomaticallyRecordCallsEnabled (bool enabled) { + mustBeInLinphoneThread(getClassName()); + mConfig->setInt(UiSection, "automatically_record_calls", enabled); + emit automaticallyRecordCallsEnabledChanged(enabled); +} + +// ============================================================================= +// VFS. +// ============================================================================= + +bool SettingsModel::getVfsEnabled () const { + mustBeInLinphoneThread(getClassName()); + return !!mConfig->getInt(UiSection, "vfs_enabled", 0); +} + +void SettingsModel::setVfsEnabled (bool enabled) { + mustBeInLinphoneThread(getClassName()); + mConfig->setInt(UiSection, "vfs_enabled", enabled); + emit vfsEnabledChanged(enabled); +} diff --git a/Linphone/model/setting/SettingsModel.hpp b/Linphone/model/setting/SettingsModel.hpp index 37e350957..3ab3332ac 100644 --- a/Linphone/model/setting/SettingsModel.hpp +++ b/Linphone/model/setting/SettingsModel.hpp @@ -25,6 +25,7 @@ #include #include #include +#include "MediastreamerUtils.hpp" #include "tool/AbstractObject.hpp" @@ -40,18 +41,101 @@ public: getEntryFullName(const std::string §ion, const std::string &name) const; // Return the full name of the entry : 'name/readonly' or 'name' - std::list getVideoDevices() const; - void setVideoDevice(const std::string &id); - std::string getVideoDevice(); - + static const std::string UiSection; - std::shared_ptr mConfig; + + bool getVfsEnabled() const; + void setVfsEnabled(const bool enabled); + + bool getVideoEnabled() const; + void setVideoEnabled(const bool enabled); + + bool getAutomaticallyRecordCallsEnabled () const; + void setAutomaticallyRecordCallsEnabled (bool enabled); + + bool getEchoCancellationEnabled () const; + void setEchoCancellationEnabled (bool enabled); + + // Audio. -------------------------------------------------------------------- + bool getIsInCall() const; + void accessCallSettings(); + void closeCallSettings(); + + void startCaptureGraph(); + void stopCaptureGraph();; + void stopCaptureGraphs(); + void resetCaptureGraph(); + void createCaptureGraph(); + void deleteCaptureGraph(); + bool getCaptureGraphRunning(); + + float getMicVolume(); + + float getPlaybackGain() const; + void setPlaybackGain(float gain); + + float getCaptureGain() const; + void setCaptureGain(float gain); + + QStringList getCaptureDevices () const; + QStringList getPlaybackDevices () const; + + QString getCaptureDevice () const; + void setCaptureDevice (const QString &device); + + QString getPlaybackDevice () const; + void setPlaybackDevice (const QString &device); + + QString getRingerDevice () const; + void setRingerDevice (const QString &device); + + QString getRingPath () const; + void setRingPath (const QString &path); + + void startEchoCancellerCalibration(); + int getEchoCancellationCalibration() const; + + QStringList getVideoDevices () const; + + QString getVideoDevice () const; + void setVideoDevice (const QString &device); + signals: - void videoDeviceChanged(const std::string &id); + + // VFS. -------------------------------------------------------------------- + void vfsEnabledChanged(bool enabled); + void videoEnabledChanged(bool enabled); + + // Call. -------------------------------------------------------------------- + void echoCancellationEnabledChanged(bool enabled); + void automaticallyRecordCallsEnabledChanged(bool enabled); + + void captureGraphRunningChanged(bool running); + + void playbackGainChanged(float gain); + void captureGainChanged(float gain); + + void captureDevicesChanged (const QStringList &devices); + void playbackDevicesChanged (const QStringList &devices); + + void captureDeviceChanged (const QString &device); + void playbackDeviceChanged (const QString &device); + void ringerDeviceChanged (const QString &device); + + void ringPathChanged (const QString &path); + + void showAudioCodecsChanged (bool status); + + void videoDevicesChanged (const QStringList &devices); + void videoDeviceChanged (const QString &device); + + void micVolumeChanged(float volume); private: + MediastreamerUtils::SimpleCaptureGraph *mSimpleCaptureGraph = nullptr; + int mCaptureGraphListenerCount = 0; DECLARE_ABSTRACT_OBJECT }; #endif // SETTINGS_MODEL_H_ diff --git a/Linphone/view/App/Layout/MainLayout.qml b/Linphone/view/App/Layout/MainLayout.qml index 4d9b4acfe..552b4f1ab 100644 --- a/Linphone/view/App/Layout/MainLayout.qml +++ b/Linphone/view/App/Layout/MainLayout.qml @@ -14,6 +14,7 @@ import UtilsCpp Item { id: mainItem property var callObj + property bool settingsHidden: true signal addAccountRequest() @@ -134,7 +135,11 @@ Item { {icon: AppIcons.videoconference, selectedIcon: AppIcons.videoconferenceSelected, label: qsTr("Réunions")} ] onCurrentIndexChanged: { - if (currentIndex === 0) accountProxy.defaultAccount.core.lResetMissedCalls() + if (currentIndex === 0) accountProxy.defaultAccount.core.lResetMissedCalls() + if (!mainItem.settingsHidden) { + mainStackView.pop() + mainItem.settingsHidden = true + } } } ColumnLayout { @@ -337,6 +342,15 @@ Item { iconSize: 32 * DefaultStyle.dp text: qsTr("Paramètres") iconSource: AppIcons.settings + onClicked: { + if (mainItem.settingsHidden) { + mainStackView.push(settingsPageComponent) + settingsButton.popup.close() + mainItem.settingsHidden = false + } else { + settingsButton.popup.close() + } + } } IconLabelButton { Layout.preferredHeight: 32 * DefaultStyle.dp @@ -366,22 +380,47 @@ Item { } } } - StackLayout { - id: mainStackLayout - currentIndex: tabbar.currentIndex - Layout.topMargin: 24 * DefaultStyle.dp - CallPage { - id: callPage - onCreateContactRequested: (name, address) => { - mainItem.createContact(name, address) + Component { + id: mainStackLayoutComponent + StackLayout { + id: mainStackLayout + currentIndex: tabbar.currentIndex + CallPage { + id: callPage + onCreateContactRequested: (name, address) => { + mainItem.createContact(name, address) + } + } + ContactPage{ + id: contactPage + } + Item{} + //ConversationPage{} + MeetingPage{} + } + } + Component { + id: settingsPageComponent + SettingsPage { + onGoBack: { + mainStackView.pop() + mainItem.settingsHidden = true } } - ContactPage{ - id: contactPage + } + Control.StackView { + id: mainStackView + property Transition noTransition: Transition { + PropertyAnimation { property: "opacity"; from: 1; to: 1; duration: 0 } } - Item{} - //ConversationPage{} - MeetingPage{} + pushEnter: noTransition + pushExit: noTransition + popEnter: noTransition + popExit: noTransition + Layout.topMargin: 24 * DefaultStyle.dp + Layout.fillWidth: true + Layout.fillHeight: true + initialItem: mainStackLayoutComponent } } } diff --git a/Linphone/view/App/Layout/Settings/CallSettingsLayout.qml b/Linphone/view/App/Layout/Settings/CallSettingsLayout.qml new file mode 100644 index 000000000..4d0348f46 --- /dev/null +++ b/Linphone/view/App/Layout/Settings/CallSettingsLayout.qml @@ -0,0 +1,197 @@ + +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls as Control +import Linphone +import SettingsCpp 1.0 + +GenericSettingsLayout { + Layout.fillWidth: true + Layout.fillHeight: true + Component { + id: settings + ColumnLayout { + spacing: 40 * DefaultStyle.dp + SwitchSetting { + titleText: qsTr("Activer la vidéo") + propertyName: "videoEnabled" + } + SwitchSetting { + titleText: qsTr("Utiliser l'annulateur d'écho") + subTitleText: qsTr("Évite que de l'écho soit entendu par votre correspondant") + propertyName: "echoCancellationEnabled" + } + SwitchSetting { + titleText: qsTr("Démarrer l'enregistrement des appels automatiquement") + propertyName: "automaticallyRecordCallsEnabled" + } + ColumnLayout { + Layout.fillWidth: true + RowLayout { + EffectImage { + imageSource: AppIcons.videoCamera + colorizationColor: DefaultStyle.main1_500_main + Layout.preferredWidth: 24 * DefaultStyle.dp + Layout.preferredHeight: 24 * DefaultStyle.dp + imageWidth: 24 * DefaultStyle.dp + imageHeight: 24 * DefaultStyle.dp + } + Text { + text: qsTr("Caméra") + Layout.fillWidth: true + } + } + ComboSetting { + Layout.fillWidth: true + Layout.preferredWidth: parent.width + model: SettingsCpp.videoDevices + propertyName: "videoDevice" + } + } + ColumnLayout { + Layout.fillWidth: true + RowLayout { + Layout.fillWidth: true + EffectImage { + imageSource: AppIcons.speaker + colorizationColor: DefaultStyle.main1_500_main + Layout.preferredWidth: 24 * DefaultStyle.dp + Layout.preferredHeight: 24 * DefaultStyle.dp + imageWidth: 24 * DefaultStyle.dp + imageHeight: 24 * DefaultStyle.dp + } + Text { + text: qsTr("Haut-parleurs") + Layout.fillWidth: true + } + } + ComboSetting { + Layout.fillWidth: true + Layout.preferredWidth: parent.width + model: SettingsCpp.playbackDevices + propertyName: "playbackDevice" + } + Slider { + id: speakerVolume + Layout.fillWidth: true + from: 0.0 + to: 1.0 + value: SettingsCpp.playbackGain + onMoved: { + SettingsCpp.setPlaybackGain(value) + } + } + } + ColumnLayout { + Layout.fillWidth: true + RowLayout { + Layout.fillWidth: true + EffectImage { + imageSource: AppIcons.speaker + colorizationColor: DefaultStyle.main1_500_main + Layout.preferredWidth: 24 * DefaultStyle.dp + Layout.preferredHeight: 24 * DefaultStyle.dp + imageWidth: 24 * DefaultStyle.dp + imageHeight: 24 * DefaultStyle.dp + } + Text { + text: qsTr("Sonnerie") + Layout.fillWidth: true + } + } + ComboSetting { + Layout.fillWidth: true + Layout.preferredWidth: parent.width + model: SettingsCpp.playbackDevices + propertyName: "ringerDevice" + } + } + ColumnLayout { + Layout.fillWidth: true + RowLayout { + Layout.fillWidth: true + EffectImage { + imageSource: AppIcons.microphone + colorizationColor: DefaultStyle.main1_500_main + Layout.preferredWidth: 24 * DefaultStyle.dp + Layout.preferredHeight: 24 * DefaultStyle.dp + imageWidth: 24 * DefaultStyle.dp + imageHeight: 24 * DefaultStyle.dp + } + Text { + text: qsTr("Microphone") + Layout.fillWidth: true + } + } + ComboSetting { + Layout.fillWidth: true + Layout.preferredWidth: parent.width + model: SettingsCpp.captureDevices + propertyName: "captureDevice" + } + Slider { + id: microVolume + Layout.fillWidth: true + from: 0.0 + to: 1.0 + value: SettingsCpp.captureGain + onMoved: { + SettingsCpp.setCaptureGain(value) + } + } + Timer { + id: audioTestSliderTimer + running: false + interval: 50 + repeat: true + onTriggered: SettingsCpp.updateMicVolume() + } + Slider { + id: audioTestSlider + visible: !SettingsCpp.isInCall + Layout.fillWidth: true + enabled: false + Layout.preferredHeight: 10 * DefaultStyle.dp + + background: Rectangle { + x: audioTestSlider.leftPadding + y: audioTestSlider.topPadding + audioTestSlider.availableHeight / 2 - height / 2 + implicitWidth: 200 * DefaultStyle.dp + implicitHeight: 10 * DefaultStyle.dp + width: audioTestSlider.availableWidth + height: implicitHeight + radius: 2 * DefaultStyle.dp + color: DefaultStyle.grey_850 + + Rectangle { + width: audioTestSlider.visualPosition * parent.width + height: parent.height + gradient: Gradient { + orientation: Gradient.Horizontal + GradientStop { position: 0.0; color: DefaultStyle.vue_meter_light_green } + GradientStop { position: 1.0; color: DefaultStyle.vue_meter_dark_green } + } + radius: 2 * DefaultStyle.dp + } + } + handle: Item {visible: false} + } + } + Connections { + target: SettingsCpp + onMicVolumeChanged: { + audioTestSlider.value = volume + } + } + Component.onCompleted: { + SettingsCpp.accessCallSettings() + audioTestSliderTimer.running = true + } + Component.onDestruction: { + audioTestSliderTimer.running = false + SettingsCpp.closeCallSettings() + } + } + } + component: settings +} diff --git a/Linphone/view/App/Layout/Settings/GenericSettingsLayout.qml b/Linphone/view/App/Layout/Settings/GenericSettingsLayout.qml new file mode 100644 index 000000000..899196ec2 --- /dev/null +++ b/Linphone/view/App/Layout/Settings/GenericSettingsLayout.qml @@ -0,0 +1,55 @@ + +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls as Control + +import Linphone + +Rectangle { + id: mainItem + Layout.fillWidth: true + Layout.fillHeight: true + color: DefaultStyle.grey_0 + + property string titleText + property var component + property int horizontalMargin: 17 * DefaultStyle.dp + property int verticalMargin: 21 * DefaultStyle.dp + + Control.ScrollView { + anchors.fill: parent + anchors.leftMargin: 55 * DefaultStyle.dp + anchors.topMargin: 85 * DefaultStyle.dp + ColumnLayout { + width: parent.width + spacing: 10 * DefaultStyle.dp + Text { + text: titleText + font: Typography.h3m + Layout.fillWidth: true + Layout.leftMargin: 10 * DefaultStyle.dp + } + Rectangle { + Layout.preferredWidth: loader.implicitWidth + 2 * mainItem.horizontalMargin + Layout.preferredHeight: loader.implicitHeight + 2 * mainItem.verticalMargin + color: DefaultStyle.grey_100 + radius: 15 * DefaultStyle.dp + Loader { + id:loader + anchors.centerIn:parent + anchors.topMargin: mainItem.verticalMargin + anchors.bottomMargin: mainItem.verticalMargin + anchors.leftMargin: mainItem.horizontalMargin + anchors.rightMargin: mainItem.horizontalMargin + sourceComponent: mainItem.component + } + } + Item { + Layout.fillHeight: true + } + } + } +} + + + diff --git a/Linphone/view/App/Layout/Settings/SecuritySettingsLayout.qml b/Linphone/view/App/Layout/Settings/SecuritySettingsLayout.qml new file mode 100644 index 000000000..ca75dd9bc --- /dev/null +++ b/Linphone/view/App/Layout/Settings/SecuritySettingsLayout.qml @@ -0,0 +1,24 @@ + +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls as Control + +import Linphone + +GenericSettingsLayout { + Layout.fillWidth: true + Layout.fillHeight: true + Component { + id: settings + ColumnLayout { + spacing: 40 * DefaultStyle.dp + SwitchSetting { + titleText: qsTr("Chiffrer tous les fichiers") + subTitleText: qsTr("Attention, vous ne pourrez pas revenir en arrière !") + propertyName: "vfsEnabled" + } + } + + } + component: settings +} diff --git a/Linphone/view/CMakeLists.txt b/Linphone/view/CMakeLists.txt index 993cfae54..b341d2aac 100644 --- a/Linphone/view/CMakeLists.txt +++ b/Linphone/view/CMakeLists.txt @@ -18,6 +18,10 @@ list(APPEND _LINPHONEAPP_QML_FILES view/Layout/RightPanelLayout.qml view/Layout/Section.qml + view/App/Layout/Settings/GenericSettingsLayout.qml + view/App/Layout/Settings/SecuritySettingsLayout.qml + view/App/Layout/Settings/CallSettingsLayout.qml + view/Item/Account/Accounts.qml view/Item/Call/CallContactsLists.qml @@ -52,6 +56,7 @@ list(APPEND _LINPHONEAPP_QML_FILES view/Item/Carousel.qml view/Item/CheckableButton.qml view/Item/CheckBox.qml + view/Item/SwitchButton.qml view/Item/ComboBox.qml view/Item/DesktopPopup.qml view/Item/Dialog.qml @@ -88,6 +93,10 @@ list(APPEND _LINPHONEAPP_QML_FILES view/Item/Test/ItemsTest.qml + view/Item/Settings/SettingsFamily.qml + view/Item/Settings/SwitchSetting.qml + view/Item/Settings/ComboSetting.qml + view/Page/Login/LoginPage.qml view/Page/Login/RegisterPage.qml view/Page/Login/RegisterCheckingPage.qml @@ -100,7 +109,7 @@ list(APPEND _LINPHONEAPP_QML_FILES view/Page/Main/ContactPage.qml view/Page/Main/MeetingPage.qml - + view/Page/Main/SettingsPage.qml view/Tool/utils.js # Prototypes @@ -115,6 +124,7 @@ list(APPEND _LINPHONEAPP_QML_FILES list(APPEND _LINPHONEAPP_QML_SINGLETONS view/Style/AppIcons.qml view/Style/DefaultStyle.qml + view/Style/Typography.qml ) set(_LINPHONEAPP_QML_FILES ${_LINPHONEAPP_QML_FILES} PARENT_SCOPE) diff --git a/Linphone/view/Item/Call/InCallSettingsPanel.qml b/Linphone/view/Item/Call/InCallSettingsPanel.qml index e16588b17..1aacaa153 100644 --- a/Linphone/view/Item/Call/InCallSettingsPanel.qml +++ b/Linphone/view/Item/Call/InCallSettingsPanel.qml @@ -51,7 +51,7 @@ ColumnLayout { Layout.fillWidth: true Layout.preferredWidth: parent.width Layout.preferredHeight: 49 * DefaultStyle.dp - model: SettingsCpp.outputAudioDevicesList + model: SettingsCpp.playbackDevices onCurrentTextChanged: { if (mainItem.call) mainItem.call.core.lSetOutputAudioDevice(currentText) } @@ -90,7 +90,7 @@ ColumnLayout { Layout.fillWidth: true Layout.preferredWidth: parent.width Layout.preferredHeight: 49 * DefaultStyle.dp - model: SettingsCpp.inputAudioDevicesList + model: SettingsCpp.captureDevices onCurrentTextChanged: { if (mainItem.call) mainItem.call.core.lSetInputAudioDevice(currentText) } @@ -163,10 +163,10 @@ ColumnLayout { Layout.fillWidth: true Layout.preferredWidth: parent.width Layout.preferredHeight: 49 * DefaultStyle.dp - model: SettingsCpp.videoDevicesList + model: SettingsCpp.videoDevices currentIndex: SettingsCpp.currentVideoDeviceIndex onCurrentTextChanged: { - SettingsCpp.lSetVideoDevice(currentText) + SettingsCpp.setVideoDevice(currentText) } } } @@ -175,4 +175,4 @@ ColumnLayout { Item { Layout.fillHeight: true } -} \ No newline at end of file +} diff --git a/Linphone/view/Item/ComboBox.qml b/Linphone/view/Item/ComboBox.qml index 7b8a7f19f..7df578f62 100644 --- a/Linphone/view/Item/ComboBox.qml +++ b/Linphone/view/Item/ComboBox.qml @@ -16,6 +16,7 @@ Control.ComboBox { property int pixelSize: 14 * DefaultStyle.dp property int weight: 400 * DefaultStyle.dp property int leftMargin: 10 * DefaultStyle.dp + property bool oneLine: false onConstantImageSourceChanged: if (constantImageSource) selectedItemImg.source = constantImageSource onCurrentIndexChanged: { @@ -59,7 +60,7 @@ Control.ComboBox { id: selectedItemText color: mainItem.enabled ? DefaultStyle.main2_600 : DefaultStyle.grey_400 elide: Text.ElideRight - maximumLineCount: 2 + maximumLineCount: oneLine ? 1 : 2 wrapMode: Text.WrapAnywhere font { pixelSize: mainItem.pixelSize diff --git a/Linphone/view/Item/Settings/ComboSetting.qml b/Linphone/view/Item/Settings/ComboSetting.qml new file mode 100644 index 000000000..c83a0cd03 --- /dev/null +++ b/Linphone/view/Item/Settings/ComboSetting.qml @@ -0,0 +1,28 @@ +import QtQuick +import QtQuick.Controls.Material +import QtQuick.Layouts +import Linphone +import SettingsCpp 1.0 +import 'qrc:/Linphone/view/Tool/utils.js' as Utils + +ComboBox { + id: comboBox + Layout.preferredHeight: 49 * DefaultStyle.dp + property string propertyName + oneLine: true + currentIndex: Utils.findIndex(model, function (entry) { + return entry === SettingsCpp[propertyName] + }) + onCurrentTextChanged: { + binding.when = currentText != SettingsCpp[propertyName] + } + Binding { + id: binding + target: SettingsCpp + property: propertyName + value: comboBox.currentText + when: false + } +} + + diff --git a/Linphone/view/Item/Settings/SettingsFamily.qml b/Linphone/view/Item/Settings/SettingsFamily.qml new file mode 100644 index 000000000..412196e33 --- /dev/null +++ b/Linphone/view/Item/Settings/SettingsFamily.qml @@ -0,0 +1,50 @@ +import QtQuick +import QtQuick.Controls as Control +import QtQuick.Layouts 1.0 +import Linphone + +Rectangle { + + id: mainItem + + height: 50 * DefaultStyle.dp + anchors.right: parent.right + anchors.left: parent.left + + property string titleText + property bool isSelected: false + + signal selected() + + MouseArea { + hoverEnabled: true + anchors.fill: parent + Rectangle { + id: background + anchors.fill: parent + color: DefaultStyle.main2_200 + radius: 35 * DefaultStyle.dp + visible: parent.containsMouse || isSelected + } + Rectangle { + id: backgroundRightFiller + anchors.right: parent.right + color: DefaultStyle.main2_200 + width: 35 * DefaultStyle.dp + height: 50 * DefaultStyle.dp + visible: parent.containsMouse || isSelected + } + onClicked: { + mainItem.selected() + } + } + Text { + anchors.margins: 25 + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + text: titleText + font: Typography.h4 + } + + +} diff --git a/Linphone/view/Item/Settings/SwitchSetting.qml b/Linphone/view/Item/Settings/SwitchSetting.qml new file mode 100644 index 000000000..fb099b113 --- /dev/null +++ b/Linphone/view/Item/Settings/SwitchSetting.qml @@ -0,0 +1,47 @@ +import QtQuick +import QtQuick.Controls.Material +import QtQuick.Layouts +import Linphone +import SettingsCpp 1.0 + +RowLayout { + id:mainItem + property string titleText + property string subTitleText + property string propertyName + property bool enabled: true + spacing : 20 * DefaultStyle.dp + property int textWidth: 286 * DefaultStyle.dp + ColumnLayout { + Layout.preferredWidth: textWidth + Text { + text: titleText + font: Typography.p2 + wrapMode: Text.WordWrap + Layout.maximumWidth: textWidth + } + Text { + text: subTitleText + font: Typography.p1 + wrapMode: Text.WordWrap + Layout.maximumWidth: textWidth + visible: subTitleText.length > 0 + } + } + SwitchButton { + id: switchButton + Layout.alignment: Qt.AlignRight + checked: SettingsCpp[mainItem.propertyName] + enabled: mainItem.enabled + onToggled: { + binding.when = true + } + } + Binding { + id: binding + target: SettingsCpp + property: mainItem.propertyName + value: switchButton.checked + when: false + } +} diff --git a/Linphone/view/Item/Slider.qml b/Linphone/view/Item/Slider.qml index c97ceb0c8..a1cb16dd6 100644 --- a/Linphone/view/Item/Slider.qml +++ b/Linphone/view/Item/Slider.qml @@ -16,7 +16,7 @@ Control.Slider { height: implicitHeight radius: 30 * DefaultStyle.dp // TODO : change the colors when mockup indicates their names - color: "#D9D9D9" + color: DefaultStyle.grey_850 Rectangle { width: mainItem.visualPosition * parent.width @@ -50,4 +50,4 @@ Control.Slider { shadowOpacity: 0.1 } } -} \ No newline at end of file +} diff --git a/Linphone/view/Item/SwitchButton.qml b/Linphone/view/Item/SwitchButton.qml new file mode 100644 index 000000000..f27c05da9 --- /dev/null +++ b/Linphone/view/Item/SwitchButton.qml @@ -0,0 +1,22 @@ +import QtQuick 2.7 +import QtQuick.Controls 2.2 as Control +import Linphone + +Control.AbstractButton { + id: mainItem + checkable: true + width: 32 * DefaultStyle.dp + height: 20 * DefaultStyle.dp + EffectImage { + visible: mainItem.checked + imageSource: AppIcons.switchOn + //colorizationColor: DefaultStyle.success_500main - not working on this icon. + anchors.fill: parent + } + EffectImage { + visible: !mainItem.checked + imageSource: AppIcons.switchOff + //colorizationColor: DefaultStyle.main2_400 - not working on this icon. + anchors.fill: parent + } +} diff --git a/Linphone/view/Page/Main/AbstractMainPage.qml b/Linphone/view/Page/Main/AbstractMainPage.qml index 00ef2382d..e2a720c50 100644 --- a/Linphone/view/Page/Main/AbstractMainPage.qml +++ b/Linphone/view/Page/Main/AbstractMainPage.qml @@ -1,5 +1,5 @@ /** -* Qml template used for overview pages : Calls, Contacts, Conversations, Meetings +* Qml template used for overview pages : Calls, Contacts, Conversations, Meetings, Settings **/ import QtQuick 2.15 diff --git a/Linphone/view/Page/Main/SettingsPage.qml b/Linphone/view/Page/Main/SettingsPage.qml new file mode 100644 index 000000000..dee6abe9d --- /dev/null +++ b/Linphone/view/Page/Main/SettingsPage.qml @@ -0,0 +1,87 @@ +import QtQuick +import QtQuick.Effects +import QtQuick.Layouts +import QtQuick.Controls as Control +import Linphone +import UtilsCpp 1.0 + +AbstractMainPage { + + id: mainItem + showDefaultItem: false + + signal goBack() + + function layoutUrl(name) { + return "qrc:/Linphone/view/App/Layout/Settings/"+name+".qml" + } + + property var settingsFamilies: [ + {title: "Sécurité", layout: "SecuritySettingsLayout"}, + {title: "Appels", layout: "CallSettingsLayout"}, + {title: "Conversations", layout: "ChatSettingsLayout"}, + {title: "Contacts", layout: "ContactSettingsLayout"}, + {title: "Réunions", layout: "MeetingsSettingsLayout"}, + {title: "Réseau", layout: "NetworkSettingsLayout"}, + {title: "Affichage", layout: "DisplaySettingsLayout"}, + {title: "Paramètres avancés", layout: "AdvancedSettingsLayout"} + ] + + leftPanelContent: ColumnLayout { + id: leftPanel + Layout.fillWidth: true + Layout.fillHeight: true + property int sideMargin: 45 * DefaultStyle.dp + + RowLayout { + Layout.fillWidth: true + Layout.leftMargin: leftPanel.sideMargin + Layout.rightMargin: leftPanel.sideMargin + Button { + Layout.preferredHeight: 24 * DefaultStyle.dp + Layout.preferredWidth: 24 * DefaultStyle.dp + icon.source: AppIcons.leftArrow + width: 24 * DefaultStyle.dp + height: 24 * DefaultStyle.dp + background: Item { + anchors.fill: parent + } + onClicked: { + mainItem.goBack() + } + } + Text { + text: qsTr("Paramètres") + color: DefaultStyle.main2_700 + font: Typography.h2 + } + Item { + Layout.fillWidth: true + } + } + + ListView { + id: settingsFamiliesList + Layout.fillWidth: true + Layout.fillHeight: true + model: mainItem.settingsFamilies + Layout.topMargin: 41 * DefaultStyle.dp + Layout.leftMargin: leftPanel.sideMargin + property int selectedIndex: 0 + + delegate: SettingsFamily { + titleText:qsTr(modelData.title) + isSelected: settingsFamiliesList.selectedIndex == index + onSelected: { + settingsFamiliesList.selectedIndex = index + rightPanelStackView.clear() + rightPanelStackView.push(layoutUrl(modelData.layout), { titleText: modelData.title }) + } + } + } + Component.onCompleted: { + let initialEntry = mainItem.settingsFamilies[settingsFamiliesList.selectedIndex] + rightPanelStackView.push(layoutUrl(initialEntry.layout), { titleText: initialEntry.title }) + } + } +} diff --git a/Linphone/view/Style/AppIcons.qml b/Linphone/view/Style/AppIcons.qml index 88a57bbf4..571632229 100644 --- a/Linphone/view/Style/AppIcons.qml +++ b/Linphone/view/Style/AppIcons.qml @@ -92,4 +92,7 @@ QtObject { property string screencast: "image://internal/screencast.svg" property string videoconference: "image://internal/video-conference.svg" property string videoconferenceSelected: "image://internal/video-conference-selected.svg" + property string switchOn: "image://internal/switch-on.svg" + property string switchOff: "image://internal/switch-off.svg" + } diff --git a/Linphone/view/Style/DefaultStyle.qml b/Linphone/view/Style/DefaultStyle.qml index 40af8cadb..99ba2108f 100644 --- a/Linphone/view/Style/DefaultStyle.qml +++ b/Linphone/view/Style/DefaultStyle.qml @@ -22,6 +22,7 @@ QtObject { property color grey_400: "#949494" property color grey_500: "#4E4E4E" property color grey_600: "#2E3030" + property color grey_850: "#D9D9D9" property color grey_900: "#070707" property color grey_1000: "#000000" @@ -30,6 +31,9 @@ QtObject { property color success_500main: "#4FAE80" property color info_500_main: "#4AA8FF" + property color vue_meter_light_green: "#6FF88D" + property color vue_meter_dark_green: "#00D916" + property double dp: 1 diff --git a/Linphone/view/Style/Typography.qml b/Linphone/view/Style/Typography.qml new file mode 100644 index 000000000..a5ceee20c --- /dev/null +++ b/Linphone/view/Style/Typography.qml @@ -0,0 +1,41 @@ +pragma Singleton +import QtQuick + +QtObject { + + // Title/H4 - Bloc title + property font h4: Qt.font( { + family: DefaultStyle.defaultFont, + pixelSize: 16 * DefaultStyle.dp, + weight: 800 * DefaultStyle.dp + }) + + // Title/H3M - Bloc title + property font h3m: Qt.font( { + family: DefaultStyle.defaultFont, + pixelSize: 16 * DefaultStyle.dp, + weight: 800 * DefaultStyle.dp + }) + + // Title/H2 - Large bloc title + property font h2: Qt.font( { + family: DefaultStyle.defaultFont, + pixelSize: 29 * DefaultStyle.dp, + weight: 800 * DefaultStyle.dp + }) + + // Text/P2 - Bold, reduced paratraph text + property font p2: Qt.font( { + family: DefaultStyle.defaultFont, + pixelSize: 13 * DefaultStyle.dp, + weight: 700 * DefaultStyle.dp + }) + + // Text/P1 - Paratraph text + property font p1: Qt.font( { + family: DefaultStyle.defaultFont, + pixelSize: 14 * DefaultStyle.dp, + weight: 400 * DefaultStyle.dp + }) + +}