Account settings & parameters

This commit is contained in:
Christophe Deschamps 2024-07-25 08:34:11 +00:00
parent cddaa90dcb
commit 4143d15f34
39 changed files with 1800 additions and 157 deletions

View file

@ -36,6 +36,7 @@
#include <QTimer>
#include "core/account/AccountCore.hpp"
#include "core/account/AccountDeviceGui.hpp"
#include "core/account/AccountProxy.hpp"
#include "core/call-history/CallHistoryProxy.hpp"
#include "core/call/CallCore.hpp"
@ -334,6 +335,7 @@ void App::initCppInterfaces() {
qmlRegisterUncreatableType<PhoneNumber>(Constants::MainQmlUri, 1, 0, "PhoneNumber", QLatin1String("Uncreatable"));
qmlRegisterType<AccountProxy>(Constants::MainQmlUri, 1, 0, "AccountProxy");
qmlRegisterType<AccountGui>(Constants::MainQmlUri, 1, 0, "AccountGui");
qmlRegisterType<AccountDeviceGui>(Constants::MainQmlUri, 1, 0, "AccountDeviceGui");
qmlRegisterUncreatableType<AccountCore>(Constants::MainQmlUri, 1, 0, "AccountCore", QLatin1String("Uncreatable"));
qmlRegisterUncreatableType<CallCore>(Constants::MainQmlUri, 1, 0, "CallCore", QLatin1String("Uncreatable"));
qmlRegisterType<CallProxy>(Constants::MainQmlUri, 1, 0, "CallProxy");

View file

@ -3,6 +3,8 @@ list(APPEND _LINPHONEAPP_SOURCES
core/account/AccountGui.cpp
core/account/AccountList.cpp
core/account/AccountProxy.cpp
core/account/AccountDeviceCore.cpp
core/account/AccountDeviceGui.cpp
core/App.cpp
core/call/CallCore.cpp
core/call/CallGui.cpp

View file

@ -22,6 +22,7 @@
#include "core/App.hpp"
#include "tool/Utils.hpp"
#include "tool/thread/SafeConnection.hpp"
#include <QHostInfo>
DEFINE_ABSTRACT_OBJECT(AccountCore)
@ -47,10 +48,42 @@ AccountCore::AccountCore(const std::shared_ptr<linphone::Account> &account) : QO
mIsDefaultAccount = CoreModel::getInstance()->getCore()->getDefaultAccount() == account;
// mUnreadNotifications = account->getUnreadChatMessageCount() + account->getMissedCallsCount(); // TODO
mUnreadNotifications = account->getMissedCallsCount();
mDisplayName = Utils::coreStringToAppString(identityAddress->getDisplayName());
mRegisterEnabled = params->registerEnabled();
mMwiServerAddress =
params->getMwiServerAddress() ? Utils::coreStringToAppString(params->getMwiServerAddress()->asString()) : "";
mTransports << "TCP" << "UDP" << "TLS" << "DTLS";
mTransport = LinphoneEnums::toString(LinphoneEnums::fromLinphone(params->getTransport()));
mServerAddress =
params->getServerAddress() ? Utils::coreStringToAppString(params->getServerAddress()->asString()) : "";
mOutboundProxyEnabled = params->outboundProxyEnabled();
auto policy = params->getNatPolicy() ? params->getNatPolicy() : account->getCore()->createNatPolicy();
mStunServer = Utils::coreStringToAppString(policy->getStunServer());
mIceEnabled = policy->iceEnabled();
mAvpfEnabled = account->avpfEnabled();
mBundleModeEnabled = params->rtpBundleEnabled();
mExpire = params->getExpires();
mConferenceFactoryAddress = params->getConferenceFactoryAddress()
? Utils::coreStringToAppString(params->getConferenceFactoryAddress()->asString())
: "";
mAudioVideoConferenceFactoryAddress =
params->getAudioVideoConferenceFactoryAddress()
? Utils::coreStringToAppString(params->getAudioVideoConferenceFactoryAddress()->asString())
: "";
mLimeServerUrl = Utils::coreStringToAppString(params->getLimeServerUrl());
// Add listener
mAccountModel = Utils::makeQObject_ptr<AccountModel>(account); // OK
mAccountModel->setSelf(mAccountModel);
mNotificationsAllowed = mAccountModel->getNotificationsAllowed();
mDialPlan = " ";
mDialPlans << mDialPlan;
for (auto dialPlan : linphone::Factory::get()->getDialPlans()) {
mDialPlans << mAccountModel->dialPlanAsString(dialPlan);
if (dialPlan->getCountryCallingCode() == account->getParams()->getInternationalPrefix()) {
mDialPlan = mAccountModel->dialPlanAsString(dialPlan);
}
}
}
AccountCore::~AccountCore() {
@ -83,6 +116,59 @@ void AccountCore::setSelf(QSharedPointer<AccountCore> me) {
this->setUnreadMessageNotifications(unreadMessagesCount);
});
});
mAccountModelConnection->makeConnectToModel(&AccountModel::displayNameChanged, [this](QString displayName) {
mAccountModelConnection->invokeToCore([this, displayName]() { this->onDisplayNameChanged(displayName); });
});
mAccountModelConnection->makeConnectToModel(&AccountModel::dialPlanChanged, [this](int index) {
auto dialPlan = mDialPlans[index + 1];
mAccountModelConnection->invokeToCore([this, dialPlan]() { this->onDialPlanChanged(dialPlan); });
});
mAccountModelConnection->makeConnectToModel(&AccountModel::registerEnabledChanged, [this](bool enabled) {
mAccountModelConnection->invokeToCore([this, enabled]() { this->onRegisterEnabledChanged(enabled); });
});
mAccountModelConnection->makeConnectToModel(&AccountModel::notificationsAllowedChanged, [this](bool value) {
mAccountModelConnection->invokeToCore([this, value]() { this->onNotificationsAllowedChanged(value); });
});
mAccountModelConnection->makeConnectToModel(&AccountModel::mwiServerAddressChanged, [this](QString value) {
mAccountModelConnection->invokeToCore([this, value]() { this->onMwiServerAddressChanged(value); });
});
mAccountModelConnection->makeConnectToModel(&AccountModel::transportChanged, [this](linphone::TransportType value) {
mAccountModelConnection->invokeToCore(
[this, value]() { this->onTransportChanged(LinphoneEnums::toString(LinphoneEnums::fromLinphone(value))); });
});
mAccountModelConnection->makeConnectToModel(&AccountModel::serverAddressChanged, [this](QString value) {
mAccountModelConnection->invokeToCore([this, value]() { this->onServerAddressChanged(value); });
});
mAccountModelConnection->makeConnectToModel(&AccountModel::outboundProxyEnabledChanged, [this](bool value) {
mAccountModelConnection->invokeToCore([this, value]() { this->onOutboundProxyEnabledChanged(value); });
});
mAccountModelConnection->makeConnectToModel(&AccountModel::stunServerChanged, [this](QString value) {
mAccountModelConnection->invokeToCore([this, value]() { this->onStunServerChanged(value); });
});
mAccountModelConnection->makeConnectToModel(&AccountModel::iceEnabledChanged, [this](bool value) {
mAccountModelConnection->invokeToCore([this, value]() { this->onIceEnabledChanged(value); });
});
mAccountModelConnection->makeConnectToModel(&AccountModel::avpfEnabledChanged, [this](bool value) {
mAccountModelConnection->invokeToCore([this, value]() { this->onAvpfEnabledChanged(value); });
});
mAccountModelConnection->makeConnectToModel(&AccountModel::bundleModeEnabledChanged, [this](bool value) {
mAccountModelConnection->invokeToCore([this, value]() { this->onBundleModeEnabledChanged(value); });
});
mAccountModelConnection->makeConnectToModel(&AccountModel::expireChanged, [this](int value) {
mAccountModelConnection->invokeToCore([this, value]() { this->onExpireChanged(value); });
});
mAccountModelConnection->makeConnectToModel(&AccountModel::conferenceFactoryAddressChanged, [this](QString value) {
mAccountModelConnection->invokeToCore([this, value]() { this->onConferenceFactoryAddressChanged(value); });
});
mAccountModelConnection->makeConnectToModel(&AccountModel::audioVideoConferenceFactoryAddressChanged,
[this](QString value) {
mAccountModelConnection->invokeToCore([this, value]() {
this->onAudioVideoConferenceFactoryAddressChanged(value);
});
});
mAccountModelConnection->makeConnectToModel(&AccountModel::limeServerUrlChanged, [this](QString value) {
mAccountModelConnection->invokeToCore([this, value]() { this->onLimeServerUrlChanged(value); });
});
// From GUI
mAccountModelConnection->makeConnectToCore(&AccountCore::lSetPictureUri, [this](QString uri) {
@ -108,6 +194,62 @@ void AccountCore::setSelf(QSharedPointer<AccountCore> me) {
mAccountModelConnection->makeConnectToCore(&AccountCore::unreadNotificationsChanged, [this]() {
mAccountModelConnection->invokeToModel([this]() { CoreModel::getInstance()->unreadNotificationsChanged(); });
});
mAccountModelConnection->makeConnectToCore(&AccountCore::lSetDisplayName, [this](QString displayName) {
mAccountModelConnection->invokeToModel([this, displayName]() { mAccountModel->setDisplayName(displayName); });
});
mAccountModelConnection->makeConnectToCore(&AccountCore::lSetDialPlan, [this](QString dialPlan) {
auto dialPlanIndex = getDialPlanIndex(dialPlan);
mAccountModelConnection->invokeToModel(
[this, dialPlanIndex]() { mAccountModel->setDialPlan(dialPlanIndex - 1); });
});
mAccountModelConnection->makeConnectToCore(&AccountCore::lSetRegisterEnabled, [this](bool enabled) {
mAccountModelConnection->invokeToModel([this, enabled]() { mAccountModel->setRegisterEnabled(enabled); });
});
mAccountModelConnection->makeConnectToCore(&AccountCore::lSetNotificationsAllowed, [this](bool value) {
mAccountModelConnection->invokeToModel([this, value]() { mAccountModel->setNotificationsAllowed(value); });
});
mAccountModelConnection->makeConnectToCore(&AccountCore::lSetMwiServerAddress, [this](QString value) {
mAccountModelConnection->invokeToModel([this, value]() { mAccountModel->setMwiServerAddress(value); });
});
mAccountModelConnection->makeConnectToCore(&AccountCore::lSetTransport, [this](QString value) {
LinphoneEnums::TransportType transport;
LinphoneEnums::fromString(value, &transport);
mAccountModelConnection->invokeToModel(
[this, value, transport]() { mAccountModel->setTransport(LinphoneEnums::toLinphone(transport)); });
});
mAccountModelConnection->makeConnectToCore(&AccountCore::lSetServerAddress, [this](QString value) {
mAccountModelConnection->invokeToModel([this, value]() { mAccountModel->setServerAddress(value); });
});
mAccountModelConnection->makeConnectToCore(&AccountCore::lSetOutboundProxyEnabled, [this](bool value) {
mAccountModelConnection->invokeToModel([this, value]() { mAccountModel->setOutboundProxyEnabled(value); });
});
mAccountModelConnection->makeConnectToCore(&AccountCore::lSetStunServer, [this](QString value) {
mAccountModelConnection->invokeToModel([this, value]() { mAccountModel->setStunServer(value); });
});
mAccountModelConnection->makeConnectToCore(&AccountCore::lSetIceEnabled, [this](bool value) {
mAccountModelConnection->invokeToModel([this, value]() { mAccountModel->setIceEnabled(value); });
});
mAccountModelConnection->makeConnectToCore(&AccountCore::lSetAvpfEnabled, [this](bool value) {
mAccountModelConnection->invokeToModel([this, value]() { mAccountModel->setAvpfEnabled(value); });
});
mAccountModelConnection->makeConnectToCore(&AccountCore::lSetBundleModeEnabled, [this](bool value) {
mAccountModelConnection->invokeToModel([this, value]() { mAccountModel->setBundleModeEnabled(value); });
});
mAccountModelConnection->makeConnectToCore(&AccountCore::lSetExpire, [this](int value) {
mAccountModelConnection->invokeToModel([this, value]() { mAccountModel->setExpire(value); });
});
mAccountModelConnection->makeConnectToCore(&AccountCore::lSetConferenceFactoryAddress, [this](QString value) {
mAccountModelConnection->invokeToModel([this, value]() { mAccountModel->setConferenceFactoryAddress(value); });
});
mAccountModelConnection->makeConnectToCore(&AccountCore::lSetAudioVideoConferenceFactoryAddress,
[this](QString value) {
mAccountModelConnection->invokeToModel([this, value]() {
mAccountModel->setAudioVideoConferenceFactoryAddress(value);
});
});
mAccountModelConnection->makeConnectToCore(&AccountCore::lSetLimeServerUrl, [this](QString value) {
mAccountModelConnection->invokeToModel([this, value]() { mAccountModel->setLimeServerUrl(value); });
});
}
QString AccountCore::getContactAddress() const {
@ -176,10 +318,231 @@ void AccountCore::onDefaultAccountChanged(bool isDefault) {
}
void AccountCore::onPictureUriChanged(QString uri) {
mPictureUri = uri;
emit pictureUriChanged();
if (uri != mPictureUri) {
mPictureUri = uri;
emit pictureUriChanged();
}
}
void AccountCore::removeAccount() {
mAccountModelConnection->invokeToModel([this]() { mAccountModel->removeAccount(); });
}
}
QString AccountCore::getDisplayName() const {
return mDisplayName;
}
void AccountCore::onDisplayNameChanged(QString displayName) {
if (displayName != mDisplayName) {
mDisplayName = displayName;
emit displayNameChanged();
}
}
QStringList AccountCore::getDialPlans() {
return mDialPlans;
}
QString AccountCore::getDialPlan() const {
return mDialPlan;
}
void AccountCore::onDialPlanChanged(QString dialPlan) {
if (dialPlan != mDialPlan) {
mDialPlan = dialPlan;
emit dialPlanChanged();
}
}
int AccountCore::getDialPlanIndex(QString dialPlanString) {
return mDialPlans.indexOf(dialPlanString);
}
QString AccountCore::getHumanReadableRegistrationState() const {
switch (mRegistrationState) {
case LinphoneEnums::RegistrationState::Ok:
return tr("Connecté");
case LinphoneEnums::RegistrationState::Refreshing:
return tr("En cours de rafraîchissement…");
case LinphoneEnums::RegistrationState::Progress:
return tr("En cours de connexion…");
case LinphoneEnums::RegistrationState::Failed:
return tr("Erreur");
case LinphoneEnums::RegistrationState::None:
case LinphoneEnums::RegistrationState::Cleared:
return tr("Désactivé");
default:
return " ";
}
}
QString AccountCore::getHumanReadableRegistrationStateExplained() const {
switch (mRegistrationState) {
case LinphoneEnums::RegistrationState::Ok:
return tr("Vous êtes en ligne et joignable.");
case LinphoneEnums::RegistrationState::Failed:
return tr("Erreur de connexion, vérifiez vos paramètres.");
case LinphoneEnums::RegistrationState::None:
case LinphoneEnums::RegistrationState::Cleared:
return tr("Compte désactivé, vous ne recevrez ni appel ni message.");
default:
return " ";
}
}
bool AccountCore::getRegisterEnabled() const {
return mRegisterEnabled;
}
void AccountCore::onRegisterEnabledChanged(bool enabled) {
if (enabled != mRegisterEnabled) {
mRegisterEnabled = enabled;
emit registerEnabledChanged();
}
}
bool AccountCore::getNotificationsAllowed() {
return mNotificationsAllowed;
}
QString AccountCore::getMwiServerAddress() {
return mMwiServerAddress;
}
QStringList AccountCore::getTransports() {
return mTransports;
}
QString AccountCore::getTransport() {
return mTransport;
}
QString AccountCore::getServerAddress() {
return mServerAddress;
}
bool AccountCore::getOutboundProxyEnabled() {
return mOutboundProxyEnabled;
}
QString AccountCore::getStunServer() {
return mStunServer;
}
bool AccountCore::getIceEnabled() {
return mIceEnabled;
}
bool AccountCore::getAvpfEnabled() {
return mAvpfEnabled;
}
bool AccountCore::getBundleModeEnabled() {
return mBundleModeEnabled;
}
int AccountCore::getExpire() {
return mExpire;
}
QString AccountCore::getConferenceFactoryAddress() {
return mConferenceFactoryAddress;
}
QString AccountCore::getAudioVideoConferenceFactoryAddress() {
return mAudioVideoConferenceFactoryAddress;
}
QString AccountCore::getLimeServerUrl() {
return mLimeServerUrl;
}
void AccountCore::onNotificationsAllowedChanged(bool value) {
if (value != mNotificationsAllowed) {
mNotificationsAllowed = value;
emit notificationsAllowedChanged();
}
}
void AccountCore::onMwiServerAddressChanged(QString value) {
if (value != mMwiServerAddress) {
mMwiServerAddress = value;
emit mwiServerAddressChanged();
}
}
void AccountCore::onTransportChanged(QString value) {
if (value != mTransport) {
mTransport = value;
emit transportChanged();
}
}
void AccountCore::onServerAddressChanged(QString value) {
if (value != mServerAddress) {
mServerAddress = value;
emit serverAddressChanged();
}
}
void AccountCore::onOutboundProxyEnabledChanged(bool value) {
if (value != mOutboundProxyEnabled) {
mOutboundProxyEnabled = value;
emit outboundProxyEnabledChanged();
}
}
void AccountCore::onStunServerChanged(QString value) {
if (value != mStunServer) {
mStunServer = value;
emit stunServerChanged();
}
}
void AccountCore::onIceEnabledChanged(bool value) {
if (value != mIceEnabled) {
mIceEnabled = value;
emit iceEnabledChanged();
}
}
void AccountCore::onAvpfEnabledChanged(bool value) {
if (value != mAvpfEnabled) {
mAvpfEnabled = value;
emit avpfEnabledChanged();
}
}
void AccountCore::onBundleModeEnabledChanged(bool value) {
if (value != mBundleModeEnabled) {
mBundleModeEnabled = value;
emit bundleModeEnabledChanged();
}
}
void AccountCore::onExpireChanged(int value) {
if (value != mExpire) {
mExpire = value;
emit expireChanged();
}
}
void AccountCore::onConferenceFactoryAddressChanged(QString value) {
if (value != mConferenceFactoryAddress) {
mConferenceFactoryAddress = value;
emit conferenceFactoryAddressChanged();
}
}
void AccountCore::onAudioVideoConferenceFactoryAddressChanged(QString value) {
if (value != mAudioVideoConferenceFactoryAddress) {
mAudioVideoConferenceFactoryAddress = value;
emit audioVideoConferenceFactoryAddressChanged();
}
}
void AccountCore::onLimeServerUrlChanged(QString value) {
if (value != mLimeServerUrl) {
mLimeServerUrl = value;
emit limeServerUrlChanged();
}
}

View file

@ -41,6 +41,36 @@ class AccountCore : public QObject, public AbstractObject {
Q_PROPERTY(int unreadCallNotifications READ getUnreadCallNotifications NOTIFY unreadCallNotificationsChanged)
Q_PROPERTY(
int unreadMessageNotifications READ getUnreadMessageNotifications NOTIFY unreadMessageNotificationsChanged)
Q_PROPERTY(QString displayName READ getDisplayName WRITE lSetDisplayName NOTIFY displayNameChanged)
Q_PROPERTY(QStringList dialPlans READ getDialPlans CONSTANT)
Q_PROPERTY(QString dialPlan READ getDialPlan WRITE lSetDialPlan NOTIFY dialPlanChanged)
Q_PROPERTY(
QString humaneReadableRegistrationState READ getHumanReadableRegistrationState NOTIFY registrationStateChanged)
Q_PROPERTY(QString humaneReadableRegistrationStateExplained READ getHumanReadableRegistrationStateExplained NOTIFY
registrationStateChanged)
Q_PROPERTY(bool registerEnabled READ getRegisterEnabled WRITE lSetRegisterEnabled NOTIFY registerEnabledChanged)
Q_PROPERTY(QVariantList devices MEMBER mDevices NOTIFY devicesChanged)
Q_PROPERTY(bool notificationsAllowed READ getNotificationsAllowed WRITE lSetNotificationsAllowed NOTIFY
notificationsAllowedChanged)
Q_PROPERTY(
QString mwiServerAddress READ getMwiServerAddress WRITE lSetMwiServerAddress NOTIFY mwiServerAddressChanged)
Q_PROPERTY(QStringList transports READ getTransports CONSTANT)
Q_PROPERTY(QString transport READ getTransport WRITE lSetTransport NOTIFY transportChanged)
Q_PROPERTY(QString serverAddress READ getServerAddress WRITE lSetServerAddress NOTIFY serverAddressChanged)
Q_PROPERTY(bool outboundProxyEnabled READ getOutboundProxyEnabled WRITE lSetOutboundProxyEnabled NOTIFY
outboundProxyEnabledChanged)
Q_PROPERTY(QString stunServer READ getStunServer WRITE lSetStunServer NOTIFY stunServerChanged)
Q_PROPERTY(bool iceEnabled READ getIceEnabled WRITE lSetIceEnabled NOTIFY iceEnabledChanged)
Q_PROPERTY(bool avpfEnabled READ getAvpfEnabled WRITE lSetAvpfEnabled NOTIFY avpfEnabledChanged)
Q_PROPERTY(
bool bundleModeEnabled READ getBundleModeEnabled WRITE lSetBundleModeEnabled NOTIFY bundleModeEnabledChanged)
Q_PROPERTY(int expire READ getExpire WRITE lSetExpire NOTIFY expireChanged)
Q_PROPERTY(QString conferenceFactoryAddress READ getConferenceFactoryAddress WRITE lSetConferenceFactoryAddress
NOTIFY conferenceFactoryAddressChanged)
Q_PROPERTY(QString audioVideoConferenceFactoryAddress READ getAudioVideoConferenceFactoryAddress WRITE
lSetAudioVideoConferenceFactoryAddress NOTIFY audioVideoConferenceFactoryAddressChanged)
Q_PROPERTY(QString limeServerUrl READ getLimeServerUrl WRITE lSetLimeServerUrl NOTIFY limeServerUrlChanged)
public:
static QSharedPointer<AccountCore> create(const std::shared_ptr<linphone::Account> &account);
@ -68,6 +98,45 @@ public:
void onDefaultAccountChanged(bool isDefault);
Q_INVOKABLE void removeAccount();
QString getDisplayName() const;
void onDisplayNameChanged(QString displayName);
QStringList getDialPlans();
int getDialPlanIndex(QString dialPlanString);
QString getDialPlan() const;
void onDialPlanChanged(QString internationalPrefix);
QString getHumanReadableRegistrationState() const;
QString getHumanReadableRegistrationStateExplained() const;
bool getRegisterEnabled() const;
void onRegisterEnabledChanged(bool enabled);
bool getNotificationsAllowed();
QString getMwiServerAddress();
QString getTransport();
QStringList getTransports();
QString getServerAddress();
bool getOutboundProxyEnabled();
QString getStunServer();
bool getIceEnabled();
bool getAvpfEnabled();
bool getBundleModeEnabled();
int getExpire();
QString getConferenceFactoryAddress();
QString getAudioVideoConferenceFactoryAddress();
QString getLimeServerUrl();
void onNotificationsAllowedChanged(bool value);
void onMwiServerAddressChanged(QString value);
void onTransportChanged(QString value);
void onServerAddressChanged(QString value);
void onOutboundProxyEnabledChanged(bool value);
void onStunServerChanged(QString value);
void onIceEnabledChanged(bool value);
void onAvpfEnabledChanged(bool value);
void onBundleModeEnabledChanged(bool value);
void onExpireChanged(int value);
void onConferenceFactoryAddressChanged(QString value);
void onAudioVideoConferenceFactoryAddressChanged(QString value);
void onLimeServerUrlChanged(QString value);
signals:
void pictureUriChanged();
@ -76,22 +145,76 @@ signals:
void unreadNotificationsChanged(int unread);
void unreadCallNotificationsChanged(int unread);
void unreadMessageNotificationsChanged(int unread);
void displayNameChanged();
void dialPlanChanged();
void registerEnabledChanged();
void allAddressesChanged();
void devicesChanged();
void notificationsAllowedChanged();
void mwiServerAddressChanged();
void transportChanged();
void serverAddressChanged();
void outboundProxyEnabledChanged();
void stunServerChanged();
void iceEnabledChanged();
void avpfEnabledChanged();
void bundleModeEnabledChanged();
void expireChanged();
void conferenceFactoryAddressChanged();
void audioVideoConferenceFactoryAddressChanged();
void limeServerUrlChanged();
// Account requests
void lSetPictureUri(QString pictureUri);
void lSetDefaultAccount();
void lResetMissedCalls();
void lRefreshNotifications();
void lSetDisplayName(QString displayName);
void lSetDialPlan(QString internationalPrefix);
void lSetRegisterEnabled(bool enabled);
void lSetNotificationsAllowed(bool value);
void lSetMwiServerAddress(QString value);
void lSetTransport(QString value);
void lSetServerAddress(QString value);
void lSetOutboundProxyEnabled(bool value);
void lSetStunServer(QString value);
void lSetIceEnabled(bool value);
void lSetAvpfEnabled(bool value);
void lSetBundleModeEnabled(bool value);
void lSetExpire(int value);
void lSetConferenceFactoryAddress(QString value);
void lSetAudioVideoConferenceFactoryAddress(QString value);
void lSetLimeServerUrl(QString value);
private:
QString mContactAddress;
QString mIdentityAddress;
QString mPictureUri;
QString mDisplayName;
QStringList mDialPlans;
QString mDialPlan;
bool mRegisterEnabled;
bool mIsDefaultAccount = false;
LinphoneEnums::RegistrationState mRegistrationState;
int mUnreadNotifications = 0;
int mUnreadCallNotifications = 0;
int mUnreadMessageNotifications = 0;
QVariantList mDevices;
bool mNotificationsAllowed;
QString mMwiServerAddress;
QString mTransport;
QStringList mTransports;
QString mServerAddress;
bool mOutboundProxyEnabled;
QString mStunServer;
bool mIceEnabled;
bool mAvpfEnabled;
bool mBundleModeEnabled;
int mExpire;
QString mConferenceFactoryAddress;
QString mAudioVideoConferenceFactoryAddress;
QString mLimeServerUrl;
std::shared_ptr<AccountModel> mAccountModel;
QSharedPointer<SafeConnection<AccountCore, AccountModel>> mAccountModelConnection;
QSharedPointer<SafeConnection<AccountCore, CoreModel>> mCoreModelConnection;

View file

@ -0,0 +1,64 @@
/*
* 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 "AccountDeviceCore.hpp"
#include "core/App.hpp"
#include "tool/Utils.hpp"
#include "tool/thread/SafeConnection.hpp"
DEFINE_ABSTRACT_OBJECT(AccountDeviceCore)
AccountDeviceCore::AccountDeviceCore(QString name, QString userAgent, QDateTime last) : QObject(nullptr) {
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
mustBeInLinphoneThread(getClassName());
mDeviceName = name;
mUserAgent = userAgent;
mLastUpdateTimestamp = last;
}
QSharedPointer<AccountDeviceCore> AccountDeviceCore::createDummy(QString name, QString userAgent, QDateTime last) {
auto core = QSharedPointer<AccountDeviceCore>(new AccountDeviceCore(name,userAgent,last));
core->moveToThread(App::getInstance()->thread());
return core;
}
QSharedPointer<AccountDeviceCore> AccountDeviceCore::create(const std::shared_ptr<linphone::AccountDevice> &device) {
auto core = QSharedPointer<AccountDeviceCore>(new AccountDeviceCore(device));
core->moveToThread(App::getInstance()->thread());
return core;
}
AccountDeviceCore::AccountDeviceCore(const std::shared_ptr<linphone::AccountDevice> &device) : QObject(nullptr) {
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
mustBeInLinphoneThread(getClassName());
mDeviceName = Utils::coreStringToAppString(device->getName());
mUserAgent = Utils::coreStringToAppString(device->getUserAgent());
mLastUpdateTimestamp = QDateTime::fromSecsSinceEpoch(device->getLastUpdateTimestamp());
}
AccountDeviceCore::~AccountDeviceCore() {
mustBeInMainThread("~" + getClassName());
}
void AccountDeviceCore::removeDevice() {
// TODO (core thread)
}

View file

@ -0,0 +1,58 @@
/*
* 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 ACCOUNT_DEVICE_CORE_H_
#define ACCOUNT_DEVICE_CORE_H_
#include <QObject>
#include <QSharedPointer>
#include <QDateTime>
#include "tool/AbstractObject.hpp"
#include <linphone++/linphone.hh>
class AccountDeviceCore : public QObject, public AbstractObject {
Q_OBJECT
Q_PROPERTY(QString deviceName MEMBER mDeviceName CONSTANT)
Q_PROPERTY(QString userAgent MEMBER mUserAgent CONSTANT)
Q_PROPERTY(QDateTime lastUpdateTimestamp MEMBER mLastUpdateTimestamp CONSTANT)
public:
static QSharedPointer<AccountDeviceCore> create(const std::shared_ptr<linphone::AccountDevice> &device);
AccountDeviceCore(const std::shared_ptr<linphone::AccountDevice> &device);
AccountDeviceCore(QString name, QString userAgent, QDateTime last);
static QSharedPointer<AccountDeviceCore> createDummy(QString name, QString userAgent, QDateTime last);
~AccountDeviceCore();
Q_INVOKABLE void removeDevice();
private:
QString mDeviceName;
QString mUserAgent;
QDateTime mLastUpdateTimestamp;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -0,0 +1,38 @@
/*
* 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 "AccountDeviceGui.hpp"
#include "core/App.hpp"
DEFINE_ABSTRACT_OBJECT(AccountDeviceGui)
AccountDeviceGui::AccountDeviceGui(QSharedPointer<AccountDeviceCore> core) {
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
mCore = core;
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
}
AccountDeviceGui::~AccountDeviceGui() {
mustBeInMainThread("~" + getClassName());
}
AccountDeviceCore *AccountDeviceGui::getCore() const {
return mCore.get();
}

View file

@ -0,0 +1,41 @@
/*
* 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 ACCOUNT_DEVICE_GUI_H_
#define ACCOUNT_DEVICE_GUI_H_
#include "AccountDeviceCore.hpp"
#include <QObject>
#include <QSharedPointer>
class AccountDeviceGui : public QObject, public AbstractObject {
Q_OBJECT
Q_PROPERTY(AccountDeviceCore *core READ getCore CONSTANT)
public:
AccountDeviceGui(QSharedPointer<AccountDeviceCore> core);
~AccountDeviceGui();
AccountDeviceCore *getCore() const;
QSharedPointer<AccountDeviceCore> mCore;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -36,6 +36,7 @@
#include "tool/LinphoneEnums.hpp"
#include "tool/providers/AvatarProvider.hpp"
#include "tool/providers/ImageProvider.hpp"
#include "model/tool/ToolModel.hpp"
DEFINE_ABSTRACT_OBJECT(Notifier)
@ -277,6 +278,16 @@ void Notifier::deleteNotification(QVariant notification) {
void Notifier::notifyReceivedCall(const shared_ptr<linphone::Call> &call) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto account = ToolModel::findAccount(call->getToAddress());
if (account) {
auto accountModel = Utils::makeQObject_ptr<AccountModel>(account);
accountModel->setSelf(accountModel);
if (!accountModel->getNotificationsAllowed()) {
qInfo() << "Notifications have been disabled for this account - not creating a notification for incoming call";
return;
}
}
auto model = CallCore::create(call);
auto gui = new CallGui(model);
gui->moveToThread(App::getInstance()->thread());

View file

@ -1 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#4e6074" viewBox="0 0 256 256"><path d="M208,32H184V24a8,8,0,0,0-16,0v8H88V24a8,8,0,0,0-16,0v8H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM72,48v8a8,8,0,0,0,16,0V48h80v8a8,8,0,0,0,16,0V48h24V80H48V48ZM208,208H48V96H208V208Zm-96-88v64a8,8,0,0,1-16,0V132.94l-4.42,2.22a8,8,0,0,1-7.16-14.32l16-8A8,8,0,0,1,112,120Zm59.16,30.45L152,176h16a8,8,0,0,1,0,16H136a8,8,0,0,1-6.4-12.8l28.78-38.37A8,8,0,1,0,145.07,132a8,8,0,1,1-13.85-8A24,24,0,0,1,176,136,23.76,23.76,0,0,1,171.16,150.45Z"></path></svg>
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.25 3.125H14.375V2.5C14.375 2.33424 14.3092 2.17527 14.1919 2.05806C14.0747 1.94085 13.9158 1.875 13.75 1.875C13.5842 1.875 13.4253 1.94085 13.3081 2.05806C13.1908 2.17527 13.125 2.33424 13.125 2.5V3.125H6.875V2.5C6.875 2.33424 6.80915 2.17527 6.69194 2.05806C6.57473 1.94085 6.41576 1.875 6.25 1.875C6.08424 1.875 5.92527 1.94085 5.80806 2.05806C5.69085 2.17527 5.625 2.33424 5.625 2.5V3.125H3.75C3.41848 3.125 3.10054 3.2567 2.86612 3.49112C2.6317 3.72554 2.5 4.04348 2.5 4.375V16.875C2.5 17.2065 2.6317 17.5245 2.86612 17.7589C3.10054 17.9933 3.41848 18.125 3.75 18.125H16.25C16.5815 18.125 16.8995 17.9933 17.1339 17.7589C17.3683 17.5245 17.5 17.2065 17.5 16.875V4.375C17.5 4.04348 17.3683 3.72554 17.1339 3.49112C16.8995 3.2567 16.5815 3.125 16.25 3.125ZM5.625 4.375V5C5.625 5.16576 5.69085 5.32473 5.80806 5.44194C5.92527 5.55915 6.08424 5.625 6.25 5.625C6.41576 5.625 6.57473 5.55915 6.69194 5.44194C6.80915 5.32473 6.875 5.16576 6.875 5V4.375H13.125V5C13.125 5.16576 13.1908 5.32473 13.3081 5.44194C13.4253 5.55915 13.5842 5.625 13.75 5.625C13.9158 5.625 14.0747 5.55915 14.1919 5.44194C14.3092 5.32473 14.375 5.16576 14.375 5V4.375H16.25V6.875H3.75V4.375H5.625ZM16.25 16.875H3.75V8.125H16.25V16.875Z" fill="#4E6074"/>
</svg>

Before

Width:  |  Height:  |  Size: 604 B

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19.6923 2.75H4.30769C3.69565 2.75 3.10868 2.99313 2.67591 3.42591C2.24313 3.85868 2 4.44565 2 5.05769V15.8269C2 16.439 2.24313 17.0259 2.67591 17.4587C3.10868 17.8915 3.69565 18.1346 4.30769 18.1346H11.2308V19.6731H8.92308C8.71906 19.6731 8.52341 19.7541 8.37915 19.8984C8.23489 20.0426 8.15385 20.2383 8.15385 20.4423C8.15385 20.6463 8.23489 20.842 8.37915 20.9862C8.52341 21.1305 8.71906 21.2115 8.92308 21.2115H15.0769C15.2809 21.2115 15.4766 21.1305 15.6209 20.9862C15.7651 20.842 15.8462 20.6463 15.8462 20.4423C15.8462 20.2383 15.7651 20.0426 15.6209 19.8984C15.4766 19.7541 15.2809 19.6731 15.0769 19.6731H12.7692V18.1346H19.6923C20.3043 18.1346 20.8913 17.8915 21.3241 17.4587C21.7569 17.0259 22 16.439 22 15.8269V5.05769C22 4.44565 21.7569 3.85868 21.3241 3.42591C20.8913 2.99313 20.3043 2.75 19.6923 2.75ZM4.30769 4.28846H19.6923C19.8963 4.28846 20.092 4.36951 20.2362 4.51376C20.3805 4.65802 20.4615 4.85368 20.4615 5.05769V12.75H3.53846V5.05769C3.53846 4.85368 3.61951 4.65802 3.76376 4.51376C3.90802 4.36951 4.10368 4.28846 4.30769 4.28846ZM19.6923 16.5962H4.30769C4.10368 16.5962 3.90802 16.5151 3.76376 16.3709C3.61951 16.2266 3.53846 16.0309 3.53846 15.8269V14.2885H20.4615V15.8269C20.4615 16.0309 20.3805 16.2266 20.2362 16.3709C20.092 16.5151 19.8963 16.5962 19.6923 16.5962Z" fill="#4E6074"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.25 1.5H7.25C6.65326 1.5 6.08097 1.73705 5.65901 2.15901C5.23705 2.58097 5 3.15326 5 3.75V20.25C5 20.8467 5.23705 21.419 5.65901 21.841C6.08097 22.2629 6.65326 22.5 7.25 22.5H16.25C16.8467 22.5 17.419 22.2629 17.841 21.841C18.2629 21.419 18.5 20.8467 18.5 20.25V3.75C18.5 3.15326 18.2629 2.58097 17.841 2.15901C17.419 1.73705 16.8467 1.5 16.25 1.5ZM17 20.25C17 20.4489 16.921 20.6397 16.7803 20.7803C16.6397 20.921 16.4489 21 16.25 21H7.25C7.05109 21 6.86032 20.921 6.71967 20.7803C6.57902 20.6397 6.5 20.4489 6.5 20.25V3.75C6.5 3.55109 6.57902 3.36032 6.71967 3.21967C6.86032 3.07902 7.05109 3 7.25 3H16.25C16.4489 3 16.6397 3.07902 16.7803 3.21967C16.921 3.36032 17 3.55109 17 3.75V20.25ZM12.875 5.625C12.875 5.8475 12.809 6.06501 12.6854 6.25002C12.5618 6.43502 12.3861 6.57922 12.1805 6.66436C11.975 6.74951 11.7488 6.77179 11.5305 6.72838C11.3123 6.68498 11.1118 6.57783 10.9545 6.4205C10.7972 6.26316 10.69 6.06271 10.6466 5.84448C10.6032 5.62625 10.6255 5.40005 10.7106 5.19448C10.7958 4.98891 10.94 4.81321 11.125 4.6896C11.31 4.56598 11.5275 4.5 11.75 4.5C12.0484 4.5 12.3345 4.61853 12.5455 4.8295C12.7565 5.04048 12.875 5.32663 12.875 5.625Z" fill="#4E6074"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -3,7 +3,7 @@ list(APPEND _LINPHONEAPP_SOURCES
model/account/AccountManager.cpp
model/account/AccountManagerServicesModel.cpp
model/account/AccountManagerServicesRequestModel.cpp
model/auth/OIDCModel.cpp
model/call/CallModel.cpp

View file

@ -57,8 +57,7 @@ void AccountModel::onRegistrationStateChanged(const std::shared_ptr<linphone::Ac
void AccountModel::setPictureUri(QString uri) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto account = std::dynamic_pointer_cast<linphone::Account>(mMonitor);
auto params = account->getParams()->clone();
auto params = mMonitor->getParams()->clone();
auto oldPictureUri = Utils::coreStringToAppString(params->getPictureUri());
if (!oldPictureUri.isEmpty()) {
QString appPrefix = QStringLiteral("image://%1/").arg(AvatarProvider::ProviderId);
@ -69,11 +68,11 @@ void AccountModel::setPictureUri(QString uri) {
if (!oldPicture.remove()) qWarning() << log().arg("Cannot delete old avatar file at " + oldPictureUri);
}
params->setPictureUri(Utils::appStringToCoreString(uri));
account->setParams(params);
mMonitor->setParams(params);
// Hack because Account doesn't provide callbacks on updated data
// emit pictureUriChanged(uri);
emit CoreModel::getInstance()->defaultAccountChanged(CoreModel::getInstance()->getCore(),
CoreModel::getInstance()->getCore()->getDefaultAccount());
emit CoreModel::getInstance() -> defaultAccountChanged(CoreModel::getInstance()->getCore(),
CoreModel::getInstance()->getCore()->getDefaultAccount());
}
void AccountModel::onDefaultAccountChanged() {
@ -107,4 +106,179 @@ int AccountModel::getMissedCallsCount() const {
int AccountModel::getUnreadMessagesCount() const {
return mMonitor->getUnreadChatMessageCount();
}
}
void AccountModel::setDisplayName(QString displayName) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto params = mMonitor->getParams()->clone();
auto address = params->getIdentityAddress()->clone();
address->setDisplayName(Utils::appStringToCoreString(displayName));
params->setIdentityAddress(address);
mMonitor->setParams(params);
emit displayNameChanged(displayName);
}
void AccountModel::setDialPlan(int index) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
std::string callingCode = "";
std::string countryCode = "";
if (index != -1) {
auto plans = linphone::Factory::get()->getDialPlans();
std::vector<std::shared_ptr<linphone::DialPlan>> vectorPlans(plans.begin(), plans.end());
auto plan = vectorPlans[index];
callingCode = plan->getCountryCallingCode();
countryCode = plan->getIsoCountryCode();
}
auto params = mMonitor->getParams()->clone();
params->setInternationalPrefix(callingCode);
params->setInternationalPrefixIsoCountryCode(countryCode);
mMonitor->setParams(params);
emit dialPlanChanged(index);
}
void AccountModel::setRegisterEnabled(bool enabled) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto params = mMonitor->getParams()->clone();
params->enableRegister(enabled);
mMonitor->setParams(params);
emit registerEnabledChanged(enabled);
}
std::string AccountModel::getConfigAccountUiSection() {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
return "ui_" + mMonitor->getParams()->getIdentityAddress()->asStringUriOnly();
}
bool AccountModel::getNotificationsAllowed() {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
return mMonitor->getCore()->getConfig()->getBool(getConfigAccountUiSection(), "notifications_allowed", true);
}
void AccountModel::setNotificationsAllowed(bool value) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
mMonitor->getCore()->getConfig()->setBool(getConfigAccountUiSection(), "notifications_allowed", value);
emit notificationsAllowedChanged(value);
}
void AccountModel::setMwiServerAddress(QString value) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto params = mMonitor->getParams()->clone();
auto address = linphone::Factory::get()->createAddress(Utils::appStringToCoreString(value));
if (address) {
params->setMwiServerAddress(address);
mMonitor->setParams(params);
emit mwiServerAddressChanged(value);
} else qWarning() << "Unable to set MWI address, failed creating address from" << value;
}
void AccountModel::setTransport(linphone::TransportType value) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto params = mMonitor->getParams()->clone();
params->setTransport(value);
mMonitor->setParams(params);
emit transportChanged(value);
}
void AccountModel::setServerAddress(QString value) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto params = mMonitor->getParams()->clone();
auto address = linphone::Factory::get()->createAddress(Utils::appStringToCoreString(value));
if (address) {
params->setServerAddress(address);
mMonitor->setParams(params);
emit serverAddressChanged(value);
} else qWarning() << "Unable to set ServerAddress, failed creating address from" << value;
}
void AccountModel::setOutboundProxyEnabled(bool value) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto params = mMonitor->getParams()->clone();
params->enableOutboundProxy(value);
mMonitor->setParams(params);
emit outboundProxyEnabledChanged(value);
}
void AccountModel::setStunServer(QString value) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto params = mMonitor->getParams()->clone();
auto policy = params->getNatPolicy();
if (!policy) policy = mMonitor->getCore()->createNatPolicy();
policy->setStunServer(Utils::appStringToCoreString(value));
params->setNatPolicy(policy);
mMonitor->setParams(params);
emit stunServerChanged(value);
qWarning() << "cdes stun server set to" << value;
}
void AccountModel::setIceEnabled(bool value) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto params = mMonitor->getParams()->clone();
auto policy = params->getNatPolicy();
if (!policy) policy = mMonitor->getCore()->createNatPolicy();
policy->enableIce(value);
params->setNatPolicy(policy);
mMonitor->setParams(params);
emit iceEnabledChanged(value);
}
void AccountModel::setAvpfEnabled(bool value) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto params = mMonitor->getParams()->clone();
params->setAvpfMode(value ? linphone::AVPFMode::Enabled : linphone::AVPFMode::Disabled);
mMonitor->setParams(params);
emit avpfEnabledChanged(value);
}
void AccountModel::setBundleModeEnabled(bool value) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto params = mMonitor->getParams()->clone();
params->enableRtpBundle(value);
mMonitor->setParams(params);
emit bundleModeEnabledChanged(value);
}
void AccountModel::setExpire(int value) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto params = mMonitor->getParams()->clone();
params->setExpires(value);
mMonitor->setParams(params);
emit expireChanged(value);
}
void AccountModel::setConferenceFactoryAddress(QString value) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto params = mMonitor->getParams()->clone();
auto address = linphone::Factory::get()->createAddress(Utils::appStringToCoreString(value));
if (address) {
params->setConferenceFactoryAddress(address);
mMonitor->setParams(params);
emit conferenceFactoryAddressChanged(value);
} else qWarning() << "Unable to set ConferenceFactoryAddress address, failed creating address from" << value;
}
void AccountModel::setAudioVideoConferenceFactoryAddress(QString value) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto params = mMonitor->getParams()->clone();
auto address = linphone::Factory::get()->createAddress(Utils::appStringToCoreString(value));
if (address) {
params->setAudioVideoConferenceFactoryAddress(address);
mMonitor->setParams(params);
emit audioVideoConferenceFactoryAddressChanged(value);
} else
qWarning() << "Unable to set AudioVideoConferenceFactoryAddress address, failed creating address from" << value;
}
void AccountModel::setLimeServerUrl(QString value) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto params = mMonitor->getParams()->clone();
auto address = linphone::Factory::get()->createAddress(Utils::appStringToCoreString(value));
params->setLimeServerUrl(Utils::appStringToCoreString(value));
mMonitor->setParams(params);
emit limeServerUrlChanged(value);
}
QString AccountModel::dialPlanAsString(const std::shared_ptr<linphone::DialPlan> &dialPlan) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
return Utils::coreStringToAppString(dialPlan->getFlag() + " " + dialPlan->getCountry() + " | +" +
dialPlan->getCountryCallingCode());
}

View file

@ -40,6 +40,8 @@ public:
const std::string &message) override;
void onDefaultAccountChanged();
std::string getConfigAccountUiSection();
void setPictureUri(QString uri);
void setDefault();
void removeAccount();
@ -47,6 +49,24 @@ public:
void refreshUnreadNotifications();
int getMissedCallsCount() const;
int getUnreadMessagesCount() const;
void setDisplayName(QString displayName);
void setDialPlan(int index);
void setRegisterEnabled(bool enabled);
bool getNotificationsAllowed();
void setNotificationsAllowed(bool value);
void setMwiServerAddress(QString value);
void setTransport(linphone::TransportType value);
void setServerAddress(QString value);
void setOutboundProxyEnabled(bool value);
void setStunServer(QString value);
void setIceEnabled(bool value);
void setAvpfEnabled(bool value);
void setBundleModeEnabled(bool value);
void setExpire(int value);
void setConferenceFactoryAddress(QString value);
void setAudioVideoConferenceFactoryAddress(QString value);
void setLimeServerUrl(QString value);
QString dialPlanAsString(const std::shared_ptr<linphone::DialPlan> &dialPlan);
signals:
void registrationStateChanged(const std::shared_ptr<linphone::Account> &account,
@ -56,6 +76,22 @@ signals:
void pictureUriChanged(QString uri);
void unreadNotificationsChanged(int unreadMessagesCount, int unreadCallsCount);
void displayNameChanged(QString displayName);
void dialPlanChanged(int index);
void registerEnabledChanged(bool enabled);
void notificationsAllowedChanged(bool value);
void mwiServerAddressChanged(QString value);
void transportChanged(linphone::TransportType value);
void serverAddressChanged(QString value);
void outboundProxyEnabledChanged(bool value);
void stunServerChanged(QString value);
void iceEnabledChanged(bool value);
void avpfEnabledChanged(bool value);
void bundleModeEnabledChanged(bool value);
void expireChanged(int value);
void conferenceFactoryAddressChanged(QString value);
void audioVideoConferenceFactoryAddressChanged(QString value);
void limeServerUrlChanged(QString value);
private:
DECLARE_ABSTRACT_OBJECT

View file

@ -32,9 +32,11 @@
#include <QClipboard>
#include <QDesktopServices>
#include <QHostAddress>
#include <QImageReader>
#include <QQuickWindow>
#include <QRandomGenerator>
#include <QRegularExpression>
// =============================================================================
@ -260,6 +262,10 @@ QString Utils::formatDate(const QDateTime &date, bool includeTime) {
return dateDay + " | " + time;
}
QString Utils::formatTime(const QDateTime &date) {
return date.time().toString("hh:mm");
}
QString Utils::formatDateElapsedTime(const QDateTime &date) {
// auto y = floor(seconds / 31104000);
// if (y > 0) return QString::number(y) + " years";
@ -300,6 +306,35 @@ QString Utils::interpretUrl(const QString &uri) {
return address;
}
bool Utils::isValidSIPAddress(const QString &uri) {
bool isValid = false;
App::postModelBlock([&isValid, uri]() mutable {
isValid = linphone::Factory::get()->createAddress(Utils::appStringToCoreString(uri)) != nullptr;
});
return isValid;
}
bool Utils::isValidIPAddress(const QString &host) {
QHostAddress address;
return address.setAddress(host);
}
bool Utils::isValidHostname(const QString &hostname) {
QRegularExpression regex("^[a-zA-Z0-9]([a-zA-Z0-9-]{0,62}[a-zA-Z0-9])?$");
QStringList labels = hostname.split('.');
if (labels.size() > 127) // More than 127 labels is invalid
return false;
foreach (const QString &label, labels) {
if (!regex.match(label).hasMatch() || label.length() > 63) // Label length must be between 1 and 63
return false;
}
return hostname.length() <= 253; // Total length should be <= 253
}
bool Utils::isValidURL(const QString &url) {
return QUrl(url).isValid();
}
QString Utils::findAvatarByAddress(const QString &address) {
QString avatar;

View file

@ -82,6 +82,7 @@ public:
bool dotsSeparator = true); // Return the elapsed time formated
Q_INVOKABLE static QString formatDate(const QDateTime &date, bool includeTime = true); // Return the date formated
Q_INVOKABLE static QString formatDateElapsedTime(const QDateTime &date);
Q_INVOKABLE static QString formatTime(const QDateTime &date); // Return the time formated
Q_INVOKABLE static QStringList generateSecurityLettersArray(int arraySize, int correctIndex, QString correctCode);
Q_INVOKABLE static int getRandomIndex(int size);
Q_INVOKABLE static bool copyToClipboard(const QString &text);
@ -112,6 +113,10 @@ public:
Q_INVOKABLE static QDateTime addSecs(QDateTime date, int secs);
Q_INVOKABLE static QDateTime addYears(QDateTime date, int years);
Q_INVOKABLE static QString interpretUrl(const QString &uri);
Q_INVOKABLE static bool isValidSIPAddress(const QString &uri);
Q_INVOKABLE static bool isValidIPAddress(const QString &host);
Q_INVOKABLE static bool isValidHostname(const QString& hostname);
Q_INVOKABLE static bool isValidURL(const QString& url);
Q_INVOKABLE static QString findAvatarByAddress(const QString &address);
Q_INVOKABLE static VariantObject *findFriendByAddress(const QString &address);
static QString generateSavedFilename(const QString &from, const QString &to);

View file

@ -7,17 +7,35 @@ import Linphone
Rectangle {
id: mainItem
anchors.fill: parent
width: container.width
height: container.height
property string titleText
property var component
property var model
color: 'white'
Rectangle {
width: parent.width - 2 * 45 * DefaultStyle.dp
property var container
Control.ScrollView {
id: scrollView
height: parent.height
width: parent.width - 2 * 45 * DefaultStyle.dp
anchors.centerIn: parent
contentHeight: content.height + 20 * DefaultStyle.dp
contentWidth: parent.width - 2 * 45 * DefaultStyle.dp
Control.ScrollBar.vertical: ScrollBar {
active: scrollView.contentHeight > container.height
interactive: true
policy: Control.ScrollBar.AsNeeded
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.rightMargin: -15 * DefaultStyle.dp
}
Control.ScrollBar.horizontal: ScrollBar {
active: false
}
ColumnLayout {
id: content
width: parent.width
spacing: 10 * DefaultStyle.dp
Text {

View file

@ -0,0 +1,385 @@
import QtCore
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls as Control
import QtQuick.Dialogs
import Linphone
import SettingsCpp 1.0
import UtilsCpp
AbstractDetailsLayout {
id: mainItem
component: main
property alias account: mainItem.model
Component {
id: main
ColumnLayout {
width: parent.width
spacing: 5 * DefaultStyle.dp
RowLayout {
Layout.topMargin: 16 * DefaultStyle.dp
spacing: 5 * DefaultStyle.dp
ColumnLayout {
Layout.fillWidth: true
spacing: 5 * DefaultStyle.dp
ColumnLayout {
Layout.preferredWidth: 341 * DefaultStyle.dp
Layout.maximumWidth: 341 * DefaultStyle.dp
Layout.minimumWidth: 341 * DefaultStyle.dp
spacing: 5 * DefaultStyle.dp
Text {
Layout.fillWidth: true
text: qsTr("Détails")
font: Typography.h4
wrapMode: Text.WordWrap
color: DefaultStyle.main2_600
}
Text {
text: qsTr("Editer les informations de votre compte.")
font: Typography.p1s
wrapMode: Text.WordWrap
color: DefaultStyle.main2_600
Layout.fillWidth: true
}
}
Item {
Layout.fillHeight: true
}
}
ColumnLayout {
Layout.fillWidth: true
spacing: 20 * DefaultStyle.dp
Layout.rightMargin: 44 * DefaultStyle.dp
Layout.topMargin: 20 * DefaultStyle.dp
Layout.leftMargin: 64 * DefaultStyle.dp
Avatar {
account: model
displayPresence: false
Layout.preferredWidth: 100 * DefaultStyle.dp
Layout.preferredHeight: 100 * DefaultStyle.dp
Layout.alignment: Qt.AlignHCenter
}
IconLabelButton {
visible: model.core.pictureUri.length === 0
Layout.preferredWidth: width
Layout.preferredHeight: 17 * DefaultStyle.dp
iconSource: AppIcons.camera
iconSize: 17 * DefaultStyle.dp
text: qsTr("Ajouter une image")
onClicked: fileDialog.open()
Layout.alignment: Qt.AlignHCenter
color: DefaultStyle.main2_600
}
RowLayout {
visible: model.core.pictureUri.length > 0
Layout.alignment: Qt.AlignHCenter
spacing: 5 * DefaultStyle.dp
IconLabelButton {
Layout.preferredWidth: width
Layout.preferredHeight: 17 * DefaultStyle.dp
iconSource: AppIcons.pencil
iconSize: 17 * DefaultStyle.dp
text: qsTr("Modifier l'image")
color: DefaultStyle.main2_600
onClicked: fileDialog.open()
}
IconLabelButton {
Layout.preferredWidth: width
Layout.preferredHeight: 17 * DefaultStyle.dp
iconSource: AppIcons.trashCan
iconSize: 17 * DefaultStyle.dp
text: qsTr("Supprimer l'image")
color: DefaultStyle.main2_600
onClicked: model.core.pictureUri = ""
}
}
FileDialog {
id: fileDialog
currentFolder: StandardPaths.standardLocations(StandardPaths.PicturesLocation)[0]
onAccepted: {
var avatarPath = UtilsCpp.createAvatar( selectedFile )
if(avatarPath){
model.core.pictureUri = avatarPath
}
}
}
RowLayout {
Layout.fillWidth: true
spacing: 5 * DefaultStyle.dp
Text {
Layout.alignment: Qt.AlignLeft
text: qsTr("Adresse SIP :")
color: DefaultStyle.main2_600
font: Typography.p2l
}
Text {
Layout.alignment: Qt.AlignLeft
text: model.core.identityAddress
color: DefaultStyle.main2_600
font: Typography.p1
}
Item {
Layout.fillWidth: true
}
IconLabelButton {
Layout.alignment: Qt.AlignRight
Layout.preferredWidth: 20 * DefaultStyle.dp
Layout.preferredHeight: 20 * DefaultStyle.dp
iconSize: 24 * DefaultStyle.dp
iconSource: AppIcons.copy
color: DefaultStyle.main2_600
onClicked: UtilsCpp.copyToClipboard(model.core.identityAddress)
}
}
ColumnLayout {
spacing: 5 * DefaultStyle.dp
Layout.alignment: Qt.AlignLeft
Text {
text: qsTr("Nom daffichage")
color: DefaultStyle.main2_600
font: Typography.p2l
}
Text {
text: qsTr("Le nom qui sera affiché à vos correspondants lors de vos échanges.")
color: DefaultStyle.main2_600
font: Typography.p1
}
}
TextField {
Layout.alignment: Qt.AlignLeft
Layout.fillWidth: true
Layout.preferredHeight: 49 * DefaultStyle.dp
initialText: model.core.displayName
backgroundColor: DefaultStyle.grey_100
onEditingFinished: {
if (text.length != 0) model.core.displayName = text
}
}
Text {
text: qsTr("Indicatif international*")
color: DefaultStyle.main2_600
font: Typography.p2l
}
ComboSetting {
Layout.fillWidth: true
Layout.topMargin: -15 * DefaultStyle.dp
entries: account.core.dialPlans
propertyName: "dialPlan"
propertyOwner: account.core
}
SwitchSetting {
titleText: account.core.humaneReadableRegistrationState
subTitleText: account.core.humaneReadableRegistrationStateExplained
propertyName: "registerEnabled"
propertyOwner: account.core
}
RowLayout {
id:mainItem
spacing : 20 * DefaultStyle.dp
ColumnLayout {
spacing : 5 * DefaultStyle.dp
Text {
text: qsTr("Supprimer mon compte")
font: Typography.p2l
wrapMode: Text.WordWrap
color: DefaultStyle.danger_500main
Layout.fillWidth: true
}
Text {
text: qsTr("Votre compte sera retiré de ce client linphone, mais vous restez connecté sur vos autres clients")
font: Typography.p1
wrapMode: Text.WordWrap
color: DefaultStyle.main2_500main
Layout.fillWidth: true
}
}
Item {
Layout.fillWidth: true
}
Button {
background: Item{}
Layout.alignment: Qt.AlignRight
Layout.rightMargin: 5 * DefaultStyle.dp
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
contentItem: RowLayout {
Layout.alignment: Qt.AlignRight
EffectImage {
imageSource: AppIcons.trashCan
width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
fillMode: Image.PreserveAspectFit
colorizationColor: DefaultStyle.danger_500main
}
}
onClicked: {
var mainWin = UtilsCpp.getMainWindow()
mainWin.showConfirmationLambdaPopup(
qsTr("Supprimer ") + (model.core.displayName.length > 0 ? model.core.displayName : qsTr("le compte")) + " ?",
qsTr("Vous pouvez vous reconnecter à tout moment en cliquant sur \"Ajouter un compte\".\nCependant toutes les informations stockées sur ce périphérique seront supprimées."),
function (confirmed) {
if (confirmed) {
account.core.removeAccount()
}
}
)
}
}
}
}
Component.onCompleted: {
}
Component.onDestruction: {
}
}
Rectangle {
Layout.fillWidth: true
Layout.topMargin: 16 * DefaultStyle.dp
height: 1 * DefaultStyle.dp
color: DefaultStyle.main2_500main
}
RowLayout {
Layout.fillWidth: true
Layout.topMargin: 16 * DefaultStyle.dp
spacing: 5 * DefaultStyle.dp
ColumnLayout {
Layout.fillWidth: true
spacing: 5 * DefaultStyle.dp
ColumnLayout {
Layout.preferredWidth: 341 * DefaultStyle.dp
Layout.maximumWidth: 341 * DefaultStyle.dp
Layout.minimumWidth: 341 * DefaultStyle.dp
spacing: 5 * DefaultStyle.dp
Text {
Layout.fillWidth: true
text: qsTr("Vos appareils")
font: Typography.h4
wrapMode: Text.WordWrap
color: DefaultStyle.main2_600
}
Text {
text: qsTr("La liste des appareils connectés à votre compte. Vous pouvez retirer les appareils que vous nutilisez plus. (TODO connecter API SDK quand dispo)")
font: Typography.p1s
wrapMode: Text.WordWrap
color: DefaultStyle.main2_600
Layout.fillWidth: true
}
}
Item {
Layout.fillHeight: true
}
}
Rectangle {
color: DefaultStyle.grey_100
Layout.fillWidth: true
Layout.minimumHeight: account.core.devices.length * 133 * DefaultStyle.dp + (account.core.devices.length - 1) * 15 * DefaultStyle.dp + 2 * 21 * DefaultStyle.dp
radius: 15 * DefaultStyle.dp
Layout.rightMargin: 30 * DefaultStyle.dp
Layout.topMargin: 20 * DefaultStyle.dp
Layout.bottomMargin: 21 * DefaultStyle.dp
Layout.leftMargin: 44 * DefaultStyle.dp
ColumnLayout {
anchors.fill: parent
anchors.bottomMargin: 21 * DefaultStyle.dp
spacing: 15 * DefaultStyle.dp
Repeater {
id: devices
model: account.core.devices
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 133 * DefaultStyle.dp
Layout.topMargin: (index == 0 ? 21 : 0) * DefaultStyle.dp
Layout.leftMargin: 17 * DefaultStyle.dp
Layout.rightMargin: 17 * DefaultStyle.dp
color: 'white'
radius: 10 * DefaultStyle.dp
ColumnLayout {
anchors.topMargin: 26 * DefaultStyle.dp
anchors.bottomMargin: 26 * DefaultStyle.dp
anchors.centerIn: parent
width: parent.width
spacing: 20 * DefaultStyle.dp
RowLayout {
Layout.rightMargin: 36 * DefaultStyle.dp
Layout.leftMargin: 33 * DefaultStyle.dp
spacing: 5 * DefaultStyle.dp
EffectImage {
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
fillMode: Image.PreserveAspectFit
colorizationColor: DefaultStyle.main2_600
imageSource: modelData.core.userAgent.toLowerCase().includes('ios') || modelData.core.userAgent.toLowerCase().includes('android') ? AppIcons.mobile : AppIcons.desktop
}
Text {
text: modelData.core.deviceName
color: DefaultStyle.main2_600
font: Typography.p2
}
Item {
Layout.fillWidth: true
}
MediumButton {
Layout.alignment: Qt.AlignRight
text: qsTr("Supprimer")
icon.source: AppIcons.trashCan
icon.width: 16 * DefaultStyle.dp
icon.height: 16 * DefaultStyle.dp
contentImageColor: DefaultStyle.main1_500_main
onClicked: {
var mainWin = UtilsCpp.getMainWindow()
mainWin.showConfirmationLambdaPopup(
qsTr("Supprimer ") + modelData.core.deviceName + " ?",
function (confirmed) {
if (confirmed) {
modelData.core.removeDevice()
}
}
)
}
}
}
RowLayout {
Layout.rightMargin: 36 * DefaultStyle.dp
Layout.leftMargin: 33 * DefaultStyle.dp
spacing: 5 * DefaultStyle.dp
Text {
text: qsTr("Dernière connexion:")
color: DefaultStyle.main2_600
font: Typography.p2
}
EffectImage {
Layout.preferredWidth: 20 * DefaultStyle.dp
Layout.preferredHeight: 20 * DefaultStyle.dp
imageSource: AppIcons.calendar
colorizationColor: DefaultStyle.main2_600
fillMode: Image.PreserveAspectFit
}
Text {
text: UtilsCpp.formatDate(modelData.core.lastUpdateTimestamp,false)
color: DefaultStyle.main2_600
font: Typography.p1
}
EffectImage {
Layout.preferredWidth: 20 * DefaultStyle.dp
Layout.preferredHeight: 20 * DefaultStyle.dp
imageSource: AppIcons.clock
colorizationColor: DefaultStyle.main2_600
fillMode: Image.PreserveAspectFit
}
Text {
text: UtilsCpp.formatTime(modelData.core.lastUpdateTimestamp)
color: DefaultStyle.main2_600
font: Typography.p1
}
}
}
}
}
}
}
}
}
}
}

View file

@ -0,0 +1,171 @@
import QtCore
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls as Control
import QtQuick.Dialogs
import Linphone
import SettingsCpp 1.0
import UtilsCpp
AbstractDetailsLayout {
id: mainItem
component: main
property alias account: mainItem.model
Component {
id: main
ColumnLayout {
width: parent.width
spacing: 5 * DefaultStyle.dp
RowLayout {
Layout.topMargin: 16 * DefaultStyle.dp
spacing: 5 * DefaultStyle.dp
ColumnLayout {
Layout.fillWidth: true
spacing: 5 * DefaultStyle.dp
ColumnLayout {
Layout.preferredWidth: 341 * DefaultStyle.dp
Layout.maximumWidth: 341 * DefaultStyle.dp
Layout.minimumWidth: 341 * DefaultStyle.dp
spacing: 5 * DefaultStyle.dp
Text {
Layout.fillWidth: true
text: qsTr("Paramètres")
font: Typography.h4
wrapMode: Text.WordWrap
color: DefaultStyle.main2_600
}
}
Item {
Layout.fillHeight: true
}
}
ColumnLayout {
id: column
Layout.fillWidth: true
spacing: 20 * DefaultStyle.dp
Layout.rightMargin: 44 * DefaultStyle.dp
Layout.leftMargin: 64 * DefaultStyle.dp
Layout.topMargin: 20 * DefaultStyle.dp
ValidatedTextField {
propertyName: "mwiServerAddress"
propertyOwner: account.core
title: qsTr("URI du serveur de messagerie vocale")
isValid: function(text) { return UtilsCpp.isValidSIPAddress(text); }
}
Item {
Layout.fillHeight: true
Layout.fillWidth: true
}
}
}
Rectangle {
Layout.fillWidth: true
Layout.topMargin: 16 * DefaultStyle.dp
height: 1 * DefaultStyle.dp
color: DefaultStyle.main2_500main
}
RowLayout {
Layout.topMargin: 16 * DefaultStyle.dp
spacing: 5 * DefaultStyle.dp
ColumnLayout {
Layout.fillWidth: true
spacing: 5 * DefaultStyle.dp
ColumnLayout {
Layout.preferredWidth: 341 * DefaultStyle.dp
Layout.maximumWidth: 341 * DefaultStyle.dp
Layout.minimumWidth: 341 * DefaultStyle.dp
spacing: 5 * DefaultStyle.dp
Text {
Layout.fillWidth: true
text: qsTr("Paramètres avancés")
font: Typography.h4
wrapMode: Text.WordWrap
color: DefaultStyle.main2_600
}
}
Item {
Layout.fillHeight: true
}
}
ColumnLayout {
Layout.fillWidth: true
spacing: 20 * DefaultStyle.dp
Layout.rightMargin: 44 * DefaultStyle.dp
Layout.topMargin: 20 * DefaultStyle.dp
Layout.leftMargin: 64 * DefaultStyle.dp
Text {
text: qsTr("Transport")
color: DefaultStyle.main2_600
font: Typography.p2l
}
ComboSetting {
Layout.fillWidth: true
Layout.topMargin: -15 * DefaultStyle.dp
entries: account.core.transports
propertyName: "transport"
propertyOwner: account.core
}
ValidatedTextField {
title: qsTr("URL du serveur mandataire")
propertyName: "serverAddress"
propertyOwner: account.core
isValid: function(text) { return UtilsCpp.isValidSIPAddress(text); }
}
SwitchSetting {
titleText: qsTr("Serveur mandataire sortant")
propertyName: "outboundProxyEnabled"
propertyOwner: account.core
}
ValidatedTextField {
propertyName: "stunServer"
propertyOwner: account.core
title: qsTr("Adresse du serveur STUN")
isValid: function(text) { return UtilsCpp.isValidIPAddress(text) || UtilsCpp.isValidHostname(text); }
}
SwitchSetting {
titleText: qsTr("Activer ICE")
propertyName: "iceEnabled"
propertyOwner: account.core
}
SwitchSetting {
titleText: qsTr("AVPF")
propertyName: "avpfEnabled"
propertyOwner: account.core
}
SwitchSetting {
titleText: qsTr("Mode bundle")
propertyName: "bundleModeEnabled"
propertyOwner: account.core
}
ValidatedTextField {
propertyName: "expire"
propertyOwner: account.core
title: qsTr("Expiration (en seconde)")
isValid: function(text) { return !isNaN(Number(text)); }
}
ValidatedTextField {
title: qsTr("URI de lusine à conversations")
propertyName: "conferenceFactoryAddress"
propertyOwner: account.core
isValid: function(text) { return UtilsCpp.isValidSIPAddress(text); }
}
ValidatedTextField {
title: qsTr("URI de lusine à réunions")
propertyName: "audioVideoConferenceFactoryAddress"
propertyOwner: account.core
isValid: function(text) { return UtilsCpp.isValidSIPAddress(text); }
}
ValidatedTextField {
title: qsTr("URL du serveur déchange de clés de chiffrement")
propertyName: "limeServerUrl"
propertyOwner: account.core
isValid: function(text) { return UtilsCpp.isValidURL(text); }
}
Item {
Layout.fillHeight: true
}
}
}
}
}
}

View file

@ -15,9 +15,8 @@ import SettingsCpp
Item {
id: mainItem
property var callObj
property bool settingsHidden: true
property bool helpHidden: true
property var contextualMenuOpenedComponent: undefined
signal addAccountRequest()
signal openNewCall()
signal openCallHistory()
@ -41,6 +40,23 @@ Item {
tabbar.currentIndex = 1
mainItem.createContactRequested(name, address)
}
function openContextualMenuComponent(component) {
if (mainItem.contextualMenuOpenedComponent && mainItem.contextualMenuOpenedComponent != component) {
mainStackView.pop()
mainItem.contextualMenuOpenedComponent = undefined
}
if (!mainItem.contextualMenuOpenedComponent) {
mainStackView.push(component)
mainItem.contextualMenuOpenedComponent = component
}
settingsButton.popup.close()
}
function closeContextualMenuComponent() {
mainStackView.pop()
mainItem.contextualMenuOpenedComponent = undefined
}
AccountProxy {
id: accountProxy
@ -99,13 +115,8 @@ Item {
]
onCurrentIndexChanged: {
if (currentIndex === 0) accountProxy.defaultAccount.core.lResetMissedCalls()
if (!mainItem.settingsHidden) {
mainStackView.pop()
mainItem.settingsHidden = true
}
if (!mainItem.helpHidden) {
mainStackView.pop()
mainItem.helpHidden = true
if (mainItem.contextualMenuOpenedComponent) {
closeContextualMenuComponent()
}
}
}
@ -302,7 +313,7 @@ Item {
iconSize: 32 * DefaultStyle.dp
text: qsTr("Mon compte")
iconSource: AppIcons.manageProfile
onClicked: console.log("TODO : manage profile")
onClicked: openContextualMenuComponent(myAccountSettingsPageComponent)
}
IconLabelButton {
Layout.preferredHeight: 32 * DefaultStyle.dp
@ -310,19 +321,7 @@ Item {
iconSize: 32 * DefaultStyle.dp
text: qsTr("Paramètres")
iconSource: AppIcons.settings
onClicked: {
if (!mainItem.helpHidden) {
mainStackView.pop()
mainItem.helpHidden = true
}
if (mainItem.settingsHidden) {
mainStackView.push(settingsPageComponent)
settingsButton.popup.close()
mainItem.settingsHidden = false
} else {
settingsButton.popup.close()
}
}
onClicked: openContextualMenuComponent(settingsPageComponent)
}
IconLabelButton {
Layout.preferredHeight: 32 * DefaultStyle.dp
@ -336,19 +335,7 @@ Item {
iconSize: 32 * DefaultStyle.dp
text: qsTr("Aide")
iconSource: AppIcons.question
onClicked: {
if (!mainItem.settingsHidden) {
mainStackView.pop()
mainItem.settingsHidden = true
}
if (mainItem.helpHidden) {
mainStackView.push(helpPageComponent)
settingsButton.popup.close()
mainItem.helpHidden = false
} else {
settingsButton.popup.close()
}
}
onClicked: openContextualMenuComponent(helpPageComponent)
}
Rectangle {
Layout.fillWidth: true
@ -402,22 +389,23 @@ Item {
MeetingPage{}
}
}
Component {
id: myAccountSettingsPageComponent
AccountSettingsPage {
account: accountProxy.defaultAccount
onGoBack: closeContextualMenuComponent()
}
}
Component {
id: settingsPageComponent
SettingsPage {
onGoBack: {
mainStackView.pop()
mainItem.settingsHidden = true
}
onGoBack: closeContextualMenuComponent()
}
}
Component {
id: helpPageComponent
HelpPage {
onGoBack: {
mainStackView.pop()
mainItem.helpHidden = true
}
onGoBack: closeContextualMenuComponent()
}
}
Control.StackView {

View file

@ -5,7 +5,7 @@ import QtQuick.Controls as Control
import Linphone
import SettingsCpp 1.0
GenericSettingsLayout {
AbstractDetailsLayout {
component: settings
width: parent.width
Component {
@ -29,12 +29,14 @@ GenericSettingsLayout {
titleText: qsTr("Annulateur d'écho")
subTitleText: qsTr("Évite que de l'écho soit entendu par votre correspondant")
propertyName: "echoCancellationEnabled"
propertyOwner: SettingsCpp
}
SwitchSetting {
Layout.fillWidth: true
titleText: qsTr("Activer lenregistrement automatique des appels")
subTitleText: qsTr("Enregistrer tous les appels par défaut")
propertyName: "automaticallyRecordCallsEnabled"
propertyOwner: SettingsCpp
}
}
}
@ -101,8 +103,9 @@ GenericSettingsLayout {
Layout.fillWidth: true
Layout.topMargin: 12 * DefaultStyle.dp
Layout.preferredWidth: parent.width
model: SettingsCpp.playbackDevices
entries: SettingsCpp.playbackDevices
propertyName: "playbackDevice"
propertyOwner: SettingsCpp
}
Slider {
id: speakerVolume
@ -145,8 +148,9 @@ GenericSettingsLayout {
Layout.topMargin: 12 * DefaultStyle.dp
Layout.bottomMargin: 22 * DefaultStyle.dp
Layout.preferredWidth: parent.width
model: SettingsCpp.captureDevices
entries: SettingsCpp.captureDevices
propertyName: "captureDevice"
propertyOwner: SettingsCpp
}
Slider {
id: microVolume
@ -224,8 +228,9 @@ GenericSettingsLayout {
Layout.fillWidth: true
Layout.topMargin: 12 * DefaultStyle.dp
Layout.preferredWidth: parent.width
model: SettingsCpp.videoDevices
entries: SettingsCpp.videoDevices
propertyName: "videoDevice"
propertyOwner: SettingsCpp
}
Item {
Layout.fillHeight: true

View file

@ -7,7 +7,7 @@ import Linphone
import SettingsCpp 1.0
import UtilsCpp 1.0
GenericSettingsLayout {
AbstractDetailsLayout {
Layout.fillWidth: true
Layout.fillHeight: true
id: mainItem
@ -50,19 +50,21 @@ GenericSettingsLayout {
SwitchSetting {
titleText: qsTr("Activer les traces de débogage")
propertyName: "logsEnabled"
propertyOwner: SettingsCpp
}
SwitchSetting {
titleText: qsTr("Activer les traces de débogage intégrales")
propertyName: "fullLogsEnabled"
propertyOwner: SettingsCpp
}
MediumButton {
text: qsTr("Supprimer les traces")
text: qsTr("Supprimer les traces")
onClicked: {
deleteLogs.open()
}
}
MediumButton {
text: qsTr("Partager les traces")
text: qsTr("Partager les traces")
enabled: SettingsCpp.logsEnabled || SettingsCpp.fullLogsEnabled
onClicked: {
UtilsCpp.getMainWindow().showLoadingPopup(qsTr("Téléversement des traces en cours ..."))

View file

@ -2,10 +2,10 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls as Control
import SettingsCpp 1.0
import Linphone
GenericSettingsLayout {
AbstractDetailsLayout {
Component {
id: settings
Column {
@ -14,6 +14,7 @@ GenericSettingsLayout {
titleText: qsTr("Chiffrer tous les fichiers")
subTitleText: qsTr("Attention, vous ne pourrez pas revenir en arrière !")
propertyName: "vfsEnabled"
propertyOwner: SettingsCpp
}
}
}

View file

@ -49,6 +49,9 @@ AppWindow {
AccountProxy {
id: accountProxy
onHaveAccountChanged: {
initStackViewItem()
}
}
StackView {
id: mainWindowStackView

View file

@ -14,14 +14,18 @@ list(APPEND _LINPHONEAPP_QML_FILES
view/Layout/Meeting/AddParticipantsLayout.qml
view/Layout/FormItemLayout.qml
view/Layout/ValidatedTextField.qml
view/Layout/Mosaic.qml
view/Layout/RightPanelLayout.qml
view/Layout/Section.qml
view/App/Layout/Settings/GenericSettingsLayout.qml
view/App/Layout/AbstractDetailsLayout.qml
view/App/Layout/Settings/SecuritySettingsLayout.qml
view/App/Layout/Settings/CallSettingsLayout.qml
view/App/Layout/Settings/DebugSettingsLayout.qml
view/App/Layout/Account/AccountSettingsGeneralLayout.qml
view/App/Layout/Account/AccountSettingsParametersLayout.qml
view/Item/Account/Accounts.qml
@ -96,7 +100,7 @@ list(APPEND _LINPHONEAPP_QML_FILES
view/Item/Test/ItemsTest.qml
view/Item/Settings/SettingsFamily.qml
view/Item/MasterDetailFamily.qml
view/Item/Settings/SwitchSetting.qml
view/Item/Settings/ComboSetting.qml
@ -114,8 +118,11 @@ list(APPEND _LINPHONEAPP_QML_FILES
view/Page/Main/ContactPage.qml
view/Page/Main/MeetingPage.qml
view/Page/Main/AbstractMasterDetailPage.qml
view/Page/Main/SettingsPage.qml
view/Page/Main/HelpPage.qml
view/Page/Main/AccountSettingsPage.qml
view/Tool/utils.js
# Prototypes

View file

@ -105,6 +105,7 @@ Control.ComboBox {
width: mainItem.width
implicitHeight: contentItem.implicitHeight
padding: 1 * DefaultStyle.dp
height: Math.min(listView.contentHeight, 300)
contentItem: ListView {
id: listView

View file

@ -2,23 +2,24 @@ 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
property var propertyOwner
property alias entries: comboBox.model
oneLine: true
currentIndex: Utils.findIndex(model, function (entry) {
return entry === SettingsCpp[propertyName]
return entry === propertyOwner[propertyName]
})
onCurrentTextChanged: {
binding.when = currentText != SettingsCpp[propertyName]
binding.when = currentText != propertyOwner[propertyName]
}
Binding {
id: binding
target: SettingsCpp
target: propertyOwner
property: propertyName
value: comboBox.currentText
when: false

View file

@ -2,19 +2,20 @@ 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 var propertyOwner
property bool enabled: true
spacing : 20 * DefaultStyle.dp
Layout.minimumHeight: 32 * DefaultStyle.dp
ColumnLayout {
Text {
text: titleText
font: Typography.p2
font: Typography.p2l
wrapMode: Text.WordWrap
color: DefaultStyle.main2_600
Layout.fillWidth: true
@ -30,8 +31,8 @@ RowLayout {
}
SwitchButton {
id: switchButton
Layout.alignment: Qt.AlignRight
checked: SettingsCpp[mainItem.propertyName]
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
checked: propertyOwner[mainItem.propertyName]
enabled: mainItem.enabled
onToggled: {
binding.when = true
@ -39,7 +40,7 @@ RowLayout {
}
Binding {
id: binding
target: SettingsCpp
target: propertyOwner
property: mainItem.propertyName
value: switchButton.checked
when: false

View file

@ -5,7 +5,8 @@ import Linphone
Control.TextField {
id: mainItem
width: 360 * DefaultStyle.dp
property var customWidth
width: (customWidth ? customWidth - 1 : 360) * DefaultStyle.dp
height: 49 * DefaultStyle.dp
leftPadding: 15 * DefaultStyle.dp
rightPadding: eyeButton.visible ? 5 * DefaultStyle.dp + eyeButton.width + eyeButton.rightMargin : 15 * DefaultStyle.dp

View file

@ -48,4 +48,4 @@ ColumnLayout {
}
}
}
}

View file

@ -0,0 +1,56 @@
import QtQuick
import QtQuick.Controls as Control
import QtQuick.Layouts 1.0
import QtQuick.Effects
import UtilsCpp
import Linphone
FormItemLayout {
id: mainItem
label: title
mandatory: false
enableErrorText: true
property string propertyName
property var propertyOwner
property var title
property var placeHolder
property bool useTitleAsPlaceHolder: true
property int idleTimeOut: 200
property var isValid: function(text) {
return true;
}
contentItem: TextField {
id: textField
property var initialReading: true
placeholderText: useTitleAsPlaceHolder ? mainItem.title : mainItem.placeHolder
initialText: mainItem.propertyOwner[mainItem.propertyName]
customWidth: mainItem.parent.width
Timer {
id: idleTimer
running: false
interval: mainItem.idleTimeOut
repeat: false
onTriggered: textField.editingFinished()
}
onEditingFinished: {
if (initialReading) {
initialReading = false
return
}
if (text.length != 0) {
if (isValid(text)) {
mainItem.errorMessage = ""
if (mainItem.propertyOwner[mainItem.propertyName] != text)
mainItem.propertyOwner[mainItem.propertyName] = text
} else {
mainItem.errorMessage = qsTr("Format non reconnu")
}
} else
mainItem.errorMessage = ""
}
onTextChanged: {
idleTimer.restart()
}
}
}

View file

@ -0,0 +1,83 @@
import QtQuick
import QtQuick.Effects
import QtQuick.Layouts
import QtQuick.Controls as Control
import Linphone
import UtilsCpp
AbstractMainPage {
id: mainItem
showDefaultItem: false
property var layoutsPath
property var titleText
signal goBack()
function layoutUrl(name) {
return layoutsPath+"/"+name+".qml"
}
property var families
leftPanelContent: ColumnLayout {
id: leftPanel
Layout.fillWidth: true
Layout.fillHeight: true
property int sideMargin: 45 * DefaultStyle.dp
spacing: 5 * DefaultStyle.dp
RowLayout {
Layout.fillWidth: true
Layout.leftMargin: leftPanel.sideMargin
Layout.rightMargin: leftPanel.sideMargin
spacing: 5 * DefaultStyle.dp
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: titleText
color: DefaultStyle.main2_700
font: Typography.h3
}
Item {
Layout.fillWidth: true
}
}
ListView {
id: familiesList
Layout.fillWidth: true
Layout.fillHeight: true
model: mainItem.families
Layout.topMargin: 41 * DefaultStyle.dp
Layout.leftMargin: leftPanel.sideMargin
property int selectedIndex: 0
delegate: MasterDetailFamily {
titleText: modelData.title
visible: modelData.visible != undefined ? modelData.visible : true
isSelected: familiesList.selectedIndex == index
onSelected: {
familiesList.selectedIndex = index
rightPanelStackView.clear()
rightPanelStackView.push(layoutUrl(modelData.layout), { titleText: modelData.title, model: modelData.model, container: rightPanelStackView})
}
}
}
Component.onCompleted: {
let initialEntry = mainItem.families[familiesList.selectedIndex]
rightPanelStackView.push(layoutUrl(initialEntry.layout), { titleText: initialEntry.title, model: initialEntry.model, container: rightPanelStackView})
}
}
}

View file

@ -0,0 +1,18 @@
import QtQuick
import QtQuick.Effects
import QtQuick.Layouts
import QtQuick.Controls as Control
import Linphone
import UtilsCpp 1.0
import SettingsCpp 1.0
AbstractMasterDetailPage {
layoutsPath: "qrc:/Linphone/view/App/Layout/Account"
titleText: qsTr("Mon compte")
property AccountProxy accounts: AccountProxy{id: accountProxy}
property AccountGui account: accountProxy.defaultAccount
families: [
{title: qsTr("Général"), layout: "AccountSettingsGeneralLayout", model: account},
{title: qsTr("Paramètres de compte"), layout: "AccountSettingsParametersLayout", model: account}
]
}

View file

@ -18,11 +18,12 @@ AbstractMainPage {
Layout.fillWidth: true
Layout.fillHeight: true
property int sideMargin: 45 * DefaultStyle.dp
spacing: 5 * DefaultStyle.dp
RowLayout {
Layout.fillWidth: true
Layout.leftMargin: leftPanel.sideMargin
Layout.rightMargin: leftPanel.sideMargin
spacing: 5 * DefaultStyle.dp
Button {
Layout.preferredHeight: 24 * DefaultStyle.dp
Layout.preferredWidth: 24 * DefaultStyle.dp

View file

@ -2,22 +2,12 @@ import QtQuick
import QtQuick.Effects
import QtQuick.Layouts
import QtQuick.Controls as Control
import Linphone
import UtilsCpp
import SettingsCpp
AbstractMainPage {
id: mainItem
showDefaultItem: false
signal goBack()
function layoutUrl(name) {
return "qrc:/Linphone/view/App/Layout/Settings/"+name+".qml"
}
property var settingsFamilies: [
AbstractMasterDetailPage {
layoutsPath: "qrc:/Linphone/view/App/Layout/Settings"
titleText: qsTr("Paramètres")
families: [
{title: qsTr("Appels"), layout: "CallSettingsLayout"},
//{title: qsTr("Sécurité"), layout: "SecuritySettingsLayout"},
{title: qsTr("Conversations"), layout: "ChatSettingsLayout", visible: !SettingsCpp.disableChatFeature},
@ -27,63 +17,4 @@ AbstractMainPage {
{title: qsTr("Réseau"), layout: "NetworkSettingsLayout"},
{title: qsTr("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.h3
}
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: modelData.title
visible: modelData.visible != undefined ? modelData.visible : true
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

@ -109,4 +109,7 @@ QtObject {
property string cellSignalMedium: "image://internal/cell-signal-medium.svg"
property string cellSignalLow: "image://internal/cell-signal-low.svg"
property string cellSignalNone: "image://internal/cell-signal-none.svg"
property string mobile: "image://internal/mobile.svg"
property string desktop: "image://internal/desktop.svg"
property string calendar: "image://internal/calendar.svg"
}

View file

@ -52,6 +52,13 @@ QtObject {
weight: 400 * DefaultStyle.dp
})
// Text/P1 - Paratraph text
property font p1s: Qt.font( {
family: DefaultStyle.defaultFont,
pixelSize: 13 * DefaultStyle.dp,
weight: 400 * DefaultStyle.dp
})
// Bouton/B2 - Medium Bouton
property font b2: Qt.font( {
family: DefaultStyle.defaultFont,