Compare commits

...

40 commits

Author SHA1 Message Date
Sylvain Berfini
ff83c1794e Updated SDK to 5.4.42 & updated CHANGELOG 2025-09-08 09:47:32 +02:00
Sylvain Berfini
fd0cbdcd41 Updated Weblate translations 2025-09-03 12:34:09 +00:00
Gaelle Braud
971e7a7201 fix wrong thread for configuringStaus connect 2025-08-27 11:02:09 +02:00
Gaelle Braud
480704664e notifications in navigation bar
fix crash macos noification in task bar
2025-08-22 15:20:55 +02:00
Gaelle Braud
e78c7d04f0 try to add xinerama for screen sharing 2025-08-07 16:56:20 +02:00
gaelle
f25b4a83db fix : do not force capture and display unless video is not supported 2025-08-07 10:07:59 +02:00
Gaelle Braud
b3734702cb fix #LINQT-1839 force switching to main page when account added and remote provisioning configured 2025-08-07 10:07:02 +02:00
Gaelle Braud
9c3922db95 update sdk 2025-08-07 09:27:31 +02:00
Gaelle Braud
18e8d29959 fix #LINQT-1868 magic search list display name/addresses ui 2025-08-05 16:07:48 +02:00
Gaelle Braud
e018173b5f update sdk 2025-08-01 09:38:43 +02:00
Gaelle Braud
f84280e09d change bell icon 2025-07-31 14:52:08 +02:00
Gaelle Braud
cdb477853b show error message when remote provisioning failed on start 2025-07-31 14:51:28 +02:00
Julien Wadel
40df729a88 Fix addresses list from phone. Fix crash with MSQOGL and destroiying QML object that is still link with other objects. 2025-07-29 17:19:34 +02:00
Gaelle Braud
9cb5758233 fix tab button width 2025-07-29 14:37:47 +02:00
Gaelle Braud
33b7cf62f0 fix #LINQT-1862 double conference info on creation (remove unnecessary signal) 2025-07-29 14:37:06 +02:00
Gaelle Braud
ea2135d875 fix #LINQT-1648 playback refresh (typo in signal) 2025-07-28 17:54:46 +02:00
Gaelle Braud
a649266c26 fix #LINQT-1836 microphone muted in conference 2025-07-28 17:18:08 +02:00
Gaelle Braud
29caaf619a fix #LINQT-1848 missing translations + title fill width 2025-07-25 14:37:30 +02:00
Gaelle Braud
ea7621cf35 update sdk 2025-07-25 11:39:35 +02:00
Gaelle Braud
f6e20a9aff fix #LINQT-1853 update popup x if exceed width 2025-07-25 11:27:14 +02:00
Gaelle Braud
18ee1566f0 fix #LINQT-1655 set height offset for new notification if there is already some displayed 2025-07-24 16:00:37 +02:00
Jehan Monnier
14a959ef57 remove login_hint as most of the time it's unrelated to sip username 2025-07-23 12:05:55 +00:00
Jehan Monnier
c0ad0de987 fix oidc connector scope parsing in case of QT >= 9.0 2025-07-23 12:05:55 +00:00
Gaelle Braud
23f5abe0f1 update sdk 2025-07-23 12:01:13 +02:00
Gaelle Braud
a3130c6067 fix error popup when searching devices for an account which has just been removed 2025-07-23 09:52:26 +02:00
Gaelle Braud
aa871c2735 fix security circle avatar 2025-07-23 09:52:26 +02:00
Julien Wadel
8391d438eb Update AppImage tool URL to remove obsolete version. 2025-07-22 10:24:43 +02:00
Gaelle Braud
5ca6876e92 display year in meeting list if not this year 2025-07-22 09:51:11 +02:00
Gaelle Braud
7490cf0ca5 display security settings ui 2025-07-17 16:42:00 +02:00
Gaelle Braud
76277fbe9d rename hide sip addresses flag 2025-07-17 16:06:00 +02:00
Gaelle Braud
ca5f4411e7 update ci file 2025-07-10 12:35:22 +02:00
Jehan Monnier
e18a1f78e8 update oidc client secret auth 2025-06-04 06:58:58 +00:00
Gaelle Braud
e08d5e0377 try to fix read access violation (Mantis 0013842) 2025-05-26 11:47:19 +02:00
Gaelle Braud
6b02ebed39 update sdk 2025-05-14 10:06:47 +02:00
Gaelle Braud
490f5cdca1 fix #LINQT-1657 do not manipulate internal gains 2025-05-14 10:06:07 +02:00
Peio Rigaux
fb786c2a9d Fix windows signature return code, as we are using Invoke-Expression, and makes sure that this job doesn't prevent upload even when it fails 2025-05-13 18:04:32 +02:00
Gaelle Braud
23e32d1953 docker update : ubuntu 22.04, qt6.9.0
update gitlab ci file
2025-05-13 15:47:48 +02:00
Gaelle Braud
d025aaaeec changelog 2025-05-07 10:50:08 +02:00
Christophe Deschamps
faeed359fb German translations 2025-05-05 08:23:02 +00:00
Jehan Monnier
1e82f48839 Fix basic auth mode for oidc.
Make sure refresh token is optionnal
2025-04-30 13:14:19 +02:00
80 changed files with 41855 additions and 2695 deletions

View file

@ -0,0 +1,171 @@
.factorize_ubuntu2204: &docker_image_platform_and_runner_tag
tags: [ "docker" ]
image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-ubuntu-22-04-lts:$UBUNTU_2204_IMAGE_VERSION
ubuntu2204-ninja-gcc:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event" && $DOCKER_UPDATE == null && $SKIP_LINUX == null
variables:
CMAKE_GENERATOR: Ninja
CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON
CC: gcc
CXX: g++
extends: .linux-desktop
<<: *docker_image_platform_and_runner_tag
#################################################
# Nightly
#################################################
ubuntu2204-makefile-gcc:
rules:
- !reference [.rules-merge-request-manual, rules]
- if: $NIGHTLY_MASTER
variables:
CMAKE_GENERATOR: Unix Makefiles
CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON
CC: gcc
CXX: g++
ADDITIONAL_BUILD_OPTIONS: -j$MAKEFILE_JOBS
extends: .linux-desktop
<<: *docker_image_platform_and_runner_tag
ubuntu2204-ninja-clang:
rules:
- !reference [.rules-merge-request-manual, rules]
- if: $NIGHTLY_MASTER
variables:
CMAKE_OPTIONS: -DENABLE_DOC=ON -DENABLE_G729=ON -DENABLE_PQCRYPTO=ON -DENABLE_GPL_THIRD_PARTIES=ON
CMAKE_GENERATOR: Ninja
CC: clang
CXX: clang++
extends: .linux-desktop
allow_failure: true
<<: *docker_image_platform_and_runner_tag
ubuntu2204-ninja-clang-small:
rules:
- !reference [.rules-merge-request-manual, rules]
- if: $NIGHTLY_MASTER
variables:
CMAKE_OPTIONS: -DENABLE_VIDEO=NO -DENABLE_ADVANCED_IM=NO -DENABLE_DB_STORAGE=NO -DENABLE_PQCRYPTO=OFF
allow_failure: true
extends: ubuntu2204-ninja-clang
ubuntu2204-makefile-gcc:
rules:
- !reference [.rules-merge-request-manual, rules]
- if: $NIGHTLY_MASTER
- if: $DEPLOY_PLUGINS
variables:
CMAKE_OPTIONS: -DLINPHONE_BUILDER_SIGNING_IDENTITY=$GPG_SIGNING_KEYID -DENABLE_G729=ON -DENABLE_PQCRYPTO=ON -DENABLE_GPL_THIRD_PARTIES=ON
CMAKE_GENERATOR: Unix Makefiles
CC: gcc
CXX: g++
ADDITIONAL_BUILD_OPTIONS: -j$MAKEFILE_JOBS
APPIMAGETOOL_SIGN_PASSPHRASE: $GPG_SIGNING_PASS
script:
- echo "$GPG_SIGNING_PUB" > file.key && sed -i 's/\r /\n/g' file.key && chmod 600 file.key
- gpg --import file.key
- rm -f file.key
- echo "$GPG_SIGNING_KEY" > file.key && sed -i 's/\r /\n/g' file.key && chmod 600 file.key
- base64 -w 0 file.key | base64 -d | gpg --import --no-tty --batch --yes
- rm -f file.key
- cmake --version
- export CC=$CC
- export CXX=$CXX
- mkdir -p build/OUTPUT
- echo $CI_BUILD_TYPE
- echo $CMAKE_GENERATOR
- echo $DEFAULT_LINUX_CMAKE_OPTIONS
- echo $CMAKE_SANITIZER_OPTIONS
- eval "$(qtchooser -qt=$QT_LINUX_VER -print-env)"
- export PATH=${QTTOOLDIR}:$PATH
- export Qt6_DIR=${QTLIBDIR}/cmake/Qt6
- echo "Using Qt $QT_LINUX_VER at ${QTLIBDIR}"
- cd build
- cmake .. -G "$CMAKE_GENERATOR" -DCMAKE_VERBOSE_MAKEFILE=ON -DLINPHONESDK_PLATFORM=Desktop -DCMAKE_BUILD_TYPE=$CI_BUILD_TYPE -DLINPHONEAPP_APPLICATION_NAME="$APPLICATION_NAME" -DLINPHONEAPP_EXECUABLE_NAME="$EXECUTABLE_NAME" $DEFAULT_LINUX_CMAKE_OPTIONS $CMAKE_OPTIONS $SCHEDULE_CMAKE_OPTIONS $CMAKE_SANITIZER_OPTIONS
- cmake --build . --target install --config $CI_BUILD_TYPE $LBC_NODEBUG_OPTIONS
extends: .linux-desktop
<<: *docker_image_platform_and_runner_tag
#################################################
# Package - Nightly
#################################################
ubuntu2204-makefile-gcc-package:
stage: package
tags: [ "docker" ]
image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-ubuntu-22-04-lts:$UBUNTU_2204_IMAGE_VERSION
dependencies: []
rules:
- !reference [.rules-merge-request-manual, rules]
- if: $NIGHTLY_MASTER
- if: $PACKAGE_LINUX
- if: $DEPLOY_LINUX
variables:
CMAKE_OPTIONS: -DENABLE_APP_PACKAGING=YES -DLINPHONE_BUILDER_SIGNING_IDENTITY=$GPG_SIGNING_KEYID -DENABLE_G729=ON -DLINPHONE_SDK_MAKE_RELEASE_FILE_URL=$MAKE_RELEASE_FILE_URL/$LINUX_PLATFORM/$APP_FOLDER -DENABLE_PQCRYPTO=ON -DENABLE_GPL_THIRD_PARTIES=ON
CMAKE_GENERATOR: Unix Makefiles
CC: gcc
CXX: g++
APPIMAGETOOL_SIGN_PASSPHRASE: $GPG_SIGNING_PASS
extends: .linux-desktop
script:
- echo "$GPG_SIGNING_PUB" > file.key && sed -i 's/\r /\n/g' file.key && chmod 600 file.key
- gpg --import file.key
- rm -f file.key
- echo "$GPG_SIGNING_KEY" > file.key && sed -i 's/\r /\n/g' file.key && chmod 600 file.key
- base64 -w 0 file.key | base64 -d | gpg --import --no-tty --batch --yes
- rm -f file.key
- cmake --version
- export CC=$CC
- export CXX=$CXX
- mkdir -p build/OUTPUT
- echo $CI_BUILD_TYPE
- echo $CMAKE_GENERATOR
- echo $DEFAULT_LINUX_CMAKE_OPTIONS
- echo $CMAKE_SANITIZER_OPTIONS
- eval "$(qtchooser -qt=$QT_LINUX_VER -print-env)"
- export PATH=${QTTOOLDIR}:$PATH
- cd build
- cmake .. -G "$CMAKE_GENERATOR" -DCMAKE_VERBOSE_MAKEFILE=ON -DLINPHONESDK_PLATFORM=Desktop -DCMAKE_BUILD_TYPE=$CI_BUILD_TYPE -DLINPHONEAPP_APPLICATION_NAME="$APPLICATION_NAME" -DLINPHONEAPP_EXECUTABLE_NAME="$EXECUTABLE_NAME" $DEFAULT_LINUX_CMAKE_OPTIONS $CMAKE_OPTIONS $SCHEDULE_CMAKE_OPTIONS $CMAKE_SANITIZER_OPTIONS
- cmake --build . --target install --config $CI_BUILD_TYPE $LBC_NODEBUG_OPTIONS
artifacts:
paths:
- build/OUTPUT/*
expire_in: 1 week
#################################################
# Deploy - Nightly
#################################################
ubuntu2204-makefile-gcc-deploy:
stage: deploy
tags: [ "deploy" ]
needs:
- ubuntu2204-makefile-gcc-package
only:
variables:
- $NIGHTLY_MASTER
- $DEPLOY_LINUX
script:
- rsync -rlv --ignore-existing build/OUTPUT/Packages/*.AppImage $DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$LINUX_PLATFORM/$APP_FOLDER
- |-
if [[ $MAKE_RELEASE_FILE_URL != "" ]]; then
rsync -rlv build/OUTPUT/Packages/RELEASE $DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$LINUX_PLATFORM
rsync -rlv build/OUTPUT/Packages/RELEASE $MAIN_DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$LINUX_PLATFORM
fi
ubuntu2204-makefile-gcc-plugins-deploy:
stage: deploy
tags: [ "deploy" ]
needs:
- ubuntu2204-makefile-gcc
only:
variables:
- $DEPLOY_PLUGINS
script:
- rsync -rlv --ignore-existing build/OUTPUT/plugins/app/*.so $DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$LINUX_PLATFORM/$APP_FOLDER/plugins/

View file

@ -204,6 +204,7 @@ win64-ninja-vs2022-package-windows:
win64-codesigning:
stage: signing
allow_failure: true
extends:
- .windows-codesigning
needs:
@ -218,6 +219,7 @@ win64-codesigning:
script:
- cd build-desktop/OUTPUT/Packages/
- Invoke-Expression "& ${WINDOWS_SIGN_TOOL} sign /fd SHA256 /t ${WINDOWS_SIGN_TIMESTAMP_URL} /sha1 ${WINDOWS_SIGN_HASH} *.exe"
- 'if (-not ($LastExitCode -eq 0)) {throw "Error: Signature failed"}'
artifacts:
paths:
- build-desktop\OUTPUT\Packages\*

View file

@ -27,6 +27,7 @@ variables:
DEBIAN_10_IMAGE_VERSION: 20210217_python3
UBUNTU_ROLLING_IMAGE_VERSION: 20211012_add_qtwebview
UBUNTU_2004_IMAGE_VERSION: 20250226_qt6-8-0
UBUNTU_2204_IMAGE_VERSION: 20250630_add-qtshadertools
workflow:
@ -50,7 +51,8 @@ include:
- '.gitlab-ci-files/rules.yml'
- '.gitlab-ci-files/linux-prepare.yml'
- '.gitlab-ci-files/linux-desktop.yml'
- '.gitlab-ci-files/linux-desktop-ubuntu-2004.yml'
# - '.gitlab-ci-files/linux-desktop-ubuntu-2004.yml'
- '.gitlab-ci-files/linux-desktop-ubuntu-2204.yml'
- '.gitlab-ci-files/windows-desktop.yml'
- '.gitlab-ci-files/macosx-desktop.yml'

45
CHANGELOG.md Normal file
View file

@ -0,0 +1,45 @@
# Change Log
All notable changes to this project will be documented in this file.
Group changes to describe their impact on the project, as follows:
Added for new features.
Changed for changes in existing functionality.
Deprecated for once-stable features removed in upcoming releases.
Removed for deprecated features removed in this release.
Fixed for any bug fixes.
Security to invite users to upgrade in case of vulnerabilities.
## [6.0.1] - 2025-09-08
### Changed
- SDK updated to 5.4.42 release
- Updated translations from Weblate
### Fixed
- Various issues related to OpenID connect
- Wrong thread issue for remote provisioning
- Other UI related fixes
## [6.0.0] - 2025-05-16
6.0.0 release is a complete rework of Linphone Desktop, with only the call and contact list features availables.
Disclaimer: this release is focused on calls, meetings/conferences are available but aren't stable yet and chat features are missing.
### Added
- Contacts trust: contacts for which all devices have been validated through a ZRTP call with SAS exchange are now highlighted with a blue circle (and with a red one in case of mistrust). That trust is now handled at contact level (instead of conversation level in previous versions).
- Security focus: security & trust is more visible than ever, and unsecure conversations & calls are even more visible than before.
- CardDAV: you can configure as many CardDAV servers you want to synchronize you contacts in Linphone (in addition or in replacement of native addressbook import).
- OpenID: when used with a SSO compliant SIP server (such as Flexisip), we support single-sign-on login.
- MWI support: display and allow to call your voicemail when you have new messages (if supported by your VoIP provider and properly configured in your account params).
- CCMP support: if you configure a CCMP server URL in your accounts params, it will be used when scheduling meetings & to fetch list of meetings you've organized/been invited to.
- Devices list: check on which device your sip.linphone.org account is connected and the last connection date & time (like on subscribe.linphone.org).
### Changed
- Separated threads: Contrary to previous versions, our SDK is now running in it's own thread, meaning it won't freeze the UI anymore in case of heavy work, thus reducing the number of ANR and greatly increasing the fluidity of the app.
- Asymmetrical video : you no longer need to send your own camera feed to receive the one from the remote end of the call, and vice versa.
- Call transfer: Blind & Attended call transfer have been merged into one: during a call, if you initiate a transfer action, either pick another call to do the attended transfer or select a contact from the list (you can input a SIP URI not already in the suggestions list) to start a blind transfer.
- Settings: a lot of them are gone, the one that are still there have been reworked to increase user friendliness.
- Default screen (between contacts, call history, conversations & meetings list) will change depending on where you were when the app was paused or killed, and you will return to that last visited screen on the next startup.
- Minimum supported Qt version is now 6.5.3
- Some settings have changed name and/or section in linphonerc file.

View file

@ -93,6 +93,12 @@
#include "tool/request/RequestDialog.hpp"
#include "tool/thread/Thread.hpp"
#if defined(Q_OS_MACOS)
#include "core/event-count-notifier/EventCountNotifierMacOs.hpp"
#else
#include "core/event-count-notifier/EventCountNotifierSystemTrayIcon.hpp"
#endif // if defined(Q_OS_MACOS)
DEFINE_ABSTRACT_OBJECT(App)
#ifdef Q_OS_LINUX
@ -259,10 +265,11 @@ void App::setAutoStart(bool enabled) {
// -----------------------------------------------------------------------------
App::App(int &argc, char *argv[])
: SingleApplication(argc, argv, true, Mode::User | Mode::ExcludeAppPath | Mode::ExcludeAppVersion) {
: SingleApplication(argc, argv, true, Mode::User | Mode::ExcludeAppPath | Mode::ExcludeAppVersion) {
// Do not use APPLICATION_NAME here.
// The EXECUTABLE_NAME will be used in qt standard paths. It's our goal.
QThread::currentThread()->setPriority(QThread::HighPriority);
qDebug() << "app thread is" << QThread::currentThread();
QCoreApplication::setApplicationName(EXECUTABLE_NAME);
QApplication::setOrganizationDomain(EXECUTABLE_NAME);
QCoreApplication::setApplicationVersion(APPLICATION_SEMVER);
@ -291,6 +298,7 @@ App::App(int &argc, char *argv[])
emit currentDateChanged();
}
});
mEventCountNotifier = new EventCountNotifier(this);
mDateUpdateTimer.start();
}
@ -355,6 +363,50 @@ void App::setSelf(QSharedPointer<App>(me)) {
auto state = CoreModel::getInstance()->getCore()->getGlobalState();
mCoreModelConnection->invokeToCore([this, state] { setCoreStarted(state == linphone::GlobalState::On); });
});
mCoreModelConnection->makeConnectToModel(
&CoreModel::accountAdded,
[this](const std::shared_ptr<linphone::Core> &core, const std::shared_ptr<linphone::Account> &account) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
qDebug() << "account added";
if (CoreModel::getInstance()->mConfigStatus == linphone::ConfiguringState::Successful) {
qDebug() << "remote prov finished";
bool accountConnected = account && account->getState() == linphone::RegistrationState::Ok;
mCoreModelConnection->invokeToCore([this, accountConnected]() {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
// There is an account added by a remote provisioning, force switching to main page
// because the account may not be connected already
QMetaObject::invokeMethod(mMainWindow, "openMainPage", Qt::DirectConnection,
Q_ARG(QVariant, accountConnected));
});
}
});
mCoreModelConnection->makeConnectToModel(&CoreModel::unreadNotificationsChanged, [this] {
int n = mEventCountNotifier->getCurrentEventCount();
mCoreModelConnection->invokeToCore([this, n] { mEventCountNotifier->notifyEventCount(n); });
});
mCoreModelConnection->makeConnectToModel(&CoreModel::defaultAccountChanged, [this] {
int n = mEventCountNotifier->getCurrentEventCount();
mCoreModelConnection->invokeToCore([this, n] { mEventCountNotifier->notifyEventCount(n); });
});
// Config error message
mCoreModelConnection->makeConnectToModel(
&CoreModel::configuringStatus, [this](const std::shared_ptr<linphone::Core> &core,
linphone::ConfiguringState status, const std::string &message) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
if (status == linphone::ConfiguringState::Failed) {
mCoreModelConnection->invokeToCore([this, message]() {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
//: Error
Utils::showInformationPopup(
tr("info_popup_error_title"),
tr("info_popup_configuration_failed_message").arg(Utils::coreStringToAppString(message)),
false);
});
}
});
//---------------------------------------------------------------------------------------------
mCliModelConnection = SafeConnection<App, CliModel>::create(me, CliModel::getInstance());
mCliModelConnection->makeConnectToCore(&App::receivedMessage, [this](int, const QByteArray &byteArray) {
@ -383,6 +435,15 @@ QThread *App::getLinphoneThread() {
Notifier *App::getNotifier() const {
return mNotifier;
}
EventCountNotifier *App::getEventCountNotifier() {
return mEventCountNotifier;
}
int App::getEventCount() const {
return mEventCountNotifier ? mEventCountNotifier->getEventCount() : 0;
}
//-----------------------------------------------------------
// Initializations
//-----------------------------------------------------------
@ -426,6 +487,7 @@ void App::initCore() {
CoreModel::create("", mLinphoneThread);
if (mParser->isSet("verbose")) QtLogger::enableVerbose(true);
if (mParser->isSet("qt-logs-only")) QtLogger::enableQtOnly(true);
QMetaObject::invokeMethod(
mLinphoneThread->getThreadId(),
[this, settings = mSettings]() mutable {
@ -522,7 +584,7 @@ void App::initCore() {
setLocale(settings->getConfigLocale());
setAutoStart(settings->getAutoStart());
setQuitOnLastWindowClosed(settings->getExitOnClose());
}
}
const QUrl url("qrc:/qt/qml/Linphone/view/Page/Window/Main/MainWindow.qml");
QObject::connect(
mEngine, &QQmlApplicationEngine::objectCreated, this,
@ -546,6 +608,20 @@ void App::initCore() {
window->show();
} else lInfo() << log().arg("Stay minimized");
firstOpen = false;
lInfo() << log().arg("Checking remote provisioning");
if (CoreModel::getInstance()->mConfigStatus == linphone::ConfiguringState::Failed) {
QMetaObject::invokeMethod(thread(), [this]() {
auto message = CoreModel::getInstance()->mConfigMessage;
//: not reachable
if (message.isEmpty()) message = tr("configuration_error_detail");
mustBeInMainThread(log().arg(Q_FUNC_INFO));
//: Error
Utils::showInformationPopup(
tr("info_popup_error_title"),
//: Remote provisioning failed : %1
tr("info_popup_configuration_failed_message").arg(message), false);
});
}
}
},
Qt::QueuedConnection);
@ -565,7 +641,6 @@ static inline bool installLocale(App &app, QTranslator &translator, const QLocal
}
void App::initLocale() {
// Try to use preferred locale.
QString locale;
@ -573,28 +648,28 @@ void App::initLocale() {
mLocale = QLocale(QLocale::English);
if (!installLocale(*this, *mDefaultTranslatorCore, mLocale)) qFatal("Unable to install default translator.");
// if (installLocale(*this, *mTranslatorCore, getLocale())) {
// qDebug() << "installed locale" << getLocale().name();
// return;
// }
// if (installLocale(*this, *mTranslatorCore, getLocale())) {
// qDebug() << "installed locale" << getLocale().name();
// return;
// }
// Try to use system locale.
// #ifdef Q_OS_MACOS
// Use this workaround if there is still an issue about detecting wrong language from system on Mac. Qt doesn't use
// the current system language on QLocale::system(). So we need to get it from user settings and overwrite its
// Locale.
// QSettings settings;
// QString preferredLanguage = settings.value("AppleLanguages").toStringList().first();
// QStringList qtLocale = QLocale::system().name().split('_');
// if(qtLocale[0] != preferredLanguage){
// qInfo() << "Override Qt language from " << qtLocale[0] << " to the preferred language : " <<
// preferredLanguage; qtLocale[0] = preferredLanguage;
// }
// QLocale sysLocale = QLocale(qtLocale.join('_'));
// #else
QLocale sysLocale(QLocale::system().name()); // Use Locale from name because Qt has a bug where it didn't use the
// QLocale::language (aka : translator.language != locale.language) on
// Mac. #endif
// Try to use system locale.
// #ifdef Q_OS_MACOS
// Use this workaround if there is still an issue about detecting wrong language from system on Mac. Qt doesn't
// use the current system language on QLocale::system(). So we need to get it from user settings and overwrite
// its Locale.
// QSettings settings;
// QString preferredLanguage = settings.value("AppleLanguages").toStringList().first();
// QStringList qtLocale = QLocale::system().name().split('_');
// if(qtLocale[0] != preferredLanguage){
// qInfo() << "Override Qt language from " << qtLocale[0] << " to the preferred language : " <<
// preferredLanguage; qtLocale[0] = preferredLanguage;
// }
// QLocale sysLocale = QLocale(qtLocale.join('_'));
// #else
QLocale sysLocale(QLocale::system().name()); // Use Locale from name because Qt has a bug where it didn't use
// the QLocale::language (aka : translator.language !=
// locale.language) on Mac. #endif
if (installLocale(*this, *mTranslatorCore, sysLocale)) {
qDebug() << "installed sys locale" << sysLocale.name();
setLocale(sysLocale.name());
@ -775,34 +850,37 @@ void App::createCommandParser() {
//: "A free and open source SIP video-phone."
mParser->setApplicationDescription(tr("application_description"));
//: "Send an order to the application towards a command line"
mParser->addPositionalArgument("command", tr("command_line_arg_order").replace("%1", APPLICATION_NAME), "[command]");
mParser->addPositionalArgument("command", tr("command_line_arg_order").replace("%1", APPLICATION_NAME),
"[command]");
mParser->addOptions({
//: "Show this help"
{{"h", "help"}, tr("command_line_option_show_help")},
//: "Show this help"
{{"h", "help"}, tr("command_line_option_show_help")},
//{"cli-help", tr("commandLineOptionCliHelp").replace("%1", APPLICATION_NAME)},
//:"Show app version"
{{"v", "version"}, tr("command_line_option_show_app_version")},
//:"Show app version"
{{"v", "version"}, tr("command_line_option_show_app_version")},
//{"config", tr("command_line_option_config").replace("%1", EXECUTABLE_NAME), tr("command_line_option_config_arg")},
//{"config", tr("command_line_option_config").replace("%1", EXECUTABLE_NAME),
// tr("command_line_option_config_arg")},
{"fetch-config",
//: "Specify the linphone configuration file to be fetched. It will be merged with the current configuration."
tr("command_line_option_config_to_fetch")
.replace("%1", EXECUTABLE_NAME),
//: "URL, path or file"
tr("command_line_option_config_to_fetch_arg")},
//: "Specify the linphone configuration file to be fetched. It will be merged with the current
//: configuration."
tr("command_line_option_config_to_fetch").replace("%1", EXECUTABLE_NAME),
//: "URL, path or file"
tr("command_line_option_config_to_fetch_arg")},
//{{"c", "call"}, tr("command_line_option_call").replace("%1", EXECUTABLE_NAME), tr("command_line_option_call_arg")},
//{{"c", "call"}, tr("command_line_option_call").replace("%1", EXECUTABLE_NAME),
// tr("command_line_option_call_arg")},
{"minimized", tr("command_line_option_minimized")},
{"minimized", tr("command_line_option_minimized")},
//: "Log to stdout some debug information while running"
{{"V", "verbose"}, tr("command_line_option_log_to_stdout")},
//: "Log to stdout some debug information while running"
{{"V", "verbose"}, tr("command_line_option_log_to_stdout")},
//: "Print only logs from the application"
{"qt-logs-only", tr("command_line_option_print_app_logs_only")},
//: "Print only logs from the application"
{"qt-logs-only", tr("command_line_option_print_app_logs_only")},
});
}
// Should be call only at first start

View file

@ -54,6 +54,9 @@ public:
static QThread *getLinphoneThread();
Notifier *getNotifier() const;
EventCountNotifier *getEventCountNotifier();
int getEventCount() const;
// App::postModelAsync(<lambda>) => run lambda in model thread and continue.
// App::postModelSync(<lambda>) => run lambda in current thread and block connection.
template <typename Func, typename... Args>
@ -119,6 +122,10 @@ public:
void restart();
bool autoStartEnabled();
void setSysTrayIcon();
QSystemTrayIcon *getSystemTrayIcon() const {
return mSystemTrayIcon;
}
void updateSysTrayCount(int n);
QLocale getLocale();
void onLoggerInitialized();
@ -184,6 +191,7 @@ private:
QCommandLineParser *mParser = nullptr;
Thread *mLinphoneThread = nullptr;
Notifier *mNotifier = nullptr;
EventCountNotifier *mEventCountNotifier = nullptr;
QSystemTrayIcon *mSystemTrayIcon = nullptr;
QQuickWindow *mMainWindow = nullptr;
QQuickWindow *mCallsWindow = nullptr;

View file

@ -19,6 +19,7 @@ list(APPEND _LINPHONEAPP_SOURCES
core/camera/CameraGui.cpp
core/camera/CameraDummy.cpp
core/camera/PreviewManager.cpp
core/event-count-notifier/AbstractEventCountNotifier.cpp
core/fps-counter/FPSCounter.cpp
core/friend/FriendCore.cpp
core/friend/FriendGui.cpp
@ -99,5 +100,9 @@ else() # Use QDBus for Linux
core/singleapplication/SingleApplicationDBusPrivate.hpp
core/singleapplication/SingleApplicationDBus.cpp)
endif()
if(APPLE)
list(APPEND _LINPHONEAPP_SOURCES core/event-count-notifier/EventCountNotifierMacOs.m)
else()
list(APPEND _LINPHONEAPP_SOURCES core/event-count-notifier/EventCountNotifierSystemTrayIcon.cpp)
endif()
set(_LINPHONEAPP_SOURCES ${_LINPHONEAPP_SOURCES} PARENT_SCOPE)

View file

@ -83,8 +83,19 @@ void AccountDeviceList::refreshDevices() {
auto requestDeviceList = [this] {
if (!mAccountManagerServicesModelConnection) return;
mAccountManagerServicesModelConnection->invokeToModel([this]() {
auto connectedAccounts = CoreModel::getInstance()->getCore()->getAccountList();
auto identityAddress = mAccountCore->getModel()->getMonitor()->getParams()->getIdentityAddress();
auto authinfo = mAccountCore->getModel()->getMonitor()->findAuthInfo();
auto found = std::find_if(connectedAccounts.begin(), connectedAccounts.end(),
[identityAddress](std::shared_ptr<linphone::Account> account) {
return account && account->getParams()->getIdentityAddress()->weakEqual(
identityAddress);
});
if (found == connectedAccounts.end()) {
qDebug() << "[AccountDeviceList] account" << identityAddress->asStringUriOnly()
<< "not connected anymore, return";
return;
}
// auto authinfo = mAccountCore->getModel()->getMonitor()->findAuthInfo();
qDebug() << "[AccountDeviceList] request devices for address" << identityAddress->asStringUriOnly();
mAccountManagerServicesModel->getDeviceList(identityAddress);
});
@ -150,14 +161,14 @@ void AccountDeviceList::setSelf(QSharedPointer<AccountDeviceList> me) {
&AccountManagerServicesModel::requestError,
[this](const std::shared_ptr<const linphone::AccountManagerServicesRequest> &request, int statusCode,
const std::string &errorMessage,
const std::shared_ptr<const linphone::Dictionary> &parameterErrors) {
lDebug() << "REQUEST ERROR" << errorMessage << "/" << int(request->getType());
QString message = QString::fromStdString(errorMessage);
if (request->getType() == linphone::AccountManagerServicesRequest::Type::GetDevicesList) {
//: "Erreur lors de la récupération des appareils"
message = tr("manage_account_no_device_found_error_message");
}
emit requestError(message);
const std::shared_ptr<const linphone::Dictionary> &parameterErrors) {
lDebug() << "REQUEST ERROR" << errorMessage << "/" << int(request->getType());
QString message = QString::fromStdString(errorMessage);
if (request->getType() == linphone::AccountManagerServicesRequest::Type::GetDevicesList) {
//: "Erreur lors de la récupération des appareils"
message = tr("manage_account_no_device_found_error_message");
}
emit requestError(message);
});
mAccountManagerServicesModelConnection->makeConnectToModel(
&AccountManagerServicesModel::devicesListFetched,

View file

@ -107,8 +107,6 @@ CallCore::CallCore(const std::shared_ptr<linphone::Call> &call) : QObject(nullpt
mCallModel->setSelf(mCallModel);
mDuration = call->getDuration();
mIsStarted = mDuration > 0;
mMicrophoneMuted = call->getMicrophoneMuted();
mSpeakerMuted = call->getSpeakerMuted();
auto videoDirection = call->getParams()->getVideoDirection();
mLocalVideoEnabled =
videoDirection == linphone::MediaDirection::SendOnly || videoDirection == linphone::MediaDirection::SendRecv;
@ -158,6 +156,8 @@ CallCore::CallCore(const std::shared_ptr<linphone::Call> &call) : QObject(nullpt
if (mIsConference) {
mConference = ConferenceCore::create(conference);
}
mMicrophoneMuted = conference ? conference->getMicrophoneMuted() : call->getMicrophoneMuted();
mSpeakerMuted = call->getSpeakerMuted();
mPaused = mState == LinphoneEnums::CallState::Pausing || mState == LinphoneEnums::CallState::Paused ||
mState == LinphoneEnums::CallState::PausedByRemote;
@ -208,10 +208,11 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
mCallModelConnection->invokeToCore([this, recording]() {
setRecording(recording);
if (recording == false) {
//: "Enregistrement terminé"
Utils::showInformationPopup(tr("call_record_end_message"),
//: "L'appel a été enregistré dans le fichier : %1"
tr("call_record_saved_in_file_message").arg(QString::fromStdString(mCallModel->getRecordFile())),
//: "Enregistrement terminé"
Utils::showInformationPopup(tr("call_record_end_message"),
//: "L'appel a été enregistré dans le fichier : %1"
tr("call_record_saved_in_file_message")
.arg(QString::fromStdString(mCallModel->getRecordFile())),
true, App::getInstance()->getCallsWindow());
}
});
@ -308,8 +309,8 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
[this, call, encryption, tokenVerified, localToken, remoteTokens, isCaseMismatch]() {
setLocalToken(localToken);
setRemoteTokens(remoteTokens);
setIsMismatch(isCaseMismatch);
setTokenVerified(tokenVerified);
setIsMismatch(isCaseMismatch);
setTokenVerified(tokenVerified);
setEncryption(encryption);
});
auto mediaEncryption = call->getParams()->getMediaEncryption();
@ -321,7 +322,7 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
zrtpStats.mHashAlgorithm = Utils::coreStringToAppString(stats->getZrtpHashAlgo());
zrtpStats.mAuthenticationAlgorithm = Utils::coreStringToAppString(stats->getZrtpAuthTagAlgo());
zrtpStats.mSasAlgorithm = Utils::coreStringToAppString(stats->getZrtpSasAlgo());
zrtpStats.mIsPostQuantum = stats->isZrtpKeyAgreementAlgoPostQuantum();
zrtpStats.mIsPostQuantum = stats->isZrtpKeyAgreementAlgoPostQuantum();
mCallModelConnection->invokeToCore([this, zrtpStats]() { setZrtpStats(zrtpStats); });
}
});
@ -388,23 +389,23 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
auto codecType = playloadType ? playloadType->getMimeType() : "";
auto codecRate = playloadType ? playloadType->getClockRate() / 1000 : 0;
audioStats.mCodec =
//: "Codec: %1 / %2 kHz"
tr("call_stats_codec_label").arg(Utils::coreStringToAppString(codecType)).arg(codecRate);
//: "Codec: %1 / %2 kHz"
tr("call_stats_codec_label").arg(Utils::coreStringToAppString(codecType)).arg(codecRate);
auto linAudioStats = call->getAudioStats();
if (linAudioStats) {
//: "Bande passante : %1 %2 kbits/s %3 %4 kbits/s"
audioStats.mBandwidth = tr("call_stats_bandwidth_label")
//: "Bande passante : %1 %2 kbits/s %3 %4 kbits/s"
audioStats.mBandwidth = tr("call_stats_bandwidth_label")
.arg("")
.arg(round(linAudioStats->getUploadBandwidth()))
.arg("")
.arg(round(linAudioStats->getDownloadBandwidth()));
//: "Taux de perte: %1% %2%"
audioStats.mLossRate = tr("call_stats_loss_rate_label")
//: "Taux de perte: %1% %2%"
audioStats.mLossRate = tr("call_stats_loss_rate_label")
.arg(linAudioStats->getSenderLossRate())
.arg(linAudioStats->getReceiverLossRate());
//: "Tampon de gigue: %1 ms"
audioStats.mJitterBufferSize =
tr("call_stats_jitter_buffer_label").arg(linAudioStats->getJitterBufferSizeMs());
//: "Tampon de gigue: %1 ms"
audioStats.mJitterBufferSize =
tr("call_stats_jitter_buffer_label").arg(linAudioStats->getJitterBufferSizeMs());
}
setAudioStats(audioStats);
} else if (stats->getType() == linphone::StreamType::Video) {
@ -414,15 +415,15 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
auto codecType = playloadType ? playloadType->getMimeType() : "";
auto codecRate = playloadType ? playloadType->getClockRate() / 1000 : 0;
videoStats.mCodec =
tr("call_stats_codec_label").arg(Utils::coreStringToAppString(codecType)).arg(codecRate);
tr("call_stats_codec_label").arg(Utils::coreStringToAppString(codecType)).arg(codecRate);
auto linVideoStats = call->getVideoStats();
if (stats) {
videoStats.mBandwidth = tr("call_stats_bandwidth_label")
videoStats.mBandwidth = tr("call_stats_bandwidth_label")
.arg("")
.arg(round(linVideoStats->getUploadBandwidth()))
.arg("")
.arg(round(linVideoStats->getDownloadBandwidth()));
videoStats.mLossRate = tr("call_stats_loss_rate_label")
videoStats.mLossRate = tr("call_stats_loss_rate_label")
.arg(linVideoStats->getSenderLossRate())
.arg(linVideoStats->getReceiverLossRate());
}
@ -430,14 +431,14 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
params->getSentVideoDefinition() ? params->getSentVideoDefinition()->getName() : "";
auto receivedResolution =
params->getReceivedVideoDefinition() ? params->getReceivedVideoDefinition()->getName() : "";
//: "Définition vidéo : %1 %2 %3 %4"
videoStats.mResolution = tr("call_stats_resolution_label")
//: "Définition vidéo : %1 %2 %3 %4"
videoStats.mResolution = tr("call_stats_resolution_label")
.arg("", Utils::coreStringToAppString(sentResolution), "",
Utils::coreStringToAppString(receivedResolution));
auto sentFps = params->getSentFramerate();
auto receivedFps = params->getReceivedFramerate();
//: "FPS : %1 %2 %3 %4"
videoStats.mFps = tr("call_stats_fps_label").arg("").arg(sentFps).arg("").arg(receivedFps);
//: "FPS : %1 %2 %3 %4"
videoStats.mFps = tr("call_stats_fps_label").arg("").arg(sentFps).arg("").arg(receivedFps);
setVideoStats(videoStats);
}
});

View file

@ -198,15 +198,6 @@ void ConferenceInfoCore::setSelf(QSharedPointer<ConferenceInfoCore> me) {
uri] {
setConferenceSchedulerState(state);
setConferenceInfoState(infoState);
if (state == LinphoneEnums::ConferenceSchedulerState::Ready) {
setUri(uri);
mConfInfoModelConnection->invokeToModel([this, uri, infoState] {
if (infoState == LinphoneEnums::ConferenceInfoState::New)
emit CoreModel::getInstance()->conferenceInfoCreated(
mConferenceInfoModel->getConferenceInfo());
});
}
setConferenceSchedulerState(state);
});
});
mConfInfoModelConnection->makeConnectToModel(

View file

@ -99,9 +99,6 @@ void ConferenceInfoList::setSelf(QSharedPointer<ConferenceInfoList> me) {
// conference (which must hidden)
mCoreModelConnection->makeConnectToModel(&CoreModel::defaultAccountChanged, [this]() { emit lUpdate(true); });
mCoreModelConnection->makeConnectToModel(
&CoreModel::conferenceInfoCreated,
[this](const std::shared_ptr<linphone::ConferenceInfo> &confInfo) { addConference(confInfo); });
mCoreModelConnection->makeConnectToModel(
&CoreModel::conferenceInfoReceived,
[this](const std::shared_ptr<linphone::Core> &core,
@ -236,7 +233,9 @@ QVariant ConferenceInfoList::data(const QModelIndex &index, int role) const {
if (role == Qt::DisplayRole) {
return QVariant::fromValue(new ConferenceInfoGui(mList[row].objectCast<ConferenceInfoCore>()));
} else if (role == Qt::DisplayRole + 1) {
return Utils::toDateMonthString(mList[row].objectCast<ConferenceInfoCore>()->getDateTimeUtc());
auto date = mList[row].objectCast<ConferenceInfoCore>()->getDateTimeUtc();
if (date.date().year() != QDate::currentDate().year()) return Utils::toDateMonthAndYearString(date);
else return Utils::toDateMonthString(date);
}
} else { // Dummy date
if (role == Qt::DisplayRole) {

View file

@ -0,0 +1,63 @@
/*
* 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 <QtDebug>
#include "core/App.hpp"
#include "model/core/CoreModel.hpp"
#include "model/setting/SettingsModel.hpp"
#include "AbstractEventCountNotifier.hpp"
// =============================================================================
using namespace std;
DEFINE_ABSTRACT_OBJECT(AbstractEventCountNotifier)
AbstractEventCountNotifier::AbstractEventCountNotifier(QObject *parent) : QObject(parent) {
}
int AbstractEventCountNotifier::getEventCount() const {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto coreModel = CoreModel::getInstance();
int count = coreModel->getCore()->getMissedCallsCount();
return count;
}
int AbstractEventCountNotifier::getCurrentEventCount() const {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
// auto coreModel = CoreModel::getInstance();
// int count = coreModel->getCore()->getMissedCallsCount();
// bool filtered = SettingsModel::getInstance()->isSystrayNotificationFiltered();
// bool global = SettingsModel::getInstance()->isSystrayNotificationGlobal();
// if (global && !filtered)
return getEventCount();
// else {
// auto currentAccount = CoreModel::getInstance()->getCore()->getDefaultAccount();
// if (currentAccount) {
// auto linphoneChatRooms = currentAccount->filterChatRooms("");
// for (const auto &chatRoom : linphoneChatRooms) {
// count += chatRoom->getUnreadMessagesCount();
// }
// }
// return count;
// }
}

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 ABSTRACT_EVENT_COUNT_NOTIFIER_H_
#define ABSTRACT_EVENT_COUNT_NOTIFIER_H_
#include <QHash>
#include <QObject>
#include <QPair>
#include <QSystemTrayIcon>
#include "tool/AbstractObject.hpp"
#include "tool/thread/SafeConnection.hpp"
// =============================================================================
namespace linphone {
class ChatMessage;
}
class CallModel;
class ChatRoomModel;
class HistoryModel;
class AbstractEventCountNotifier : public QObject, public AbstractObject {
Q_OBJECT
public:
AbstractEventCountNotifier(QObject *parent = Q_NULLPTR);
int getEventCount() const; // global
int getCurrentEventCount() const; // Current account
protected:
virtual void notifyEventCount(int n) = 0;
private:
DECLARE_ABSTRACT_OBJECT
};
#endif // ABSTRACT_EVENT_COUNT_NOTIFIER_H_

View file

@ -0,0 +1,40 @@
/*
* 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 EVENT_COUNT_NOTIFIER_MAC_OS_H_
#define EVENT_COUNT_NOTIFIER_MAC_OS_H_
#include "AbstractEventCountNotifier.hpp"
// =============================================================================
extern "C" void notifyEventCountMacOs(int n);
class EventCountNotifier : public AbstractEventCountNotifier {
public:
EventCountNotifier(QObject *parent = Q_NULLPTR) : AbstractEventCountNotifier(parent) {
}
void notifyEventCount(int n) override {
notifyEventCountMacOs(n);
}
};
#endif // EVENT_COUNT_NOTIFIER_MAC_OS_H_

View file

@ -0,0 +1,30 @@
/*
* EventCountNotifierMacOs.m
* Copyright (C) 2017-2018 Belledonne Communications, Grenoble, France
*
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Created on: June 30, 2017
* Author: Ghislain MARY
*/
#import <Cocoa/Cocoa.h>
// =============================================================================
void notifyEventCountMacOs (int n) {
NSString *badgeStr = (n > 0) ? [NSString stringWithFormat:@"%d", n] : @"";
[[NSApp dockTile] setBadgeLabel:badgeStr];
}

View file

@ -0,0 +1,134 @@
/*
* 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 <QIcon>
#include <QPainter>
#include <QSvgRenderer>
#include <QSystemTrayIcon>
#include <QTimer>
#include <QWindow>
#include "core/App.hpp"
#include "model/core/CoreModel.hpp"
#include "model/setting/SettingsModel.hpp"
#include "tool/Constants.hpp"
#include "tool/Utils.hpp"
#include "EventCountNotifierSystemTrayIcon.hpp"
// =============================================================================
namespace {
constexpr int IconWidth = 256;
constexpr int IconHeight = 256;
constexpr int IconCounterBackgroundRadius = 100;
constexpr int IconCounterBlinkInterval = 1000;
constexpr int IconCounterTextPixelSize = 144;
} // namespace
DEFINE_ABSTRACT_OBJECT(EventCountNotifier)
QSharedPointer<EventCountNotifier> EventCountNotifier::create(QObject *parent) {
auto sharedPointer = QSharedPointer<EventCountNotifier>(new EventCountNotifier(parent), &QObject::deleteLater);
sharedPointer->setSelf(sharedPointer);
sharedPointer->moveToThread(App::getInstance()->thread());
return sharedPointer;
}
EventCountNotifier::EventCountNotifier(QObject *parent) : AbstractEventCountNotifier(parent) {
QSvgRenderer renderer((QString(Constants::WindowIconPath)));
if (!renderer.isValid()) qFatal("Invalid SVG Image.");
QPixmap buf(IconWidth, IconHeight);
buf.fill(QColor(Qt::transparent));
QPainter painter(&buf);
renderer.render(&painter);
mBuf = new QPixmap(buf);
mBufWithCounter = new QPixmap();
mBlinkTimer = new QTimer(this);
mBlinkTimer->setInterval(IconCounterBlinkInterval);
connect(mBlinkTimer, &QTimer::timeout, this, &EventCountNotifier::update);
}
void EventCountNotifier::setSelf(QSharedPointer<EventCountNotifier> me) {
}
EventCountNotifier::~EventCountNotifier() {
delete mBuf;
delete mBufWithCounter;
}
// -----------------------------------------------------------------------------
void EventCountNotifier::notifyEventCount(int n) {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
n = n > 99 ? 99 : n;
QSystemTrayIcon *sysTrayIcon = App::getInstance()->getSystemTrayIcon();
if (!sysTrayIcon) return;
if (!n) {
mBlinkTimer->stop();
sysTrayIcon->setIcon(QIcon(*mBuf));
return;
}
*mBufWithCounter = *mBuf;
QPainter p(mBufWithCounter);
const int width = mBufWithCounter->width();
const int height = mBufWithCounter->height();
// Draw background.
{
p.setBrush(QColor(Utils::getDefaultStyleColor("main1_100")));
p.drawEllipse(QPointF(width / 2, height / 2), IconCounterBackgroundRadius, IconCounterBackgroundRadius);
}
// Draw text.
{
QFont font = p.font();
font.setPixelSize(IconCounterTextPixelSize);
p.setFont(font);
p.setPen(QPen(QColor(Utils::getDefaultStyleColor("main1_500_main"))));
p.drawText(QRect(0, 0, width, height), Qt::AlignCenter, QString::number(n));
}
// Change counter.
mBlinkTimer->stop();
auto coreModel = CoreModel::getInstance();
if (!coreModel->isInitialized() || SettingsModel::getInstance()->isSystrayNotificationBlinkEnabled())
mBlinkTimer->start();
mDisplayCounter = true;
update();
}
void EventCountNotifier::update() {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
QSystemTrayIcon *sysTrayIcon = App::getInstance()->getSystemTrayIcon();
if (sysTrayIcon) {
sysTrayIcon->setIcon(QIcon(mDisplayCounter ? *mBufWithCounter : *mBuf));
}
mDisplayCounter = !mDisplayCounter;
}

View file

@ -0,0 +1,50 @@
/*
* 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 EVENT_COUNT_NOTIFIER_SYSTEM_TRAY_ICON_H_
#define EVENT_COUNT_NOTIFIER_SYSTEM_TRAY_ICON_H_
#include "AbstractEventCountNotifier.hpp"
// =============================================================================
class QTimer;
class EventCountNotifier : public AbstractEventCountNotifier {
public:
static QSharedPointer<EventCountNotifier> create(QObject *parent = Q_NULLPTR);
EventCountNotifier(QObject *parent = Q_NULLPTR);
void setSelf(QSharedPointer<EventCountNotifier> me);
~EventCountNotifier();
void notifyEventCount(int n) override;
private:
const QPixmap *mBuf = nullptr;
QPixmap *mBufWithCounter = nullptr;
QTimer *mBlinkTimer = nullptr;
bool mDisplayCounter = false;
QSharedPointer<SafeConnection<EventCountNotifier, CoreModel>> mCoreModelConnection;
void update();
DECLARE_ABSTRACT_OBJECT
};
#endif // EVENT_COUNT_NOTIFIER_SYSTEM_TRAY_ICON_H_

View file

@ -445,8 +445,9 @@ QList<QVariant> FriendCore::getAllAddresses() const {
auto phoneNumbers = mPhoneNumberList;
while (addressIt != mAddressList.end()) {
auto username = Utils::getUsername(addressIt->toMap()["address"].toString());
std::remove_if(phoneNumbers.begin(), phoneNumbers.end(),
[username](const QVariant &data) { return data.toMap()["address"].toString() == username; });
phoneNumbers.erase(std::remove_if(phoneNumbers.begin(), phoneNumbers.end(),
[username](const QVariant &data) { return data.toMap()["address"].toString() == username; })
, phoneNumbers.end());
++addressIt;
}
addresses << phoneNumbers;

View file

@ -171,28 +171,24 @@ bool Notifier::createNotification(Notifier::NotificationType type, QVariantMap d
// window->setProperty(it.key().toLatin1(), it.value());
const int timeout = Notifications[type].getTimeout() * 1000;
auto updateNotificationCoordinates = [this, window, screen](int width, int height) {
int *screenHeightOffset = &mScreenHeightOffset[screen->name()]; // Access optimization
auto screenHeightOffset =
screen ? mScreenHeightOffset.value(screen->name()) : 0; // Access optimization
QRect availableGeometry = screen->availableGeometry();
int heightOffset = availableGeometry.y() +
(availableGeometry.height() -
height); //*screen->devicePixelRatio(); when using manual scaler
window->setX(availableGeometry.x() +
(availableGeometry.width() -
width)); //*screen->devicePixelRatio()); when using manual scaler
window->setY(heightOffset - (*screenHeightOffset % heightOffset));
window->setY(availableGeometry.y() + availableGeometry.height() - screenHeightOffset -
height);
};
QObject::connect(window, &QQuickWindow::widthChanged,
[window, updateNotificationCoordinates](int w) {
updateNotificationCoordinates(w, window->height());
});
QObject::connect(window, &QQuickWindow::heightChanged,
[window, updateNotificationCoordinates](int h) {
updateNotificationCoordinates(window->width(), h);
});
updateNotificationCoordinates(window->width(), window->height());
QObject::connect(window, &QQuickWindow::closing, window,
[this, window] { deleteNotification(QVariant::fromValue(window)); });
auto screenHeightOffset =
screen ? mScreenHeightOffset.take(screen->name()) : 0; // Access optimization
mScreenHeightOffset.insert(screen->name(), screenHeightOffset + window->height());
QObject::connect(window, &QQuickWindow::closing, window, [this, window] {
qDebug() << "closing notification";
deleteNotification(QVariant::fromValue(window));
});
showNotification(window, timeout);
lInfo() << QStringLiteral("Create notification:") << QVariant::fromValue(window);
}

View file

@ -115,10 +115,13 @@ void MagicSearchList::setSelf(QSharedPointer<MagicSearchList> me) {
splitted.removeFirst();
contact->setFamilyName(splitted.join(" "));
} else {
contact->setGivenName(Utils::coreStringToAppString(address->getUsername()));
contact->setGivenName(displayName.isEmpty()
? Utils::coreStringToAppString(address->getUsername())
: displayName);
}
contact->setDefaultFullAddress(Utils::coreStringToAppString(
address->asString())); // linphone Friend object remove specific address.
contact->setDefaultAddress(Utils::coreStringToAppString(address->asString()));
contacts->append(contact);
} else if (!it->getPhoneNumber().empty()) {
auto phoneNumber = it->getPhoneNumber();

View file

@ -106,7 +106,7 @@ SettingsCore::SettingsCore(QObject *parent) : QObject(parent) {
INIT_CORE_MEMBER(AssistantDisableQrCode, settingsModel)
INIT_CORE_MEMBER(AssistantHideThirdPartyAccount, settingsModel)
INIT_CORE_MEMBER(OnlyDisplaySipUriUsername, settingsModel)
INIT_CORE_MEMBER(HideSipAddresses, settingsModel)
INIT_CORE_MEMBER(DarkModeAllowed, settingsModel)
INIT_CORE_MEMBER(MaxAccount, settingsModel)
INIT_CORE_MEMBER(AssistantGoDirectlyToThirdPartySipAccountLogin, settingsModel)
@ -179,7 +179,7 @@ SettingsCore::SettingsCore(const SettingsCore &settingsCore) {
mAssistantDisableQrCode = settingsCore.mAssistantDisableQrCode;
mAssistantHideThirdPartyAccount = settingsCore.mAssistantHideThirdPartyAccount;
mOnlyDisplaySipUriUsername = settingsCore.mOnlyDisplaySipUriUsername;
mHideSipAddresses = settingsCore.mHideSipAddresses;
mDarkModeAllowed = settingsCore.mDarkModeAllowed;
mMaxAccount = settingsCore.mMaxAccount;
mAssistantGoDirectlyToThirdPartySipAccountLogin = settingsCore.mAssistantGoDirectlyToThirdPartySipAccountLogin;
@ -206,6 +206,13 @@ void SettingsCore::setSelf(QSharedPointer<SettingsCore> me) {
mustBeInLinphoneThread(getClassName());
mSettingsModelConnection = SafeConnection<SettingsCore, SettingsModel>::create(me, SettingsModel::getInstance());
mSettingsModelConnection->makeConnectToModel(&SettingsModel::captureGraphRunningChanged, [this](bool running) {
mSettingsModelConnection->invokeToCore([this, running] {
mCaptureGraphRunning = running;
emit captureGraphRunningChanged(running);
});
});
// VFS
mSettingsModelConnection->makeConnectToModel(&SettingsModel::vfsEnabledChanged, [this](const bool enabled) {
mSettingsModelConnection->invokeToCore([this, enabled]() { setVfsEnabled(enabled); });
@ -379,7 +386,7 @@ void SettingsCore::setSelf(QSharedPointer<SettingsCore> me) {
DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, bool,
assistantHideThirdPartyAccount, AssistantHideThirdPartyAccount)
DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, bool,
onlyDisplaySipUriUsername, OnlyDisplaySipUriUsername)
hideSipAddresses, HideSipAddresses)
DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, bool,
darkModeAllowed, DarkModeAllowed)
DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, int, maxAccount,
@ -487,7 +494,7 @@ void SettingsCore::reset(const SettingsCore &settingsCore) {
setAssistantDisableQrCode(settingsCore.mAssistantDisableQrCode);
setAssistantHideThirdPartyAccount(settingsCore.mAssistantHideThirdPartyAccount);
setOnlyDisplaySipUriUsername(settingsCore.mOnlyDisplaySipUriUsername);
setHideSipAddresses(settingsCore.mHideSipAddresses);
setDarkModeAllowed(settingsCore.mDarkModeAllowed);
setMaxAccount(settingsCore.mMaxAccount);
setAssistantGoDirectlyToThirdPartySipAccountLogin(settingsCore.mAssistantGoDirectlyToThirdPartySipAccountLogin);
@ -560,7 +567,7 @@ QVariantList SettingsCore::getPlaybackDevices() const {
void SettingsCore::setPlaybackDevices(QVariantList devices) {
mPlaybackDevices = devices;
emit captureDevicesChanged(devices);
emit playbackDevicesChanged(devices);
}
QVariantList SettingsCore::getRingerDevices() const {
@ -958,7 +965,7 @@ void SettingsCore::writeIntoModel(std::shared_ptr<SettingsModel> model) const {
model->setAssistantDisableQrCode(mAssistantDisableQrCode);
model->setAssistantHideThirdPartyAccount(mAssistantHideThirdPartyAccount);
model->setOnlyDisplaySipUriUsername(mOnlyDisplaySipUriUsername);
model->setHideSipAddresses(mHideSipAddresses);
model->setDarkModeAllowed(mDarkModeAllowed);
model->setMaxAccount(mMaxAccount);
model->setAssistantGoDirectlyToThirdPartySipAccountLogin(mAssistantGoDirectlyToThirdPartySipAccountLogin);
@ -1024,7 +1031,7 @@ void SettingsCore::writeFromModel(const std::shared_ptr<SettingsModel> &model) {
mAssistantDisableQrCode = model->getAssistantDisableQrCode();
mAssistantHideThirdPartyAccount = model->getAssistantHideThirdPartyAccount();
mOnlyDisplaySipUriUsername = model->getOnlyDisplaySipUriUsername();
mHideSipAddresses = model->getHideSipAddresses();
mDarkModeAllowed = model->getDarkModeAllowed();
mMaxAccount = model->getMaxAccount();
mAssistantGoDirectlyToThirdPartySipAccountLogin = model->getAssistantGoDirectlyToThirdPartySipAccountLogin();

View file

@ -211,7 +211,7 @@ public:
DECLARE_CORE_GETSET_MEMBER(bool, assistantHideCreateAccount, AssistantHideCreateAccount)
DECLARE_CORE_GETSET_MEMBER(bool, assistantDisableQrCode, AssistantDisableQrCode)
DECLARE_CORE_GETSET_MEMBER(bool, assistantHideThirdPartyAccount, AssistantHideThirdPartyAccount)
DECLARE_CORE_GETSET_MEMBER(bool, onlyDisplaySipUriUsername, OnlyDisplaySipUriUsername)
DECLARE_CORE_GETSET_MEMBER(bool, hideSipAddresses, HideSipAddresses)
DECLARE_CORE_GETSET_MEMBER(bool, darkModeAllowed, DarkModeAllowed)
DECLARE_CORE_GETSET_MEMBER(int, maxAccount, MaxAccount)
DECLARE_CORE_GETSET_MEMBER(bool,

View file

@ -1,3 +0,0 @@
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.75 20C13.75 20.1989 13.671 20.3897 13.5303 20.5303C13.3897 20.671 13.1989 20.75 13 20.75H7.00002C6.80111 20.75 6.61034 20.671 6.46969 20.5303C6.32904 20.3897 6.25002 20.1989 6.25002 20C6.25002 19.8011 6.32904 19.6103 6.46969 19.4697C6.61034 19.329 6.80111 19.25 7.00002 19.25H13C13.1989 19.25 13.3897 19.329 13.5303 19.4697C13.671 19.6103 13.75 19.8011 13.75 20ZM19.3178 4.65499C18.4749 3.00748 17.2092 1.61364 15.6503 0.616239C15.567 0.563035 15.4741 0.526817 15.3767 0.509668C15.2794 0.492519 15.1796 0.494776 15.0832 0.516311C14.9867 0.537846 14.8955 0.578233 14.8147 0.635151C14.7339 0.692069 14.6651 0.764396 14.6124 0.847973C14.5596 0.931549 14.5239 1.02473 14.5073 1.12215C14.4906 1.21958 14.4934 1.31933 14.5155 1.41567C14.5376 1.51201 14.5784 1.60304 14.6358 1.68353C14.6931 1.76402 14.7658 1.83238 14.8497 1.88468C16.1871 2.73546 17.2707 3.93076 17.9866 5.34499C18.0812 5.51593 18.2388 5.64321 18.4258 5.69974C18.6128 5.75628 18.8145 5.73761 18.9879 5.6477C19.1614 5.5578 19.2929 5.40376 19.3545 5.21836C19.4161 5.03296 19.403 4.83083 19.3178 4.65499ZM1.34783 5.74999C1.48523 5.74993 1.61998 5.71213 1.73736 5.64071C1.85474 5.56929 1.95025 5.46699 2.01346 5.34499C2.72933 3.93076 3.81291 2.73546 5.15033 1.88468C5.23419 1.83238 5.30689 1.76402 5.36424 1.68353C5.4216 1.60304 5.46248 1.51201 5.48454 1.41567C5.50659 1.31933 5.50939 1.21958 5.49277 1.12215C5.47615 1.02473 5.44043 0.931549 5.38768 0.847973C5.33493 0.764396 5.26617 0.692069 5.18538 0.635151C5.10458 0.578233 5.01333 0.537846 4.91687 0.516311C4.82041 0.494776 4.72065 0.492519 4.62331 0.509668C4.52598 0.526817 4.433 0.563035 4.34971 0.616239C2.79085 1.61364 1.52514 3.00748 0.682206 4.65499C0.622991 4.76929 0.594192 4.8969 0.598581 5.02556C0.602969 5.15421 0.640397 5.27957 0.707263 5.38957C0.774128 5.49956 0.868181 5.5905 0.980369 5.65363C1.09256 5.71675 1.2191 5.74994 1.34783 5.74999ZM18.7947 15.4944C18.9276 15.7222 18.998 15.9811 18.9989 16.2448C18.9998 16.5086 18.9312 16.7679 18.7999 16.9967C18.6686 17.2255 18.4793 17.4156 18.251 17.5478C18.0228 17.6801 17.7638 17.7498 17.5 17.75H2.50002C2.23641 17.7495 1.9776 17.6795 1.74965 17.5471C1.5217 17.4147 1.33266 17.2246 1.20158 16.9959C1.0705 16.7672 1.002 16.508 1.00299 16.2444C1.00398 15.9808 1.07441 15.7221 1.20721 15.4944C2.05189 14.0366 2.50002 11.9637 2.50002 9.49999C2.50002 7.51087 3.29019 5.60321 4.69672 4.19669C6.10324 2.79017 8.01089 1.99999 10 1.99999C11.9891 1.99999 13.8968 2.79017 15.3033 4.19669C16.7098 5.60321 17.5 7.51087 17.5 9.49999C17.5 11.9628 17.9481 14.0356 18.7947 15.4944ZM17.5 16.25C16.5025 14.5372 16 12.2666 16 9.49999C16 7.90869 15.3679 6.38257 14.2427 5.25735C13.1174 4.13213 11.5913 3.49999 10 3.49999C8.40872 3.49999 6.8826 4.13213 5.75738 5.25735C4.63216 6.38257 4.00002 7.90869 4.00002 9.49999C4.00002 12.2675 3.49564 14.5381 2.50002 16.25H17.5Z" fill="#000000"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.9 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M224,71.1a8,8,0,0,1-10.78-3.42,94.13,94.13,0,0,0-33.46-36.91,8,8,0,1,1,8.54-13.54,111.46,111.46,0,0,1,39.12,43.09A8,8,0,0,1,224,71.1ZM35.71,72a8,8,0,0,0,7.1-4.32A94.13,94.13,0,0,1,76.27,30.77a8,8,0,1,0-8.54-13.54A111.46,111.46,0,0,0,28.61,60.32,8,8,0,0,0,35.71,72Zm186.1,103.94A16,16,0,0,1,208,200H167.2a40,40,0,0,1-78.4,0H48a16,16,0,0,1-13.79-24.06C43.22,160.39,48,138.28,48,112a80,80,0,0,1,160,0C208,138.27,212.78,160.38,221.81,175.94ZM150.62,200H105.38a24,24,0,0,0,45.24,0ZM208,184c-10.64-18.27-16-42.49-16-72a64,64,0,0,0-128,0c0,29.52-5.38,53.74-16,72Z"></path></svg>

After

Width:  |  Height:  |  Size: 680 B

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M168,224a8,8,0,0,1-8,8H96a8,8,0,1,1,0-16h64A8,8,0,0,1,168,224Zm53.85-32A15.8,15.8,0,0,1,208,200H48a16,16,0,0,1-13.8-24.06C39.75,166.38,48,139.34,48,104a80,80,0,1,1,160,0c0,35.33,8.26,62.38,13.81,71.94A15.89,15.89,0,0,1,221.84,192ZM208,184c-7.73-13.27-16-43.95-16-80a64,64,0,1,0-128,0c0,36.06-8.28,66.74-16,80Z"></path></svg>

Before

Width:  |  Height:  |  Size: 433 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M221.8,175.94C216.25,166.38,208,139.33,208,104a80,80,0,1,0-160,0c0,35.34-8.26,62.38-13.81,71.94A16,16,0,0,0,48,200H88.81a40,40,0,0,0,78.38,0H208a16,16,0,0,0,13.8-24.06ZM128,216a24,24,0,0,1-22.62-16h45.24A24,24,0,0,1,128,216ZM48,184c7.7-13.24,16-43.92,16-80a64,64,0,1,1,128,0c0,36.05,8.28,66.73,16,80Z"></path></svg>

After

Width:  |  Height:  |  Size: 424 B

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -75,21 +75,41 @@ OIDCModel::OIDCModel(const std::shared_ptr<linphone::AuthInfo> &authInfo, QObjec
qDebug() << "OIDC Client ID set to [" << clientid << "]";
// find an auth info from LinphoneCore where username = clientid
auto clientSecret = CoreModel::getInstance()->getCore()->findAuthInfo("", clientid.toStdString(), "");
if (clientSecret != nullptr) {
qDebug() << "client secret found for client id [" << clientid << "]";
mOidc.setClientIdentifierSharedKey(clientSecret->getPassword().c_str());
std::shared_ptr<linphone::AuthInfo> clientSecret = nullptr;
// search for auth info for this client id
for (const auto &authInfo : CoreModel::getInstance()->getCore()->getAuthInfoList()) {
if (authInfo->getClientId() == clientid.toStdString()) {
qDebug() << "AuthInfo found for client id [" << clientid << "]";
clientSecret = authInfo;
break;
}
}
QString scope = OIDCScope;
;
if (clientSecret != nullptr) {
qDebug() << "client secret found for client id [" << clientid << "]";
mOidc.setClientIdentifierSharedKey(clientSecret->getClientSecret().c_str());
}
QSet<QByteArray> scopeTokens;
if (autorizationUrl.hasQuery()) {
QUrlQuery query(autorizationUrl);
if (query.hasQueryItem("scope")) {
scope = query.queryItemValue("scope");
auto scopeList = query.queryItemValue("scope").split(' ');
for (const auto &scopeItem : scopeList) {
scopeTokens.insert(scopeItem.toUtf8());
}
}
}
mOidc.setScope(scope);
if (scopeTokens.isEmpty()) {
scopeTokens.insert(OIDCScope);
qDebug() << "No scope found in authorization URL, using default scope [" << OIDCScope << "]";
}
#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)
mOidc.setRequestedScopeTokens(scopeTokens);
#else
mOidc.setScope(QStringList(scopeTokens.begin(), scopeTokens.end()).join(' '));
#endif
mTimeout.setInterval(1000 * 60 * 2); // 2minutes
connect(&mTimeout, &QTimer::timeout, [this]() {
@ -189,10 +209,34 @@ OIDCModel::OIDCModel(const std::shared_ptr<linphone::AuthInfo> &authInfo, QObjec
connect(this, &OIDCModel::authenticated, this, &OIDCModel::setBearers);
#if QT_VERSION < QT_VERSION_CHECK(6, 9, 0)
// Connect the signal to the tokensReceived handler to get id_token
connect(mOidc.replyHandler(), &QOAuthHttpServerReplyHandler::tokensReceived, this,
[this](const QVariantMap &tokens) {
// for (auto it = tokens.cbegin(); it != tokens.cend(); ++it) {
// qDebug() << "Token key:" << it.key() << ", value:" << it.value().toString();
// }
if (tokens.contains("id_token") &&
CoreModel::getInstance()->getCore()->getConfig()->getBool("app", "oidc_use_id_token", false)) {
auto idToken = tokens["id_token"].toString();
qDebug() << "ID Token received:" << idToken.left(3) + "..." + idToken.right(3);
mIdToken = idToken;
} else if (tokens.contains("access_token")) {
auto accessToken = tokens["access_token"].toString();
qDebug() << "Access Token received:" << accessToken.left(3) + "..." + accessToken.right(3);
mIdToken = accessToken;
} else {
mIdToken.clear();
qWarning() << "No ID Token or Access Token found in the tokens.";
emit requestFailed(tr("oidc_authentication_no_token_found_error"));
emit finished();
}
});
#endif
// in case we want to add parameters. Needed to override redirect_url
mOidc.setModifyParametersFunction([&, username = Utils::coreStringToAppString(authInfo->getUsername())](
QAbstractOAuth::Stage stage, QMultiMap<QString, QVariant> *parameters) {
parameters->insert("login_hint", username);
parameters->replace("application_type", "native");
switch (stage) {
case QAbstractOAuth::Stage::RequestingAccessToken: {
@ -244,7 +288,11 @@ void OIDCModel::openIdConfigReceived() {
return;
}
if (rootArray.contains("token_endpoint")) {
#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)
mOidc.setTokenUrl(QUrl(rootArray["token_endpoint"].toString()));
#else
mOidc.setAccessTokenUrl(QUrl(rootArray["token_endpoint"].toString()));
#endif
mAuthInfo->setTokenEndpointUri(
Utils::appStringToCoreString(QUrl(rootArray["token_endpoint"].toString()).toString()));
} else {
@ -262,13 +310,35 @@ void OIDCModel::setBearers() {
auto expiration = QDateTime::currentDateTime().secsTo(mOidc.expirationAt());
auto timeT = mOidc.expirationAt().toSecsSinceEpoch();
qDebug() << "Authenticated for " << expiration << "s";
auto refreshBearer =
linphone::Factory::get()->createBearerToken(Utils::appStringToCoreString(mOidc.refreshToken()), timeT);
auto accessBearer = linphone::Factory::get()->createBearerToken(Utils::appStringToCoreString(mOidc.token()), timeT);
mAuthInfo->setRefreshToken(refreshBearer);
auto accessBearer = linphone::Factory::get()->createBearerToken(Utils::appStringToCoreString(idToken()), timeT);
mAuthInfo->setAccessToken(accessBearer);
if (mOidc.refreshToken() != nullptr) {
auto refreshBearer =
linphone::Factory::get()->createBearerToken(Utils::appStringToCoreString(mOidc.refreshToken()), timeT);
mAuthInfo->setRefreshToken(refreshBearer);
} else {
qWarning() << "No refresh token found";
}
CoreModel::getInstance()->getCore()->addAuthInfo(mAuthInfo);
emit CoreModel::getInstance()->bearerAccountAdded();
emit CoreModel::getInstance() -> bearerAccountAdded();
emit finished();
}
QString OIDCModel::idToken() const {
#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)
if (CoreModel::getInstance()->getCore()->getConfig()->getBool("app", "oidc_use_id_token", false)) {
if (!mOidc.idToken().isEmpty()) {
return mOidc.idToken();
} else {
return mOidc.token();
}
} else {
return mOidc.token();
}
#else
return mIdToken;
#endif
}

View file

@ -44,11 +44,26 @@ signals:
void finished();
private:
/**
* @brief Retrieves the ID token.
*
* This function returns the ID token as a QString. The ID token is typically
* used for authentication and authorization purposes in OpenID Connect (OIDC)
* workflows. Implementation is specific to QT version
*
* @return The ID token as a QString.
*/
QString idToken() const;
#if QT_VERSION < QT_VERSION_CHECK(6, 9, 0)
QString mIdToken;
#endif
QOAuth2AuthorizationCodeFlow mOidc;
std::shared_ptr<linphone::AuthInfo> mAuthInfo;
QTimer mTimeout;
DECLARE_ABSTRACT_OBJECT
};
;
#endif

View file

@ -116,8 +116,10 @@ void CallModel::terminateAllCalls() {
void CallModel::setMicrophoneMuted(bool isMuted) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
mMonitor->setMicrophoneMuted(isMuted);
emit microphoneMutedChanged(isMuted);
if (mMonitor->getConference()) mMonitor->getConference()->setMicrophoneMuted(isMuted);
else mMonitor->setMicrophoneMuted(isMuted);
emit microphoneMutedChanged(mMonitor->getConference() ? mMonitor->getConference()->getMicrophoneMuted()
: mMonitor->getMicrophoneMuted());
}
void CallModel::setSpeakerMuted(bool isMuted) {
@ -411,7 +413,7 @@ void CallModel::onStateChanged(const std::shared_ptr<linphone::Call> &call,
emit localVideoEnabledChanged(videoDirection == linphone::MediaDirection::SendOnly ||
videoDirection == linphone::MediaDirection::SendRecv);
emit remoteVideoEnabledChanged(remoteVideoDirection == linphone::MediaDirection::SendOnly ||
remoteVideoDirection == linphone::MediaDirection::SendRecv);
remoteVideoDirection == linphone::MediaDirection::SendRecv);
updateConferenceVideoLayout();
} else if (state == linphone::Call::State::End || state == linphone::Call::State::Error) {
mDurationTimer.stop();

View file

@ -34,6 +34,12 @@
#include "model/tool/ToolModel.hpp"
#include "tool/Utils.hpp"
#if defined(Q_OS_MACOS)
#include "core/event-count-notifier/EventCountNotifierMacOs.hpp"
#else
#include "core/event-count-notifier/EventCountNotifierSystemTrayIcon.hpp"
#endif // if defined(Q_OS_MACOS)
// =============================================================================
DEFINE_ABSTRACT_OBJECT(CoreModel)
@ -85,9 +91,9 @@ void CoreModel::start() {
// Useful if the app was built without video support.
// (The capture/display attributes are reset by the core in this case.)
auto config = mCore->getConfig();
if (mCore->videoSupported()) {
config->setInt("video", "capture", 1);
config->setInt("video", "display", 1);
if (!mCore->videoSupported()) {
config->setInt("video", "capture", 0);
config->setInt("video", "display", 0);
}
// TODO : set the real transport type when sdk will be updated
@ -116,18 +122,27 @@ void CoreModel::start() {
mCore->enableFriendListSubscription(true);
if (mCore->getLogCollectionUploadServerUrl().empty())
mCore->setLogCollectionUploadServerUrl(Constants::DefaultUploadLogsServer);
/// These 2 API should not be used as they manage internal gains insterad of those of the soundcard.
// Use playback/capture gain from capture graph and call only
mCore->setMicGainDb(0.0);
mCore->setPlaybackGainDb(0.0);
mIterateTimer->start();
auto linphoneSearch = mCore->createMagicSearch();
linphoneSearch->setLimitedSearch(true);
mMagicSearch = Utils::makeQObject_ptr<MagicSearchModel>(linphoneSearch);
mMagicSearch->setSelf(mMagicSearch);
connect(mMagicSearch.get(), &MagicSearchModel::searchResultsReceived, this, [this] {
emit magicSearchResultReceived(mMagicSearch->mLastSearch);
});
connect(mMagicSearch.get(), &MagicSearchModel::searchResultsReceived, this,
[this] { emit magicSearchResultReceived(mMagicSearch->mLastSearch); });
mStarted = true;
}
// -----------------------------------------------------------------------------
bool CoreModel::isInitialized() const {
return mStarted;
}
std::shared_ptr<CoreModel> CoreModel::getInstance() {
return gCoreModel;
}
@ -352,10 +367,11 @@ void CoreModel::migrate() {
config->setInt(SettingsModel::UiSection, Constants::RcVersionName, Constants::RcVersionCurrent);
}
void CoreModel::searchInMagicSearch(QString filter, int sourceFlags,
LinphoneEnums::MagicSearchAggregation aggregation,
int maxResults) {
mMagicSearch->search(filter, sourceFlags, aggregation, maxResults);
void CoreModel::searchInMagicSearch(QString filter,
int sourceFlags,
LinphoneEnums::MagicSearchAggregation aggregation,
int maxResults) {
mMagicSearch->search(filter, sourceFlags, aggregation, maxResults);
}
//---------------------------------------------------------------------------------------------------------------------------
@ -446,6 +462,8 @@ void CoreModel::onConferenceStateChanged(const std::shared_ptr<linphone::Core> &
void CoreModel::onConfiguringStatus(const std::shared_ptr<linphone::Core> &core,
linphone::ConfiguringState status,
const std::string &message) {
mConfigStatus = status;
mConfigMessage = Utils::coreStringToAppString(message);
emit configuringStatus(core, status, message);
}
void CoreModel::onDefaultAccountChanged(const std::shared_ptr<linphone::Core> &core,

View file

@ -25,6 +25,7 @@
#include <QObject>
#include <QSharedPointer>
#include <QString>
#include <QSystemTrayIcon>
#include <QThread>
#include <QTimer>
#include <linphone++/linphone.hh>
@ -34,11 +35,14 @@
#include "model/cli/CliModel.hpp"
#include "model/listener/Listener.hpp"
#include "model/logger/LoggerModel.hpp"
#include "tool/AbstractObject.hpp"
#include "model/search/MagicSearchModel.hpp"
#include "tool/AbstractObject.hpp"
// =============================================================================
class AbstractEventCountNotifier;
class EventCountNotifier;
class CoreModel : public ::Listener<linphone::Core, linphone::CoreListener>,
public linphone::CoreListener,
public AbstractObject {
@ -52,6 +56,8 @@ public:
std::shared_ptr<linphone::Core> getCore();
std::shared_ptr<LoggerModel> getLogger();
bool isInitialized() const;
void start();
void setConfigPath(QString path);
@ -61,11 +67,13 @@ public:
void migrate();
void searchInMagicSearch(QString filter,
int sourceFlags,
LinphoneEnums::MagicSearchAggregation aggregation,
int maxResults);
int sourceFlags,
LinphoneEnums::MagicSearchAggregation aggregation,
int maxResults);
bool mEnd = false;
linphone::ConfiguringState mConfigStatus;
QString mConfigMessage;
std::shared_ptr<linphone::Core> mCore;
std::shared_ptr<LoggerModel> mLogger;
@ -76,7 +84,6 @@ signals:
void friendRemoved(const std::shared_ptr<linphone::Friend> &f);
void friendUpdated(const std::shared_ptr<linphone::Friend> &f);
void bearerAccountAdded();
void conferenceInfoCreated(const std::shared_ptr<linphone::ConferenceInfo> &confInfo);
void unreadNotificationsChanged();
void requestFetchConfig(QString path);
void requestRestart();
@ -88,6 +95,7 @@ private:
QTimer *mIterateTimer = nullptr;
QMap<QString, OIDCModel *> mOpenIdConnections;
std::shared_ptr<MagicSearchModel> mMagicSearch;
bool mStarted = false;
void setPathBeforeCreation();
void setPathsAfterCreation();

View file

@ -83,14 +83,19 @@ void MagicSearchModel::onSearchResultsReceived(const std::shared_ptr<linphone::M
auto ldapFriends = ToolModel::getLdapFriendList();
emit searchResultsReceived(results);
for (auto result : results) {
if (!result) continue;
auto f = result->getFriend();
auto friendsManager = FriendsManager::getInstance();
if (f) {
qDebug() << "friend exists, append to unknown map";
auto friendAddress = f->getAddress();
friendsManager->appendUnknownFriend(friendAddress->clone(), f);
if (friendsManager->isInOtherAddresses(Utils::coreStringToAppString(friendAddress->asStringUriOnly()))) {
friendsManager->removeOtherAddress(Utils::coreStringToAppString(friendAddress->asStringUriOnly()));
auto friendAddress = f->getAddress() ? f->getAddress()->clone() : nullptr;
if (friendAddress) {
friendAddress->clean();
friendsManager->appendUnknownFriend(friendAddress, f);
if (friendsManager->isInOtherAddresses(
Utils::coreStringToAppString(friendAddress->asStringUriOnly()))) {
friendsManager->removeOtherAddress(Utils::coreStringToAppString(friendAddress->asStringUriOnly()));
}
}
}
auto fList = f ? f->getFriendList() : nullptr;
@ -127,7 +132,7 @@ void MagicSearchModel::updateFriendListWithFriend(const std::shared_ptr<linphone
if (ToolModel::friendIsInFriendList(friendList, linphoneFriend))
return; // Already exist. We don't need to manipulate list.
for (auto address : linphoneFriend->getAddresses()) {
auto existingFriend = friendList->findFriendByAddress(address);
auto existingFriend = friendList ? friendList->findFriendByAddress(address) : nullptr;
if (existingFriend) {
friendList->removeFriend(existingFriend);
friendList->addFriend(linphoneFriend);
@ -135,7 +140,7 @@ void MagicSearchModel::updateFriendListWithFriend(const std::shared_ptr<linphone
}
}
for (auto number : linphoneFriend->getPhoneNumbers()) {
auto existingFriend = friendList->findFriendByPhoneNumber(number);
auto existingFriend = friendList ? friendList->findFriendByPhoneNumber(number) : nullptr;
if (existingFriend) {
friendList->removeFriend(existingFriend);
friendList->addFriend(linphoneFriend);
@ -143,6 +148,6 @@ void MagicSearchModel::updateFriendListWithFriend(const std::shared_ptr<linphone
}
}
qInfo() << log().arg("Adding Friend:") << linphoneFriend.get();
friendList->addFriend(linphoneFriend);
if (friendList) friendList->addFriend(linphoneFriend);
emit CoreModel::getInstance()->friendCreated(linphoneFriend);
}

View file

@ -172,6 +172,7 @@ void SettingsModel::stopCaptureGraph() {
void SettingsModel::accessCallSettings() {
// Audio
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
startCaptureGraph();
CoreModel::getInstance()->getCore()->reloadSoundDevices();
emit captureDevicesChanged(getCaptureDevices());
emit playbackDevicesChanged(getPlaybackDevices());
@ -182,7 +183,6 @@ void SettingsModel::accessCallSettings() {
emit playbackGainChanged(getPlaybackGain());
emit captureGainChanged(getCaptureGain());
startCaptureGraph();
// Video
CoreModel::getInstance()->getCore()->reloadVideoDevices();
emit videoDevicesChanged(getVideoDevices());
@ -202,13 +202,12 @@ bool SettingsModel::getCaptureGraphRunning() {
float SettingsModel::getMicVolume() {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
float v = 0.0;
if (mSimpleCaptureGraph && mSimpleCaptureGraph->isRunning()) {
v = mSimpleCaptureGraph->getCaptureVolume();
} else {
auto call = CoreModel::getInstance()->getCore()->getCurrentCall();
if (call) {
v = MediastreamerUtils::computeVu(call->getRecordVolume());
v = call->getRecordVolume();
}
}
@ -218,33 +217,49 @@ float SettingsModel::getMicVolume() {
float SettingsModel::getPlaybackGain() const {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
float dbGain = CoreModel::getInstance()->getCore()->getPlaybackGainDb();
return MediastreamerUtils::dbToLinear(dbGain);
if (mSimpleCaptureGraph && mSimpleCaptureGraph->isRunning()) {
return mSimpleCaptureGraph->getPlaybackGain();
} else {
auto call = CoreModel::getInstance()->getCore()->getCurrentCall();
if (call) return call->getSpeakerVolumeGain();
else return 0.0;
}
}
void SettingsModel::setPlaybackGain(float gain) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
float oldGain = getPlaybackGain();
CoreModel::getInstance()->getCore()->setPlaybackGainDb(MediastreamerUtils::linearToDb(gain));
if (mSimpleCaptureGraph && mSimpleCaptureGraph->isRunning()) {
mSimpleCaptureGraph->setPlaybackGain(gain);
}
auto currentCall = CoreModel::getInstance()->getCore()->getCurrentCall();
if (currentCall) {
currentCall->setSpeakerVolumeGain(gain);
}
if ((int)(oldGain * 1000) != (int)(gain * 1000)) emit playbackGainChanged(gain);
}
float SettingsModel::getCaptureGain() const {
mustBeInLinphoneThread(getClassName());
float dbGain = CoreModel::getInstance()->getCore()->getMicGainDb();
return MediastreamerUtils::dbToLinear(dbGain);
if (mSimpleCaptureGraph && mSimpleCaptureGraph->isRunning()) {
return mSimpleCaptureGraph->getCaptureGain();
} else {
auto call = CoreModel::getInstance()->getCore()->getCurrentCall();
if (call) return call->getMicrophoneVolumeGain();
else return 0.0;
}
}
void SettingsModel::setCaptureGain(float gain) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
float oldGain = getCaptureGain();
CoreModel::getInstance()->getCore()->setMicGainDb(MediastreamerUtils::linearToDb(gain));
if (mSimpleCaptureGraph && mSimpleCaptureGraph->isRunning()) {
mSimpleCaptureGraph->setCaptureGain(gain);
}
auto currentCall = CoreModel::getInstance()->getCore()->getCurrentCall();
if (currentCall) {
currentCall->setMicrophoneVolumeGain(gain);
}
if ((int)(oldGain * 1000) != (int)(gain * 1000)) emit captureGainChanged(gain);
}
@ -256,6 +271,8 @@ QVariantList SettingsModel::getCaptureDevices() const {
for (const auto &device : core->getExtendedAudioDevices()) {
if (device->hasCapability(linphone::AudioDevice::Capabilities::CapabilityRecord)) {
list << ToolModel::createVariant(device);
} else if (device->hasCapability(linphone::AudioDevice::Capabilities::CapabilityAll)) {
list << ToolModel::createVariant(device);
}
}
return list;
@ -267,8 +284,11 @@ QVariantList SettingsModel::getPlaybackDevices() const {
QVariantList list;
for (const auto &device : core->getExtendedAudioDevices()) {
if (device->hasCapability(linphone::AudioDevice::Capabilities::CapabilityPlay))
if (device->hasCapability(linphone::AudioDevice::Capabilities::CapabilityPlay)) {
list << ToolModel::createVariant(device);
} else if (device->hasCapability(linphone::AudioDevice::Capabilities::CapabilityAll)) {
list << ToolModel::createVariant(device);
}
}
return list;
@ -706,6 +726,20 @@ QString SettingsModel::getDefaultDomain() const {
mConfig->getString(SettingsModel::AppSection, "default_domain", "sip.linphone.org"));
}
bool SettingsModel::isSystrayNotificationBlinkEnabled() const {
return !!mConfig->getInt(UiSection, "systray_notification_blink", 1);
}
bool SettingsModel::isSystrayNotificationGlobal() const {
return !!mConfig->getInt(UiSection, "systray_notification_global", 1);
}
bool SettingsModel::isSystrayNotificationFiltered() const {
return !!mConfig->getInt(UiSection, "systray_notification_filtered", 0);
}
bool SettingsModel::getLimeIsSupported() const {
return CoreModel::getInstance()->getCore()->limeX3DhAvailable();
}
// clang-format off
void SettingsModel::notifyConfigReady(){
DEFINE_NOTIFY_CONFIG_READY(disableChatFeature, DisableChatFeature)
@ -717,7 +751,7 @@ void SettingsModel::notifyConfigReady(){
DEFINE_NOTIFY_CONFIG_READY(assistantHideCreateAccount, AssistantHideCreateAccount)
DEFINE_NOTIFY_CONFIG_READY(assistantDisableQrCode, AssistantDisableQrCode)
DEFINE_NOTIFY_CONFIG_READY(assistantHideThirdPartyAccount, AssistantHideThirdPartyAccount)
DEFINE_NOTIFY_CONFIG_READY(onlyDisplaySipUriUsername, OnlyDisplaySipUriUsername)
DEFINE_NOTIFY_CONFIG_READY(hideSipAddresses, HideSipAddresses)
DEFINE_NOTIFY_CONFIG_READY(darkModeAllowed, DarkModeAllowed)
DEFINE_NOTIFY_CONFIG_READY(assistantGoDirectlyToThirdPartySipAccountLogin,
AssistantGoDirectlyToThirdPartySipAccountLogin)
@ -779,9 +813,9 @@ DEFINE_GETSET_CONFIG(SettingsModel,
DEFINE_GETSET_CONFIG(SettingsModel,
bool,
Bool,
onlyDisplaySipUriUsername,
OnlyDisplaySipUriUsername,
"only_display_sip_uri_username",
hideSipAddresses,
HideSipAddresses,
"hide_sip_addresses",
false)
DEFINE_GETSET_CONFIG(SettingsModel,
bool,

View file

@ -157,6 +157,11 @@ public:
static bool clearLocalLdapFriendsUponStartup(const std::shared_ptr<linphone::Config> &config);
bool isSystrayNotificationBlinkEnabled() const;
bool isSystrayNotificationGlobal() const;
bool isSystrayNotificationFiltered() const;
bool getLimeIsSupported() const;
// UI
DECLARE_GETSET(bool, disableChatFeature, DisableChatFeature)
DECLARE_GETSET(bool, disableMeetingsFeature, DisableMeetingsFeature)
@ -168,7 +173,7 @@ public:
DECLARE_GETSET(bool, assistantHideCreateAccount, AssistantHideCreateAccount)
DECLARE_GETSET(bool, assistantDisableQrCode, AssistantDisableQrCode)
DECLARE_GETSET(bool, assistantHideThirdPartyAccount, AssistantHideThirdPartyAccount)
DECLARE_GETSET(bool, onlyDisplaySipUriUsername, OnlyDisplaySipUriUsername)
DECLARE_GETSET(bool, hideSipAddresses, HideSipAddresses)
DECLARE_GETSET(bool, darkModeAllowed, DarkModeAllowed)
DECLARE_GETSET(int, maxAccount, MaxAccount)
DECLARE_GETSET(bool, assistantGoDirectlyToThirdPartySipAccountLogin, AssistantGoDirectlyToThirdPartySipAccountLogin)

View file

@ -20,6 +20,7 @@
#include "ToolModel.hpp"
#include "core/App.hpp"
#include "core/conference/ConferenceInfoCore.hpp"
#include "core/path/Paths.hpp"
#include "model/core/CoreModel.hpp"
#include "model/friend/FriendsManager.hpp"
@ -120,20 +121,18 @@ std::shared_ptr<linphone::Friend> ToolModel::findFriendByAddress(const QString &
auto defaultFriendList = CoreModel::getInstance()->getCore()->getDefaultFriendList();
if (!defaultFriendList) return nullptr;
auto linphoneAddr = ToolModel::interpretUrl(address);
if (linphoneAddr)
return ToolModel::findFriendByAddress(linphoneAddr);
else
return nullptr;
if (linphoneAddr) return ToolModel::findFriendByAddress(linphoneAddr);
else return nullptr;
}
std::shared_ptr<linphone::Friend> ToolModel::findFriendByAddress(std::shared_ptr<linphone::Address> linphoneAddr) {
auto friendsManager = FriendsManager::getInstance();
QString key = Utils::coreStringToAppString(linphoneAddr->asStringUriOnly());
if (friendsManager->isInKnownFriends(key)) {
// qDebug() << key << "have been found in known friend, return it";
// qDebug() << key << "have been found in known friend, return it";
return friendsManager->getKnownFriendAtKey(key);
} else if (friendsManager->isInUnknownFriends(key)) {
// qDebug() << key << "have been found in unknown friend, return it";
} else if (friendsManager->isInUnknownFriends(key)) {
// qDebug() << key << "have been found in unknown friend, return it";
return friendsManager->getUnknownFriendAtKey(key);
}
auto f = CoreModel::getInstance()->getCore()->findFriend(linphoneAddr);
@ -141,20 +140,21 @@ std::shared_ptr<linphone::Friend> ToolModel::findFriendByAddress(std::shared_ptr
if (friendsManager->isInUnknownFriends(key)) {
friendsManager->removeUnknownFriend(key);
}
// qDebug() << "found friend, add to known map";
// qDebug() << "found friend, add to known map";
friendsManager->appendKnownFriend(linphoneAddr, f);
}
if (!f) {
if (friendsManager->isInOtherAddresses(key)) {
// qDebug() << "A magic search has already be done for address" << key << "and nothing was found, return";
// qDebug() << "A magic search has already be done for address" << key << "and nothing was found,
// return";
return nullptr;
}
friendsManager->appendOtherAddress(key);
// qDebug() << "Couldn't find friend" << linphoneAddr->asStringUriOnly() << "in core, use magic search";
// qDebug() << "Couldn't find friend" << linphoneAddr->asStringUriOnly() << "in core, use magic search";
CoreModel::getInstance()->searchInMagicSearch(Utils::coreStringToAppString(linphoneAddr->asStringUriOnly()),
(int)linphone::MagicSearch::Source::LdapServers
| (int)linphone::MagicSearch::Source::RemoteCardDAV
, LinphoneEnums::MagicSearchAggregation::Friend, 50);
(int)linphone::MagicSearch::Source::LdapServers |
(int)linphone::MagicSearch::Source::RemoteCardDAV,
LinphoneEnums::MagicSearchAggregation::Friend, 50);
}
return f;
}
@ -378,6 +378,7 @@ std::shared_ptr<linphone::FriendList> ToolModel::getLdapFriendList() {
bool ToolModel::friendIsInFriendList(const std::shared_ptr<linphone::FriendList> &friendList,
const std::shared_ptr<linphone::Friend> &f) {
if (!friendList) return false;
auto friends = friendList->getFriends();
auto it = std::find_if(friends.begin(), friends.end(),
[f](std::shared_ptr<linphone::Friend> linFriend) { return linFriend == f; });

View file

@ -36,10 +36,13 @@
#include <limits.h>
#include <QClipboard>
#include <QColor>
#include <QCryptographicHash>
#include <QDesktopServices>
#include <QHostAddress>
#include <QImageReader>
#include <QQmlComponent>
#include <QQmlProperty>
#include <QQuickWindow>
#include <QRandomGenerator>
#include <QRegularExpression>
@ -425,6 +428,25 @@ VariantObject *Utils::findFriendByAddress(const QString &address) {
return data;
}
VariantObject *Utils::getFriendSecurityLevel(const QString &address) {
VariantObject *data = new VariantObject("getFriendAddressSecurityLevel");
if (!data) return nullptr;
data->makeRequest([address]() {
auto defaultFriendList = ToolModel::getAppFriendList();
if (!defaultFriendList) return QVariant();
auto linphoneAddr = ToolModel::interpretUrl(address);
auto linFriend = CoreModel::getInstance()->getCore()->findFriend(linphoneAddr);
if (!linFriend) return QVariant();
auto linAddr = ToolModel::interpretUrl(address);
if (!linAddr) return QVariant();
auto devices = linFriend->getDevicesForAddress(linphoneAddr);
int verified = 0;
return QVariant::fromValue(LinphoneEnums::fromLinphone(linFriend->getSecurityLevel()));
});
data->requestValue();
return data;
}
VariantObject *Utils::getFriendAddressSecurityLevel(const QString &address) {
VariantObject *data = new VariantObject("getFriendAddressSecurityLevel");
if (!data) return nullptr;
@ -532,6 +554,16 @@ QString Utils::getOsProduct() {
return product + "/" + version;
}
QColor Utils::getDefaultStyleColor(const QString &colorName) {
mustBeInMainThread(sLog().arg(Q_FUNC_INFO));
static QObject *defaultStyleSingleton = nullptr;
if (!defaultStyleSingleton) {
QQmlComponent component(App::getInstance()->mEngine, QUrl("qrc:/qt/qml/Linphone/view/Style/DefaultStyle.qml"));
defaultStyleSingleton = component.create();
}
return QQmlProperty::read(defaultStyleSingleton, colorName).value<QColor>();
}
QString Utils::getCountryName(const QLocale::Territory &p_country) {
QString countryName;
switch (p_country) {
@ -1440,7 +1472,7 @@ QList<QVariant> Utils::append(const QList<QVariant> a, const QList<QVariant> b)
QString Utils::getAddressToDisplay(QVariantList addressList, QString filter, QString defaultAddress) {
if (filter.isEmpty()) return defaultAddress;
for (auto& item: addressList) {
for (auto &item : addressList) {
QString address = item.toMap()["address"].toString();
if (address.contains(filter)) return address;
}

View file

@ -127,6 +127,7 @@ public:
Q_INVOKABLE static VariantObject *findAvatarByAddress(const QString &address);
Q_INVOKABLE static VariantObject *findFriendByAddress(const QString &address);
Q_INVOKABLE static VariantObject *getFriendAddressSecurityLevel(const QString &address);
Q_INVOKABLE static VariantObject *getFriendSecurityLevel(const QString &address);
static QString generateSavedFilename(const QString &from, const QString &to);
Q_INVOKABLE static VariantObject *isMe(const QString &address);
Q_INVOKABLE static VariantObject *isLocal(const QString &address);
@ -145,6 +146,8 @@ public:
static QString getApplicationProduct();
static QString getOsProduct();
static QColor getDefaultStyleColor(const QString &colorName);
static QList<QSharedPointer<DownloadablePayloadTypeCore>> getDownloadableVideoPayloadTypes();
static void checkDownloadedCodecsUpdates();

View file

@ -155,6 +155,10 @@ Button {
if (y < mainItem.height && y + popupHeight > 0) {
x += mainItem.width
}
var globalPos = mapToItem(mainItem.Window.contentItem, x, y)
if (globalPos.x + popupWidth >= mainItem.Window.width) {
x = -popupWidth
}
}
onHeightChanged: Qt.callLater(updatePosition)

View file

@ -111,7 +111,7 @@ ColumnLayout {
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
visible: mainItem.specificAddress != ""
text: SettingsCpp.onlyDisplaySipUriUsername ? UtilsCpp.getUsername(mainItem.specificAddress) : mainItem.specificAddress
text: SettingsCpp.hideSipAddresses ? UtilsCpp.getUsername(mainItem.specificAddress) : mainItem.specificAddress
elide: Text.ElideMiddle
maximumLineCount: 1
font {

View file

@ -85,7 +85,7 @@ Control.TabBar {
contentItem: Text {
id: tabText
width: Math.min(implicitWidth, mainItem.width / mainItem.model.length)
width: implicitWidth
font.weight: mainItem.textWeight
color: mainItem.currentIndex === index ? DefaultStyle.main2_600 : DefaultStyle.main2_400
font.family: DefaultStyle.defaultFont

View file

@ -32,6 +32,7 @@ ListView {
Layout.preferredWidth: Math.round(45 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(45 * DefaultStyle.dp)
_address: modelData.core.remoteAddress
secured: securityLevel === LinphoneEnums.SecurityLevel.EndToEndEncryptedAndVerified
isConference: modelData.core.isConference
shadowEnabled: false
}

View file

@ -14,7 +14,6 @@ Flickable {
flickableDirection: Flickable.VerticalFlick
property bool showInitials: true // Display Initials of Display name.
property bool showDefaultAddress: true // Display address below display name.
property bool showActions: false // Display actions layout (call buttons)
property bool showContactMenu: true // Display the dot menu for contacts.
property bool showFavorites: true // Display the favorites in the header
@ -269,7 +268,7 @@ Flickable {
showActions: mainItem.showActions
showInitials: mainItem.showInitials
showContactMenu: mainItem.showContactMenu
showDefaultAddress: mainItem.showDefaultAddress
showDefaultAddress: false
selectionEnabled: mainItem.selectionEnabled
multiSelectionEnabled: mainItem.multiSelectionEnabled
selectedContacts: mainItem.selectedContacts
@ -315,7 +314,7 @@ Flickable {
showActions: mainItem.showActions
showInitials: mainItem.showInitials
showContactMenu: mainItem.showContactMenu
showDefaultAddress: mainItem.showDefaultAddress
showDefaultAddress: false
selectionEnabled: mainItem.selectionEnabled
multiSelectionEnabled: mainItem.multiSelectionEnabled
selectedContacts: mainItem.selectedContacts
@ -368,7 +367,8 @@ Flickable {
highlightText: mainItem.highlightText
showActions: mainItem.showActions
showContactMenu: mainItem.showContactMenu
showDefaultAddress: mainItem.showDefaultAddress
showDefaultAddress: true
showDisplayName: false
selectionEnabled: mainItem.selectionEnabled
multiSelectionEnabled: mainItem.multiSelectionEnabled
selectedContacts: mainItem.selectedContacts

View file

@ -24,7 +24,7 @@ Loader{
: contact
? contact.core.defaultAddress
: ''
readonly property string address: SettingsCpp.onlyDisplaySipUriUsername ? UtilsCpp.getUsername(_address) : _address
readonly property string address: SettingsCpp.hideSipAddresses ? UtilsCpp.getUsername(_address) : _address
property var displayNameObj: UtilsCpp.getDisplayName(_address)
property string displayNameVal: account && account.core.displayName
? account.core.displayName
@ -40,6 +40,11 @@ Loader{
: computedAvatarUri.length != 0
property var avatarObj: UtilsCpp.findAvatarByAddress(_address)
property string computedAvatarUri: avatarObj ? avatarObj.value : ''
// To get the secured property for a friend using an address to find it,
// override it as secured: friendSecurityLevel === LinphoneEnums.SecurityLevel.EndToEndEncryptedAndVerified
property var friendSecurityLevelObj: UtilsCpp.getFriendSecurityLevel(_address)
property var friendSecurityLevel: friendSecurityLevelObj ? securityLevelObj.value : LinphoneEnums.SecurityLevel.None
// To get the secured property for a specific address,
// override it as secured: securityLevel === LinphoneEnums.SecurityLevel.EndToEndEncryptedAndVerified

View file

@ -14,6 +14,7 @@ FocusScope {
property var searchResultItem
property bool showInitials: true // Display Initials of Display name.
property bool showDefaultAddress: true // Display address below display name.
property bool showDisplayName: true // Display name above address.
property bool showActions: false // Display actions layout (call buttons)
property bool showContactMenu: true // Display the dot menu for contacts.
property string highlightText
@ -34,8 +35,7 @@ FocusScope {
property real itemsRightMargin: Math.round(39 * DefaultStyle.dp)
property var displayName: searchResultItem.core.fullName
property string initial: displayName ? displayName[0].toLocaleLowerCase(
ConstantsCpp.DefaultLocale) : ''
property var initial: displayName ? displayName[0].toLocaleLowerCase(ConstantsCpp.DefaultLocale) : ''
signal clicked(var mouse)
signal contactDeletionRequested(FriendGui contact)
@ -50,7 +50,7 @@ FocusScope {
verticalAlignment: Text.AlignVCenter
width: Math.round(20 * DefaultStyle.dp)
opacity: previousInitial != mainItem.initial ? 1 : 0
text: mainItem.initial
text: mainItem.initial || ""
color: DefaultStyle.main2_400
font {
pixelSize: Math.round(20 * DefaultStyle.dp)
@ -77,6 +77,10 @@ FocusScope {
ColumnLayout {
spacing: 0
Text {
id: displayNameText
visible: mainItem.showDisplayName
Layout.fillWidth: true
Layout.preferredHeight: visible ? implicitHeight: 0
text: UtilsCpp.boldTextPart(mainItem.displayName,
mainItem.highlightText)
font {
@ -85,13 +89,13 @@ FocusScope {
weight: mainItem.showDefaultAddress ? Typography.h4.weight : Typography.p1.weight
}
maximumLineCount: 1
Layout.fillWidth: true
}
Text {
Layout.topMargin: Math.round(2 * DefaultStyle.dp)
Layout.topMargin: displayNameText.visible ? Math.round(2 * DefaultStyle.dp) : 0
Layout.fillWidth: true
Layout.preferredHeight: visible ? implicitHeight: 0
visible: mainItem.showDefaultAddress
property string address: SettingsCpp.onlyDisplaySipUriUsername
property string address: SettingsCpp.hideSipAddresses
? UtilsCpp.getUsername(mainItem.addressFromFilter)
: mainItem.addressFromFilter
text: UtilsCpp.boldTextPart(address, mainItem.highlightText)

View file

@ -15,6 +15,7 @@ ListView {
property string title
property bool showInitials: true // Display Initials of Display name.
property bool showDefaultAddress: true // Display address below display name.
property bool showDisplayName: true // Display name above address.
property bool showActions: false // Display actions layout (call buttons)
property bool showContactMenu: true // Display the dot menu for contacts.
property bool showFavorites: true // Display the favorites in the header
@ -195,6 +196,7 @@ ListView {
searchResultItem: $modelData
showInitials: mainItem.showInitials && isStored
showDefaultAddress: mainItem.showDefaultAddress
showDisplayName: mainItem.showDisplayName
showActions: mainItem.showActions
showContactMenu: mainItem.showContactMenu && searchResultItem.core.isStored
highlightText: mainItem.highlightText

View file

@ -57,7 +57,7 @@ Rectangle{
EffectImage {
anchors.fill: parent
anchors.margins: Math.round(1.5 * DefaultStyle.dp) * scaleFactor
imageSource: AppIcons.bellMwi
imageSource: AppIcons.bell
colorizationColor: DefaultStyle.grey_0
}
}

View file

@ -47,6 +47,7 @@ ListView {
Layout.preferredWidth: Math.round(45 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(45 * DefaultStyle.dp)
_address: modelData.core.address
secured: securityLevel === LinphoneEnums.SecurityLevel.EndToEndEncryptedAndVerified
shadowEnabled: false
}
Text {

View file

@ -52,6 +52,7 @@ ListView {
Layout.preferredWidth: Math.round(45 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(45 * DefaultStyle.dp)
_address: modelData.core.sipAddress
secured: friendSecurityLevel === LinphoneEnums.SecurityLevel.EndToEndEncryptedAndVerified
shadowEnabled: false
}
Text {

View file

@ -59,8 +59,10 @@ Item {
Text {
anchors.margins: 25
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
text: titleText
maximumLineCount: 1
font: Typography.h4
}

View file

@ -111,6 +111,7 @@ Item {
call: !mainItem.previewEnabled ? mainItem.call : null
displayNameVal: mainItem.displayName
securityBreach: mainItem.securityBreach ? mainItem.securityBreach : securityLevel === LinphoneEnums.SecurityLevel.Unsafe
secured: securityLevel === LinphoneEnums.SecurityLevel.EndToEndEncryptedAndVerified
}
ColumnLayout{
id: joiningView
@ -183,7 +184,7 @@ Item {
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
property string _text: mainItem.call && mainItem.call.core.remoteAddress
text: SettingsCpp.onlyDisplaySipUriUsername ? UtilsCpp.getUsername(_text) : _text
text: SettingsCpp.hideSipAddresses ? UtilsCpp.getUsername(_text) : _text
color: DefaultStyle.grey_0
font {
pixelSize: Math.round(14 * DefaultStyle.dp)
@ -260,7 +261,7 @@ Item {
: mainItem.account && mainItem.identityAddress
? mainItem.identityAddress.value
: ""
text: SettingsCpp.onlyDisplaySipUriUsername ? UtilsCpp.getUsername(_text) : _text
text: SettingsCpp.hideSipAddresses ? UtilsCpp.getUsername(_text) : _text
color: DefaultStyle.grey_0
font {
pixelSize: Math.round(14 * DefaultStyle.dp)

View file

@ -10,7 +10,6 @@ Quick.Text {
family: DefaultStyle.defaultFont
pixelSize: Math.round(10 * DefaultStyle.dp)
weight: Typography.p1.weight
bold: true
}
color: DefaultStyle.main2_600
wrapMode: Quick.Text.Wrap

View file

@ -44,7 +44,8 @@ import Qt.labs.platform
property bool isFrameLess : false;
property bool showAsTool : false
// Don't use Popup for flags : it could lead to error in geometry. On Mac, Using Tool ensure to have the Window on Top and fullscreen independant
flags: Qt.BypassWindowManagerHint | (showAsTool?Qt.Tool:Qt.WindowStaysOnTopHint) | Qt.Window | Qt.FramelessWindowHint;
// flags: Qt.WindowDoesNotAcceptFocus | Qt.BypassWindowManagerHint | (showAsTool?Qt.Tool:Qt.WindowStaysOnTopHint) | Qt.Window | Qt.FramelessWindowHint;
flags: Qt.Popup | Qt.Dialog | Qt.WindowDoesNotAcceptFocus | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint
opacity: 1.0
height: _content[0] != null ? _content[0].height : 0
width: _content[0] != null ? _content[0].width : 0

View file

@ -72,7 +72,6 @@ Dialog {
id: passwordEdit
hidden: true
isError: passwordItem.errorTextVisible
KeyNavigation.up: usernameEdit
KeyNavigation.down: cancelButton
}
}

View file

@ -66,6 +66,7 @@ Notification {
Layout.preferredHeight: Math.round(60 * DefaultStyle.dp)
Layout.alignment: Qt.AlignHCenter
call: mainItem.call
secured: securityLevel === LinphoneEnums.SecurityLevel.EndToEndEncryptedAndVerified
isConference: mainItem.call && mainItem.call.core.isConference
}
ColumnLayout {

View file

@ -10,7 +10,7 @@ ColumnLayout{
id: mainItem
property AccountGui account: null
property string topText: account ? account.core.displayName : ""
property string bottomText: account ? SettingsCpp.onlyDisplaySipUriUsername ? UtilsCpp.getUsername(account.core.identityAddress) : account.core.identityAddress : ""
property string bottomText: account ? SettingsCpp.hideSipAddresses ? UtilsCpp.getUsername(account.core.identityAddress) : account.core.identityAddress : ""
spacing: 0
width: topTextItem.implicitWidth
Text {

View file

@ -305,7 +305,7 @@ MainRightPanel {
if (text.length != 0) mainItem.contact.core.setAddressAt(index, label, text)
}
property string _initialText: modelData.address
initialText: SettingsCpp.onlyDisplaySipUriUsername ? UtilsCpp.getUsername(_initialText) : _initialText
initialText: SettingsCpp.hideSipAddresses ? UtilsCpp.getUsername(_initialText) : _initialText
backgroundColor: DefaultStyle.grey_0
focus: true
KeyNavigation.right: removeAddressButton

View file

@ -50,6 +50,7 @@ FocusScope{
Layout.preferredHeight: Math.round(45 * DefaultStyle.dp)
_address: modelData
shadowEnabled: false
secured: friendSecurityLevel === LinphoneEnums.SecurityLevel.EndToEndEncryptedAndVerified
}
Text {
Layout.fillWidth: true

View file

@ -288,6 +288,7 @@ FocusScope {
Layout.preferredWidth: Math.round(45 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(45 * DefaultStyle.dp)
_address: modelData.address
secured: friendSecurityLevel === LinphoneEnums.SecurityLevel.EndToEndEncryptedAndVerified
shadowEnabled: false
}
Text {

View file

@ -49,6 +49,7 @@ AbstractMainPage {
text: titleText
color: DefaultStyle.main2_700
font: Typography.h3
Layout.fillWidth: true
}
Item {
Layout.fillWidth: true
@ -67,6 +68,7 @@ AbstractMainPage {
delegate: SettingsMenuItem {
titleText: modelData.title
width: familiesList.width
visible: modelData.visible != undefined ? modelData.visible : true
isSelected: familiesList.selectedIndex == index
focus: index == 0

View file

@ -19,8 +19,10 @@ AbstractSettingsMenu {
{title: qsTr("settings_contacts_title"), layout: "ContactsSettingsLayout"},
//: "Réunions"
{title: qsTr("settings_meetings_title"), layout: "MeetingsSettingsLayout", visible: !SettingsCpp.disableMeetingsFeature},
//: "Affichage"
////: "Affichage"
//{title: qsTr("settings_user_interface_title"), layout: "DisplaySettingsLayout"},
//: "Security"
{title: qsTr("settings_security_title"), layout: "SecuritySettingsLayout"},
//: "Réseau"
{title: qsTr("settings_network_title"), layout: "NetworkSettingsLayout"},
//: "Paramètres avancés"

View file

@ -1,8 +1,3 @@
/**
* Qml template used for welcome and login/register pages
**/
import QtCore
import QtQuick
import QtQuick.Layouts
@ -99,10 +94,16 @@ Item {
x: mainItem.width / 2 - width / 2
y: contentItem.height / 2
property var currentCall: callsModel.currentCall ? callsModel.currentCall : null
property var conference: currentCall ? currentCall.core.conference : null
property string remoteName: currentCall ? currentCall.core.remoteName : ""
property string subject: conference ? conference.core.subject : ''
contentItem: MediumButton {
style: ButtonStyle.toast
text: currentCallNotif.currentCall ? currentCallNotif.currentCall.core.conference ? ("Réunion en cours : ") + currentCallNotif.currentCall.core.conference.core.subject : (("Appel en cours : ") + currentCallNotif.remoteName) : "appel en cours"
text: currentCallNotif.currentCall
? currentCallNotif.conference
? ("Réunion en cours : ") + currentCallNotif.subject
: (("Appel en cours : ") + currentCallNotif.remoteName)
: "appel en cours"
onClicked: {
var callsWindow = UtilsCpp.getCallsWindow(
currentCallNotif.currentCall)
@ -178,6 +179,7 @@ Item {
}
initButtons()
currentIndex = SettingsCpp.getLastActiveTabIndex()
if (currentIndex === -1) currentIndex = 0
}
}
ColumnLayout {
@ -267,7 +269,6 @@ Item {
showActions: true
showFavorites: false
selectionEnabled: false
showDefaultAddress: true
searchOnEmpty: false
sectionsPixelSize: Typography.p2.pixelSize
@ -459,8 +460,7 @@ Item {
icon.height: Math.round(32 * DefaultStyle.dp)
text: qsTr("settings_title")
icon.source: AppIcons.settings
onClicked: openContextualMenuComponent(
settingsPageComponent)
onClicked: openContextualMenuComponent(settingsPageComponent)
KeyNavigation.up: visibleChildren.length
!= 0 ? settingsMenuButton.getPreviousItem(
2) : null

View file

@ -4,7 +4,6 @@ import QtQuick.Layouts
import QtQuick.Controls.Basic as Control
import SettingsCpp 1.0
import Linphone
import UtilsCpp
AbstractSettingsLayout {
width: parent?.width

View file

@ -290,7 +290,6 @@ FriendGui{
Layout.rightMargin: Math.round(8 * DefaultStyle.dp)
searchBarText: searchBar.text
hideSuggestions: true
showDefaultAddress: false
sourceFlags: LinphoneEnums.MagicSearchSource.Friends
| LinphoneEnums.MagicSearchSource.FavoriteFriends
| LinphoneEnums.MagicSearchSource.LdapServers
@ -536,7 +535,7 @@ FriendGui{
Text {
Layout.fillWidth: true
property string _text: listViewModelData.address
text: SettingsCpp.onlyDisplaySipUriUsername ? UtilsCpp.getUsername(_text) : _text
text: SettingsCpp.hideSipAddresses ? UtilsCpp.getUsername(_text) : _text
font {
pixelSize: Typography.p1.pixelSize
weight: Typography.p1.weight

View file

@ -764,6 +764,7 @@ AbstractMainPage {
Layout.preferredWidth: Math.round(45 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(45 * DefaultStyle.dp)
_address: mainItem.selectedConference && mainItem.selectedConference.core ? mainItem.selectedConference.core.organizerAddress : ""
secured: friendSecurityLevel === LinphoneEnums.SecurityLevel.EndToEndEncryptedAndVerified
}
Text {
text: mainItem.selectedConference && mainItem.selectedConference.core ? mainItem.selectedConference.core.organizerName : ""
@ -801,6 +802,7 @@ AbstractMainPage {
Layout.preferredWidth: Math.round(45 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(45 * DefaultStyle.dp)
_address: modelData.address
secured: friendSecurityLevel === LinphoneEnums.SecurityLevel.EndToEndEncryptedAndVerified
shadowEnabled: false
}
Text {

View file

@ -119,7 +119,7 @@ ApplicationWindow {
}
Text {
Layout.leftMargin: Math.round(5 * DefaultStyle.dp)
text: SettingsCpp.onlyDisplaySipUriUsername ? UtilsCpp.getUsername(modelData.address) : modelData.address
text: SettingsCpp.hideSipAddresses ? UtilsCpp.getUsername(modelData.address) : modelData.address
font {
pixelSize: Typography.p1.pixelSize
weight: Typography.p1.weight

View file

@ -84,8 +84,6 @@ QtObject {
property string mediaEncryptionZrtpPq: "image://internal/media_encryption_zrtp_pq.svg"
property string pencil: "image://internal/pencil-simple.svg"
property string shareNetwork: "image://internal/share-network.svg"
property string bell: "image://internal/bell-simple.svg"
property string bellSlash: "image://internal/bell-slash.svg"
property string question: "image://internal/question.svg"
property string settings: "image://internal/gear.svg"
property string clock: "image://internal/clock.svg"
@ -117,11 +115,12 @@ QtObject {
property string mobile: "image://internal/device-mobile-camera.svg"
property string desktop: "image://internal/desktop.svg"
property string calendar: "image://internal/calendar-blank.svg"
property string bell: "image://internal/bell.svg"
property string bellSlash: "image://internal/bell-slash.svg"
property string bellDnd: "image://internal/bell-dnd.svg"
property string bellRinger: "image://internal/bell-ringer.svg"
property string bellRinger: "image://internal/bell-ringing.svg"
property string voicemail: "image://internal/voicemail.svg"
property string power: "image://internal/power.svg"
property string resourcePackage: "image://internal/package.svg"
property string appWindow: "image://internal/app-window.svg"
property string bellMwi: "image://internal/bell-simple.svg"
}

View file

@ -118,7 +118,7 @@ else
if [ -f "${WORK_DIR}/AppBin/appimagetool-x86_64.AppImage" ]; then
echo "appimagetool-x86_64.AppImage exists"
else
wget -P "${WORK_DIR}/AppBin" https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
wget -P "${WORK_DIR}/AppBin" https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage
chmod +x "${WORK_DIR}/AppBin/appimagetool-x86_64.AppImage"
fi
./${WORK_DIR}/AppBin/linuxdeploy-x86_64.AppImage --appimage-extract-and-run --appdir=${WORK_DIR}/AppDir -e ${WORK_DIR}/AppDir/usr/bin/${APP_NAME} --desktop-file=${WORK_DIR}/AppDir/usr/share/applications/${APP_NAME}.desktop -i ${WORK_DIR}/AppDir/usr/share/icons/hicolor/scalable/apps/${APP_NAME}.svg --plugin qt

View file

@ -10,7 +10,7 @@ ARG QT6_VERSION=6.8.0
#Do not use it. It seems that it cannot be used from python command.
#ARG QT_MODULES=qtnetworkauth qtquick3d qtmultimedia
MAINTAINER Gaelle Braud <gaelle.braud@belledonne-communications.com>
LABEL Gaelle Braud <gaelle.braud@belledonne-communications.com>
# Use a Swiss mirror
RUN sed -i -E 's/(archive|security)\.ubuntu\.com/ch.archive.ubuntu.com/' /etc/apt/sources.list

View file

@ -0,0 +1,100 @@
###############################################################################
# Dockerfile used to make gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-ubuntu-20-04-lts:20231024_add_multimedia
###############################################################################
FROM ubuntu:22.04
# Qt on Ubuntu 22.04 is too old. Use a downloader.
ARG QT_VERSION=5.15.2
ARG QT6_VERSION=6.9.1
#Do not use it. It seems that it cannot be used from python command.
#ARG QT_MODULES=qtnetworkauth qtquick3d qtmultimedia qt5compat qtshadertools
LABEL Gaelle Braud <gaelle.braud@belledonne-communications.com>
# Use a Swiss mirror
RUN sed -i -E 's/(archive|security)\.ubuntu\.com/ch.archive.ubuntu.com/' /etc/apt/sources.list
# add fallbacks for timeout connections.
#France
RUN echo "deb http://fr.archive.ubuntu.com/ubuntu/ jammy main restricted" >> /etc/apt/sources.list
RUN echo "deb http://security.ubuntu.com/ubuntu jammy-security main restricted" >> /etc/apt/sources.list
RUN echo "deb http://ch.archive.ubuntu.com/ubuntu/ jammy-updates main restricted" >> /etc/apt/sources.list
#Belgium
RUN echo "deb http://be.archive.ubuntu.com/ubuntu/ jammy main restricted" >> /etc/apt/sources.list
RUN echo "deb http://security.ubuntu.com/ubuntu jammy-security main restricted" >> /etc/apt/sources.list
RUN echo "deb http://be.archive.ubuntu.com/ubuntu/ jammy-updates main restricted" >> /etc/apt/sources.list
#International
RUN echo "deb http://archive.ubuntu.com/ubuntu/ jammy main restricted" >> /etc/apt/sources.list
RUN echo "deb http://security.ubuntu.com/ubuntu jammy-security main restricted" >> /etc/apt/sources.list
RUN echo "deb http://archive.ubuntu.com/ubuntu/ jammy-updates main restricted" >> /etc/apt/sources.list
# Configure locale
RUN apt-get update && \
apt-get install -y locales && \
apt-get clean && \
echo "en_US.UTF-8 UTF-8" > /etc/locale.gen && \
locale-gen
ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' LC_ALL='en_US.UTF-8'
ENV TZ=Europe/Paris
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
ARG DEBIAN_FRONTEND=noninteractive
ENV SHELL=/bin/bash
#ENV PS1='\[\e[33m\]\u@bc-dev-ubuntu-20-04>\[\e[0m\] '
# Install common general tools
RUN apt-get update && \
apt-get install -y nano sudo vim && \
apt-get clean
# Install development tools
RUN apt-get update && \
apt-get install -y alien at autoconf bison ccache clang doxygen elfutils g++ gdb git graphviz intltool libtool lsb-release make ninja-build openssh-client patch perl python3-pip python3-pystache python3-six python3-jsonschema python3-jinja2 meson yasm && \
apt-get clean
# Install linphone & flexisip dependencies development packages
RUN apt-get update && \
apt-get install -y libasound2-dev libavcodec-dev libavutil-dev libbsd-dev libegl1-mesa-dev libglew-dev libgsm1-dev libjansson-dev libmariadb-dev-compat libmbedtls-dev libopus-dev libpq-dev libprotobuf-dev libpulse-dev libqt5svg5-dev libsnmp-dev libspeex-dev libspeexdsp-dev libsqlite3-dev libsrtp2-dev libssl-dev libswscale-dev libturbojpeg0-dev libv4l-dev libvpx-dev libxerces-c-dev libxml2-dev libxv-dev libxinerama-dev protobuf-compiler qttools5-dev qttools5-dev-tools xsdcxx cmake nasm && \
apt-get clean
# Install signing tools
RUN sudo apt-get install -y gnupg2
# Install configuration tools
RUN sudo apt-get install -y wget
# Configure user bc
RUN useradd -ms /bin/bash bc && \
echo 'bc:cotcot' | chpasswd && \
echo 'bc ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
########### QT
RUN sudo apt-get update && sudo apt-get install -y libxkbcommon* flite1-dev libspeechd-dev speech-dispatcher libfontconfig1-dev libfreetype6-dev libx11-dev libxext-dev libxfixes-dev libxi-dev libxrender-dev libx11-xcb* libxcb* qdbus-qt5 libqt5dbus5 libdbus-1-dev libdbus-glib-1-dev libatspi2.0-0 libatspi2.0-dev
RUN sudo apt-get update && sudo apt-get install -y libxcb-keysyms1-dev libxcb-image0-dev libxcb-shm0-dev libxcb-icccm4-dev libxcb-sync0-dev libxcb-xfixes0-dev libxcb-shape0-dev libxcb-randr0-dev libxcb-render-util0-dev
USER bc
WORKDIR /home/bc
# RUN sudo apt install python3-pip --upgrade -y
RUN sudo apt install --upgrade -y python3-importlib-metadata
RUN sudo apt install --upgrade -y python3-setuptools
# RUN sudo apt install python3-ez_setup
## Install Qt download tool
# installation is split because there is a way where some modules are not downloaded in the first attempt.
RUN sudo apt install -y python3-py7zr
RUN sudo pip3 install --upgrade aqtinstall
RUN sudo python3 -m aqt install-qt linux desktop $QT_VERSION -O /opt/Qt
RUN sudo python3 -m aqt install-qt linux desktop $QT_VERSION -O /opt/Qt --noarchives -m qtnetworkauth qtquick3d
RUN sudo python3 -m aqt install-qt linux desktop $QT6_VERSION -O /opt/Qt
RUN sudo python3 -m aqt install-qt linux desktop $QT6_VERSION -O /opt/Qt --noarchives -m qtnetworkauth qtquick3d qtmultimedia qt5compat qtshadertools
RUN sudo chown -R bc:bc /opt/Qt/
RUN qtchooser -install $QT_VERSION /opt/Qt/$QT_VERSION/gcc_64/bin/qmake
RUN qtchooser -install $QT6_VERSION /opt/Qt/$QT6_VERSION/gcc_64/bin/qmake
CMD bash

@ -1 +1 @@
Subproject commit 96a6385bfdb3e6c0ed85df42bda99467e8f6fc5f
Subproject commit 52fa0abf7e60d1f7614b8ee8e8dbbee52f3f5f88