audio settings

video settings
app settings in settings file
This commit is contained in:
Gaelle Braud 2024-02-13 17:10:00 +01:00
parent ac528fc05c
commit fb4ee6b579
28 changed files with 902 additions and 359 deletions

View file

@ -49,6 +49,7 @@
#include "core/phone-number/PhoneNumber.hpp"
#include "core/phone-number/PhoneNumberProxy.hpp"
#include "core/search/MagicSearchProxy.hpp"
#include "core/setting/SettingsCore.hpp"
#include "core/singleapplication/singleapplication.h"
#include "core/variant/VariantList.hpp"
#include "model/object/VariantObject.hpp"
@ -67,6 +68,9 @@ App::App(int &argc, char *argv[])
init();
}
App::~App() {
}
App *App::getInstance() {
return dynamic_cast<App *>(QApplication::instance());
}
@ -81,8 +85,53 @@ Notifier *App::getNotifier() const {
void App::init() {
// Core. Manage the logger so it must be instantiate at first.
auto coreModel = CoreModel::create("", mLinphoneThread);
connect(mLinphoneThread, &QThread::started, coreModel.get(), &CoreModel::start);
mFirstLaunch = mSettings.value("firstLaunch", 1).toInt();
connect(
mLinphoneThread, &QThread::started, coreModel.get(),
[this, coreModel]() mutable {
coreModel->start();
auto settings = Settings::create();
QMetaObject::invokeMethod(App::getInstance()->thread(), [this, settings]() mutable {
mSettings = settings;
settings.reset();
// QML
mEngine = new QQmlApplicationEngine(this);
// Provide `+custom` folders for custom components and `5.9` for old components.
QStringList selectors("custom");
const QVersionNumber &version = QLibraryInfo::version();
if (version.majorVersion() == 5 && version.minorVersion() == 9) selectors.push_back("5.9");
auto selector = new QQmlFileSelector(mEngine, mEngine);
selector->setExtraSelectors(selectors);
qInfo() << log().arg("Activated selectors:") << selector->selector()->allSelectors();
mEngine->addImportPath(":/");
mEngine->rootContext()->setContextProperty("applicationDirPath", QGuiApplication::applicationDirPath());
initCppInterfaces();
mEngine->addImageProvider(ImageProvider::ProviderId, new ImageProvider());
mEngine->addImageProvider(AvatarProvider::ProviderId, new AvatarProvider());
// Enable notifications.
mNotifier = new Notifier(mEngine);
const QUrl url(u"qrc:/Linphone/view/App/Main.qml"_qs);
QObject::connect(
mEngine, &QQmlApplicationEngine::objectCreated, this,
[this, url](QObject *obj, const QUrl &objUrl) {
if (url == objUrl) {
if (!obj) {
qCritical() << log().arg("Main.qml couldn't be load. The app will exit");
exit(-1);
}
mMainWindow = qobject_cast<QQuickWindow *>(obj);
Q_ASSERT(mMainWindow);
}
},
Qt::QueuedConnection);
mEngine->load(url);
});
coreModel.reset();
},
Qt::SingleShotConnection);
// Console Commands
createCommandParser();
mParser->parse(this->arguments());
@ -102,40 +151,6 @@ void App::init() {
qInfo() << log().arg("Display server : %1").arg(platformName());
// QML
mEngine = new QQmlApplicationEngine(this);
// Provide `+custom` folders for custom components and `5.9` for old components.
QStringList selectors("custom");
const QVersionNumber &version = QLibraryInfo::version();
if (version.majorVersion() == 5 && version.minorVersion() == 9) selectors.push_back("5.9");
auto selector = new QQmlFileSelector(mEngine, mEngine);
selector->setExtraSelectors(selectors);
qInfo() << log().arg("Activated selectors:") << selector->selector()->allSelectors();
mEngine->addImportPath(":/");
mEngine->rootContext()->setContextProperty("applicationDirPath", QGuiApplication::applicationDirPath());
initCppInterfaces();
mEngine->addImageProvider(ImageProvider::ProviderId, new ImageProvider());
mEngine->addImageProvider(AvatarProvider::ProviderId, new AvatarProvider());
// Enable notifications.
mNotifier = new Notifier(mEngine);
const QUrl url(u"qrc:/Linphone/view/App/Main.qml"_qs);
QObject::connect(
mEngine, &QQmlApplicationEngine::objectCreated, this,
[this, url](QObject *obj, const QUrl &objUrl) {
if (url == objUrl) {
if (!obj) {
qCritical() << log().arg("Main.qml couldn't be load. The app will exit");
exit(-1);
}
mMainWindow = qobject_cast<QQuickWindow *>(obj);
Q_ASSERT(mMainWindow);
}
},
Qt::QueuedConnection);
mEngine->load(url);
// mEngine->load(u"qrc:/Linphone/view/Prototype/CameraPrototype.qml"_qs);
}
@ -152,6 +167,10 @@ void App::initCppInterfaces() {
"EnumsToStringCpp", 1, 0, "EnumsToStringCpp",
[](QQmlEngine *engine, QJSEngine *) -> QObject * { return new EnumsToString(engine); });
qmlRegisterSingletonType<Settings>(
"SettingsCpp", 1, 0, "SettingsCpp",
[this](QQmlEngine *engine, QJSEngine *) -> QObject * { return mSettings.get(); });
qmlRegisterType<PhoneNumberProxy>(Constants::MainQmlUri, 1, 0, "PhoneNumberProxy");
qmlRegisterType<VariantObject>(Constants::MainQmlUri, 1, 0, "VariantObject");
@ -180,6 +199,8 @@ void App::clean() {
mNotifier = nullptr;
delete mEngine;
mEngine = nullptr;
mSettings.reset();
mSettings = nullptr;
mLinphoneThread->wait(250);
qApp->processEvents(QEventLoop::AllEvents, 250);
mLinphoneThread->exit();
@ -267,17 +288,6 @@ void App::closeCallsWindow() {
}
}
void App::setFirstLaunch(bool first) {
if (mFirstLaunch != first) {
mFirstLaunch = first;
mSettings.setValue("firstLaunch", first);
}
}
bool App::getFirstLaunch() const {
return mFirstLaunch;
}
QQuickWindow *App::getMainWindow() {
return mMainWindow;
}

View file

@ -20,9 +20,9 @@
#include <QCommandLineParser>
#include <QQmlApplicationEngine>
#include <QSettings>
#include <QSharedPointer>
#include "core/setting/SettingsCore.hpp"
#include "core/singleapplication/singleapplication.h"
#include "model/core/CoreModel.hpp"
#include "tool/AbstractObject.hpp"
@ -35,6 +35,7 @@ class QQuickWindow;
class App : public SingleApplication, public AbstractObject {
public:
App(int &argc, char *argv[]);
~App();
static App *getInstance();
Notifier *getNotifier() const;
@ -94,9 +95,6 @@ public:
QQuickWindow *getCallsWindow(QVariant callGui);
void closeCallsWindow();
bool getFirstLaunch() const;
void setFirstLaunch(bool first);
QQuickWindow *getMainWindow();
QQmlApplicationEngine *mEngine = nullptr;
@ -112,10 +110,7 @@ private:
Notifier *mNotifier = nullptr;
QQuickWindow *mMainWindow = nullptr;
QQuickWindow *mCallsWindow = nullptr;
QSettings mSettings;
bool mFirstLaunch = true;
// TODO : changer ce count lorsqu'on aura liste d'appels
int callsCount = 0;
QSharedPointer<Settings> mSettings;
DECLARE_ABSTRACT_OBJECT
};

View file

@ -28,7 +28,7 @@ list(APPEND _LINPHONEAPP_SOURCES
core/search/MagicSearchList.cpp
core/search/MagicSearchProxy.cpp
core/setting/Settings.cpp
core/setting/SettingsCore.cpp
core/proxy/ListProxy.cpp
core/proxy/Proxy.cpp

View file

@ -26,6 +26,13 @@
DEFINE_ABSTRACT_OBJECT(CallCore)
QVariant createDeviceVariant(const QString &id, const QString &name) {
QVariantMap map;
map.insert("id", id);
map.insert("name", name);
return map;
}
QSharedPointer<CallCore> CallCore::create(const std::shared_ptr<linphone::Call> &call) {
auto sharedPointer = QSharedPointer<CallCore>(new CallCore(call), &QObject::deleteLater);
sharedPointer->setSelf(sharedPointer);
@ -45,16 +52,15 @@ CallCore::CallCore(const std::shared_ptr<linphone::Call> &call) : QObject(nullpt
mMicrophoneMuted = call->getMicrophoneMuted();
mSpeakerMuted = call->getSpeakerMuted();
mCameraEnabled = call->cameraEnabled();
mDuration = call->getDuration();
mState = LinphoneEnums::fromLinphone(call->getState());
mPeerAddress = Utils::coreStringToAppString(mCallModel->getRemoteAddress()->asStringUriOnly());
mPeerAddress = Utils::coreStringToAppString(call->getRemoteAddress()->asStringUriOnly());
mStatus = LinphoneEnums::fromLinphone(call->getCallLog()->getStatus());
mTransferState = LinphoneEnums::fromLinphone(call->getTransferState());
auto token = Utils::coreStringToAppString(mCallModel->getAuthenticationToken());
auto localToken = mDir == LinphoneEnums::CallDir::Incoming ? token.left(2).toUpper() : token.right(2).toUpper();
auto remoteToken = mDir == LinphoneEnums::CallDir::Outgoing ? token.left(2).toUpper() : token.right(2).toUpper();
mEncryption = LinphoneEnums::fromLinphone(call->getParams()->getMediaEncryption());
auto tokenVerified = mCallModel->getAuthenticationTokenVerified();
auto tokenVerified = call->getAuthenticationTokenVerified();
mLocalSas = localToken;
mRemoteSas = remoteToken;
mIsSecured = (mEncryption == LinphoneEnums::MediaEncryption::Zrtp && tokenVerified) ||
@ -65,6 +71,19 @@ CallCore::CallCore(const std::shared_ptr<linphone::Call> &call) : QObject(nullpt
mRemoteVideoEnabled = call->getRemoteParams() && call->getRemoteParams()->videoEnabled();
mRecording = call->getParams() && call->getParams()->isRecording();
mRemoteRecording = call->getRemoteParams() && call->getRemoteParams()->isRecording();
mSpeakerVolumeGain = mCallModel->getSpeakerVolumeGain();
// TODO : change this with settings value when settings done
if (mSpeakerVolumeGain < 0) {
call->setSpeakerVolumeGain(0.5);
mSpeakerVolumeGain = 0.5;
}
mMicrophoneVolumeGain = call->getMicrophoneVolumeGain();
// TODO : change this with settings value when settings done
if (mMicrophoneVolumeGain < 0) {
call->setMicrophoneVolumeGain(0.5);
mMicrophoneVolumeGain = 0.5;
}
mMicrophoneVolume = call->getRecordVolume();
mRecordable = mState == LinphoneEnums::CallState::StreamsRunning;
}
@ -121,6 +140,9 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
mCallModelConnection->makeConnectToModel(&CallModel::durationChanged, [this](int duration) {
mCallModelConnection->invokeToCore([this, duration]() { setDuration(duration); });
});
mCallModelConnection->makeConnectToModel(&CallModel::microphoneVolumeChanged, [this](float volume) {
mCallModelConnection->invokeToCore([this, volume]() { setMicrophoneVolume(volume); });
});
mCallModelConnection->makeConnectToModel(
&CallModel::stateChanged, [this](linphone::Call::State state, const std::string &message) {
mCallModelConnection->invokeToCore([this, state, message]() {
@ -179,6 +201,35 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
encryption == LinphoneEnums::MediaEncryption::Dtls);
});
});
mCallModelConnection->makeConnectToCore(&CallCore::lSetSpeakerVolumeGain, [this](float gain) {
mCallModelConnection->invokeToModel([this, gain]() { mCallModel->setSpeakerVolumeGain(gain); });
});
mCallModelConnection->makeConnectToModel(&CallModel::speakerVolumeGainChanged, [this](float gain) {
mCallModelConnection->invokeToCore([this, gain]() { setSpeakerVolumeGain(gain); });
});
mCallModelConnection->makeConnectToCore(&CallCore::lSetMicrophoneVolumeGain, [this](float gain) {
mCallModelConnection->invokeToModel([this, gain]() { mCallModel->setMicrophoneVolumeGain(gain); });
});
mCallModelConnection->makeConnectToModel(&CallModel::microphoneVolumeGainChanged, [this](float gain) {
mCallModelConnection->invokeToCore([this, gain]() { setMicrophoneVolumeGain(gain); });
});
mCallModelConnection->makeConnectToCore(&CallCore::lSetInputAudioDevice, [this](const QString &id) {
mCallModelConnection->invokeToModel([this, id]() {
if (auto device = ToolModel::findAudioDevice(id)) {
mCallModel->setInputAudioDevice(device);
}
});
});
mCallModelConnection->makeConnectToModel(&CallModel::inputAudioDeviceChanged, [this](const std::string &id) {
mCallModelConnection->invokeToCore([this, id]() {});
});
mCallModelConnection->makeConnectToCore(&CallCore::lSetOutputAudioDevice, [this](const QString &id) {
mCallModelConnection->invokeToModel([this, id]() {
if (auto device = ToolModel::findAudioDevice(id)) {
mCallModel->setOutputAudioDevice(device);
}
});
});
mCallModelConnection->makeConnectToCore(&CallCore::lAccept, [this](bool withVideo) {
mCallModelConnection->invokeToModel([this, withVideo]() { mCallModel->accept(withVideo); });
});
@ -383,6 +434,36 @@ void CallCore::setRecordable(bool recordable) {
}
}
float CallCore::getSpeakerVolumeGain() const {
return mSpeakerVolumeGain;
}
void CallCore::setSpeakerVolumeGain(float gain) {
if (mSpeakerVolumeGain != gain) {
mSpeakerVolumeGain = gain;
emit speakerVolumeGainChanged();
}
}
float CallCore::getMicrophoneVolume() const {
return mMicrophoneVolume;
}
void CallCore::setMicrophoneVolume(float vol) {
if (mMicrophoneVolume != vol) {
mMicrophoneVolume = vol;
emit microphoneVolumeChanged();
}
}
float CallCore::getMicrophoneVolumeGain() const {
return mMicrophoneVolumeGain;
}
void CallCore::setMicrophoneVolumeGain(float gain) {
if (mMicrophoneVolumeGain != gain) {
mMicrophoneVolumeGain = gain;
emit microphoneVolumeGainChanged();
}
}
LinphoneEnums::CallState CallCore::getTransferState() const {
return mTransferState;
}

View file

@ -51,6 +51,11 @@ class CallCore : public QObject, public AbstractObject {
Q_PROPERTY(bool recording READ getRecording WRITE setRecording NOTIFY recordingChanged)
Q_PROPERTY(bool remoteRecording READ getRemoteRecording WRITE setRemoteRecording NOTIFY remoteRecordingChanged)
Q_PROPERTY(bool recordable READ getRecordable WRITE setRecordable NOTIFY recordableChanged)
Q_PROPERTY(
float speakerVolumeGain READ getSpeakerVolumeGain WRITE setSpeakerVolumeGain NOTIFY speakerVolumeGainChanged)
Q_PROPERTY(float microphoneVolumeGain READ getMicrophoneVolumeGain WRITE setMicrophoneVolumeGain NOTIFY
microphoneVolumeGainChanged)
Q_PROPERTY(float microVolume READ getMicrophoneVolume WRITE setMicrophoneVolume NOTIFY microphoneVolumeChanged)
Q_PROPERTY(LinphoneEnums::CallState transferState READ getTransferState NOTIFY transferStateChanged)
public:
@ -112,6 +117,18 @@ public:
bool getRecordable() const;
void setRecordable(bool recordable);
float getSpeakerVolumeGain() const;
void setSpeakerVolumeGain(float gain);
float getMicrophoneVolumeGain() const;
void setMicrophoneVolumeGain(float gain);
float getMicrophoneVolume() const;
void setMicrophoneVolume(float vol);
QString getInputDeviceName() const;
void setInputDeviceName(const QString &id);
LinphoneEnums::CallState getTransferState() const;
void setTransferState(LinphoneEnums::CallState state, const QString &message);
@ -135,6 +152,9 @@ signals:
void recordingChanged();
void remoteRecordingChanged();
void recordableChanged();
void speakerVolumeGainChanged();
void microphoneVolumeChanged();
void microphoneVolumeGainChanged();
// Linphone commands
void lAccept(bool withVideo); // Accept an incoming call
@ -149,6 +169,10 @@ signals:
void lStartRecording();
void lStopRecording();
void lVerifyAuthenticationToken(bool verified);
void lSetSpeakerVolumeGain(float gain);
void lSetMicrophoneVolumeGain(float gain);
void lSetInputAudioDevice(const QString &id);
void lSetOutputAudioDevice(const QString &id);
/* TODO
Q_INVOKABLE void acceptWithVideo();
@ -189,6 +213,9 @@ private:
bool mRecordable = false;
QString mLocalSas;
QString mRemoteSas;
float mSpeakerVolumeGain;
float mMicrophoneVolume;
float mMicrophoneVolumeGain;
QSharedPointer<SafeConnection<CallCore, CallModel>> mCallModelConnection;
DECLARE_ABSTRACT_OBJECT

View file

@ -1,45 +0,0 @@
/*
* Copyright (c) 2010-2024 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 "Settings.hpp"
#include <QUrl>
#include "core/path/Paths.hpp"
// =============================================================================
Settings::Settings(QObject *parent) : QObject(parent) {
}
Settings::~Settings() {
}
QString Settings::getConfigPath(const QCommandLineParser &parser) {
QString filePath = parser.isSet("config") ? parser.value("config") : "";
QString configPath;
if (!QUrl(filePath).isRelative()) {
// configPath = FileDownloader::synchronousDownload(filePath,
// Utils::coreStringToAppString(Paths::getConfigDirPath(false)), true));
}
if (configPath == "") configPath = Paths::getConfigFilePath(filePath, false);
if (configPath == "" && !filePath.isEmpty()) configPath = Paths::getConfigFilePath("", false);
return configPath;
}

View file

@ -1,36 +0,0 @@
/*
* Copyright (c) 2010-2024 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 SETTINGS_H_
#define SETTINGS_H_
#include <QCommandLineParser>
#include <QObject>
#include <QVariantMap>
class Settings : public QObject {
Q_OBJECT
public:
Settings(QObject *parent = Q_NULLPTR);
virtual ~Settings();
QString getConfigPath(const QCommandLineParser &parser = QCommandLineParser());
};
#endif

View file

@ -0,0 +1,111 @@
/*
* Copyright (c) 2010-2024 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 "SettingsCore.hpp"
#include "core/App.hpp"
#include "core/path/Paths.hpp"
#include "model/tool/ToolModel.hpp"
#include "tool/Utils.hpp"
#include <QUrl>
#include <QVariant>
DEFINE_ABSTRACT_OBJECT(Settings)
// =============================================================================
QSharedPointer<Settings> Settings::create() {
auto sharedPointer = QSharedPointer<Settings>(new Settings(), &QObject::deleteLater);
sharedPointer->setSelf(sharedPointer);
sharedPointer->moveToThread(App::getInstance()->thread());
return sharedPointer;
}
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));
}
}
Settings::~Settings() {
}
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) {
mSettingsModelConnection->invokeToModel(
[this, id]() { mSettingsModel->setVideoDevice(Utils::appStringToCoreString(id)); });
});
}
QString Settings::getConfigPath(const QCommandLineParser &parser) {
QString filePath = parser.isSet("config") ? parser.value("config") : "";
QString configPath;
if (!QUrl(filePath).isRelative()) {
// configPath = FileDownloader::synchronousDownload(filePath,
// Utils::coreStringToAppString(Paths::getConfigDirPath(false)), true));
}
if (configPath == "") configPath = Paths::getConfigFilePath(filePath, false);
if (configPath == "" && !filePath.isEmpty()) configPath = Paths::getConfigFilePath("", false);
return configPath;
}
QStringList Settings::getInputAudioDevicesList() const {
return mInputAudioDevices;
}
QStringList Settings::getOutputAudioDevicesList() const {
return mOutputAudioDevices;
}
QStringList Settings::getVideoDevicesList() const {
return mVideoDevices;
}
bool Settings::getFirstLaunch() const {
auto val = mAppSettings.value("firstLaunch", 1).toInt();
return val;
}
void Settings::setFirstLaunch(bool first) {
auto firstLaunch = getFirstLaunch();
if (firstLaunch != first) {
mAppSettings.setValue("firstLaunch", (int)first);
mAppSettings.sync();
}
}

View file

@ -0,0 +1,74 @@
/*
* Copyright (c) 2010-2024 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 SETTINGS_H_
#define SETTINGS_H_
#include "model/setting/SettingsModel.hpp"
#include "tool/thread/SafeConnection.hpp"
#include <QCommandLineParser>
#include <QObject>
#include <QSettings>
#include <QVariantMap>
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)
public:
static QSharedPointer<Settings> create();
Settings(QObject *parent = Q_NULLPTR);
virtual ~Settings();
void setSelf(QSharedPointer<Settings> me);
QString getConfigPath(const QCommandLineParser &parser = QCommandLineParser());
QStringList getInputAudioDevicesList() const;
QStringList getOutputAudioDevicesList() const;
QStringList getVideoDevicesList() const;
Q_INVOKABLE void setFirstLaunch(bool first);
Q_INVOKABLE bool getFirstLaunch() const;
signals:
void inputAudioDeviceChanged(const QString &id);
void outputAudioDeviceChanged(const QString &id);
void videoDeviceChanged();
void lSetInputAudioDevice(const QString &device);
void lSetOutputAudioDevice(const QString &device);
void lSetVideoDevice(const QString &device);
private:
std::shared_ptr<SettingsModel> mSettingsModel;
QStringList mInputAudioDevices;
QStringList mOutputAudioDevices;
QStringList mVideoDevices;
QSettings mAppSettings;
QSharedPointer<SafeConnection<Settings, SettingsModel>> mSettingsModelConnection;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -35,6 +35,13 @@ CallModel::CallModel(const std::shared_ptr<linphone::Call> &call, QObject *paren
mDurationTimer.setSingleShot(false);
connect(&mDurationTimer, &QTimer::timeout, this, [this]() { this->durationChanged(mMonitor->getDuration()); });
mDurationTimer.start();
mMicroVolumeTimer.setInterval(50);
mMicroVolumeTimer.setSingleShot(false);
connect(&mMicroVolumeTimer, &QTimer::timeout, this,
[this]() { this->microphoneVolumeChanged(Utils::computeVu(mMonitor->getRecordVolume())); });
mMicroVolumeTimer.start();
connect(this, &CallModel::stateChanged, this, [this] {
auto state = mMonitor->getState();
if (state == linphone::Call::State::Paused) setPaused(true);
@ -144,6 +151,54 @@ void CallModel::setRecordFile(const std::string &path) {
mMonitor->update(params);
}
void CallModel::setSpeakerVolumeGain(float gain) {
mMonitor->setSpeakerVolumeGain(gain);
emit speakerVolumeGainChanged(gain);
}
float CallModel::getSpeakerVolumeGain() const {
auto gain = mMonitor->getSpeakerVolumeGain();
if (gain < 0) gain = CoreModel::getInstance()->getCore()->getPlaybackGainDb();
return gain;
}
void CallModel::setMicrophoneVolumeGain(float gain) {
mMonitor->setMicrophoneVolumeGain(gain);
emit microphoneVolumeGainChanged(gain);
}
float CallModel::getMicrophoneVolumeGain() const {
auto gain = mMonitor->getMicrophoneVolumeGain();
return gain;
}
float CallModel::getMicrophoneVolume() const {
auto volume = mMonitor->getRecordVolume();
return volume;
}
void CallModel::setInputAudioDevice(const std::shared_ptr<linphone::AudioDevice> &device) {
mMonitor->setInputAudioDevice(device);
std::string deviceName;
if (device) deviceName = device->getDeviceName();
emit inputAudioDeviceChanged(deviceName);
}
std::shared_ptr<const linphone::AudioDevice> CallModel::getInputAudioDevice() const {
return mMonitor->getInputAudioDevice();
}
void CallModel::setOutputAudioDevice(const std::shared_ptr<linphone::AudioDevice> &device) {
mMonitor->setOutputAudioDevice(device);
std::string deviceName;
if (device) deviceName = device->getDeviceName();
emit outputAudioDeviceChanged(deviceName);
}
std::shared_ptr<const linphone::AudioDevice> CallModel::getOutputAudioDevice() const {
return mMonitor->getOutputAudioDevice();
}
std::string CallModel::getRecordFile() const {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
return mMonitor->getParams()->getRecordFile();

View file

@ -46,11 +46,20 @@ public:
void startRecording();
void stopRecording();
void setRecordFile(const std::string &path);
void setSpeakerVolumeGain(float gain);
void setMicrophoneVolumeGain(float gain);
void setInputAudioDevice(const std::shared_ptr<linphone::AudioDevice> &id);
std::shared_ptr<const linphone::AudioDevice> getInputAudioDevice() const;
void setOutputAudioDevice(const std::shared_ptr<linphone::AudioDevice> &id);
std::shared_ptr<const linphone::AudioDevice> getOutputAudioDevice() const;
void setPaused(bool paused);
void transferTo(const std::shared_ptr<linphone::Address> &address);
void terminateAllCalls();
float getMicrophoneVolumeGain() const;
float getMicrophoneVolume() const;
float getSpeakerVolumeGain() const;
std::string getRecordFile() const;
std::shared_ptr<const linphone::Address> getRemoteAddress();
bool getAuthenticationTokenVerified() const;
@ -62,13 +71,19 @@ signals:
void speakerMutedChanged(bool isMuted);
void cameraEnabledChanged(bool enabled);
void durationChanged(int);
void microphoneVolumeChanged(float);
void pausedChanged(bool paused);
void remoteVideoEnabledChanged(bool remoteVideoEnabled);
void recordingChanged(bool recording);
void authenticationTokenVerifiedChanged(bool verified);
void speakerVolumeGainChanged(float volume);
void microphoneVolumeGainChanged(float volume);
void inputAudioDeviceChanged(const std::string &id);
void outputAudioDeviceChanged(const std::string &id);
private:
QTimer mDurationTimer;
QTimer mMicroVolumeTimer;
DECLARE_ABSTRACT_OBJECT
//--------------------------------------------------------------------------------

View file

@ -19,6 +19,8 @@
*/
#include "SettingsModel.hpp"
#include "model/core/CoreModel.hpp"
// =============================================================================
DEFINE_ABSTRACT_OBJECT(SettingsModel)
@ -41,3 +43,20 @@ std::string SettingsModel::getEntryFullName(const std::string &section, const st
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
return isReadOnly(section, name) ? name + "/readonly" : name;
}
std::list<std::string> SettingsModel::getVideoDevices() const {
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();
}
}

View file

@ -30,6 +30,7 @@
class SettingsModel : public QObject, public AbstractObject {
Q_OBJECT
public:
SettingsModel(QObject *parent = Q_NULLPTR);
virtual ~SettingsModel();
@ -39,10 +40,17 @@ 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;
signals:
void videoDeviceChanged();
private:
DECLARE_ABSTRACT_OBJECT
};

View file

@ -56,6 +56,18 @@ std::shared_ptr<linphone::FriendPhoneNumber> ToolModel::makeLinphoneNumber(const
return linphoneNumber;
}
std::shared_ptr<linphone::AudioDevice> ToolModel::findAudioDevice(const QString &id) {
std::string devId = Utils::appStringToCoreString(id);
auto devices = CoreModel::getInstance()->getCore()->getExtendedAudioDevices();
auto audioDevice =
find_if(devices.cbegin(), devices.cend(),
[&](const std::shared_ptr<linphone::AudioDevice> &audioItem) { return audioItem->getId() == devId; });
if (audioDevice != devices.cend()) {
return *audioDevice;
}
return nullptr;
}
QString ToolModel::getDisplayName(const std::shared_ptr<const linphone::Address> &address) {
QString displayName;
if (address) {

View file

@ -36,6 +36,7 @@ public:
static std::shared_ptr<linphone::Address> interpretUrl(const QString &address);
static std::shared_ptr<linphone::FriendPhoneNumber> makeLinphoneNumber(const QString &label, const QString &number);
static std::shared_ptr<linphone::AudioDevice> findAudioDevice(const QString &id);
static QString getDisplayName(const std::shared_ptr<const linphone::Address> &address);
static QString getDisplayName(QString address);

View file

@ -120,14 +120,6 @@ VariantObject *Utils::createCall(const QString &sipAddress,
return data;
}
void Utils::setFirstLaunch(bool first) {
App::getInstance()->setFirstLaunch(first);
}
bool Utils::getFirstLaunch() {
return App::getInstance()->getFirstLaunch();
}
void Utils::openCallsWindow(CallGui *call) {
if (call) App::getInstance()->getCallsWindow(QVariant::fromValue(call))->show();
}

View file

@ -60,8 +60,6 @@ public:
bool withVideo = false,
const QString &prepareTransfertAddress = "",
const QHash<QString, QString> &headers = {});
Q_INVOKABLE static void setFirstLaunch(bool first);
Q_INVOKABLE static bool getFirstLaunch();
Q_INVOKABLE static void openCallsWindow(CallGui *call);
Q_INVOKABLE static QQuickWindow *getMainWindow();
Q_INVOKABLE static QQuickWindow *getCallsWindow(CallGui *callGui);
@ -98,6 +96,16 @@ public:
static std::shared_ptr<T> makeQObject_ptr(Args &&...args) {
return std::shared_ptr<T>(new T(args...), [](T *obj) { obj->deleteLater(); });
}
static 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);
}
};
#endif // UTILS_H_

View file

@ -5,6 +5,7 @@ import QtQuick.Controls as Control
import Linphone
import EnumsToStringCpp 1.0
import UtilsCpp 1.0
import SettingsCpp 1.0
Window {
id: mainWindow
@ -277,7 +278,8 @@ Window {
bottomPadding: 8 * DefaultStyle.dp
leftPadding: 10 * DefaultStyle.dp
rightPadding: 10 * DefaultStyle.dp
visible: mainWindow.call.core.isSecured
width: 269 * DefaultStyle.dp
visible: mainWindow.call && mainWindow.call.core.isSecured
background: Rectangle {
anchors.fill: parent
border.color: DefaultStyle.info_500_main
@ -482,198 +484,341 @@ Window {
weight: 800 * DefaultStyle.dp
}
}
contentItem: Control.StackView {
Control.StackView {
id: rightPanelStack
}
Component {
id: contactsListPanel
CallContactsLists {
sideMargin: 10 * DefaultStyle.dp
topMargin: 15 * DefaultStyle.dp
groupCallVisible: false
searchBarColor: DefaultStyle.grey_0
searchBarBorderColor: DefaultStyle.grey_200
onCallButtonPressed: (address) => {
mainWindow.call.core.lTransferCall(address)
width: parent.width
height: parent.height
initialItem: callsListPanel
Component {
id: contactsListPanel
CallContactsLists {
sideMargin: 10 * DefaultStyle.dp
topMargin: 15 * DefaultStyle.dp
groupCallVisible: false
searchBarColor: DefaultStyle.grey_0
searchBarBorderColor: DefaultStyle.grey_200
onCallButtonPressed: (address) => {
mainWindow.call.core.lTransferCall(address)
}
Control.StackView.onActivated: rightPanelTitle.text = qsTr("Transfert d'appel")
}
Control.StackView.onActivated: rightPanelTitle.text = qsTr("Transfert d'appel")
}
}
Component {
id: dialerPanel
ColumnLayout {
Control.StackView.onActivated: rightPanelTitle.text = qsTr("Dialer")
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
SearchBar {
id: dialerTextInput
Layout.fillWidth: true
Layout.leftMargin: 10 * DefaultStyle.dp
Layout.rightMargin: 10 * DefaultStyle.dp
magnifierVisible: false
color: DefaultStyle.grey_0
borderColor: DefaultStyle.grey_200
placeholderText: ""
numericPad: numPad
numericPadButton.visible: false
}
Item {
Layout.fillWidth: true
Layout.preferredHeight: numPad.height
Layout.topMargin: 10 * DefaultStyle.dp
property var callObj
NumericPad {
id: numPad
width: parent.width
visible: parent.visible
closeButtonVisible: false
onLaunchCall: {
callObj = UtilsCpp.createCall(dialerTextInput.text)
Component {
id: dialerPanel
ColumnLayout {
Control.StackView.onActivated: rightPanelTitle.text = qsTr("Dialer")
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
SearchBar {
id: dialerTextInput
Layout.fillWidth: true
Layout.leftMargin: 10 * DefaultStyle.dp
Layout.rightMargin: 10 * DefaultStyle.dp
magnifierVisible: false
color: DefaultStyle.grey_0
borderColor: DefaultStyle.grey_200
placeholderText: ""
numericPad: numPad
numericPadButton.visible: false
}
Item {
Layout.fillWidth: true
Layout.preferredHeight: numPad.height
Layout.topMargin: 10 * DefaultStyle.dp
property var callObj
NumericPad {
id: numPad
width: parent.width
visible: parent.visible
closeButtonVisible: false
onLaunchCall: {
callObj = UtilsCpp.createCall(dialerTextInput.text + "@sip.linphone.org")
}
}
}
}
}
}
Component {
id: callsListPanel
Control.Control {
Control.StackView.onActivated: rightPanelTitle.text = qsTr("Liste d'appel")
// width: callList.width
// height: callList.height
// padding: 15 * DefaultStyle.dp
topPadding: 15 * DefaultStyle.dp
bottomPadding: 15 * DefaultStyle.dp
leftPadding: 15 * DefaultStyle.dp
rightPadding: 15 * DefaultStyle.dp
background: Rectangle {
anchors.fill: parent
anchors.leftMargin: 10 * DefaultStyle.dp
anchors.rightMargin: 10 * DefaultStyle.dp
anchors.topMargin: 10 * DefaultStyle.dp
anchors.bottomMargin: 10 * DefaultStyle.dp
color: DefaultStyle.main2_0
radius: 15 * DefaultStyle.dp
}
Component {
id: callsListPanel
ColumnLayout {
RoundedBackgroundControl {
Control.StackView.onActivated: rightPanelTitle.text = qsTr("Liste d'appel")
Layout.fillWidth: true
// height: Math.min(callList.height + topPadding + bottomPadding, rightPanelStack.height)
onHeightChanged: console.log("calls list height changed", height)
height: Math.min(callList.height + topPadding + bottomPadding, rightPanelStack.height)
contentItem: ListView {
id: callList
model: callsModel
height: contentHeight
spacing: 15 * DefaultStyle.dp
topPadding: 15 * DefaultStyle.dp
bottomPadding: 15 * DefaultStyle.dp
leftPadding: 15 * DefaultStyle.dp
rightPadding: 15 * DefaultStyle.dp
backgroundColor: DefaultStyle.main2_0
contentItem: ListView {
id: callList
model: callsModel
height: Math.min(contentHeight, rightPanelStack.height)
spacing: 15 * DefaultStyle.dp
onCountChanged: forceLayout()
onCountChanged: forceLayout()
delegate: Item {
id: callDelegate
width: callList.width
height: 45 * DefaultStyle.dp
delegate: Item {
id: callDelegate
width: callList.width
height: 45 * DefaultStyle.dp
RowLayout {
id: delegateContent
anchors.fill: parent
anchors.leftMargin: 10 * DefaultStyle.dp
anchors.rightMargin: 10 * DefaultStyle.dp
Avatar {
id: delegateAvatar
address: modelData.core.peerAddress
Layout.preferredWidth: 45 * DefaultStyle.dp
Layout.preferredHeight: 45 * DefaultStyle.dp
RowLayout {
id: delegateContent
anchors.fill: parent
anchors.leftMargin: 10 * DefaultStyle.dp
anchors.rightMargin: 10 * DefaultStyle.dp
Avatar {
id: delegateAvatar
address: modelData.core.peerAddress
Layout.preferredWidth: 45 * DefaultStyle.dp
Layout.preferredHeight: 45 * DefaultStyle.dp
}
Text {
id: delegateName
property var remoteAddress: UtilsCpp.getDisplayName(modelData.core.peerAddress)
text: remoteAddress ? remoteAddress.value : ""
Connections {
target: modelData.core
}
}
Item {
Layout.fillHeight: true
Layout.fillWidth: true
}
Text {
id: callStateText
text: modelData.core.state === LinphoneEnums.CallState.Paused
|| modelData.core.state === LinphoneEnums.CallState.PausedByRemote
? qsTr("Appel en pause") : qsTr("Appel en cours")
}
PopupButton {
id: listCallOptionsButton
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
Layout.alignment: Qt.AlignRight
popup.contentItem: ColumnLayout {
spacing: 0
Control.Button {
background: Item {}
contentItem: RowLayout {
Image {
source: modelData.core.state === LinphoneEnums.CallState.Paused
|| modelData.core.state === LinphoneEnums.CallState.PausedByRemote
? AppIcons.phone : AppIcons.pause
sourceSize.width: 32 * DefaultStyle.dp
sourceSize.height: 32 * DefaultStyle.dp
Layout.preferredWidth: 32 * DefaultStyle.dp
Layout.preferredHeight: 32 * DefaultStyle.dp
fillMode: Image.PreserveAspectFit
}
Text {
text: modelData.core.state === LinphoneEnums.CallState.Paused
|| modelData.core.state === LinphoneEnums.CallState.PausedByRemote
? qsTr("Reprendre l'appel") : qsTr("Mettre en pause")
color: DefaultStyle.main2_500main
Layout.preferredWidth: metrics.width
}
TextMetrics {
id: metrics
text: qsTr("Reprendre l'appel")
}
Item {
Layout.fillWidth: true
}
}
onClicked: modelData.core.lSetPaused(!modelData.core.paused)
}
Control.Button {
background: Item {}
contentItem: RowLayout {
EffectImage {
source: AppIcons.endCall
colorizationColor: DefaultStyle.danger_500main
width: 32 * DefaultStyle.dp
height: 32 * DefaultStyle.dp
}
Text {
color: DefaultStyle.danger_500main
text: qsTr("Terminer l'appel")
}
Item {
Layout.fillWidth: true
}
}
onClicked: mainWindow.endCall(modelData)
}
}
}
}
// MouseArea{
// anchors.fill: delegateLayout
// onClicked: {
// callsModel.currentCall = modelData
// }
// }
}
Text {
id: delegateName
property var remoteAddress: UtilsCpp.getDisplayName(modelData.core.peerAddress)
text: remoteAddress ? remoteAddress.value : ""
Connections {
target: modelData.core
}
}
Item {
Layout.fillHeight: true
}
}
}
Component {
id: settingsPanel
ColumnLayout {
RoundedBackgroundControl {
Layout.alignment: Qt.AlignHCenter
Control.StackView.onActivated: {
rightPanelTitle.text = qsTr("Paramètres")
}
backgroundColor: DefaultStyle.main2_0
height: contentItem.implicitHeight + topPadding + bottomPadding
Layout.fillWidth: true
topPadding: 25 * DefaultStyle.dp
bottomPadding: 25 * DefaultStyle.dp
leftPadding: 25 * DefaultStyle.dp
rightPadding: 25 * DefaultStyle.dp
contentItem: ColumnLayout {
spacing: 10 * DefaultStyle.dp
RowLayout {
Layout.fillWidth: true
EffectImage {
source: 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
}
}
Item {
Layout.fillHeight: true
ComboBox {
Layout.fillWidth: true
Layout.preferredWidth: parent.width
model: SettingsCpp.outputAudioDevicesList
onCurrentTextChanged: {
mainWindow.call.core.lSetOutputAudioDevice(currentText)
}
}
Text {
id: callStateText
text: mainWindow.call.core.state === LinphoneEnums.CallState.Paused
|| mainWindow.call.core.state === LinphoneEnums.CallState.PausedByRemote
? qsTr("Appel en pause") : qsTr("Appel en cours")
Slider {
id: speakerVolume
Layout.fillWidth: true
from: 0.0
to: 1.0
value: mainWindow.call && mainWindow.call.core.speakerVolumeGain
onMoved: {
mainWindow.call.core.lSetSpeakerVolumeGain(value)
}
}
PopupButton {
id: listCallOptionsButton
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
Layout.alignment: Qt.AlignRight
RowLayout {
Layout.fillWidth: true
EffectImage {
source: 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
}
}
ComboBox {
Layout.fillWidth: true
Layout.preferredWidth: parent.width
model: SettingsCpp.inputAudioDevicesList
onCurrentTextChanged: {
mainWindow.call.core.lSetInputAudioDevice(currentText)
}
}
Slider {
id: microVolume
Layout.fillWidth: true
from: 0.0
to: 1.0
value: mainWindow.call && mainWindow.call.core.microphoneVolumeGain
onMoved: {
mainWindow.call.core.lSetMicrophoneVolumeGain(value)
}
}
Timer {
interval: 50
repeat: true
running: mainWindow.call || false
onTriggered: audioTestSlider.value = (mainWindow.call && mainWindow.call.core.microVolume)
}
Slider {
id: audioTestSlider
Layout.fillWidth: true
enabled: false
Layout.preferredHeight: 10 * DefaultStyle.dp
popup.contentItem: ColumnLayout {
spacing: 0
Button {
leftPadding: 0
topPadding: 5 * DefaultStyle.dp
bottomPadding: 5 * DefaultStyle.dp
background: Item {}
contentItem: RowLayout {
Image {
source: modelData.core.paused
? AppIcons.phone : AppIcons.pause
sourceSize.width: 32 * DefaultStyle.dp
sourceSize.height: 32 * DefaultStyle.dp
Layout.preferredWidth: 32 * DefaultStyle.dp
Layout.preferredHeight: 32 * DefaultStyle.dp
fillMode: Image.PreserveAspectFit
}
Text {
text: modelData.core.paused
? qsTr("Reprendre l'appel") : qsTr("Mettre en pause")
color: DefaultStyle.main2_500main
Layout.preferredWidth: metrics.width
}
TextMetrics {
id: metrics
text: qsTr("Reprendre l'appel")
}
Item {
Layout.fillWidth: true
}
}
onClicked: modelData.core.lSetPaused(!modelData.core.paused)
}
Button {
leftPadding: 0
topPadding: 5 * DefaultStyle.dp
bottomPadding: 5 * DefaultStyle.dp
background: Item {}
contentItem: RowLayout {
EffectImage {
source: AppIcons.endCall
colorizationColor: DefaultStyle.danger_500main
width: 32 * DefaultStyle.dp
height: 32 * DefaultStyle.dp
}
Text {
color: DefaultStyle.danger_500main
text: qsTr("Terminer l'appel")
}
Item {
Layout.fillWidth: true
}
}
onClicked: {
mainWindow.endCall(modelData)
mainWindow.callTerminatedByUser = true
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: "#D9D9D9"
Rectangle {
width: audioTestSlider.visualPosition * parent.width
height: parent.height
gradient: Gradient {
orientation: Gradient.Horizontal
GradientStop { position: 0.0; color: "#6FF88D" }
GradientStop { position: 1.0; color: "#00D916" }
}
radius: 2 * DefaultStyle.dp
}
}
handle: Item {visible: false}
}
RowLayout {
Layout.fillWidth: true
EffectImage {
source: 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
}
}
ComboBox {
Layout.fillWidth: true
Layout.preferredWidth: parent.width
model: SettingsCpp.videoDevicesList
onCurrentTextChanged: {
SettingsCpp.lSetVideoDevice(currentText)
}
}
}
// MouseArea{
// anchors.fill: delegateLayout
// onClicked: {
// callsModel.currentCall = modelData
// }
// }
}
Item {
Layout.fillHeight: true
}
}
}
@ -845,9 +990,7 @@ Window {
Button {
id: callListButton
Layout.fillWidth: true
background: Item {
visible: false
}
background: Item {}
contentItem: RowLayout {
Image {
Layout.preferredWidth: 24 * DefaultStyle.dp
@ -868,9 +1011,7 @@ Window {
Button {
id: dialerButton
Layout.fillWidth: true
background: Item {
visible: false
}
background: Item {}
contentItem: RowLayout {
Image {
Layout.preferredWidth: 24 * DefaultStyle.dp
@ -893,9 +1034,7 @@ Window {
Layout.fillWidth: true
enabled: mainWindow.call.core.recordable
checkable: true
background: Item {
visible: false
}
background: Item {}
contentItem: RowLayout {
EffectImage {
Layout.preferredWidth: 24 * DefaultStyle.dp
@ -914,29 +1053,47 @@ Window {
mainWindow.call.core.recording ? mainWindow.call.core.lStopRecording() : mainWindow.call.core.lStartRecording()
}
}
Button {
Control.Button {
id: speakerButton
Layout.fillWidth: true
checkable: true
background: Item {
visible: false
}
background: Item {}
contentItem: RowLayout {
EffectImage {
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
fillMode: Image.PreserveAspectFit
source: mainWindow.call.core.speakerMuted ? AppIcons.speakerSlash : AppIcons.speaker
colorizationColor: mainWindow.call.core.speakerMuted ? DefaultStyle.danger_500main : undefined
source: AppIcons.recordFill
colorizationColor: mainWindow.call.core.recording ? DefaultStyle.danger_500main : undefined
}
Text {
text: mainWindow.call.core.speakerMuted ? qsTr("Activer le son") : qsTr("Désactiver le son")
color: mainWindow.call.core.speakerMuted ? DefaultStyle.danger_500main : DefaultStyle.main2_600
color: mainWindow.call.core.recording ? DefaultStyle.danger_500main : DefaultStyle.main2_600
text: mainWindow.call.core.recording ? qsTr("Terminer l'enregistrement") : qsTr("Enregistrer l'appel")
}
}
onClicked: {
mainWindow.call.core.lSetSpeakerMuted(!mainWindow.call.core.speakerMuted)
mainWindow.call.core.recording ? mainWindow.call.core.lStopRecording() : mainWindow.call.core.lStartRecording()
}
}
Control.Button {
id: settingsButton
Layout.fillWidth: true
background: Item{}
contentItem: RowLayout {
Image {
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
source: AppIcons.settings
}
Text {
text: qsTr("Paramètres")
}
}
onClicked: {
rightPanel.visible = true
rightPanel.replace(settingsPanel)
moreOptionsButton.close()
}
}
}

View file

@ -3,6 +3,7 @@ import QtQuick.Layouts 1.3
import QtQuick.Controls
import Linphone
import UtilsCpp 1.0
import SettingsCpp 1.0
Window {
id: mainWindow
@ -35,14 +36,14 @@ Window {
StackView {
id: mainWindowStackView
anchors.fill: parent
initialItem: accountProxy.haveAccount ? mainPage : UtilsCpp.getFirstLaunch() ? welcomePage : loginPage
initialItem: accountProxy.haveAccount ? mainPage : SettingsCpp.getFirstLaunch() ? welcomePage : loginPage
}
Component {
id: welcomePage
WelcomePage {
onStartButtonPressed: {
mainWindowStackView.replace(loginPage)// Replacing the first item will destroy the old.
UtilsCpp.setFirstLaunch(false)
SettingsCpp.setFirstLaunch(false)
}
}
}

View file

@ -44,6 +44,7 @@ list(APPEND _LINPHONEAPP_QML_FILES
view/Item/RectangleTest.qml
view/Item/RoundedBackgroundControl.qml
view/Item/SearchBar.qml
view/Item/Slider.qml
view/Item/TabBar.qml
view/Item/Text.qml
view/Item/TextInput.qml

View file

@ -70,7 +70,7 @@ ColumnLayout {
radius: 30 * DefaultStyle.dp
color: DefaultStyle.main1_500_main
z: 1
x: carouselButton.itemAt(mainItem.currentIndex).x
x: mainItem.currentIndex >= 0 && carouselButton.itemAt(mainItem.currentIndex) ? carouselButton.itemAt(mainItem.currentIndex).x : 0
Behavior on x { NumberAnimation {duration: 100}}
}
RowLayout {

View file

@ -10,6 +10,7 @@ ColumnLayout {
// Usage : each item of the model list must be {text: ..., img: ...}
// If string list, only text part of the delegate will be filled
property var model: []
property alias combobox: combobox
readonly property string currentText: selectedItemText.text
property bool enableBackgroundColors: true
readonly property bool hasActiveFocus: combobox.activeFocus
@ -54,6 +55,7 @@ ColumnLayout {
id: selectedItemText
color: combobox.enabled ? DefaultStyle.main2_600 : DefaultStyle.grey_400
elide: Text.ElideRight
maximumLineCount: 1
font {
pixelSize: 14 * DefaultStyle.dp
weight: 400 * DefaultStyle.dp
@ -67,13 +69,13 @@ ColumnLayout {
Component.onCompleted: {
var index = combobox.currentIndex < 0 ? 0 : combobox.currentIndex
if (mainItem.model[index].img) {
selectedItemImg.source = mainItem.model[0].img
if (mainItem.model[index] && mainItem.model[index].img) {
selectedItemImg.source = mainItem.model[index].img
}
if (mainItem.model[index].text)
selectedItemText.text = mainItem.model[0].text
if (mainItem.model[index] && mainItem.model[index].text)
selectedItemText.text = mainItem.model[index].text
else if (mainItem.model[index])
selectedItemText.text = mainItem.model[0]
selectedItemText.text = mainItem.model[index]
}
}
@ -132,6 +134,7 @@ ColumnLayout {
? modelData
: ""
elide: Text.ElideRight
maximumLineCount: 1
font {
pixelSize: 14 * DefaultStyle.dp
weight: 400 * DefaultStyle.dp

View file

@ -20,7 +20,7 @@ Loader {
Image {
id: image
visible: !effect2.enabled
source: mainItem.source
source: mainItem.source ? mainItem.source : ""
fillMode: mainItem.fillMode
sourceSize.width: width
sourceSize.height: height

View file

@ -29,7 +29,6 @@ MouseArea{
preventStealing: true
propagateComposedEvents: true
hoverEnabled: true
onMScaleChanged: updateScale()
function updateScale(){// Avoid scaling if leading outside movableArea.
drag.target.height = Math.max(0, Math.min(movableArea.height, heightOrigin * mScale))
@ -42,6 +41,7 @@ MouseArea{
drag.target.x = Math.max(parentTLBounds.x + margin, Math.min(parentBRBounds.x - drag.target.width - margin, drag.target.x + x - margin))
drag.target.y = Math.max(parentTLBounds.y + margin, Math.min(parentBRBounds.y - drag.target.height - margin, drag.target.y + y - margin))
}
onMScaleChanged: updateScale()
onPositionChanged: (mouse) => {
if(dragging){
updatePosition(mouse.x - xClicked, mouse.y - yClicked)

View file

@ -116,7 +116,7 @@ ColumnLayout {
width: listView.width
height: listView.height
color: DefaultStyle.main2_300
radius: 15 * DefaultStyle.dp
// radius: 15 * DefaultStyle.dp
y: listView.currentItem? listView.currentItem.y : 0
}

View file

@ -36,6 +36,7 @@ Button {
closePolicy: Popup.CloseOnPressOutsideParent |Popup.CloseOnPressOutside
onAboutToShow: {
if (mainApplicationWindow == undefined) return;
var coord = mapToGlobal(mainItem.x, mainItem.y)
if (coord.y + popup.height >= mainApplicationWindow.height) {
y = -popup.height

View file

@ -3,17 +3,17 @@ import QtQuick.Controls as Control
import Linphone
Control.Control {
width: 360 * DefaultStyle.dp
property color backgroundColor
topPadding: 10 * DefaultStyle.dp
bottomPadding: 10 * DefaultStyle.dp
leftPadding: 10 * DefaultStyle.dp
rightPadding: 10 * DefaultStyle.dp
id: mainItem
// width: 360 * DefaultStyle.dp
property color backgroundColor: DefaultStyle.grey_0
padding: 10 * DefaultStyle.dp
background: Rectangle {
// anchors.fill: parent
width: parent.width
height: parent.height
anchors.fill: parent
radius: 15 * DefaultStyle.dp
color: mainItem.backgroundColor ? mainItem.backgroundColor : DefaultStyle.grey_0
color: mainItem.backgroundColor
anchors.leftMargin: 10 * DefaultStyle.dp
anchors.rightMargin: 10 * DefaultStyle.dp
anchors.topMargin: 10 * DefaultStyle.dp
anchors.bottomMargin: 10 * DefaultStyle.dp
}
}

View file

@ -0,0 +1,53 @@
import QtQuick
import QtQuick.Effects
import QtQuick.Layouts
import QtQuick.Controls as Control
import Linphone
Control.Slider {
id: mainItem
background: Rectangle {
x: mainItem.leftPadding
y: mainItem.topPadding + mainItem.availableHeight / 2 - height / 2
implicitWidth: 200 * DefaultStyle.dp
implicitHeight: 4 * DefaultStyle.dp
width: mainItem.availableWidth
height: implicitHeight
radius: 30 * DefaultStyle.dp
// TODO : change the colors when mockup indicates their names
color: "#D9D9D9"
Rectangle {
width: mainItem.visualPosition * parent.width
height: parent.height
gradient: Gradient {
orientation: Gradient.Horizontal
GradientStop { position: 0.0; color: "#FF9E79" }
GradientStop { position: 1.0; color: "#FE5E00" }
}
radius: 40 * DefaultStyle.dp
}
}
handle: Item {
x: mainItem.leftPadding + mainItem.visualPosition * (mainItem.availableWidth - width)
y: mainItem.topPadding + mainItem.availableHeight / 2 - height / 2
implicitWidth: 16 * DefaultStyle.dp
implicitHeight: 16 * DefaultStyle.dp
Rectangle {
id: handleRect
anchors.fill: parent
radius: 30 * DefaultStyle.dp
color: DefaultStyle.grey_0
}
MultiEffect {
source: handleRect
anchors.fill: handleRect
shadowEnabled: true
shadowColor: DefaultStyle.grey_1000
shadowBlur: 1
shadowOpacity: 0.1
}
}
}