Application settings

This commit is contained in:
Christophe Deschamps 2024-06-11 10:27:16 +00:00
parent f39a3c4a15
commit 57534e2c44
27 changed files with 1715 additions and 108 deletions

View file

@ -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.

View file

@ -41,23 +41,28 @@ QSharedPointer<Settings> Settings::create() {
Settings::Settings(QObject *parent) : QObject(parent) {
mustBeInLinphoneThread(getClassName());
mSettingsModel = Utils::makeQObject_ptr<SettingsModel>();
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<Settings> me) {
mustBeInLinphoneThread(getClassName());
mSettingsModelConnection = QSharedPointer<SafeConnection<Settings, SettingsModel>>(
new SafeConnection<Settings, SettingsModel>(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();
}
}
}
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(); }
);
}

View file

@ -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<Settings> 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<SettingsModel> 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<SafeConnection<Settings, SettingsModel>> mSettingsModelConnection;

View file

@ -0,0 +1,4 @@
<svg width="32" height="20" viewBox="0 0 32 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="32" height="20" rx="10" fill="#9AABB5"/>
<circle cx="10" cy="10" r="6" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 202 B

View file

@ -0,0 +1,4 @@
<svg width="32" height="20" viewBox="0 0 32 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="32" height="20" rx="10" fill="#4FAE80"/>
<circle cx="22" cy="10" r="6" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 202 B

View file

@ -28,6 +28,8 @@ list(APPEND _LINPHONEAPP_SOURCES
model/search/MagicSearchModel.cpp
model/setting/SettingsModel.cpp
model/setting/MediastreamerUtils.cpp
model/tool/ToolModel.cpp

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <linphone/linphonecore.h>
#include <mediastreamer2/msvolume.h>
#include <mediastreamer2/mssndcard.h>
#include <mediastreamer2/msticker.h>
#include <model/core/CoreModel.hpp>
#include "MediastreamerUtils.hpp"
#include <qlogging.h>
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<unsigned int>(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<float>(10.0 * log10(volume));
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef MEDIASTREAMER_UTILS_H_
#define MEDIASTREAMER_UTILS_H_
#include <cmath>
#include <linphone++/linphone.hh>
#include <QtGlobal>
// =============================================================================
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<float>(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_

View file

@ -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 &section, const st
return isReadOnly(section, name) ? name + "/readonly" : name;
}
std::list<std::string> 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));
}
}
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<linphone::Core> 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<linphone::Core> 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<linphone::AudioDevice> & 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<linphone::AudioDevice> & 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);
}

View file

@ -25,6 +25,7 @@
#include <QObject>
#include <QVariantMap>
#include <linphone++/linphone.hh>
#include "MediastreamerUtils.hpp"
#include "tool/AbstractObject.hpp"
@ -40,18 +41,101 @@ public:
getEntryFullName(const std::string &section,
const std::string &name) const; // Return the full name of the entry : 'name/readonly' or 'name'
std::list<std::string> getVideoDevices() const;
void setVideoDevice(const std::string &id);
std::string getVideoDevice();
static const std::string UiSection;
std::shared_ptr<linphone::Config> 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_

View file

@ -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
}
}
}

View file

@ -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
}

View file

@ -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
}
}
}
}

View file

@ -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
}

View file

@ -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)

View file

@ -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
}
}
}

View file

@ -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

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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
}
}
}
}

View file

@ -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
}
}

View file

@ -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

View file

@ -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 })
}
}
}

View file

@ -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"
}

View file

@ -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

View file

@ -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
})
}