diff --git a/.gitlab-ci-files/windows-desktop.yml b/.gitlab-ci-files/windows-desktop.yml index cefdffc85..bd475dc94 100644 --- a/.gitlab-ci-files/windows-desktop.yml +++ b/.gitlab-ci-files/windows-desktop.yml @@ -180,14 +180,22 @@ win64-ninja-vs2022-scheduled-windows: needs: [] rules: - !reference [.rules-merge-request-manual, rules] - - if: $NIGHTLY_MASTER - if: $NIGHTLY_RELEASE - if: $PACKAGE_WINDOWS - - if: $DEPLOY_WINDOWS variables: CMAKE_OPTIONS: -DENABLE_APP_PACKAGING=YES -DENABLE_G729=ON -DENABLE_PQCRYPTO=ON -DENABLE_GPL_THIRD_PARTIES=OFF RELEASE_FILE: -DLINPHONE_SDK_MAKE_RELEASE_FILE_URL=$MAKE_RELEASE_FILE_URL/$WINDOWS_PLATFORM/$APP_FOLDER +.vs-win64-package-cr: + stage: package + needs: [] + rules: + - if: $NIGHTLY_MASTER + - if: $DEPLOY_WINDOWS + variables: + CMAKE_OPTIONS: -DENABLE_APP_PACKAGING=YES -DENABLE_G729=ON -DENABLE_PQCRYPTO=ON -DENABLE_GPL_THIRD_PARTIES=OFF -DENABLE_BUGSPLAT_SYMBOLS_UPLOAD=ON -DBUGSPLAT_CLIENT_ID=$BUGSPLAT_CLIENT_ID -DBUGSPLAT_CLIENT_SECRET=$BUGSPLAT_CLIENT_SECRET -DBUGSPLAT_DATABASE=$BUGSPLAT_DATABASE + RELEASE_FILE: -DLINPHONE_SDK_MAKE_RELEASE_FILE_URL=$MAKE_RELEASE_FILE_URL/$WINDOWS_PLATFORM/$APP_FOLDER + win64-ninja-vs2022-package-windows: variables: CMAKE_GENERATOR: "Ninja" @@ -198,6 +206,18 @@ win64-ninja-vs2022-package-windows: - .windows-ninja-variables - .vs-win64-package +#Packaging for deployment (Upload symbols at the same time of the packaging) +win64-ninja-vs2022-package-cr-windows: + variables: + CMAKE_GENERATOR: "Ninja" + CMAKE_ARCHITECTURE: "" + PARALLEL_OPTIONS: "" + extends: + - .windows-vs2022 + - .windows-ninja-variables + - .vs-win64-package-cr + + ################################################# # SIGNING ################################################# @@ -213,8 +233,28 @@ win64-codesigning: MINGW_TYPE: mingw64 rules: - !reference [.rules-merge-request-manual, rules] - - if: $NIGHTLY_MASTER - if: $PACKAGE_WINDOWS + 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\* + when: always + expire_in: 1 week + +win64-codesigning-cr: + stage: signing + allow_failure: true + extends: + - .windows-codesigning + needs: + - win64-ninja-vs2022-package-cr-windows + variables: + MINGW_TYPE: mingw64 + rules: + - if: $NIGHTLY_MASTER - if: $DEPLOY_WINDOWS script: - cd build-desktop/OUTPUT/Packages/ @@ -246,7 +286,7 @@ win64-ninja-vs2022-upload: extends: - .win64-upload needs: - - win64-codesigning + - win64-codesigning-cr .win64-plugins-upload: stage: deploy diff --git a/.gitmodules b/.gitmodules index aa3e6c896..a4d6dacd3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,12 @@ [submodule "linphone-sdk"] path = external/linphone-sdk url = https://gitlab.linphone.org/BC/public/linphone-sdk.git +[submodule "external/google/gn"] + path = external/google/gn + url = https://gitlab.linphone.org/BC/public/external/gn.git +[submodule "external/google/crashpad"] + path = external/google/crashpad + url = https://gitlab.linphone.org/BC/public/external/crashpad.git +[submodule "external/google/chromium-depot-tools"] + path = external/google/chromium-depot-tools + url = https://gitlab.linphone.org/BC/public/external/chromium-depot-tools.git diff --git a/CHANGELOG.md b/CHANGELOG.md index bb3dd4c7f..ff0a024c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,26 @@ Group changes to describe their impact on the project, as follows: Fixed for any bug fixes. Security to invite users to upgrade in case of vulnerabilities. +## [6.1.0] - Unreleased + +6.1.0 release is the complete version of the new Linphone Desktop with all features including chat + +### Added +- Chat: chat with your contacts, including text messaging, voice recording, sharing files or medias +- Presence: get your friend's presence status as long as you both are in your contact list +- Translations: Linphone is now available in English, French, Chinese, Czech, German, Portuguese, Russian and Ukrainian thank's to the Weblate contributors +- Check for update : you will get a notification on start if a new version is available, and you can look for a new version from the help page +- Bugsplat integration: add Bugsplat database parameters to improve crash reporting. + +### Fixed +- Fixed "End-to-end encrypted call" label while in conference, the call may be end-to-end encrypted but only to the conference server, not to all participants +- Audio device list : display the correct devices in multimedia settings according to their functions (capture / playback / video) + +### Changed +- Minimum supported Qt version is now 6.10.0 +- Removed QtMultimedia dependency + + ## [6.0.0] - 2025-04-17 6.0.0 release is a complete rework of Linphone Desktop, with only the call and contact list features availables @@ -30,10 +50,4 @@ Group changes to describe their impact on the project, as follows: - 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. - - -## [6.1.0] - XXXX-XX-XX - -### Changed -- Minimum supported Qt version is now 6.10.0 \ No newline at end of file +- Some settings have changed name and/or section in linphonerc file. \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 48d937bde..793a4bb15 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -177,6 +177,7 @@ add_option(OPTION_LIST ENABLE_UNIT_TESTS "Enable unit test of SDK." OFF) add_option(OPTION_LIST ENABLE_UPDATE_CHECK "Enable update check." ON) add_option(OPTION_LIST ENABLE_VIDEO "Enable Video support." YES) add_option(OPTION_LIST ENABLE_WINDOWS_TOOLS_CHECK "Enable tools checks on Windows for auto install." OFF) +add_option(OPTION_LIST ENABLE_CRASH_HANDLER "Enable Crash Handler" YES) add_cache(OPTION_LIST LINPHONE_SDK_MAKE_RELEASE_FILE_URL "Make a RELEASE file that work along check_version and use this URL" "") @@ -184,6 +185,13 @@ add_cache(OPTION_LIST LINPHONE_SDK_MAKE_RELEASE_FILE_URL "Make a RELEASE file th add_option(OPTION_LIST ENABLE_OPENH264 "Enable the use of OpenH264 codec" ${ENABLE_VIDEO}) add_option(OPTION_LIST ENABLE_SCREENSHARING "Enable screen sharing." ${ENABLE_VIDEO}) +add_option(OPTION_LIST ENABLE_BUGSPLAT_SYMBOLS_UPLOAD "Generate and upload symbols to Bugsplat." OFF) +add_cache(OPTION_LIST BUGSPLAT_CLIENT_ID "Client ID for Bugsplat." "") +add_cache(OPTION_LIST BUGSPLAT_CLIENT_SECRET "Client Secret for Bugsplat." "") +add_cache(OPTION_LIST BUGSPLAT_DATABASE "Database name for Bugsplat." "Linphone") + + + # QtKeychain add_option(OPTION_LIST LIBSECRET_SUPPORT "Build with libsecret support" OFF) # Need libsecret-devel if(WIN32) @@ -226,8 +234,8 @@ if(LINPHONEAPP_BUILD_TYPE STREQUAL "Default") endif() + if(NOT APPLE OR MONO_ARCH) - add_custom_target(linphone-deps) if(NOT LINPHONE_QT_ONLY) function(add_external) set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) # Prevent project from overriding the options we just set here @@ -235,6 +243,9 @@ if(NOT APPLE OR MONO_ARCH) endfunction() add_external() endif() + if(TARGET Crashpad) + set(HAVE_CRASH_HANDLER 1) + endif() function(add_linphone_app) set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) # Prevent project from overriding the options we just set here if(UNIX) diff --git a/Linphone/CMakeLists.txt b/Linphone/CMakeLists.txt index 133a0d76c..673a0b7ed 100644 --- a/Linphone/CMakeLists.txt +++ b/Linphone/CMakeLists.txt @@ -22,7 +22,7 @@ set(APP_TARGETS ${LinphoneCxx_TARGET} ${LibLinphone_TARGET})#Liblinphone set(QT_DEFAULT_MAJOR_VERSION 6) -set(QT_PACKAGES Quick Qml Widgets Svg Multimedia Test NetworkAuth Concurrent)# Search Core at first for initialize Qt scripts for next find_packages. +set(QT_PACKAGES Quick Qml Widgets Svg Test NetworkAuth Concurrent)# Search Core at first for initialize Qt scripts for next find_packages. if (UNIX AND NOT APPLE) list(APPEND QT_PACKAGES DBus) endif() @@ -96,15 +96,12 @@ else() set(MSPLUGINS_DIR "${CMAKE_INSTALL_LIBDIR}/mediastreamer/plugins") endif() -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/config.h") - if(${Qt6_VERSION} VERSION_LESS "6.10.0") message( FATAL_ERROR "Linphone requires Qt 6.10.0 or newer. Exiting CMake." ) endif() qt6_standard_project_setup() - ################################################################ # SOURCES ################################################################ @@ -124,6 +121,7 @@ add_subdirectory(core) set(LANGUAGES_DIRECTORY "data/languages") set(I18N_FILENAME i18n.qrc) set(LANGUAGES en fr de) +set(CMAKE_CPP_COMPILER gcc) # Add languages support. add_subdirectory("${LANGUAGES_DIRECTORY}" "data/languages") @@ -154,7 +152,7 @@ qt6_add_big_resources(_LINPHONEAPP_FONTS_FILES data/fonts.qrc) list(APPEND _LINPHONEAPP_FONTS_FILES data/fonts.qrc) set_property(SOURCE data/fonts.qrc PROPERTY SKIP_AUTORCC ON) -qt6_add_executable(Linphone +qt6_add_executable(${TARGET_NAME} ${_LINPHONEAPP_SOURCES} ${_LINPHONEAPP_FONTS_FILES} ${_APPDETAILS_RC_FILE} @@ -170,15 +168,19 @@ qt6_add_qml_module(Linphone RESOURCES data/fonts.qrc ) -if (WIN32) - if(MSVC AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) - install(FILES "$" DESTINATION ${CMAKE_INSTALL_BINDIR}) - endif() -endif() - qt6_add_resources(Linphone "resources" PREFIX "/" FILES ${_LINPHONEAPP_RC_FILES}) set_property(TARGET Linphone PROPERTY POSITION_INDEPENDENT_CODE ON) #Need by Qt +################################################################ +# CRASHPAD +################################################################ + +if (HAVE_CRASH_HANDLER) + add_dependencies(${TARGET_NAME} Crashpad) + target_link_libraries(${TARGET_NAME} PRIVATE Crashpad) + get_target_property(CRASHPAD_EXECUTABLE_NAME Crashpad CRASHPAD_EXECUTABLE_NAME) +endif() + ################################################################ # TARGETS LINKS ################################################################ @@ -223,3 +225,14 @@ foreach(T ${QT_PACKAGES}) endforeach() + +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/config.h") + +if (WIN32) + if(MSVC AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) + install(FILES "$" DESTINATION ${CMAKE_INSTALL_BINDIR}) + foreach(T ${APP_TARGETS}) + install(FILES "$" DESTINATION ${CMAKE_INSTALL_BINDIR}) + endforeach () + endif() +endif() \ No newline at end of file diff --git a/Linphone/config.h.cmake b/Linphone/config.h.cmake index f462cbfaf..6a578790e 100644 --- a/Linphone/config.h.cmake +++ b/Linphone/config.h.cmake @@ -39,3 +39,6 @@ #cmakedefine LINPHONEAPP_SHORT_VERSION "${LINPHONEAPP_SHORT_VERSION}" #cmakedefine GIT_BRANCH_NAME "${GIT_BRANCH_NAME}" #cmakedefine LINPHONESDK_VERSION "${LINPHONESDK_VERSION}" +#cmakedefine HAVE_CRASH_HANDLER "${HAVE_CRASH_HANDLER}" +#cmakedefine CRASHPAD_EXECUTABLE_NAME "${CRASHPAD_EXECUTABLE_NAME}" +#cmakedefine BUGSPLAT_DATABASE "${BUGSPLAT_DATABASE}" diff --git a/Linphone/core/App.cpp b/Linphone/core/App.cpp index 026d0373e..055928b1f 100644 --- a/Linphone/core/App.cpp +++ b/Linphone/core/App.cpp @@ -101,6 +101,9 @@ #include "tool/accessibility/AccessibilityHelper.hpp" #include "tool/accessibility/FocusHelper.hpp" #include "tool/accessibility/KeyboardShortcuts.hpp" +#ifdef HAVE_CRASH_HANDLER +#include "tool/crash_reporter/CrashReporter.hpp" +#endif #include "tool/native/DesktopTools.hpp" #include "tool/providers/AvatarProvider.hpp" #include "tool/providers/EmojiProvider.hpp" @@ -293,11 +296,21 @@ App::App(int &argc, char *argv[]) QCoreApplication::setApplicationName(EXECUTABLE_NAME); QApplication::setOrganizationDomain(EXECUTABLE_NAME); QCoreApplication::setApplicationVersion(APPLICATION_SEMVER); + // CarshReporter must be call after app initialization like names. +#ifdef HAVE_CRASH_HANDLER + CrashReporter::start(); +#else + lWarning() << "[Main] The application doesn't support the CrashReporter."; +#endif + // If not OpenGL, createRender is never call. QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL); setWindowIcon(QIcon(Constants::WindowIconPath)); initFonts(); //------------------- + mOIDCRefreshTimer.setInterval(1000); + mOIDCRefreshTimer.setSingleShot(false); + mLinphoneThread = new Thread(this); init(); @@ -376,9 +389,29 @@ void App::setSelf(QSharedPointer(me)) { mCoreModelConnection->makeConnectToModel( &CoreModel::globalStateChanged, [this](const std::shared_ptr &core, linphone::GlobalState gstate, const std::string &message) { - if (gstate == linphone::GlobalState::On) { - mCoreModelConnection->invokeToCore([this] { setCoreStarted(true); }); - } + mCoreModelConnection->invokeToCore([this, gstate] { + setCoreStarted(gstate == linphone::GlobalState::On); + if (gstate == linphone::GlobalState::Configuring) { + if (mMainWindow) { + QMetaObject::invokeMethod(mMainWindow, "openSSOPage", Qt::DirectConnection); + } else { + connect( + this, &App::mainWindowChanged, this, + [this] { + mCoreModelConnection->invokeToModel([this] { + auto gstate = CoreModel::getInstance()->getCore()->getGlobalState(); + if (gstate == linphone::GlobalState::Configuring) + mCoreModelConnection->invokeToCore([this] { + if (mMainWindow) + QMetaObject::invokeMethod(mMainWindow, "openSSOPage", + Qt::DirectConnection); + }); + }); + }, + Qt::SingleShotConnection); + } + } + }); }); mCoreModelConnection->makeConnectToModel(&CoreModel::authenticationRequested, &App::onAuthenticationRequested); // Config error message @@ -386,7 +419,7 @@ void App::setSelf(QSharedPointer(me)) { &CoreModel::configuringStatus, [this](const std::shared_ptr &core, linphone::ConfiguringState status, const std::string &message) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); - if (status == linphone::ConfiguringState::Failed) { + if (mIsRestarting && status == linphone::ConfiguringState::Failed) { mCoreModelConnection->invokeToCore([this, message]() { mustBeInMainThread(log().arg(Q_FUNC_INFO)); //: Error @@ -403,16 +436,20 @@ void App::setSelf(QSharedPointer(me)) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); if (CoreModel::getInstance()->mConfigStatus == linphone::ConfiguringState::Successful) { bool accountConnected = account && account->getState() == linphone::RegistrationState::Ok; + // update settings if case config contains changes + if (mSettings) mSettings->reloadSettings(); 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 - // if (accountConnected) if (mPossiblyLookForAddedAccount) { QMetaObject::invokeMethod(mMainWindow, "openMainPage", Qt::DirectConnection, Q_ARG(QVariant, accountConnected)); } mPossiblyLookForAddedAccount = false; + // setLocale(mSettings->getConfigLocale()); + // setAutoStart(mSettings->getAutoStart()); + // setQuitOnLastWindowClosed(mSettings->getExitOnClose()); }); } }); @@ -420,7 +457,28 @@ void App::setSelf(QSharedPointer(me)) { // Synchronize state for because linphoneCore was ran before any connections. mCoreModelConnection->invokeToModel([this]() { auto state = CoreModel::getInstance()->getCore()->getGlobalState(); - mCoreModelConnection->invokeToCore([this, state] { setCoreStarted(state == linphone::GlobalState::On); }); + mCoreModelConnection->invokeToCore([this, state] { + setCoreStarted(state == linphone::GlobalState::On); + if (state == linphone::GlobalState::Configuring) { + if (mMainWindow) { + QMetaObject::invokeMethod(mMainWindow, "openSSOPage", Qt::DirectConnection); + } else { + connect( + this, &App::mainWindowChanged, this, + [this] { + mCoreModelConnection->invokeToModel([this] { + auto gstate = CoreModel::getInstance()->getCore()->getGlobalState(); + if (gstate == linphone::GlobalState::Configuring) + mCoreModelConnection->invokeToCore([this] { + if (mMainWindow) + QMetaObject::invokeMethod(mMainWindow, "openSSOPage", Qt::DirectConnection); + }); + }); + }, + Qt::SingleShotConnection); + } + } + }); }); mCoreModelConnection->makeConnectToModel(&CoreModel::unreadNotificationsChanged, [this] { @@ -472,6 +530,29 @@ void App::setSelf(QSharedPointer(me)) { }); }); + mCoreModelConnection->makeConnectToModel(&CoreModel::oidcRemainingTimeBeforeTimeoutChanged, + [this](int remainingTime) { + mCoreModelConnection->invokeToCore([this, remainingTime] { + mRemainingTimeBeforeOidcTimeout = remainingTime; + emit remainingTimeBeforeOidcTimeoutChanged(); + }); + }); + mCoreModelConnection->makeConnectToCore(&App::lForceOidcTimeout, [this] { + qDebug() << "App: force oidc timeout"; + mCoreModelConnection->invokeToModel([this] { emit CoreModel::getInstance()->forceOidcTimeout(); }); + }); + mCoreModelConnection->makeConnectToModel(&CoreModel::timeoutTimerStarted, [this]() { + qDebug() << "App: oidc timer started"; + mCoreModelConnection->invokeToCore([this] { mOIDCRefreshTimer.start(); }); + }); + mCoreModelConnection->makeConnectToModel(&CoreModel::timeoutTimerStopped, [this]() { + qDebug() << "App: oidc timer stopped"; + mCoreModelConnection->invokeToCore([this] { mOIDCRefreshTimer.stop(); }); + }); + connect(&mOIDCRefreshTimer, &QTimer::timeout, this, [this]() { + mCoreModelConnection->invokeToModel([this] { CoreModel::getInstance()->refreshOidcRemainingTime(); }); + }); + //--------------------------------------------------------------------------------------------- mCliModelConnection = SafeConnection::create(me, CliModel::getInstance()); mCliModelConnection->makeConnectToCore(&App::receivedMessage, [this](int, const QByteArray &byteArray) { @@ -494,7 +575,7 @@ App *App::getInstance() { } Thread *App::getLinphoneThread() { - return App::getInstance()->mLinphoneThread; + return App::getInstance() ? App::getInstance()->mLinphoneThread : nullptr; } Notifier *App::getNotifier() const { @@ -565,7 +646,7 @@ void App::initCore() { lDebug() << log().arg("Creating SettingsModel"); SettingsModel::create(); lDebug() << log().arg("Creating SettingsCore"); - if (!settings) settings = SettingsCore::create(); + settings = SettingsCore::create(); lDebug() << log().arg("Checking downloaded codecs updates"); Utils::checkDownloadedCodecsUpdates(); lDebug() << log().arg("Setting Video Codec Priority Policy"); @@ -622,45 +703,55 @@ void App::initCore() { mNotifier = new Notifier(mEngine); mEngine->setObjectOwnership(settings.get(), QQmlEngine::CppOwnership); mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership); - if (!mAccountList) setAccountList(AccountList::create()); - else { - mAccountList->setInitialized(false); - mAccountList->lUpdate(true); - } - // Update global unread Notifications when an account updates his unread Notifications - connect(mAccountList.get(), &AccountList::unreadNotificationsChanged, this, [this]() { - lDebug() << "unreadNotificationsChanged of AccountList"; - mCoreModelConnection->invokeToModel([this] { - int n = mEventCountNotifier->getCurrentEventCount(); - mCoreModelConnection->invokeToCore([this, n] { mEventCountNotifier->notifyEventCount(n); }); - }); - }); - if (!mCallList) setCallList(CallList::create()); - else mCallList->lUpdate(); - if (!mSettings) { - mSettings = settings; - setLocale(settings->getConfigLocale()); - setAutoStart(settings->getAutoStart()); - setQuitOnLastWindowClosed(settings->getExitOnClose()); - mEngine->setObjectOwnership(mSettings.get(), QQmlEngine::CppOwnership); - connect(mSettings.get(), &SettingsCore::exitOnCloseChanged, this, &App::onExitOnCloseChanged, - Qt::UniqueConnection); - QObject::connect(mSettings.get(), &SettingsCore::autoStartChanged, [this]() { - mustBeInMainThread(log().arg(Q_FUNC_INFO)); - setAutoStart(mSettings->getAutoStart()); - }); - QObject::connect(mSettings.get(), &SettingsCore::configLocaleChanged, [this]() { - mustBeInMainThread(log().arg(Q_FUNC_INFO)); - if (mSettings) setLocale(mSettings->getConfigLocale()); - }); - connect(mSettings.get(), &SettingsCore::exitOnCloseChanged, this, &App::onExitOnCloseChanged, - Qt::UniqueConnection); - } else { - setLocale(settings->getConfigLocale()); - setAutoStart(settings->getAutoStart()); - setQuitOnLastWindowClosed(settings->getExitOnClose()); - } + connect(this, &App::coreStartedChanged, this, [this] { + if (mCoreStarted) { + if (!mAccountList) setAccountList(AccountList::create()); + else { + mAccountList->setInitialized(false); + mAccountList->lUpdate(true); + } + // Update global unread Notifications when an account updates his unread Notifications + connect(mAccountList.get(), &AccountList::unreadNotificationsChanged, this, [this]() { + lDebug() << "unreadNotificationsChanged of AccountList"; + mCoreModelConnection->invokeToModel([this] { + int n = mEventCountNotifier->getCurrentEventCount(); + mCoreModelConnection->invokeToCore( + [this, n] { mEventCountNotifier->notifyEventCount(n); }); + }); + }); + if (!mCallList) setCallList(CallList::create()); + else mCallList->lUpdate(); + if (!mChatList) setChatList(ChatList::create()); + else mChatList->lUpdate(); + disconnect(this, &App::coreStartedChanged, this, nullptr); + } + }); + + // if (!mSettings) { + mSettings = settings; + setLocale(settings->getConfigLocale()); + setAutoStart(settings->getAutoStart()); + setQuitOnLastWindowClosed(settings->getExitOnClose()); + mEngine->setObjectOwnership(mSettings.get(), QQmlEngine::CppOwnership); + + connect(mSettings.get(), &SettingsCore::exitOnCloseChanged, this, &App::onExitOnCloseChanged, + Qt::UniqueConnection); + QObject::connect(mSettings.get(), &SettingsCore::autoStartChanged, [this]() { + mustBeInMainThread(log().arg(Q_FUNC_INFO)); + setAutoStart(mSettings->getAutoStart()); + }); + QObject::connect(mSettings.get(), &SettingsCore::configLocaleChanged, [this]() { + mustBeInMainThread(log().arg(Q_FUNC_INFO)); + if (mSettings) setLocale(mSettings->getConfigLocale()); + }); + connect(mSettings.get(), &SettingsCore::exitOnCloseChanged, this, &App::onExitOnCloseChanged, + Qt::UniqueConnection); + // } else { + // 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, @@ -703,10 +794,20 @@ void App::initCore() { }); } else { mPossiblyLookForAddedAccount = true; + if (mAccountList && mAccountList->getCount() > 0) { + auto defaultConnected = + mAccountList->getDefaultAccountCore() && + mAccountList->getDefaultAccountCore()->getRegistrationState() == + LinphoneEnums::RegistrationState::Ok; + QMetaObject::invokeMethod(mMainWindow, "openMainPage", Qt::DirectConnection, + Q_ARG(QVariant, defaultConnected)); + } } } checkForUpdate(); mIsRestarting = false; + window->show(); + window->requestActivate(); //--------------------------------------------------------------------------------------------- lDebug() << log().arg("Creating KeyboardShortcuts"); @@ -823,7 +924,6 @@ void App::initCppInterfaces() { qmlRegisterType(Constants::MainQmlUri, 1, 0, "CallHistoryProxy"); qmlRegisterType(Constants::MainQmlUri, 1, 0, "CallGui"); qmlRegisterType(Constants::MainQmlUri, 1, 0, "CallProxy"); - qmlRegisterType(Constants::MainQmlUri, 1, 0, "ChatList"); qmlRegisterType(Constants::MainQmlUri, 1, 0, "ChatProxy"); qmlRegisterType(Constants::MainQmlUri, 1, 0, "ChatGui"); qmlRegisterType(Constants::MainQmlUri, 1, 0, "EventLogGui"); @@ -946,8 +1046,14 @@ void App::restart() { CoreModel::getInstance()->getCore()->stop(); mCoreModelConnection->invokeToCore([this]() { mIsRestarting = true; + if (mAccountList) mAccountList->resetData(); + if (mCallList) mCallList->resetData(); + if (mCallHistoryList) mCallHistoryList->resetData(); + if (mChatList) mChatList->resetData(); + if (mConferenceInfoList) mConferenceInfoList->resetData(); closeCallsWindow(); setMainWindow(nullptr); + setCoreStarted(false); mEngine->clearComponentCache(); mEngine->clearSingletons(); delete mEngine; @@ -1060,7 +1166,7 @@ bool App::notify(QObject *receiver, QEvent *event) { } if (event->type() == QEvent::MouseButtonPress) { auto window = findParentWindow(receiver); - if (getMainWindow() == window) { + if (getMainWindow() == window && mAccountList) { auto defaultAccountCore = mAccountList->getDefaultAccountCore(); if (defaultAccountCore && defaultAccountCore->getUnreadCallNotifications() > 0) { emit defaultAccountCore->lResetMissedCalls(); @@ -1070,27 +1176,30 @@ bool App::notify(QObject *receiver, QEvent *event) { return done; } +void App::handleAccountActivity(QSharedPointer accountCore) { + if (!accountCore) return; + auto accountPresence = accountCore->getPresence(); + if ((mMainWindow && mMainWindow->isActive() || (mCallsWindow && mCallsWindow->isActive())) && + accountPresence == LinphoneEnums::Presence::Away) { + accountCore->lSetPresence(LinphoneEnums::Presence::Online, false); + } else if (((!mMainWindow || !mMainWindow->isActive() || !mMainWindow->isVisible()) && + (!mCallsWindow || !mCallsWindow->isActive() || !mCallsWindow->isVisible())) && + accountPresence == LinphoneEnums::Presence::Online) { + accountCore->lSetPresence(LinphoneEnums::Presence::Away, false); + } +} + void App::handleAppActivity() { - auto handle = [this](QSharedPointer accountCore) { - if (!accountCore) return; - auto accountPresence = accountCore->getPresence(); - if ((mMainWindow && mMainWindow->isActive() || (mCallsWindow && mCallsWindow->isActive())) && - accountPresence == LinphoneEnums::Presence::Away) - accountCore->lSetPresence(LinphoneEnums::Presence::Online); - if (((!mMainWindow || !mMainWindow->isActive()) && (!mCallsWindow || !mCallsWindow->isActive())) && - accountPresence == LinphoneEnums::Presence::Online) - accountCore->lSetPresence(LinphoneEnums::Presence::Away); - }; if (mAccountList) { for (auto &account : mAccountList->getSharedList()) - handle(account); + handleAccountActivity(account); } else { connect( this, &App::accountsChanged, this, - [this, &handle] { + [this] { if (mAccountList) { for (auto &account : mAccountList->getSharedList()) - handle(account); + handleAccountActivity(account); } }, Qt::SingleShotConnection); @@ -1192,6 +1301,41 @@ AccountList *App::getAccounts() const { return mAccountList.get(); } +QSharedPointer App::getConferenceInfoList() const { + return mConferenceInfoList; +} +void App::setConferenceInfoList(QSharedPointer data) { + if (mConferenceInfoList != data) { + mConferenceInfoList = data; + emit conferenceInfosChanged(); + } +} + +QSharedPointer App::getCallHistoryList() const { + return mCallHistoryList; +} +void App::setCallHistoryList(QSharedPointer data) { + if (mCallHistoryList != data) { + mCallHistoryList = data; + emit callHistoryChanged(); + } +} + +QSharedPointer App::getChatList() const { + return mChatList; +} + +ChatList *App::getChats() const { + return mChatList.get(); +} + +void App::setChatList(QSharedPointer data) { + if (mChatList != data) { + mChatList = data; + emit chatsChanged(); + } +} + QSharedPointer App::getCallList() const { return mCallList; } @@ -1221,12 +1365,24 @@ void App::onAuthenticationRequested(const std::shared_ptr &core, const std::shared_ptr &authInfo, linphone::AuthMethod method) { bool authInfoIsInAccounts = false; - for (auto &account : core->getAccountList()) { - auto accountAuthInfo = account->findAuthInfo(); - if (authInfo && accountAuthInfo && authInfo->isEqualButAlgorithms(accountAuthInfo)) { - authInfoIsInAccounts = true; - if (account->getState() == linphone::RegistrationState::Ok) return; - break; + if (authInfo) { + for (auto &account : core->getAccountList()) { + if (!account) continue; + auto accountAuthInfo = account->findAuthInfo(); + if (accountAuthInfo) { + if (authInfo->isEqualButAlgorithms(accountAuthInfo)) { + authInfoIsInAccounts = true; + break; + } + } else { + auto identityAddress = account->getParams()->getIdentityAddress(); + if (!identityAddress) continue; + if (authInfo->getUsername() == identityAddress->getUsername() && + authInfo->getDomain() == identityAddress->getDomain()) { + authInfoIsInAccounts = true; + break; + } + } } } if (!authInfoIsInAccounts) return; @@ -1374,12 +1530,12 @@ bool App::event(QEvent *event) { } else if (event->type() == QEvent::ApplicationActivate) { for (int i = 0; i < getAccountList()->rowCount(); ++i) { auto accountCore = getAccountList()->getAt(i); - emit accountCore->lSetPresence(LinphoneEnums::Presence::Online, false, false); + handleAccountActivity(accountCore); } } else if (event->type() == QEvent::ApplicationDeactivate) { for (int i = 0; i < getAccountList()->rowCount(); ++i) { auto accountCore = getAccountList()->getAt(i); - emit accountCore->lSetPresence(LinphoneEnums::Presence::Away, false, false); + handleAccountActivity(accountCore); } } @@ -1488,12 +1644,17 @@ void App::setMacOSDockActions() { void App::setLocale(QString configLocale) { if (!configLocale.isEmpty()) mLocale = QLocale(configLocale); else mLocale = QLocale(QLocale::system().name()); + emit localeChanged(); } QLocale App::getLocale() { return mLocale; } +QString App::getLocaleAsString() { + return mLocale.name(); +} + //----------------------------------------------------------- // Version infos. //----------------------------------------------------------- diff --git a/Linphone/core/App.hpp b/Linphone/core/App.hpp index a18387e21..25b06e313 100644 --- a/Linphone/core/App.hpp +++ b/Linphone/core/App.hpp @@ -24,8 +24,11 @@ #include #include "core/account/AccountProxy.hpp" +#include "core/call-history/CallHistoryList.hpp" #include "core/call/CallProxy.hpp" #include "core/chat/ChatGui.hpp" +#include "core/chat/ChatList.hpp" +#include "core/conference/ConferenceInfoList.hpp" #include "core/setting/SettingsCore.hpp" #include "core/singleapplication/singleapplication.h" #include "model/cli/CliModel.hpp" @@ -45,11 +48,15 @@ class App : public SingleApplication, public AbstractObject { Q_PROPERTY(bool coreStarted READ getCoreStarted WRITE setCoreStarted NOTIFY coreStartedChanged) Q_PROPERTY(AccountList *accounts READ getAccounts NOTIFY accountsChanged) Q_PROPERTY(CallList *calls READ getCalls NOTIFY callsChanged) + Q_PROPERTY(ChatList *chats READ getChats NOTIFY chatsChanged) Q_PROPERTY(QString shortApplicationVersion READ getShortApplicationVersion CONSTANT) Q_PROPERTY(QString qtVersion READ getQtVersion CONSTANT) Q_PROPERTY(QString gitBranchName READ getGitBranchName CONSTANT) Q_PROPERTY(QString sdkVersion READ getSdkVersion CONSTANT) Q_PROPERTY(ChatGui *currentChat READ getCurrentChat WRITE setCurrentChat NOTIFY currentChatChanged) + Q_PROPERTY(QString localeAsString READ getLocaleAsString CONSTANT) + Q_PROPERTY(int remainingTimeBeforeOidcTimeout MEMBER mRemainingTimeBeforeOidcTimeout NOTIFY + remainingTimeBeforeOidcTimeoutChanged) public: App(int &argc, char *argv[]); @@ -132,6 +139,7 @@ public: } void updateSysTrayCount(int n); QLocale getLocale(); + QString getLocaleAsString(); void onLoggerInitialized(); void sendCommand(); @@ -140,6 +148,7 @@ public: void setCoreStarted(bool started); QQuickWindow *getCallsWindow(); + void handleAccountActivity(QSharedPointer accountCore); Q_INVOKABLE void handleAppActivity(); QQuickWindow *getOrCreateCallsWindow(QVariant callGui = QVariant()); void setCallsWindowProperty(const char *id, QVariant property); @@ -154,6 +163,16 @@ public: void setAccountList(QSharedPointer data); Q_INVOKABLE AccountList *getAccounts() const; + QSharedPointer getConferenceInfoList() const; + void setConferenceInfoList(QSharedPointer data); + + QSharedPointer getCallHistoryList() const; + void setCallHistoryList(QSharedPointer data); + + QSharedPointer getChatList() const; + ChatList *getChats() const; + void setChatList(QSharedPointer data); + QSharedPointer getCallList() const; void setCallList(QSharedPointer data); Q_INVOKABLE CallList *getCalls() const; @@ -198,6 +217,12 @@ signals: void callsChanged(); void currentDateChanged(); void currentChatChanged(); + void conferenceInfosChanged(); + void chatsChanged(); + void callHistoryChanged(); + void localeChanged(); + void lForceOidcTimeout(); + void remainingTimeBeforeOidcTimeoutChanged(); // void executeCommand(QString command); private: @@ -220,7 +245,10 @@ private: ChatGui *mCurrentChat = nullptr; QSharedPointer mSettings; QSharedPointer mAccountList; + QSharedPointer mConferenceInfoList; + QSharedPointer mChatList; QSharedPointer mCallList; + QSharedPointer mCallHistoryList; QSharedPointer> mCoreModelConnection; QSharedPointer> mCliModelConnection; bool mAutoStart = false; @@ -233,6 +261,8 @@ private: QTimer mDateUpdateTimer; QDate mCurrentDate; float mScreenRatio = 1; + QTimer mOIDCRefreshTimer; + int mRemainingTimeBeforeOidcTimeout = 0; DECLARE_ABSTRACT_OBJECT }; diff --git a/Linphone/core/CMakeLists.txt b/Linphone/core/CMakeLists.txt index 48bba6ffc..a3e4c34f4 100644 --- a/Linphone/core/CMakeLists.txt +++ b/Linphone/core/CMakeLists.txt @@ -18,7 +18,6 @@ list(APPEND _LINPHONEAPP_SOURCES core/call-history/CallHistoryProxy.cpp core/camera/CameraGui.cpp core/camera/CameraDummy.cpp - core/camera/PreviewManager.cpp core/chat/ChatCore.cpp core/chat/ChatGui.cpp core/chat/ChatList.cpp diff --git a/Linphone/core/account/AccountCore.cpp b/Linphone/core/account/AccountCore.cpp index 6cdfff27f..768e756e9 100644 --- a/Linphone/core/account/AccountCore.cpp +++ b/Linphone/core/account/AccountCore.cpp @@ -50,6 +50,7 @@ AccountCore::AccountCore(const std::shared_ptr &account) : QO mIsDefaultAccount = CoreModel::getInstance()->getCore()->getDefaultAccount() == account; mUnreadNotifications = account->getMissedCallsCount() + account->getUnreadChatMessageCount(); mDisplayName = Utils::coreStringToAppString(identityAddress->getDisplayName()); + mPublishEnabled = params->publishEnabled(); if (mDisplayName.isEmpty()) { mDisplayName = ToolModel::getDisplayName(identityAddress); auto copyAddress = identityAddress->clone(); @@ -320,20 +321,20 @@ void AccountCore::reset(const AccountCore &accountCore) { setUnreadNotifications(accountCore.mUnreadNotifications); setUnreadCallNotifications(accountCore.mUnreadCallNotifications); setUnreadMessageNotifications(accountCore.mUnreadMessageNotifications); - setMwiServerAddress(accountCore.mMwiServerAddress); - setVoicemailAddress(accountCore.mVoicemailAddress); - setTransport(accountCore.mTransport); - setRegistrarUri(accountCore.mRegistrarUri); - setOutboundProxyUri(accountCore.mOutboundProxyUri); + onMwiServerAddressChanged(accountCore.mMwiServerAddress); + onVoicemailAddressChanged(accountCore.mVoicemailAddress); + onTransportChanged(accountCore.mTransport); + onRegistrarUriChanged(accountCore.mRegistrarUri); + onOutboundProxyUriChanged(accountCore.mOutboundProxyUri); setStunServer(accountCore.mStunServer); - setIceEnabled(accountCore.mIceEnabled); - setAvpfEnabled(accountCore.mAvpfEnabled); - setBundleModeEnabled(accountCore.mBundleModeEnabled); - setExpire(accountCore.mExpire); - setConferenceFactoryAddress(accountCore.mConferenceFactoryAddress); - setAudioVideoConferenceFactoryAddress(accountCore.mAudioVideoConferenceFactoryAddress); - setLimeServerUrl(accountCore.mLimeServerUrl); - setCcmpServerUrl(accountCore.mCcmpServerUrl); + onIceEnabledChanged(accountCore.mIceEnabled); + onAvpfEnabledChanged(accountCore.mAvpfEnabled); + onBundleModeEnabledChanged(accountCore.mBundleModeEnabled); + onExpireChanged(accountCore.mExpire); + onConferenceFactoryAddressChanged(accountCore.mConferenceFactoryAddress); + onAudioVideoConferenceFactoryAddressChanged(accountCore.mAudioVideoConferenceFactoryAddress); + onLimeServerUrlChanged(accountCore.mLimeServerUrl); + onCcmpServerUrlChanged(accountCore.mCcmpServerUrl); } const std::shared_ptr &AccountCore::getModel() const { diff --git a/Linphone/core/account/AccountCore.hpp b/Linphone/core/account/AccountCore.hpp index badc4f9c3..6a90cc32f 100644 --- a/Linphone/core/account/AccountCore.hpp +++ b/Linphone/core/account/AccountCore.hpp @@ -84,6 +84,7 @@ public: Q_PROPERTY(LinphoneEnums::Presence explicitPresence MEMBER mExplicitPresence NOTIFY presenceChanged) Q_PROPERTY(QString presenceNote READ getPresenceNote WRITE setPresenceNote NOTIFY presenceChanged) Q_PROPERTY(int maxPresenceNoteSize MEMBER mMaxPresenceNoteSize CONSTANT) + Q_PROPERTY(bool publishEnabled MEMBER mPublishEnabled CONSTANT) Q_PROPERTY(QString ccmpServerUrl READ getCcmpServerUrl WRITE setCcmpServerUrl NOTIFY ccmpServerUrlChanged) DECLARE_CORE_GET(int, voicemailCount, VoicemailCount) @@ -278,6 +279,7 @@ private: LinphoneEnums::Presence mExplicitPresence; QString mPresenceNote; int mMaxPresenceNoteSize; + bool mPublishEnabled = false; bool mIsSaved = true; diff --git a/Linphone/core/account/AccountProxy.cpp b/Linphone/core/account/AccountProxy.cpp index 4b363e191..bcb3acc17 100644 --- a/Linphone/core/account/AccountProxy.cpp +++ b/Linphone/core/account/AccountProxy.cpp @@ -24,6 +24,12 @@ #include "core/App.hpp" AccountProxy::AccountProxy(QObject *parent) : LimitProxy(parent) { + if (!App::getInstance()->getAccountList()) { + mList = AccountList::create(); + App::getInstance()->setAccountList(mList); + } + mList = App::getInstance()->getAccountList(); + setSourceModel(mList.get()); connect(this, &AccountProxy::initializedChanged, this, &AccountProxy::resetDefaultAccount); connect(this, &AccountProxy::initializedChanged, this, &AccountProxy::haveAccountChanged); } diff --git a/Linphone/core/account/AccountProxy.hpp b/Linphone/core/account/AccountProxy.hpp index 69e39db02..2cb2bdf10 100644 --- a/Linphone/core/account/AccountProxy.hpp +++ b/Linphone/core/account/AccountProxy.hpp @@ -59,6 +59,7 @@ signals: protected: bool mInitialized = false; + QSharedPointer mList; QSharedPointer mDefaultAccount; // When null, a new UI object is build from List }; diff --git a/Linphone/core/address-books/carddav/CarddavCore.cpp b/Linphone/core/address-books/carddav/CarddavCore.cpp index cde454eba..01ff930c9 100644 --- a/Linphone/core/address-books/carddav/CarddavCore.cpp +++ b/Linphone/core/address-books/carddav/CarddavCore.cpp @@ -75,7 +75,7 @@ void CarddavCore::setSelf(QSharedPointer me) { mCarddavModelConnection = SafeConnection::create(me, mCarddavModel); mCarddavModelConnection->makeConnectToModel(&CarddavModel::saved, [this](bool success, QString message) { mCarddavModelConnection->invokeToCore([this, success, message]() { - if (success) emit App::getInstance() -> getSettings()->cardDAVAddressBookSynchronized(); + if (success) emit App::getInstance()->getSettings()->cardDAVAddressBookSynchronized(); emit saved(success, message); }); }); diff --git a/Linphone/core/call-history/CallHistoryList.cpp b/Linphone/core/call-history/CallHistoryList.cpp index 58dec0086..8b02d5184 100644 --- a/Linphone/core/call-history/CallHistoryList.cpp +++ b/Linphone/core/call-history/CallHistoryList.cpp @@ -70,7 +70,7 @@ void CallHistoryList::setSelf(QSharedPointer me) { for (auto it : linphoneCallLogs) { auto model = createCallHistoryCore(it); toConnect(model.get()); - callLogs->push_back(model); + callLogs->push_front(model); } mModelConnection->invokeToCore([this, callLogs]() { mustBeInMainThread(getClassName()); diff --git a/Linphone/core/call-history/CallHistoryProxy.cpp b/Linphone/core/call-history/CallHistoryProxy.cpp index 57552f85b..f60385a13 100644 --- a/Linphone/core/call-history/CallHistoryProxy.cpp +++ b/Linphone/core/call-history/CallHistoryProxy.cpp @@ -27,6 +27,11 @@ DEFINE_ABSTRACT_OBJECT(CallHistoryProxy) CallHistoryProxy::CallHistoryProxy(QObject *parent) : LimitProxy(parent) { mHistoryList = CallHistoryList::create(); + if (!App::getInstance()->getCallHistoryList()) { + mHistoryList = CallHistoryList::create(); + App::getInstance()->setCallHistoryList(mHistoryList); + } + mHistoryList = App::getInstance()->getCallHistoryList(); connect(mHistoryList.get(), &CallHistoryList::listAboutToBeReset, this, &CallHistoryProxy::listAboutToBeReset); setSourceModels(new SortFilterList(mHistoryList.get(), Qt::DescendingOrder)); connect(App::getInstance(), &App::currentDateChanged, this, [this] { emit mHistoryList->lUpdate(); }); @@ -63,8 +68,5 @@ bool CallHistoryProxy::SortFilterList::filterAcceptsRow(int sourceRow, const QMo } bool CallHistoryProxy::SortFilterList::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const { - auto l = getItemAtSource(sourceLeft.row()); - auto r = getItemAtSource(sourceRight.row()); - - return l->mDate < r->mDate; + return true; } diff --git a/Linphone/core/call/CallCore.cpp b/Linphone/core/call/CallCore.cpp index 9aed9f56e..1a56a234d 100644 --- a/Linphone/core/call/CallCore.cpp +++ b/Linphone/core/call/CallCore.cpp @@ -113,7 +113,8 @@ CallCore::CallCore(const std::shared_ptr &call) : QObject(nullpt auto videoDirection = callParams->getVideoDirection(); mLocalVideoEnabled = videoDirection == linphone::MediaDirection::SendOnly || videoDirection == linphone::MediaDirection::SendRecv; - mCameraEnabled = callParams->cameraEnabled(); + mCameraEnabled = mLocalVideoEnabled && callParams->cameraEnabled(); + qDebug() << "create call with camera enabled" << mLocalVideoEnabled << callParams->cameraEnabled(); auto remoteParams = call->getRemoteParams(); videoDirection = remoteParams ? remoteParams->getVideoDirection() : linphone::MediaDirection::Inactive; mRemoteVideoEnabled = diff --git a/Linphone/core/camera/CameraGui.cpp b/Linphone/core/camera/CameraGui.cpp index d1b826949..92662399d 100644 --- a/Linphone/core/camera/CameraGui.cpp +++ b/Linphone/core/camera/CameraGui.cpp @@ -25,7 +25,6 @@ #include "CameraDummy.hpp" #include "CameraGui.hpp" -#include "PreviewManager.hpp" #include "core/App.hpp" #include "core/call/CallCore.hpp" #include "core/call/CallGui.hpp" @@ -51,6 +50,10 @@ CameraGui::CameraGui(QQuickItem *parent) : QQuickFramebufferObject(parent) { CameraGui::~CameraGui() { mustBeInMainThread("~" + getClassName()); mRefreshTimer.stop(); + if (mIsPreview) { + lDebug() << "[CameraGui] Deactivation"; + CoreModel::getInstance()->getCore()->enableVideoPreview(false); + } setWindowIdLocation(None); } @@ -84,9 +87,12 @@ QQuickFramebufferObject::Renderer *CameraGui::createRenderer() const { // A renderer is mandatory, we cannot wait async. switch (getSourceLocation()) { case CorePreview: { - // if (resetWindowId) PreviewManager::getInstance()->unsubscribe(this); - renderer = PreviewManager::getInstance()->subscribe(this); - //(QQuickFramebufferObject::Renderer *)CoreModel::getInstance()->getCore()->createNativePreviewWindowId(); + App::postModelBlock([qmlName = mQmlName, callGui = mCallGui, &renderer]() { + lInfo() << "[Camera] (" << qmlName << ") Camera create from Preview"; + renderer = (QQuickFramebufferObject::Renderer *)CoreModel::getInstance() + ->getCore() + ->createNativePreviewWindowId(nullptr); + }); } break; case Call: { @@ -135,7 +141,10 @@ void CameraGui::updateSDKRenderer(QQuickFramebufferObject::Renderer *renderer) { lDebug() << log().arg("Apply Qt Renderer to SDK") << renderer; switch (getSourceLocation()) { case CorePreview: { - + App::postModelBlock([qmlName = mQmlName, renderer]() { + lInfo() << "[Camera] (" << qmlName << ") Camera to CorePreview"; + CoreModel::getInstance()->getCore()->setNativePreviewWindowId(renderer); + }); } break; case Call: { App::postModelAsync([qmlName = mQmlName, callGui = mCallGui, renderer]() { @@ -197,6 +206,11 @@ bool CameraGui::getIsPreview() const { void CameraGui::setIsPreview(bool status) { if (mIsPreview != status) { mIsPreview = status; + // We block it to serialize the action and allow only one CameraGui to change the state. + App::postModelBlock([status]() { + lDebug() << "[CameraGui] " << (status ? "Activation" : "Deactivation"); + CoreModel::getInstance()->getCore()->enableVideoPreview(status); + }); updateWindowIdLocation(); update(); @@ -240,18 +254,9 @@ CameraGui::WindowIdLocation CameraGui::getSourceLocation() const { void CameraGui::setWindowIdLocation(const WindowIdLocation &location) { if (mWindowIdLocation != location) { lDebug() << log().arg("Update Window Id location from %2 to %3").arg(mWindowIdLocation).arg(location); - if (mWindowIdLocation == CorePreview) PreviewManager::getInstance()->unsubscribe(this); - // else if (mWindowIdLocation != None) resetWindowId(); // Location change: Reset old window ID. resetWindowId(); mWindowIdLocation = location; - if (mWindowIdLocation == CorePreview) PreviewManager::getInstance()->subscribe(this); - else updateSDKRenderer(); - // QTimer::singleShot(100, this, &CameraGui::requestNewRenderer); - // if (mWindowIdLocation == WindowIdLocation::CorePreview) { - // mLastVideoDefinition = - // CoreManager::getInstance()->getSettingsModel()->getCurrentPreviewVideoDefinition(); emit - // videoDefinitionChanged(); mLastVideoDefinitionChecker.start(); - // } else mLastVideoDefinitionChecker.stop(); + updateSDKRenderer(); } } void CameraGui::updateWindowIdLocation() { @@ -262,7 +267,7 @@ void CameraGui::updateWindowIdLocation() { setWindowIdLocation(WindowIdLocation::Device); else setWindowIdLocation(WindowIdLocation::CorePreview); } - +// TODO to remove void CameraGui::callStateChanged(LinphoneEnums::CallState state) { if (getSourceLocation() == CorePreview && state == LinphoneEnums::CallState::Connected) { if (!getIsReady()) { diff --git a/Linphone/core/camera/PreviewManager.cpp b/Linphone/core/camera/PreviewManager.cpp deleted file mode 100644 index 8804635df..000000000 --- a/Linphone/core/camera/PreviewManager.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2010-2024 Belledonne Communications SARL. - * - * This file is part of linphone-desktop - * (see https://www.linphone.org). - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include - -#include "../App.hpp" -#include "PreviewManager.hpp" -#include "tool/Utils.hpp" - -DEFINE_ABSTRACT_OBJECT(PreviewManager) - -// ============================================================================= -PreviewManager *PreviewManager::gInstance = nullptr; - -PreviewManager::PreviewManager(QObject *parent) : QObject(parent) { -} - -PreviewManager::~PreviewManager() { -} - -PreviewManager *PreviewManager::getInstance() { - if (gInstance) return gInstance; - else { - gInstance = new PreviewManager(); - return gInstance; - } -} - -// Create a Renderer from SDK preview -QQuickFramebufferObject::Renderer *PreviewManager::subscribe(const CameraGui *candidate) { - QQuickFramebufferObject::Renderer *renderer = nullptr; - mCounterMutex.lock(); - - if (mCandidates.size() == 0) { - activate(); - } - auto itCandidate = - std::find_if(mCandidates.begin(), mCandidates.end(), - [candidate](const QPair &item) { - return item.first == candidate; - }); - if (itCandidate == mCandidates.end()) { - connect(candidate, &QObject::destroyed, this, qOverload(&PreviewManager::unsubscribe)); - mCandidates.append({candidate, nullptr}); - itCandidate = mCandidates.end() - 1; - lDebug() << log().arg("Subscribing New") << itCandidate->first->getQmlName(); - } else { - lDebug() << log().arg("Resubscribing") << itCandidate->first->getQmlName(); - } - mCounterMutex.unlock(); - App::postModelBlock([&renderer, isFirst = (itCandidate == mCandidates.begin()), - name = itCandidate->first->getQmlName()]() { - renderer = - (QQuickFramebufferObject::Renderer *)CoreModel::getInstance()->getCore()->createNativePreviewWindowId( - nullptr); - if (!renderer) { // TODO debug - renderer = - (QQuickFramebufferObject::Renderer *)CoreModel::getInstance()->getCore()->createNativePreviewWindowId( - nullptr); - } - if (isFirst) { - lDebug() << "[PreviewManager] " << name << " Set Native Preview Id with " << renderer; - CoreModel::getInstance()->getCore()->setNativePreviewWindowId(renderer); - } - }); - mCounterMutex.lock(); - itCandidate->second = renderer; - mCounterMutex.unlock(); - return renderer; -} - -void PreviewManager::unsubscribe(const CameraGui *candidate) { // If nullptr, Use of sender() - mCounterMutex.lock(); - auto itCandidate = std::find_if(mCandidates.begin(), mCandidates.end(), - [candidate = (candidate ? candidate : sender())]( - const QPair &item) { - return item.first == candidate; - }); - if (itCandidate != mCandidates.end()) { - lDebug() << log().arg("Unsubscribing") << itCandidate->first->getQmlName(); - disconnect(candidate, nullptr, this, nullptr); - if (mCandidates.size() == 1) { - mCandidates.erase(itCandidate); - deactivate(); - } else if (mCandidates.begin() == itCandidate) { - mCandidates.erase(itCandidate); - lDebug() << log().arg("Update") << mCandidates.first().first->getQmlName(); - auto renderer = mCandidates.first().second; - if (renderer) - App::postModelBlock([renderer = mCandidates.first().second]() { - CoreModel::getInstance()->getCore()->setNativePreviewWindowId(renderer); - }); - } else { - mCandidates.erase(itCandidate); - } - } - mCounterMutex.unlock(); -} - -void PreviewManager::unsubscribe(QObject *sender) { - unsubscribe(dynamic_cast(sender)); -} - -void PreviewManager::activate() { - App::postModelAsync([]() { - lDebug() << "[PreviewManager] Activation"; - CoreModel::getInstance()->getCore()->enableVideoPreview(true); - }); -} - -void PreviewManager::deactivate() { - App::postModelAsync([]() { - lDebug() << "[PreviewManager] Deactivation"; - CoreModel::getInstance()->getCore()->enableVideoPreview(false); - }); -} diff --git a/Linphone/core/camera/PreviewManager.hpp b/Linphone/core/camera/PreviewManager.hpp deleted file mode 100644 index f30749fa9..000000000 --- a/Linphone/core/camera/PreviewManager.hpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 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 . - */ - -#ifndef PREVIEW_MANAGER_H_ -#define PREVIEW_MANAGER_H_ - -#include "CameraGui.hpp" -#include "tool/AbstractObject.hpp" -#include -#include -#include -#include - -// Manage the SDK preview as a singleton. -// The goal is to process the limitation that only one preview can be displayed. -// On asynchronized application, the destruction of a previous Preview can be done AFTER the creation on a new Preview -// Sticker. - -// ============================================================================= - -class PreviewManager : public QObject, public AbstractObject { - Q_OBJECT -public: - PreviewManager(QObject *parent = nullptr); - virtual ~PreviewManager(); - - static PreviewManager *getInstance(); - - QQuickFramebufferObject::Renderer *subscribe(const CameraGui *candidate); - void unsubscribe(const CameraGui *candidate); - - void activate(); - void deactivate(); -public slots: - void unsubscribe(QObject *sender); - -private: - QMutex mCounterMutex; - QList> mCandidates; - static PreviewManager *gInstance; - QQuickFramebufferObject::Renderer *mPreviewRenderer = nullptr; - DECLARE_ABSTRACT_OBJECT -}; - -#endif diff --git a/Linphone/core/chat/ChatCore.cpp b/Linphone/core/chat/ChatCore.cpp index 339244531..c39303a5f 100644 --- a/Linphone/core/chat/ChatCore.cpp +++ b/Linphone/core/chat/ChatCore.cpp @@ -43,7 +43,7 @@ QSharedPointer ChatCore::create(const std::shared_ptr &chatRoom) : QObject(nullptr) { - // lDebug() << "[ChatCore] new" << this; + lDebug() << "[ChatCore] new" << this; mustBeInLinphoneThread(getClassName()); App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership); mLastUpdatedTime = QDateTime::fromSecsSinceEpoch(chatRoom->getLastUpdateTime()); @@ -118,11 +118,10 @@ ChatCore::ChatCore(const std::shared_ptr &chatRoom) : QObjec ChatCore::~ChatCore() { lDebug() << "[ChatCore] delete" << this; mustBeInMainThread("~" + getClassName()); - if (mChatModelConnection) mChatModelConnection->disconnect(); emit mChatModel->removeListener(); } -void ChatCore::setSelf(QSharedPointer me) { +void ChatCore::setSelf(const QSharedPointer &me) { mChatModelConnection = SafeConnection::create(me, mChatModel); mChatModelConnection->makeConnectToCore(&ChatCore::lDeleteHistory, [this]() { mChatModelConnection->invokeToModel([this]() { mChatModel->deleteHistory(); }); @@ -199,7 +198,11 @@ void ChatCore::setSelf(QSharedPointer me) { emit conferenceJoined(); }); } - mChatModelConnection->invokeToCore([this, participants]() { setParticipants(participants); }); + auto meAdmin = chatRoom->getMe() && chatRoom->getMe()->isAdmin(); + mChatModelConnection->invokeToCore([this, participants, meAdmin]() { + setParticipants(participants); + setMeAdmin(meAdmin); + }); }); // Events (excluding messages) @@ -207,7 +210,8 @@ void ChatCore::setSelf(QSharedPointer me) { &ChatModel::newEvent, [this](const std::shared_ptr &chatRoom, const std::shared_ptr &eventLog) { if (mChatModel->getMonitor() != chatRoom) return; - lDebug() << "EVENT LOG RECEIVED IN CHATROOM" << mChatModel->getTitle(); + if (!eventLog) return; + lDebug() << log().arg("EVENT LOG RECEIVED IN CHATROOM") << this << mChatModel->getTitle(); auto event = EventLogCore::create(eventLog, chatRoom); if (event->isHandled()) { mChatModelConnection->invokeToCore([this, event]() { emit eventsInserted({event}); }); @@ -220,7 +224,7 @@ void ChatCore::setSelf(QSharedPointer me) { &ChatModel::chatMessagesReceived, [this](const std::shared_ptr &chatRoom, const std::list> &eventsLog) { if (mChatModel->getMonitor() != chatRoom) return; - lDebug() << "CHAT MESSAGE RECEIVED IN CHATROOM" << mChatModel->getTitle(); + lDebug() << log().arg("CHAT MESSAGE RECEIVED IN CHATROOM") << this << mChatModel->getTitle(); QList> list; for (auto &e : eventsLog) { auto event = EventLogCore::create(e, chatRoom); @@ -394,7 +398,6 @@ void ChatCore::setSelf(QSharedPointer me) { [this](std::shared_ptr f) { updateInfo(f); }); mCoreModelConnection->makeConnectToModel(&CoreModel::friendRemoved, [this](std::shared_ptr f) { updateInfo(f, true); }); - } QDateTime ChatCore::getLastUpdatedTime() const { @@ -623,6 +626,8 @@ ChatCore::buildParticipants(const std::shared_ptr &chatRoom) auto participantCore = ParticipantCore::create(participant); result.append(participantCore); } + auto meCore = ParticipantCore::create(chatRoom->getMe()); + if (meCore) result.append(meCore); return result; } diff --git a/Linphone/core/chat/ChatCore.hpp b/Linphone/core/chat/ChatCore.hpp index ec7ee734a..d8100cd79 100644 --- a/Linphone/core/chat/ChatCore.hpp +++ b/Linphone/core/chat/ChatCore.hpp @@ -74,7 +74,7 @@ public: static QSharedPointer create(const std::shared_ptr &chatRoom); ChatCore(const std::shared_ptr &chatRoom); ~ChatCore(); - void setSelf(QSharedPointer me); + void setSelf(const QSharedPointer &me); QDateTime getLastUpdatedTime() const; void setLastUpdatedTime(QDateTime time); diff --git a/Linphone/core/chat/ChatList.cpp b/Linphone/core/chat/ChatList.cpp index 8061f19b6..00e4bc416 100644 --- a/Linphone/core/chat/ChatList.cpp +++ b/Linphone/core/chat/ChatList.cpp @@ -50,7 +50,7 @@ ChatList::ChatList(QObject *parent) : ListProxy(parent) { ChatList::~ChatList() { mustBeInMainThread("~" + getClassName()); - mModelConnection = nullptr; + mModelConnection->disconnect(); } void ChatList::connectItem(QSharedPointer chat) { @@ -73,7 +73,9 @@ void ChatList::connectItem(QSharedPointer chat) { }; connect(chat.get(), &ChatCore::unreadMessagesCountChanged, this, [this, dataChange] { dataChange(); - auto defaultAccount = App::getInstance()->getAccountList()->getDefaultAccountCore(); + auto defaultAccount = App::getInstance()->getAccountList() + ? App::getInstance()->getAccountList()->getDefaultAccountCore() + : nullptr; if (defaultAccount) emit defaultAccount->lRefreshNotifications(); }); connect(chat.get(), &ChatCore::lastUpdatedTimeChanged, this, dataChange); @@ -93,15 +95,17 @@ void ChatList::setSelf(QSharedPointer me) { return; } setIsUpdating(true); + beginResetModel(); mModelConnection->invokeToModel([this]() { mustBeInLinphoneThread(getClassName()); - beginResetModel(); // Avoid copy to lambdas QList> *chats = new QList>(); auto currentAccount = CoreModel::getInstance()->getCore()->getDefaultAccount(); if (!currentAccount) { - setIsUpdating(false); - endResetModel(); + mModelConnection->invokeToCore([this, chats]() { + setIsUpdating(false); + endResetModel(); + }); return; } auto linphoneChatRooms = currentAccount->filterChatRooms(Utils::appStringToCoreString(mFilter)); @@ -122,10 +126,11 @@ void ChatList::setSelf(QSharedPointer me) { mList.clear(); for (auto &chat : *chats) { connectItem(chat); + mList.append(chat); } - add(*chats); endResetModel(); setIsUpdating(false); + chats->clear(); delete chats; }); }); @@ -141,11 +146,11 @@ void ChatList::setSelf(QSharedPointer me) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); if (!message) return; if (room->getAccount() != core->getDefaultAccount()) { - qWarning() << log().arg("Chat room does not refer to current account, return"); + qInfo() << log().arg("Chat room does not refer to current account, return"); return; } auto chatCore = ChatCore::create(room); - mModelConnection->invokeToCore([this, chatCore] { addChatInList(chatCore); }); + mModelConnection->invokeToCore([this, chatCore] { addChatInList(chatCore, false); }); }; mModelConnection->makeConnectToModel(&CoreModel::messageReceived, [this, addChatToList](const std::shared_ptr &core, @@ -168,6 +173,33 @@ void ChatList::setSelf(QSharedPointer me) { const std::shared_ptr &reaction) { addChatToList(core, room, message); }); + mModelConnection->makeConnectToModel( + &CoreModel::chatRoomStateChanged, + [this, addChatToList](const std::shared_ptr &core, + const std::shared_ptr &chatRoom, linphone::ChatRoom::State state) { + if (state == linphone::ChatRoom::State::Created) { + bool sendAddSignal = false; + if (chatRoom == CoreModel::getInstance()->mChatRoomBeingCreated) { + sendAddSignal = true; + } + CoreModel::getInstance()->mChatRoomBeingCreated = nullptr; + if (chatRoom->getConferenceInfo()) { + qWarning() << log().arg("Chatroom created during a conference, return"); + return; + } + auto chatAccount = chatRoom->getAccount(); + auto defaultAccount = core->getDefaultAccount(); + if (!chatAccount || !defaultAccount) return; + if (!chatAccount->getParams()->getIdentityAddress()->weakEqual( + defaultAccount->getParams()->getIdentityAddress())) { + qWarning() << log().arg("Chatroom does not refer to current account, return"); + return; + } + auto chatCore = ChatCore::create(chatRoom); + mModelConnection->invokeToCore( + [this, chatCore, sendAddSignal] { addChatInList(chatCore, sendAddSignal); }); + } + }); connect(this, &ChatList::filterChanged, [this](QString filter) { mFilter = filter; @@ -186,8 +218,12 @@ int ChatList::findChatIndex(ChatGui *chatGui) { return it == chatList.end() ? -1 : std::distance(chatList.begin(), it); } -bool ChatList::addChatInList(QSharedPointer chatCore) { +bool ChatList::addChatInList(QSharedPointer chatCore, bool emitAddSignal) { mustBeInMainThread(log().arg(Q_FUNC_INFO)); + if (chatCore->getIdentifier().isEmpty()) { + qWarning() << "ChatRoom with invalid identifier cannot be added to the list, return"; + return false; + } auto chatList = getSharedList(); auto it = std::find_if(chatList.begin(), chatList.end(), [chatCore](const QSharedPointer item) { return item && chatCore && item->getIdentifier() == chatCore->getIdentifier(); @@ -195,7 +231,7 @@ bool ChatList::addChatInList(QSharedPointer chatCore) { if (it == chatList.end()) { connectItem(chatCore); add(chatCore); - emit chatAdded(); + if (emitAddSignal) emit chatAdded(chatCore); return true; } return false; diff --git a/Linphone/core/chat/ChatList.hpp b/Linphone/core/chat/ChatList.hpp index 0ec7ab87a..66ccbead6 100644 --- a/Linphone/core/chat/ChatList.hpp +++ b/Linphone/core/chat/ChatList.hpp @@ -42,13 +42,13 @@ public: void connectItem(QSharedPointer chat); int findChatIndex(ChatGui *chat); - bool addChatInList(QSharedPointer chatCore); + bool addChatInList(QSharedPointer chatCore, bool emitAddSignal); virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; signals: void lUpdate(); void filterChanged(QString filter); - void chatAdded(); + void chatAdded(QSharedPointer chatCore); void chatUpdated(); private: diff --git a/Linphone/core/chat/ChatProxy.cpp b/Linphone/core/chat/ChatProxy.cpp index fd30a71a6..492008cf8 100644 --- a/Linphone/core/chat/ChatProxy.cpp +++ b/Linphone/core/chat/ChatProxy.cpp @@ -28,7 +28,6 @@ DEFINE_ABSTRACT_OBJECT(ChatProxy) ChatProxy::ChatProxy(QObject *parent) { mList = ChatList::create(); setSourceModel(mList.get()); - setDynamicSortFilter(true); } ChatProxy::~ChatProxy() { @@ -45,11 +44,17 @@ void ChatProxy::setSourceModel(QAbstractItemModel *model) { if (newChatList) { connect(this, &ChatProxy::filterTextChanged, newChatList, [this, newChatList] { emit newChatList->filterChanged(getFilterText()); }); - connect(newChatList, &ChatList::chatAdded, this, [this] { invalidate(); }); + connect(newChatList, &ChatList::chatAdded, this, [this](QSharedPointer chatCore) { + if (chatCore) { + invalidate(); + emit chatAdded(new ChatGui(chatCore)); + } + }); connect(newChatList, &ChatList::dataChanged, this, [this] { invalidate(); }); } QSortFilterProxyModel::setSourceModel(newChatList); sort(0); + emit modelChanged(); } int ChatProxy::findChatIndex(ChatGui *chatGui) { @@ -67,7 +72,7 @@ int ChatProxy::findChatIndex(ChatGui *chatGui) { bool ChatProxy::addChatInList(ChatGui *chatGui) { auto chatList = dynamic_cast(sourceModel()); if (chatList && chatGui) { - return chatList->addChatInList(chatGui->mCore); + return chatList->addChatInList(chatGui->mCore, true); } return false; } diff --git a/Linphone/core/chat/ChatProxy.hpp b/Linphone/core/chat/ChatProxy.hpp index f9e6c48cf..2356ccfa2 100644 --- a/Linphone/core/chat/ChatProxy.hpp +++ b/Linphone/core/chat/ChatProxy.hpp @@ -30,6 +30,7 @@ class ChatProxy : public SortFilterProxy, public AbstractObject { Q_OBJECT + Q_PROPERTY(QAbstractItemModel *model WRITE setSourceModel NOTIFY modelChanged) public: ChatProxy(QObject *parent = Q_NULLPTR); @@ -42,6 +43,10 @@ public: Q_INVOKABLE int findChatIndex(ChatGui *chatGui); Q_INVOKABLE bool addChatInList(ChatGui *chatGui); +signals: + void chatAdded(ChatGui *chat); + void modelChanged(); + protected: QSharedPointer mList; DECLARE_ABSTRACT_OBJECT diff --git a/Linphone/core/chat/files/ChatMessageFileList.cpp b/Linphone/core/chat/files/ChatMessageFileList.cpp index ea311c0f3..4f7f4da79 100644 --- a/Linphone/core/chat/files/ChatMessageFileList.cpp +++ b/Linphone/core/chat/files/ChatMessageFileList.cpp @@ -77,10 +77,12 @@ void ChatMessageFileList::setSelf(QSharedPointer me) { docs = chatModel->getSharedDocuments(); } for (auto it : medias) { + if (it->isVoiceRecording()) continue; auto model = ChatMessageContentCore::create(it, nullptr); contents->push_back(model); } for (auto it : docs) { + if (it->isVoiceRecording()) continue; auto model = ChatMessageContentCore::create(it, nullptr); contents->push_back(model); } diff --git a/Linphone/core/chat/message/ChatMessageCore.cpp b/Linphone/core/chat/message/ChatMessageCore.cpp index 188e81f74..1591eb13f 100644 --- a/Linphone/core/chat/message/ChatMessageCore.cpp +++ b/Linphone/core/chat/message/ChatMessageCore.cpp @@ -163,7 +163,7 @@ ChatMessageCore::ChatMessageCore(const std::shared_ptr &c mTotalReactionsLabel = tr("all_reactions_label"); auto reac = chatmessage->getOwnReaction(); mOwnReaction = reac ? Utils::coreStringToAppString(reac->getBody()) : QString(); - for (auto &reaction : chatmessage->getReactions()) { + for (const auto &reaction : chatmessage->getReactions()) { if (reaction) { auto fromAddr = reaction->getFromAddress()->clone(); fromAddr->clean(); @@ -239,9 +239,11 @@ void ChatMessageCore::setSelf(QSharedPointer me) { }); } }); - mChatMessageModelConnection->makeConnectToModel(&ChatMessageModel::messageRead, [this]() { - mChatMessageModelConnection->invokeToCore([this] { setIsRead(true); }); - }); + mChatMessageModelConnection->makeConnectToModel( + &ChatMessageModel::messageRead, [this](const std::shared_ptr &chatMessage) { + bool isRead = chatMessage->isRead(); + mChatMessageModelConnection->invokeToCore([this, isRead] { setIsRead(isRead); }); + }); mChatMessageModelConnection->makeConnectToCore(&ChatMessageCore::lSendReaction, [this](const QString &reaction) { mChatMessageModelConnection->invokeToModel([this, reaction] { mChatMessageModel->sendReaction(reaction); }); }); diff --git a/Linphone/core/chat/message/EventLogCore.cpp b/Linphone/core/chat/message/EventLogCore.cpp index c2e689d71..8a67a07b5 100644 --- a/Linphone/core/chat/message/EventLogCore.cpp +++ b/Linphone/core/chat/message/EventLogCore.cpp @@ -107,13 +107,13 @@ void EventLogCore::computeEvent(const std::shared_ptr switch (eventLog->getType()) { case linphone::EventLog::Type::ConferenceCreated: - if (chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::OneToOne) && + if (chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::OneToOne) || !chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::Conference)) mHandled = false; mEventDetails = tr("conference_created_event"); break; case linphone::EventLog::Type::ConferenceTerminated: - if (chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::OneToOne) && + if (chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::OneToOne) || !chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::Conference)) mHandled = false; mEventDetails = tr("conference_created_terminated"); diff --git a/Linphone/core/chat/message/EventLogList.cpp b/Linphone/core/chat/message/EventLogList.cpp index 3d476561a..2374fdbbe 100644 --- a/Linphone/core/chat/message/EventLogList.cpp +++ b/Linphone/core/chat/message/EventLogList.cpp @@ -110,7 +110,7 @@ void EventLogList::setChatCore(QSharedPointer core) { connect(mChatCore.get(), &ChatCore::eventListCleared, this, [this] { resetData(); }); connect(mChatCore.get(), &ChatCore::eventsInserted, this, [this](QList> list) { auto eventsList = getSharedList(); - for (auto &event : list) { + for (const auto &event : list) { auto it = std::find_if(eventsList.begin(), eventsList.end(), [event](const QSharedPointer item) { return item == event; }); if (it == eventsList.end()) { @@ -242,15 +242,20 @@ void EventLogList::findChatMessageWithFilter(QString filter, int startIndex, boo lInfo() << log().arg("searching event starting from index") << startIndex << "| event :" << (startEvent && startEvent->getChatMessageCore() ? startEvent->getChatMessageCore()->getText() : "null") - << "| filter :" << filter; + << "| filter :" << filter << "forward =" << forward; auto startEventModel = startEvent ? startEvent->getModel() : nullptr; mCoreModelConnection->invokeToModel([this, chatModel, startEventModel, filter, forward, isFirstResearch] { auto linStartEvent = startEventModel ? startEventModel->getEventLog() : nullptr; auto eventLog = chatModel->searchMessageByText(filter, linStartEvent, forward); - if (!eventLog) { + if (!eventLog && isFirstResearch) { + // event not found, search backward + lInfo() << log().arg("not found, search backward"); + eventLog = chatModel->searchMessageByText(filter, linStartEvent, !forward); + } + if (!eventLog && isFirstResearch) { // event not found, search in the entire history lInfo() << log().arg("not found, search in entire history"); - auto eventLog = chatModel->searchMessageByText(filter, nullptr, forward); + eventLog = chatModel->searchMessageByText(filter, nullptr, forward); } int index = -1; if (eventLog) { @@ -312,12 +317,14 @@ void EventLogList::setSelf(QSharedPointer me) { if (!mChatCore) { endResetModel(); setIsUpdating(false); + emit modelUpdated(); return; } auto chatModel = mChatCore->getModel(); if (!chatModel) { endResetModel(); setIsUpdating(false); + emit modelUpdated(); return; } mCoreModelConnection->invokeToModel([this, chatModel]() { @@ -335,6 +342,7 @@ void EventLogList::setSelf(QSharedPointer me) { } endResetModel(); setIsUpdating(false); + emit modelUpdated(); }); }); }); diff --git a/Linphone/core/chat/message/EventLogList.hpp b/Linphone/core/chat/message/EventLogList.hpp index deec5b88e..a9393ba5d 100644 --- a/Linphone/core/chat/message/EventLogList.hpp +++ b/Linphone/core/chat/message/EventLogList.hpp @@ -67,6 +67,7 @@ public: signals: void lUpdate(); + void modelUpdated(); void filterChanged(QString filter); void eventInsertedByUser(int index); void messageWithFilterFound(int index); diff --git a/Linphone/core/chat/message/EventLogProxy.cpp b/Linphone/core/chat/message/EventLogProxy.cpp index 5f03799fe..bb916b1ec 100644 --- a/Linphone/core/chat/message/EventLogProxy.cpp +++ b/Linphone/core/chat/message/EventLogProxy.cpp @@ -57,6 +57,7 @@ void EventLogProxy::setSourceModel(QAbstractItemModel *model) { int proxyIndex = mapFromSource(newEventLogList->index(i, 0)).row(); emit eventInsertedByUser(proxyIndex); }); + connect(newEventLogList, &EventLogList::modelUpdated, this, &EventLogProxy::modelUpdated); } QSortFilterProxyModel::setSourceModel(model); } @@ -148,7 +149,7 @@ void EventLogProxy::setDisplayItemsStep(int step) { } void EventLogProxy::loadUntil(int index) { - if (mMaxDisplayItems < index) setMaxDisplayItems(index + mDisplayItemsStep); + if (mMaxDisplayItems <= index) setMaxDisplayItems(index + mDisplayItemsStep); } int EventLogProxy::findFirstUnreadIndex() { @@ -157,7 +158,7 @@ int EventLogProxy::findFirstUnreadIndex() { auto listIndex = eventLogList->findFirstUnreadIndex(); if (listIndex != -1) { listIndex = mapFromSource(eventLogList->index(listIndex, 0)).row(); - if (mMaxDisplayItems <= listIndex) setMaxDisplayItems(listIndex + mDisplayItemsStep); + loadUntil(listIndex); return listIndex; } else { return 0; diff --git a/Linphone/core/chat/message/EventLogProxy.hpp b/Linphone/core/chat/message/EventLogProxy.hpp index 093c19543..dde4f67c2 100644 --- a/Linphone/core/chat/message/EventLogProxy.hpp +++ b/Linphone/core/chat/message/EventLogProxy.hpp @@ -87,6 +87,7 @@ signals: void maxDisplayItemsChanged(); void displayItemsStepChanged(); void filterTextChanged(); + void modelUpdated(); protected: QSharedPointer mList; diff --git a/Linphone/core/chat/message/content/ChatMessageContentCore.cpp b/Linphone/core/chat/message/content/ChatMessageContentCore.cpp index c68fd10e6..a1e365c5b 100644 --- a/Linphone/core/chat/message/content/ChatMessageContentCore.cpp +++ b/Linphone/core/chat/message/content/ChatMessageContentCore.cpp @@ -64,7 +64,13 @@ ChatMessageContentCore::ChatMessageContentCore(const std::shared_ptrgetUtf8Text()); auto chatRoom = chatMessageModel ? chatMessageModel->getMonitor()->getChatRoom() : nullptr; - mRichFormatText = ToolModel::encodeTextToQmlRichFormat(mUtf8Text, {}, chatRoom); + mRichFormatText = ToolModel::encodeTextToQmlRichFormat(mUtf8Text, mSearchedTextPart, {}, chatRoom); + connect(this, &ChatMessageContentCore::searchedTextPartChanged, this, [this] { + auto chatroom = mChatMessageContentModel->getChatMessageModel() + ? mChatMessageContentModel->getChatMessageModel()->getMonitor()->getChatRoom() + : nullptr; + setRichFormatText(ToolModel::encodeTextToQmlRichFormat(mUtf8Text, mSearchedTextPart, {}, chatroom)); + }); mWasDownloaded = !mFilePath.isEmpty() && QFileInfo(mFilePath).isFile(); mThumbnail = mFilePath.isEmpty() ? QUrl() @@ -246,10 +252,6 @@ ConferenceInfoGui *ChatMessageContentCore::getConferenceInfoGui() const { return mConferenceInfo ? new ConferenceInfoGui(mConferenceInfo) : nullptr; } -bool ChatMessageContentCore::wasDownloaded() const { - return mWasDownloaded; -} - QUrl ChatMessageContentCore::getThumbnail() const { return mThumbnail; } @@ -260,6 +262,10 @@ void ChatMessageContentCore::setThumbnail(const QUrl &data) { emit thumbnailChanged(); } } + +bool ChatMessageContentCore::wasDownloaded() const { + return mWasDownloaded; +} void ChatMessageContentCore::setWasDownloaded(bool wasDownloaded) { if (mWasDownloaded != wasDownloaded) { mWasDownloaded = wasDownloaded; @@ -267,6 +273,24 @@ void ChatMessageContentCore::setWasDownloaded(bool wasDownloaded) { } } +void ChatMessageContentCore::setRichFormatText(const QString &richFormatText) { + if (mRichFormatText != richFormatText) { + mRichFormatText = richFormatText; + emit richFormatTextChanged(); + } +} + +void ChatMessageContentCore::setSearchedTextPart(const QString &searchedTextPart) { + if (mSearchedTextPart != searchedTextPart) { + mSearchedTextPart = searchedTextPart; + emit searchedTextPartChanged(); + } +} + +QString ChatMessageContentCore::getSearchedTextPart() const { + return mSearchedTextPart; +} + const std::shared_ptr &ChatMessageContentCore::getContentModel() const { return mChatMessageContentModel; } diff --git a/Linphone/core/chat/message/content/ChatMessageContentCore.hpp b/Linphone/core/chat/message/content/ChatMessageContentCore.hpp index b0f33c9d2..23260cc1d 100644 --- a/Linphone/core/chat/message/content/ChatMessageContentCore.hpp +++ b/Linphone/core/chat/message/content/ChatMessageContentCore.hpp @@ -40,7 +40,8 @@ class ChatMessageContentCore : public QObject, public AbstractObject { Q_PROPERTY(bool wasDownloaded READ wasDownloaded WRITE setWasDownloaded NOTIFY wasDownloadedChanged) Q_PROPERTY(QString filePath READ getFilePath WRITE setFilePath NOTIFY filePathChanged) Q_PROPERTY(QString utf8Text READ getUtf8Text CONSTANT) - Q_PROPERTY(QString richFormatText MEMBER mRichFormatText CONSTANT) + Q_PROPERTY(QString richFormatText WRITE setRichFormatText MEMBER mRichFormatText NOTIFY richFormatTextChanged) + Q_PROPERTY(QString searchTextPart READ getSearchedTextPart WRITE setSearchedTextPart NOTIFY searchedTextPartChanged) Q_PROPERTY(bool isFile READ isFile WRITE setIsFile NOTIFY isFileChanged) Q_PROPERTY(bool isFileEncrypted READ isFileEncrypted WRITE setIsFileEncrypted NOTIFY isFileEncryptedChanged) Q_PROPERTY(bool isFileTransfer READ isFileTransfer WRITE setIsFileTransfer NOTIFY isFileTransferChanged) @@ -90,6 +91,10 @@ public: bool wasDownloaded() const; void setWasDownloaded(bool downloaded); + void setRichFormatText(const QString &richFormatText); + Q_INVOKABLE void setSearchedTextPart(const QString &searchedTextPart); + QString getSearchedTextPart() const; + const std::shared_ptr &getContentModel() const; signals: @@ -102,6 +107,8 @@ signals: void isFileEncryptedChanged(); void wasDownloadedChanged(bool downloaded); void isVideoChanged(); + void searchedTextPartChanged(); + void richFormatTextChanged(); void lCreateThumbnail(const bool &force = false); void lRemoveDownloadedFile(); @@ -124,6 +131,7 @@ private: QUrl mThumbnail; QString mUtf8Text; QString mRichFormatText; + QString mSearchedTextPart; QString mFilePath; QString mName; quint64 mFileSize; diff --git a/Linphone/core/conference/ConferenceInfoCore.cpp b/Linphone/core/conference/ConferenceInfoCore.cpp index c6c45a986..6cbed6a23 100644 --- a/Linphone/core/conference/ConferenceInfoCore.cpp +++ b/Linphone/core/conference/ConferenceInfoCore.cpp @@ -198,15 +198,14 @@ void ConferenceInfoCore::setSelf(QSharedPointer me) { mConfInfoModelConnection->makeConnectToModel( &ConferenceInfoModel::schedulerStateChanged, [this](linphone::ConferenceScheduler::State state) { auto confInfoState = mConferenceInfoModel->getState(); - QString uri; if (state == linphone::ConferenceScheduler::State::Ready) { - uri = mConferenceInfoModel->getConferenceScheduler()->getUri(); - emit CoreModel::getInstance() -> conferenceInfoReceived( - CoreModel::getInstance()->getCore(), mConferenceInfoModel->getConferenceInfo()); + if (confInfoState == linphone::ConferenceInfo::State::New) { + emit CoreModel::getInstance()->conferenceInfoReceived( + CoreModel::getInstance()->getCore(), mConferenceInfoModel->getConferenceInfo()); + } } mConfInfoModelConnection->invokeToCore([this, state = LinphoneEnums::fromLinphone(state), - infoState = LinphoneEnums::fromLinphone(confInfoState), - uri] { + infoState = LinphoneEnums::fromLinphone(confInfoState)] { setConferenceSchedulerState(state); setConferenceInfoState(infoState); }); diff --git a/Linphone/core/conference/ConferenceInfoList.hpp b/Linphone/core/conference/ConferenceInfoList.hpp index da57e7818..ca0b2796c 100644 --- a/Linphone/core/conference/ConferenceInfoList.hpp +++ b/Linphone/core/conference/ConferenceInfoList.hpp @@ -40,6 +40,9 @@ public: void setSelf(QSharedPointer me); void resetData(QList> data); + void resetData() override { + ListProxy::resetData(); + } void addConference(const std::shared_ptr &confInfo); diff --git a/Linphone/core/conference/ConferenceInfoProxy.cpp b/Linphone/core/conference/ConferenceInfoProxy.cpp index 81816d985..3d77ba3f6 100644 --- a/Linphone/core/conference/ConferenceInfoProxy.cpp +++ b/Linphone/core/conference/ConferenceInfoProxy.cpp @@ -27,7 +27,11 @@ DEFINE_ABSTRACT_OBJECT(ConferenceInfoProxy) ConferenceInfoProxy::ConferenceInfoProxy(QObject *parent) : LimitProxy(parent) { - mList = ConferenceInfoList::create(); + if (!App::getInstance()->getConferenceInfoList()) { + mList = ConferenceInfoList::create(); + App::getInstance()->setConferenceInfoList(mList); + } + mList = App::getInstance()->getConferenceInfoList(); setSourceModels(new SortFilterList(mList.get(), Qt::AscendingOrder)); connect( mList.get(), &ConferenceInfoList::haveCurrentDateChanged, this, diff --git a/Linphone/core/notifier/Notifier.cpp b/Linphone/core/notifier/Notifier.cpp index 8612d3bf8..c38663038 100644 --- a/Linphone/core/notifier/Notifier.cpp +++ b/Linphone/core/notifier/Notifier.cpp @@ -358,7 +358,7 @@ void Notifier::notifyReceivedMessages(const std::shared_ptr if (content->isText()) txt += content->getUtf8Text().c_str(); } } else if (fileContent->isVoiceRecording()) - //: 'Voice message received!' : message to warn the user in a notofication for voice messages. + //: 'Voice message received!' : message to warn the user in a notification for voice messages. txt = tr("new_voice_message"); else txt = tr("new_file_message"); if (txt.isEmpty() && message->hasConferenceInvitationContent()) diff --git a/Linphone/core/participant/ParticipantCore.cpp b/Linphone/core/participant/ParticipantCore.cpp index 04d22c5e5..3accf5ac0 100644 --- a/Linphone/core/participant/ParticipantCore.cpp +++ b/Linphone/core/participant/ParticipantCore.cpp @@ -20,10 +20,8 @@ #include -#include "core/App.hpp" - #include "ParticipantCore.hpp" -// #include "ParticipantDeviceList.hpp" +#include "core/App.hpp" #include "model/participant/ParticipantModel.hpp" #include "model/tool/ToolModel.hpp" #include "tool/Utils.hpp" @@ -77,6 +75,48 @@ void ParticipantCore::setSelf(QSharedPointer me) { QTimer::singleShot(secs * 1000, this, &ParticipantCore::onEndOfInvitation); }); connect(this, &ParticipantCore::sipAddressChanged, this, &ParticipantCore::updateIsMe); + auto update = [this, remoteAddress = mSipAddress](const std::shared_ptr &updatedFriend) { + bool isThisFriend = false; + std::shared_ptr friendAddress; + auto participantAddress = mParticipantModel->getAddress(); + for (auto address : updatedFriend->getAddresses()) { + if (address->weakEqual(participantAddress)) { + isThisFriend = true; + friendAddress = address; + break; + } + } + if (isThisFriend) { + auto displayName = friendAddress->getDisplayName(); + mCoreModelConnection->invokeToCore([this, displayName] { + auto me = mCoreModelConnection->mCore.mQData; // Locked from previous call. + setDisplayName(Utils::coreStringToAppString(displayName)); + }); + } + }; + auto onRemoved = [this](const std::shared_ptr &updatedFriend) { + bool isThisFriend = false; + std::shared_ptr friendAddress; + auto participantAddress = mParticipantModel->getAddress(); + for (auto address : updatedFriend->getAddresses()) { + if (address->weakEqual(participantAddress)) { + isThisFriend = true; + friendAddress = address; + break; + } + } + if (isThisFriend) { + auto displayName = ToolModel::getDisplayName(participantAddress); + mCoreModelConnection->invokeToCore([this, displayName] { + auto me = mCoreModelConnection->mCore.mQData; // Locked from previous call. + setDisplayName(displayName); + }); + } + }; + mCoreModelConnection = SafeConnection::create(me, CoreModel::getInstance()); + mCoreModelConnection->makeConnectToModel(&CoreModel::friendCreated, update); + mCoreModelConnection->makeConnectToModel(&CoreModel::friendUpdated, update); + mCoreModelConnection->makeConnectToModel(&CoreModel::friendRemoved, onRemoved); } LinphoneEnums::SecurityLevel ParticipantCore::getSecurityLevel() const { diff --git a/Linphone/core/participant/ParticipantCore.hpp b/Linphone/core/participant/ParticipantCore.hpp index 2daf6101c..275cae001 100644 --- a/Linphone/core/participant/ParticipantCore.hpp +++ b/Linphone/core/participant/ParticipantCore.hpp @@ -107,6 +107,7 @@ signals: private: std::shared_ptr mParticipantModel; QSharedPointer> mParticipantConnection; + QSharedPointer> mCoreModelConnection; QList mParticipantDevices; diff --git a/Linphone/core/participant/ParticipantDeviceProxy.cpp b/Linphone/core/participant/ParticipantDeviceProxy.cpp index a5ffcb81f..8562e28fd 100644 --- a/Linphone/core/participant/ParticipantDeviceProxy.cpp +++ b/Linphone/core/participant/ParticipantDeviceProxy.cpp @@ -63,8 +63,8 @@ void ParticipantDeviceProxy::setCurrentCall(CallGui *call) { mCurrentCall = call; if (mCurrentCall) callCore = mCurrentCall->getCore(); if (callCore) { - connect(callCore, &CallCore::conferenceChanged, mParticipants.get(), [this]() { - auto conference = mCurrentCall->getCore()->getConferenceCore(); + connect(callCore, &CallCore::conferenceChanged, mParticipants.get(), [this, callCore]() { + auto conference = callCore->getConferenceCore(); lDebug() << log().arg("Set conference") << this << " => " << conference; mParticipants->setConferenceModel(conference ? conference->getModel() : nullptr); }); diff --git a/Linphone/core/participant/ParticipantInfoProxy.cpp b/Linphone/core/participant/ParticipantInfoProxy.cpp index 94c55e3d1..0acdf6ebd 100644 --- a/Linphone/core/participant/ParticipantInfoProxy.cpp +++ b/Linphone/core/participant/ParticipantInfoProxy.cpp @@ -44,7 +44,7 @@ ChatGui *ParticipantInfoProxy::getChat() const { } void ParticipantInfoProxy::setChat(ChatGui *chat) { - lDebug() << "[ParticipantInfoProxy] set current chat " << chat; + lDebug() << "[ParticipantInfoProxy] set current chat " << chat << (chat ? chat->mCore->getTitle() : "NULL"); if (mChat != chat) { mChat = chat; mParticipants->setChatCore(chat ? chat->mCore : nullptr); diff --git a/Linphone/core/path/Paths.cpp b/Linphone/core/path/Paths.cpp index 5dac393e5..a74d86e8d 100644 --- a/Linphone/core/path/Paths.cpp +++ b/Linphone/core/path/Paths.cpp @@ -127,7 +127,8 @@ static inline QString getAppPackageDataDirPath() { dir.mkdir("share"); dir.cd("share"); } - lInfo() << executableDir.absolutePath() << " VS " << dir.absolutePath() << " == " << executableDir.relativeFilePath(dir.absolutePath()); + lInfo() << executableDir.absolutePath() << " VS " << dir.absolutePath() + << " == " << executableDir.relativeFilePath(dir.absolutePath()); return executableDir.relativeFilePath(dir.absolutePath()); } @@ -141,7 +142,11 @@ static inline QString getAppPackageMsPluginsDirPath() { static inline QString getAppPackagePluginsDirPath() { QDir executableDir(QCoreApplication::applicationDirPath()); QDir packageDir = getAppPackageDir(); - return executableDir.relativeFilePath( packageDir.absolutePath() + Constants::PathPlugins); + return executableDir.relativeFilePath(packageDir.absolutePath() + Constants::PathPlugins); +} + +static inline QString getAppBinDirPath() { + return getAppPackageDir().absolutePath() + Constants::PathBin; } static inline QString getAppAssistantConfigDirPath() { @@ -270,51 +275,71 @@ QString Paths::getLogsDirPath() { QString Paths::getAppRootCaFilePath() { // Hardcoded because it comes from linphone and is not customizable. return getReadableFilePath(getAppPackageDataDirPath() + "/linphone/rootca.pem"); -} + QString Paths::getCrashpadDirPath() { +#ifdef HAVE_CRASH_HANDLER + return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + + Constants::PathCrashpad); +#else + return ""; +#endif + } -QString Paths::getMessageHistoryFilePath() { - return getReadableFilePath( - getAppMessageHistoryFilePath()); // No need to ensure that the file exists as this DB is deprecated -} + QString Paths::getMetricsDirPath() { + return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + + Constants::PathMetrics); + } -QString Paths::getPackageMsPluginsDirPath() { - return getReadableDirPath(getAppPackageMsPluginsDirPath()); -} + QString Paths::getMessageHistoryFilePath() { + return getReadableFilePath( + getAppMessageHistoryFilePath()); // No need to ensure that the file exists as this DB is deprecated + } -QString Paths::getPackagePluginsAppDirPath() { - return getReadableDirPath(getAppPackagePluginsDirPath() + Constants::PathPluginsApp); -} + QString Paths::getPackageMsPluginsDirPath() { + return getReadableDirPath(getAppPackageMsPluginsDirPath()); + } -QString Paths::getPackageTopDirPath() { - return getReadableDirPath(getAppPackageDataDirPath()); -} + QString Paths::getPackagePluginsAppDirPath() { + return getReadableDirPath(getAppPackagePluginsDirPath() + Constants::PathPluginsApp); + } -QString Paths::getPluginsAppDirPath() { - return getWritableDirPath(getAppPluginsDirPath() + Constants::PathPluginsApp); -} + QString Paths::getPackageTopDirPath() { + return getReadableDirPath(getAppPackageDataDirPath()); + } -QStringList Paths::getPluginsAppFolders() { - QStringList pluginPaths; - pluginPaths << Paths::getPluginsAppDirPath(); - pluginPaths << Paths::getPackagePluginsAppDirPath(); - return pluginPaths; -} + QString Paths::getPluginsAppDirPath() { + return getWritableDirPath(getAppPluginsDirPath() + Constants::PathPluginsApp); + } -QString Paths::getToolsDirPath() { - return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + - Constants::PathTools); -} -QString Paths::getUserCertificatesDirPath() { - return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + - Constants::PathUserCertificates); -} + QStringList Paths::getPluginsAppFolders() { + QStringList pluginPaths; + pluginPaths << Paths::getPluginsAppDirPath(); + pluginPaths << Paths::getPackagePluginsAppDirPath(); + return pluginPaths; + } -QString Paths::getZrtpSecretsFilePath() { - return getWritableFilePath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + - Constants::PathZrtpSecrets); -} + QString Paths::getToolsDirPath() { + return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + + Constants::PathTools); + } + QString Paths::getUserCertificatesDirPath() { + return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + + Constants::PathUserCertificates); + } -// ----------------------------------------------------------------------------- + QString Paths::getZrtpSecretsFilePath() { + return getWritableFilePath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + + Constants::PathZrtpSecrets); + } -void Paths::migrate() { -} + QString Paths::getCrashpadHandlerFilePath() { +#ifdef HAVE_CRASH_HANDLER + return getAppBinDirPath() + Constants::PathCrashpadHandler; +#else + return ""; +#endif + } + + // ----------------------------------------------------------------------------- + + void Paths::migrate() { + } diff --git a/Linphone/core/path/Paths.hpp b/Linphone/core/path/Paths.hpp index 0c4d8b95c..9509db6f8 100644 --- a/Linphone/core/path/Paths.hpp +++ b/Linphone/core/path/Paths.hpp @@ -27,7 +27,7 @@ namespace Paths { bool filePathExists(const QString &path, const bool isWritable = false); -//bool convertToRelativePath(const QString &path, QString *relativePath); +// bool convertToRelativePath(const QString &path, QString *relativePath); // Return true if paths are different and point to the same file bool isSameRelativeFile(const QString &filePath, const QString &relativeFilePath); @@ -48,6 +48,8 @@ QString getFactoryConfigFilePath(); QString getFriendsListFilePath(); QString getLimeDatabasePath(); QString getLogsDirPath(); +QString getCrashpadDirPath(); +QString getMetricsDirPath(); QString getMessageHistoryFilePath(); QString getPackageMsPluginsDirPath(); QString getPackagePluginsAppDirPath(); @@ -58,6 +60,7 @@ QString getToolsDirPath(); QString getUserCertificatesDirPath(); QString getZrtpDataFilePath(); QString getZrtpSecretsFilePath(); +QString getCrashpadHandlerFilePath(); void migrate(); } // namespace Paths diff --git a/Linphone/core/payload-type/PayloadTypeCore.hpp b/Linphone/core/payload-type/PayloadTypeCore.hpp index 0ef736e4e..d74bd7d58 100644 --- a/Linphone/core/payload-type/PayloadTypeCore.hpp +++ b/Linphone/core/payload-type/PayloadTypeCore.hpp @@ -43,7 +43,7 @@ public: const std::shared_ptr &payloadType); PayloadTypeCore(Family family, const std::shared_ptr &payloadType); - PayloadTypeCore() {}; + PayloadTypeCore(){}; ~PayloadTypeCore(); void setSelf(QSharedPointer me); diff --git a/Linphone/core/payload-type/PayloadTypeProxy.hpp b/Linphone/core/payload-type/PayloadTypeProxy.hpp index 102c67ab7..5e0cf43b8 100644 --- a/Linphone/core/payload-type/PayloadTypeProxy.hpp +++ b/Linphone/core/payload-type/PayloadTypeProxy.hpp @@ -31,14 +31,7 @@ class PayloadTypeProxy : public LimitProxy, public AbstractObject { Q_OBJECT public: - enum PayloadTypeProxyFiltering { - All = 0, - Audio = 2, - Video = 4, - Text = 8, - Downloadable = 16, - NotDownloadable = 32 - }; + enum PayloadTypeProxyFiltering { All = 0, Audio = 2, Video = 4, Text = 8, Downloadable = 16, NotDownloadable = 32 }; Q_ENUMS(PayloadTypeProxyFiltering) DECLARE_SORTFILTER_CLASS() diff --git a/Linphone/core/proxy/SortFilterProxy.cpp b/Linphone/core/proxy/SortFilterProxy.cpp index 887aad0d6..5e34337a4 100644 --- a/Linphone/core/proxy/SortFilterProxy.cpp +++ b/Linphone/core/proxy/SortFilterProxy.cpp @@ -57,7 +57,7 @@ int SortFilterProxy::getCount() const { QVariant SortFilterProxy::getAt(const int &atIndex) const { auto modelIndex = index(atIndex, 0); - return sourceModel()->data(mapToSource(modelIndex), 0); + return sourceModel() ? sourceModel()->data(mapToSource(modelIndex), 0) : QVariant(); } int SortFilterProxy::getFilterType() const { diff --git a/Linphone/core/search/MagicSearchList.cpp b/Linphone/core/search/MagicSearchList.cpp index 984ae9c16..922e6d7ba 100644 --- a/Linphone/core/search/MagicSearchList.cpp +++ b/Linphone/core/search/MagicSearchList.cpp @@ -49,7 +49,7 @@ MagicSearchList::~MagicSearchList() { mustBeInMainThread("~" + getClassName()); } -void MagicSearchList::setSelf(QSharedPointer me) { +void MagicSearchList::setSelf(const QSharedPointer &me) { mCoreModelConnection = SafeConnection::create(me, CoreModel::getInstance()); mCoreModelConnection->makeConnectToModel( &CoreModel::friendCreated, [this](const std::shared_ptr &f) { diff --git a/Linphone/core/search/MagicSearchList.hpp b/Linphone/core/search/MagicSearchList.hpp index ea61dec1d..bcb33bb41 100644 --- a/Linphone/core/search/MagicSearchList.hpp +++ b/Linphone/core/search/MagicSearchList.hpp @@ -40,7 +40,7 @@ public: MagicSearchList(QObject *parent = Q_NULLPTR); ~MagicSearchList(); - void setSelf(QSharedPointer me); + void setSelf(const QSharedPointer &me); void connectContact(FriendCore *data); void setSearch(const QString &search); void setResults(const QList> &contacts); diff --git a/Linphone/core/setting/SettingsCore.cpp b/Linphone/core/setting/SettingsCore.cpp index 5ef5201dc..ff32c150c 100644 --- a/Linphone/core/setting/SettingsCore.cpp +++ b/Linphone/core/setting/SettingsCore.cpp @@ -97,6 +97,7 @@ SettingsCore::SettingsCore(QObject *parent) : QObject(parent) { // Logs mLogsEnabled = settingsModel->getLogsEnabled(); mFullLogsEnabled = settingsModel->getFullLogsEnabled(); + mCrashReporterEnabled = settingsModel->getCrashReporterEnabled(); mLogsFolder = settingsModel->getLogsFolder(); mLogsEmail = settingsModel->getLogsEmail(); @@ -125,7 +126,6 @@ SettingsCore::SettingsCore(QObject *parent) : QObject(parent) { INIT_CORE_MEMBER(HideAccountSettings, settingsModel) INIT_CORE_MEMBER(DisableCallRecordings, settingsModel) INIT_CORE_MEMBER(AssistantHideCreateAccount, settingsModel) - INIT_CORE_MEMBER(AssistantHideCreateAccount, settingsModel) INIT_CORE_MEMBER(AssistantDisableQrCode, settingsModel) INIT_CORE_MEMBER(AssistantHideThirdPartyAccount, settingsModel) @@ -189,6 +189,7 @@ SettingsCore::SettingsCore(const SettingsCore &settingsCore) { // Logs mLogsEnabled = settingsCore.mLogsEnabled; mFullLogsEnabled = settingsCore.mFullLogsEnabled; + mCrashReporterEnabled = settingsCore.mCrashReporterEnabled; mLogsFolder = settingsCore.mLogsFolder; mLogsEmail = settingsCore.mLogsEmail; @@ -237,6 +238,110 @@ SettingsCore::SettingsCore(const SettingsCore &settingsCore) { SettingsCore::~SettingsCore() { } +void SettingsCore::reloadSettings() { + mustBeInLinphoneThread(getClassName()); + auto settingsModel = SettingsModel::getInstance(); + assert(settingsModel); + + // Security + setVfsEnabled(settingsModel->getVfsEnabled()); + + // Call + setVideoEnabled(settingsModel->getVideoEnabled()); + setEchoCancellationEnabled(settingsModel->getEchoCancellationEnabled()); + setAutoDownloadReceivedFiles(settingsModel->getAutoDownloadReceivedFiles()); + setAutomaticallyRecordCallsEnabled(settingsModel->getAutomaticallyRecordCallsEnabled()); + setRingtone(settingsModel->getRingtone()); + + // Network + setIpv6Enabled(settingsModel->getIpv6Enabled()); + + // Advanced + setAutoStart(settingsModel->getAutoStart()); + setHideFps(settingsModel->getHideFps()); + + // Audio + setCaptureDevices(settingsModel->getCaptureDevices()); + setPlaybackDevices(settingsModel->getPlaybackDevices()); + setRingerDevices(settingsModel->getRingerDevices()); + setCaptureDevice(settingsModel->getCaptureDevice()); + setPlaybackDevice(settingsModel->getPlaybackDevice()); + setRingerDevice(settingsModel->getRingerDevice()); + + setConferenceLayout( + LinphoneEnums::toVariant(LinphoneEnums::fromLinphone(settingsModel->getDefaultConferenceLayout()))); + + setMediaEncryption( + LinphoneEnums::toVariant(LinphoneEnums::fromLinphone(settingsModel->getDefaultMediaEncryption()))); + + setMediaEncryptionMandatory(settingsModel->getMediaEncryptionMandatory()); + setCreateEndToEndEncryptedMeetingsAndGroupCalls(settingsModel->getCreateEndToEndEncryptedMeetingsAndGroupCalls()); + + setCaptureGain(settingsModel->getCaptureGain()); + setPlaybackGain(settingsModel->getPlaybackGain()); + + // Video + setVideoDevice(settingsModel->getVideoDevice()); + setVideoDevices(settingsModel->getVideoDevices()); + + // Logs + setLogsEnabled(settingsModel->getLogsEnabled()); + setFullLogsEnabled(settingsModel->getFullLogsEnabled()); + setCrashReporterEnabled(settingsModel->getCrashReporterEnabled()); + setLogsFolder(settingsModel->getLogsFolder()); + mLogsEmail = settingsModel->getLogsEmail(); + + // DND + setDndEnabled(settingsModel->dndEnabled()); + + mDefaultDomain = settingsModel->getDefaultDomain(); + auto currentAccount = CoreModel::getInstance()->getCore()->getDefaultAccount(); + if (currentAccount) { + auto accountDomain = Utils::coreStringToAppString(currentAccount->getParams()->getDomain()); + setShowAccountDevices(accountDomain == mDefaultDomain); + } + + // Chat + mEmojiFont = settingsModel->getEmojiFont(); + mTextMessageFont = settingsModel->getTextMessageFont(); + + // Check for update + mIsCheckForUpdateAvailable = settingsModel->isCheckForUpdateAvailable(); + + setDisableChatFeature(settingsModel->getDisableChatFeature()); + setDisableMeetingsFeature(settingsModel->getDisableMeetingsFeature()); + setDisableBroadcastFeature(settingsModel->getDisableBroadcastFeature()); + + setHideSettings(settingsModel->getHideSettings()); + setHideAccountSettings(settingsModel->getHideAccountSettings()); + + setDisableCallRecordings(settingsModel->getDisableCallRecordings()); + setAssistantHideCreateAccount(settingsModel->getAssistantHideCreateAccount()); + setAssistantDisableQrCode(settingsModel->getAssistantDisableQrCode()); + setAssistantHideThirdPartyAccount(settingsModel->getAssistantHideThirdPartyAccount()); + setHideSipAddresses(settingsModel->getHideSipAddresses()); + setDarkModeAllowed(settingsModel->getDarkModeAllowed()); + setMaxAccount(settingsModel->getMaxAccount()); + setAssistantGoDirectlyToThirdPartySipAccountLogin( + settingsModel->getAssistantGoDirectlyToThirdPartySipAccountLogin()); + setAssistantGoDirectlyToThirdPartySipAccountLogin( + settingsModel->getAssistantGoDirectlyToThirdPartySipAccountLogin()); + setAssistantThirdPartySipAccountDomain(settingsModel->getAssistantThirdPartySipAccountDomain()); + setAssistantThirdPartySipAccountTransport(settingsModel->getAssistantThirdPartySipAccountTransport()); + setAutoStart(settingsModel->getAutoStart()); + setExitOnClose(settingsModel->getExitOnClose()); + setSyncLdapContacts(settingsModel->getSyncLdapContacts()); + setConfigLocale(settingsModel->getConfigLocale()); + setDownloadFolder(settingsModel->getDownloadFolder()); + + setCallToneIndicationsEnabled(settingsModel->getCallToneIndicationsEnabled()); + setCommandLine(settingsModel->getCommandLine()); + setDisableCommandLine(settingsModel->getDisableCommandLine()); + setCallForwardToAddress(settingsModel->getCallForwardToAddress()); + setThemeMainColor(settingsModel->getThemeMainColor()); + setThemeAboutPictureUrl(settingsModel->getThemeAboutPictureUrl()); +} + void SettingsCore::setSelf(QSharedPointer me) { mustBeInLinphoneThread(getClassName()); mSettingsModelConnection = SafeConnection::create(me, SettingsModel::getInstance()); @@ -416,7 +521,10 @@ void SettingsCore::setSelf(QSharedPointer me) { // Logs mSettingsModelConnection->makeConnectToModel(&SettingsModel::logsEnabledChanged, [this](const bool status) { - mSettingsModelConnection->invokeToCore([this, status]() { setLogsEnabled(status); }); + mSettingsModelConnection->invokeToCore([this, status]() { + setCrashReporterEnabled(status); + setLogsEnabled(status); + }); }); mSettingsModelConnection->makeConnectToModel(&SettingsModel::fullLogsEnabledChanged, [this](const bool status) { @@ -558,6 +666,7 @@ void SettingsCore::reset(const SettingsCore &settingsCore) { // Logs setLogsEnabled(settingsCore.mLogsEnabled); setFullLogsEnabled(settingsCore.mFullLogsEnabled); + setCrashReporterEnabled(settingsCore.mCrashReporterEnabled); setLogsFolder(settingsCore.mLogsFolder); // DND @@ -1007,6 +1116,18 @@ void SettingsCore::setFullLogsEnabled(bool enabled) { } } +bool SettingsCore::getCrashReporterEnabled() const { + return mCrashReporterEnabled; +} + +void SettingsCore::setCrashReporterEnabled(bool enabled) { + if (mCrashReporterEnabled != enabled) { + mCrashReporterEnabled = enabled; + emit crashReporterEnabledChanged(); + setIsSaved(false); + } +} + void SettingsCore::setRingtone(QString path) { if (mRingtonePath != path) { mRingtonePath = path; @@ -1103,6 +1224,7 @@ QString SettingsCore::getDownloadFolder() const { auto path = mDownloadFolder; if (mDownloadFolder.isEmpty()) path = Paths::getDownloadDirPath(); QString cleanPath = QDir::cleanPath(path); + if (!cleanPath.endsWith(QDir::separator())) cleanPath.append(QDir::separator()); return cleanPath; } @@ -1143,6 +1265,7 @@ void SettingsCore::writeIntoModel(std::shared_ptr model) const { // Logs model->setLogsEnabled(mLogsEnabled); model->setFullLogsEnabled(mFullLogsEnabled); + model->setCrashReporterEnabled(mLogsEnabled); // UI model->setDisableChatFeature(mDisableChatFeature); @@ -1219,6 +1342,7 @@ void SettingsCore::writeFromModel(const std::shared_ptr &model) { // Logs mLogsEnabled = model->getLogsEnabled(); mFullLogsEnabled = model->getFullLogsEnabled(); + mCrashReporterEnabled = model->getCrashReporterEnabled(); mLogsFolder = model->getLogsFolder(); mLogsEmail = model->getLogsEmail(); diff --git a/Linphone/core/setting/SettingsCore.hpp b/Linphone/core/setting/SettingsCore.hpp index 8714126fe..25cde25cd 100644 --- a/Linphone/core/setting/SettingsCore.hpp +++ b/Linphone/core/setting/SettingsCore.hpp @@ -91,6 +91,8 @@ public: Q_PROPERTY(bool logsEnabled READ getLogsEnabled WRITE setLogsEnabled NOTIFY logsEnabledChanged) Q_PROPERTY(bool fullLogsEnabled READ getFullLogsEnabled WRITE setFullLogsEnabled NOTIFY fullLogsEnabledChanged) + Q_PROPERTY(bool crashReporterEnabled READ getCrashReporterEnabled WRITE setCrashReporterEnabled NOTIFY + crashReporterEnabledChanged) Q_PROPERTY(QString logsEmail READ getLogsEmail) Q_PROPERTY(QString logsFolder READ getLogsFolder) Q_PROPERTY(QString ringtoneName READ getRingtoneFileName NOTIFY ringtoneChanged) @@ -107,6 +109,8 @@ public: SettingsCore(const SettingsCore &settingsCore); virtual ~SettingsCore(); + void reloadSettings(); + void setSelf(QSharedPointer me); void reset(const SettingsCore &settingsCore); @@ -234,9 +238,10 @@ public: bool getLogsEnabled() const; void setLogsEnabled(bool enabled); - bool getFullLogsEnabled() const; void setFullLogsEnabled(bool enabled); + bool getCrashReporterEnabled() const; + void setCrashReporterEnabled(bool enabled); void setRingtone(QString path); QString getRingtoneFileName() const; @@ -364,6 +369,7 @@ signals: void logsEnabledChanged(); void fullLogsEnabledChanged(); + void crashReporterEnabledChanged(); void logsUploadTerminated(bool status, QString url); void logsFolderChanged(const QString &folder); @@ -439,6 +445,7 @@ private: // Debug logs bool mLogsEnabled; bool mFullLogsEnabled; + bool mCrashReporterEnabled; QString mLogsFolder; QString mLogsEmail; diff --git a/Linphone/core/singleapplication/singleapplication_p.cpp b/Linphone/core/singleapplication/singleapplication_p.cpp index cb27b384a..dff307aed 100644 --- a/Linphone/core/singleapplication/singleapplication_p.cpp +++ b/Linphone/core/singleapplication/singleapplication_p.cpp @@ -296,7 +296,7 @@ bool SingleApplicationPrivate::writeConfirmedFrame(int msecs, const QByteArray & socket->write(msg); socket->flush(); - bool result = socket->waitForReadyRead( msecs < 0 ? -1 : msecs); // await ack byte + bool result = socket->waitForReadyRead(msecs < 0 ? -1 : msecs); // await ack byte if (result) { socket->read(1); return true; diff --git a/Linphone/core/translator/DefaultTranslatorCore.hpp b/Linphone/core/translator/DefaultTranslatorCore.hpp index f69b1bd73..b2a20bb6c 100644 --- a/Linphone/core/translator/DefaultTranslatorCore.hpp +++ b/Linphone/core/translator/DefaultTranslatorCore.hpp @@ -54,8 +54,8 @@ private: // class MAC_APPLICATION_MENU : public QObject{ // QString forcedTranslation(){ -// return tr("About %1") + tr("Preferences…") + tr("Services") + tr("Hide %1") + tr("Hide Others") + tr("Show All") + -//tr("Quit %1"); +// return tr("About %1") + tr("Preferences…") + tr("Services") + tr("Hide %1") + tr("Hide Others") + tr("Show All") +//+ tr("Quit %1"); // } // }; diff --git a/Linphone/data/assistant/use-app-sip-account.rc b/Linphone/data/assistant/use-app-sip-account.rc index fc29feeae..159f32ffb 100644 --- a/Linphone/data/assistant/use-app-sip-account.rc +++ b/Linphone/data/assistant/use-app-sip-account.rc @@ -2,6 +2,7 @@
10000000 + 1
1 @@ -35,11 +36,20 @@
sips:rls@sip.linphone.org + -2 + -2 + -2
1 0 2 + MSQOGL + 0 +
+
+ 0 + 0
sip.linphone.org diff --git a/Linphone/data/assistant/use-other-sip-account.rc b/Linphone/data/assistant/use-other-sip-account.rc index f8b2a87ea..9dbe10580 100644 --- a/Linphone/data/assistant/use-other-sip-account.rc +++ b/Linphone/data/assistant/use-other-sip-account.rc @@ -2,6 +2,7 @@
10000000 + 1
0 @@ -30,11 +31,20 @@
+ -2 + -2 + -2
1 0 2 + MSQOGL + 0 +
+
+ 0 + 0
diff --git a/Linphone/data/emoji/emoji.json b/Linphone/data/emoji/emoji.json index b4203bae4..8fec1d567 100644 --- a/Linphone/data/emoji/emoji.json +++ b/Linphone/data/emoji/emoji.json @@ -128,6 +128,20 @@ "smile" ] }, + { + "code": "1fae0", + "char": "🫠", + "name": "melting face", + "keywords": [ + "face", + "melting", + "smile", + "embarrassed", + "overwhelmed", + "shame", + "dread" + ] + }, { "code": "1f609", "char": "😉", @@ -360,6 +374,30 @@ "surprise" ] }, + { + "code": "1fae2", + "char": "🫢", + "name": "face with open eyes and hand over mouth", + "keywords": [ + "face with open eyes hand over mouth", + "whoops", + "shock", + "gasp", + "sudden realization", + "surprise" + ] + }, + { + "code": "1fae3", + "char": "🫣", + "name": "face with peeking eye", + "keywords": [ + "face with peeking eye", + "hide", + "hidden", + "eye" + ] + }, { "code": "1f92b", "char": "🤫", @@ -378,6 +416,17 @@ "thinking face" ] }, + { + "code": "1fae1", + "char": "🫡", + "name": "saluting face", + "keywords": [ + "face", + "saluting face", + "respect", + "honor" + ] + }, { "code": "1f910", "char": "🤐", @@ -437,6 +486,32 @@ "silent" ] }, + { + "code": "1fae5", + "char": "🫥", + "name": "dotted line face", + "keywords": [ + "dotted line face", + "shy", + "embarrased", + "quiet", + "ignore" + ] + }, + { + "code": "1f636-200d-1f32b-feof", + "char": "😶‍🌫️", + "name": "face in clouds", + "keywords": [ + "face in clouds", + "confused", + "embarrased", + "lost", + "daydreaming", + "overwhelmed", + "hazy" + ] + }, { "code": "1f60f", "char": "😏", @@ -476,6 +551,20 @@ "grimace face" ] }, + { + "code": "1f62e-200d-1f4a8", + "char": "😮‍💨", + "name": "face exhaling", + "keywords": [ + "face", + "face exhaling", + "relief", + "exhausted", + "frustrated", + "exasperation", + "calm" + ] + }, { "code": "1f925", "char": "🤥", @@ -487,6 +576,39 @@ "lying face" ] }, + { + "code": "1fae8", + "char": "🫨", + "name": "shaking face", + "keywords": [ + "face", + "shaking", + "overwhelmed", + "excited", + "shocked", + "anxious" + ] + }, + { + "code": "1f642-200d-2194-fe0f", + "char": "🙂‍↔️", + "name": "head shaking horizontally", + "keywords": [ + "head shaking horizontally", + "no" + ] + }, + { + "code": "1f642-200d-2195-fe0f", + "char": "🙂‍↕️", + "name": "head shaking vertically", + "keywords": [ + "head shaking vertically", + "yes", + "support", + "confirm" + ] + }, { "code": "1f60c", "char": "😌", @@ -536,6 +658,18 @@ "zzz" ] }, + { + "code": "1fae9", + "char": "🫩", + "name": "face with bags under eyes", + "keywords": [ + "face", + "sleeping", + "bags under eyes", + "tired", + "stressed" + ] + }, { "code": "1f637", "char": "😷", diff --git a/Linphone/data/emoji/emojiSvgs/1f62e-200d-1f4a8.svg b/Linphone/data/emoji/emojiSvgs/1f62e-200d-1f4a8.svg new file mode 100644 index 000000000..40ad46f5e --- /dev/null +++ b/Linphone/data/emoji/emojiSvgs/1f62e-200d-1f4a8.svg @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Linphone/data/emoji/emojiSvgs/1f635-200d-1f4ab.svg b/Linphone/data/emoji/emojiSvgs/1f635-200d-1f4ab.svg new file mode 100644 index 000000000..12adaf7c7 --- /dev/null +++ b/Linphone/data/emoji/emojiSvgs/1f635-200d-1f4ab.svg @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Linphone/data/emoji/emojiSvgs/1f636-200d-1f32b-feof.svg b/Linphone/data/emoji/emojiSvgs/1f636-200d-1f32b-feof.svg new file mode 100644 index 000000000..2ee480547 --- /dev/null +++ b/Linphone/data/emoji/emojiSvgs/1f636-200d-1f32b-feof.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Linphone/data/emoji/emojiSvgs/1f642-200d-2194-fe0f.svg b/Linphone/data/emoji/emojiSvgs/1f642-200d-2194-fe0f.svg new file mode 100644 index 000000000..bd2133a18 --- /dev/null +++ b/Linphone/data/emoji/emojiSvgs/1f642-200d-2194-fe0f.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Linphone/data/emoji/emojiSvgs/1f642-200d-2195-fe0f.svg b/Linphone/data/emoji/emojiSvgs/1f642-200d-2195-fe0f.svg new file mode 100644 index 000000000..2e0c16397 --- /dev/null +++ b/Linphone/data/emoji/emojiSvgs/1f642-200d-2195-fe0f.svg @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Linphone/data/emoji/emojiSvgs/1f979.svg b/Linphone/data/emoji/emojiSvgs/1f979.svg index 3cc2c8c11..af71d6ab9 100644 --- a/Linphone/data/emoji/emojiSvgs/1f979.svg +++ b/Linphone/data/emoji/emojiSvgs/1f979.svg @@ -1,56 +1,79 @@ - + - + - - - + + + - + - + - + - + - + - + - + - + - + - + - - + + - - - \ No newline at end of file + + + diff --git a/Linphone/data/emoji/emojiSvgs/1fae0.svg b/Linphone/data/emoji/emojiSvgs/1fae0.svg new file mode 100644 index 000000000..b6737767e --- /dev/null +++ b/Linphone/data/emoji/emojiSvgs/1fae0.svg @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Linphone/data/emoji/emojiSvgs/1fae1.svg b/Linphone/data/emoji/emojiSvgs/1fae1.svg new file mode 100644 index 000000000..c37b91b5f --- /dev/null +++ b/Linphone/data/emoji/emojiSvgs/1fae1.svg @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Linphone/data/emoji/emojiSvgs/1fae2.svg b/Linphone/data/emoji/emojiSvgs/1fae2.svg new file mode 100644 index 000000000..66aaaae48 --- /dev/null +++ b/Linphone/data/emoji/emojiSvgs/1fae2.svg @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Linphone/data/emoji/emojiSvgs/1fae3.svg b/Linphone/data/emoji/emojiSvgs/1fae3.svg new file mode 100644 index 000000000..55fbc9f29 --- /dev/null +++ b/Linphone/data/emoji/emojiSvgs/1fae3.svg @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Linphone/data/emoji/emojiSvgs/1fae4.svg b/Linphone/data/emoji/emojiSvgs/1fae4.svg new file mode 100644 index 000000000..57c2641e2 --- /dev/null +++ b/Linphone/data/emoji/emojiSvgs/1fae4.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Linphone/data/emoji/emojiSvgs/1fae5.svg b/Linphone/data/emoji/emojiSvgs/1fae5.svg new file mode 100644 index 000000000..8abc1befc --- /dev/null +++ b/Linphone/data/emoji/emojiSvgs/1fae5.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Linphone/data/emoji/emojiSvgs/1fae6.svg b/Linphone/data/emoji/emojiSvgs/1fae6.svg new file mode 100644 index 000000000..1e3a16fcb --- /dev/null +++ b/Linphone/data/emoji/emojiSvgs/1fae6.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + diff --git a/Linphone/data/emoji/emojiSvgs/1fae8.svg b/Linphone/data/emoji/emojiSvgs/1fae8.svg new file mode 100644 index 000000000..692d936dd --- /dev/null +++ b/Linphone/data/emoji/emojiSvgs/1fae8.svg @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Linphone/data/emoji/emojiSvgs/1fae9.svg b/Linphone/data/emoji/emojiSvgs/1fae9.svg new file mode 100644 index 000000000..a3609eea4 --- /dev/null +++ b/Linphone/data/emoji/emojiSvgs/1fae9.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Linphone/data/emoji/emojiSvgs/1faf0.svg b/Linphone/data/emoji/emojiSvgs/1faf0.svg new file mode 100644 index 000000000..a12082d53 --- /dev/null +++ b/Linphone/data/emoji/emojiSvgs/1faf0.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Linphone/data/emoji/emojiSvgs/1faf5.svg b/Linphone/data/emoji/emojiSvgs/1faf5.svg new file mode 100644 index 000000000..d832ec8e6 --- /dev/null +++ b/Linphone/data/emoji/emojiSvgs/1faf5.svg @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Linphone/data/emoji/emojiSvgs/1faf6.svg b/Linphone/data/emoji/emojiSvgs/1faf6.svg new file mode 100644 index 000000000..40d28e15e --- /dev/null +++ b/Linphone/data/emoji/emojiSvgs/1faf6.svg @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Linphone/data/languages/ca.ts b/Linphone/data/languages/ca.ts index 4efecfa43..07b403bcb 100644 --- a/Linphone/data/languages/ca.ts +++ b/Linphone/data/languages/ca.ts @@ -361,23 +361,6 @@ settings_account_title - - - info_popup_invalid_registrar_uri_message - Registrar uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - - - - - info_popup_invalid_outbound_proxy_message - Outbound proxy uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - - - - - info_popup_error_title - Error - information_popup_success_title @@ -406,12 +389,6 @@ "URI de messagerie vocale" - - - account_settings_transport_title - "Transport" - - account_settings_registrar_uri_title @@ -652,29 +629,29 @@ configuration_error_detail not reachable - + no accessible application_description "A free and open source SIP video-phone." - + Programari de videotrucada SIP lliure i de codi obert. command_line_arg_order "Send an order to the application towards a command line" - + Enviar una comanda a l'aplicació via l'intèrpret de comandes command_line_option_show_help - + Mostra'm l'ajut command_line_option_show_app_version - + Mostra la versió de l'aplicatiu @@ -726,6 +703,36 @@ mark_all_read_action + + + info_popup_error_checking_update + Ha succeït un error mentre es mirava d'actualitzar. Si et plau, prova-ho més tard o posa't en contacte amb l'equip de suport. + + + + info_popup_new_version_download_label + Descarrega'l! + + + + info_popup_new_version_available_title + Nova versió a l'abast! + + + + info_popup_new_version_available_message + Una nova versió del Linphone (1%) és accessible a l'1% + + + + info_popup_version_up_to_date_title + Actualitzat + + + + info_popup_version_up_to_date_message + Actualitzat, disposes de la darrera versió + AuthenticationDialog @@ -891,31 +898,6 @@ settings_call_forward_address_cannot_be_empty - - - settings_call_forward_address_timeout - - - - - settings_call_forward_address_progress_disabling - - - - - settings_call_forward_address_progress_enabling - - - - - settings_call_forward_activation_success - - - - - settings_call_forward_deactivation_success - - CallHistoryLayout @@ -1138,7 +1120,7 @@ call_error_server_timeout_toast - "Server tiemout" + "Server timeout" @@ -2484,6 +2466,11 @@ Error Creation de la conversation en cours … + + + info_popup_error_title + Error + ChatSettingsLayout @@ -4323,11 +4310,6 @@ Error "Erreur dans le code de validation" - - - information_popup_error_title - Error - ManageParticipants diff --git a/Linphone/data/languages/cs.ts b/Linphone/data/languages/cs.ts index 0452638e6..66add0604 100644 --- a/Linphone/data/languages/cs.ts +++ b/Linphone/data/languages/cs.ts @@ -145,6 +145,16 @@ "Unable to add account." Nelze přidat účet. + + + assistant_account_login_outbound_proxy_uri_error + URI odchozího proxy serveru je neplatné. Ujistěte se, že odpovídá následujícímu formátu: sip:host>:<port>;transport=<transport> (:<port> je volitelný) + + + + assistant_account_login_registrar_uri_error + Registrátor uri je neplatný. Ujistěte se, že odpovídá následujícímu formátu: sip:host>:<port>;transport=<transport> (:<port> je volitelný) + AccountModel @@ -164,7 +174,7 @@ set_outbound_proxy_uri_failed_error_message Unable to set outbound proxy uri, failed creating address from %1 - Nelze nastavit odchozí proxy URI, selhalo vytvoření adresy z %1 + Nelze nastavit odchozí proxy URI z adresy %1. Ujistěte se, že odpovídá následujícímu formátu: sip:host>:<port>;transport=<transport> (:<port> je volitelný) @@ -304,6 +314,21 @@ "No information" Žádné informace + + + copied + Zkopírováno + + + + account_settings_sip_address_copied_message + Vaše SIP adresa byla zkopírována do schránky + + + + account_settings_sip_address_copied_error_message + Chyba při kopírování vaší SIP adresy + AccountSettingsPage @@ -361,23 +386,6 @@ settings_account_title Nastavení účtu - - - info_popup_invalid_registrar_uri_message - Registrar uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - Registrační uri je neplatná. Ujistěte se, že odpovídá následujícímu formátu: sip:<host>:<port>;transport=<transport> (:<port> je volitelný) - - - - info_popup_invalid_outbound_proxy_message - Outbound proxy uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - Uri odchozího proxy serveru je neplatné. Ujistěte se, že odpovídá následujícímu formátu: sip:<host>:<port>;transport=<transport> (:<port> je volitelný) - - - - info_popup_error_title - Chyba - information_popup_success_title @@ -406,12 +414,6 @@ "URI de messagerie vocale" URI hlasových zpráv - - - account_settings_transport_title - "Transport" - Transport - account_settings_registrar_uri_title @@ -476,6 +478,16 @@ "URL du serveur d’échange de clés de chiffrement" URL adresa Lime serveru + + + mwi_server_address_tooltip + Adresa serveru MWI, který odesílá oznámení SIP pro zobrazení nových indikátorů hlasové pošty + + + + voicemail_address_tooltip + SIP adresa vytočená po kliknutí na tlačítko hlasové schránky + AddParticipantsForm @@ -730,7 +742,37 @@ info_popup_new_version_download_label - Žádný token nenalezen + Stáhněte si ji! + + + + info_popup_error_checking_update + Při pokusu o kontrolu aktualizací došlo k chybě. Zkuste to prosím znovu později nebo kontaktujte tým podpory. + + + + info_popup_new_version_available_title + Nová verze k dispozici! + + + + info_popup_new_version_available_message + Nová verze Linphone (%1) je k dispozici na %1 + + + + info_popup_version_up_to_date_title + Aktuální + + + + info_popup_version_up_to_date_message + Vaše verze je aktuální + + + + check_for_update + Zkontrolovat aktualizace @@ -897,31 +939,6 @@ settings_call_forward_address_cannot_be_empty Číslo nebo SIP adresa je vyžadována - - - settings_call_forward_address_timeout - Nelze nastavit přesměrování hovoru, vypršel časový limit požadavku - - - - settings_call_forward_address_progress_disabling - Vypnutí přesměrování hovoru - - - - settings_call_forward_address_progress_enabling - Zapnutí přesměrování hovoru na: - - - - settings_call_forward_activation_success - Zapnuto přesměrování hovoru na: - - - - settings_call_forward_deactivation_success - Přesměrování hovoru vypnuto - CallHistoryLayout @@ -1144,7 +1161,7 @@ call_error_server_timeout_toast - "Server tiemout" + "Server timeout" Prodleva serveru @@ -1925,7 +1942,7 @@ settings_contacts_carddav_popup_synchronization_error_message "Erreur de synchronisation!" - Chyba synchronizace! + Chyba synchronizace: %1 @@ -2179,6 +2196,11 @@ "Delete" Smazat + + + chat_message_send_again + Znovu odeslat + ChatMessageContentCore @@ -2199,6 +2221,11 @@ info_popup_error_titile Chyba + + + download_file_default_error + Chyba při stahování souboru %1 + ChatMessageContentList @@ -2254,6 +2281,36 @@ Error ChatMessageContentModel + + + download_file_server_error + Chyba při pokusu o stažení obsahu: %1 + + + + download_file_error_no_safe_file_path + Nelze vytvořit bezpečné umístění souboru: %1 + + + + download_file_error_null_name + Název obsahu je prázdný, nelze jej stáhnout! + + + + download_file_error_unable_to_download + Nelze stáhnout soubor položky %1 + + + + download_error_object_doesnt_exist + Vnitřní chyba: objekt zprávy spojený s tímto obsahem již neexistuje! + + + + download_file_error_file_transfer_unavailable + Tento soubor již byl stažen a není už na serveru. Váš partner vám jej musí znovu poslat, pokud jej chcete získat + ChatMessageCore @@ -2495,6 +2552,26 @@ proto nezveřejňujte žádné citlivé informace! Creation de la conversation en cours … Probíhá vytváření chatu… + + + info_popup_error_title + Chyba + + + + group_chat_error_no_participant + Vyberte prosím alespoň jednoho účastníka + + + + info_popup_chatroom_creation_failed + Vytvoření chatovací místnosti se nezdařilo! + + + + loading_popup_chatroom_creation_pending_message + Chatovací místnost se vytváří... + ChatSettingsLayout @@ -3453,6 +3530,11 @@ proto nezveřejňujte žádné citlivé informace! "Le téléversement des traces a échoué. Vous pouvez partager les fichiers de trace directement depuis le répertoire suivant : %1" Nahrávání záznamů se nezdařilo. Soubory záznamů můžete sdílet přímo z následující složky: %1 + + + settings_debug_qt_version_title + Verze Qt + DecoratedTextField @@ -3802,6 +3884,11 @@ Expirace : %1 "Dépannage" Řešení potíží + + + help_check_for_update_button_label + Zkontrolovat aktualizace + LdapSettingsLayout @@ -4337,9 +4424,14 @@ Expirace : %1 Chyba v ověřovacím kódu - - information_popup_error_title - Chyba + + oidc_connection_waiting_message + Žádný token nenalezen + + + + cancel + Zrušit @@ -5150,6 +5242,11 @@ Expirace : %1 "Audio uniquement" Pouze zvuk + + + message_state_idle + nečinný + RegisterCheckingPage diff --git a/Linphone/data/languages/en.ts b/Linphone/data/languages/en.ts index 383ceeba5..014420bbf 100644 --- a/Linphone/data/languages/en.ts +++ b/Linphone/data/languages/en.ts @@ -25,13 +25,13 @@ AbstractWindow - + contact_dialog_pick_phone_number_or_sip_address_title "Choisissez un numéro ou adresse SIP" Choose a SIP number or address - + fps_counter %1 FPS @@ -39,45 +39,45 @@ AccountCore - + drawer_menu_account_connection_status_connected "Connecté" Connected - + drawer_menu_account_connection_status_refreshing Refreshing… - + drawer_menu_account_connection_status_progress Connecting… - + drawer_menu_account_connection_status_failed Error - + drawer_menu_account_connection_status_cleared Disabled - + manage_account_status_connected_summary "Vous êtes en ligne et joignable." You are online and reachable. - + manage_account_status_failed_summary "Erreur de connexion, vérifiez vos paramètres." Connection error, check your settings. - + manage_account_status_cleared_summary "Compte désactivé, vous ne recevrez ni appel ni message." Account disabled, you will not receive calls or messages. @@ -95,7 +95,7 @@ AccountListView - + add_an_account Add an account Add an account @@ -104,43 +104,55 @@ AccountManager - + assistant_account_login_already_connected_error "The account is already connected" The account is already connected - + + assistant_account_login_outbound_proxy_uri_error + Outbound proxy uri is invalid. Please make sure it matches the following format : sip:host>:<port>;transport=<transport> (:<port> is optional) + Outbound proxy uri is invalid. Please make sure it matches the following format : sip:host>:<port>;transport=<transport> (:<port> is optional) + + + assistant_account_login_proxy_address_error "Unable to create proxy address. Please check the domain name." Unable to create proxy address. Please check the domain name. - + assistant_account_login_address_configuration_error "Unable to configure address: `%1`." Unable to configure address: `%1`. - + + assistant_account_login_registrar_uri_error + Registrar uri is invalid. Please make sure it matches the following format : sip:host>:<port>;transport=<transport> (:<port> is optional) + Registrar uri is invalid. Please make sure it matches the following format : sip:host>:<port>;transport=<transport> (:<port> is optional) + + + assistant_account_login_params_configuration_error "Unable to configure account settings." Unable to configure account settings. - + assistant_account_login_forbidden_error "Username and password do not match" Username and password do not match - + assistant_account_login_error "Error during connection, please verify your parameters" Error during connection - + assistant_account_add_error "Unable to add account." Unable to add account. @@ -161,25 +173,25 @@ Unable to set server address, failed creating address from %1 - + set_outbound_proxy_uri_failed_error_message - Unable to set outbound proxy uri, failed creating address from %1 - Unable to set outbound proxy uri, failed creating address from %1 + Unable to set outbound proxy uri from address %1. Please make sure it matches the following format : sip:host>:<port>;transport=<transport> (:<port> is optional) + Unable to set outbound proxy uri from address %1. Please make sure it matches the following format : sip:host>:<port>;transport=<transport> (:<port> is optional) - + set_conference_factory_address_failed_error_message "Unable to set the conversation server address, failed creating address from %1" Unable to set the conversation server address, failed creating address from %1 - + set_audio_conference_factory_address_failed_error_message "Unable to set the meeting server address, failed creating address from %1" Unable to set the meeting server address, failed creating address from %1 - + set_voicemail_address_failed_error_message Unable to set voicemail address, failed creating address from %1 Unable to set voicemail address, failed creating address from %1 @@ -382,52 +394,49 @@ Account settings - info_popup_invalid_registrar_uri_message - Registrar uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - Registrar uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - - - info_popup_invalid_outbound_proxy_message - Outbound proxy uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - Outbound proxy uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - - - info_popup_error_title - Error - - - + information_popup_success_title Success - + contact_editor_saved_changes_toast "Modifications sauvegardés" Changes saved - + information_popup_error_title Error - + account_settings_mwi_uri_title - "URI du serveur de messagerie vocale" + "MWI server address" Voicemail server URI - + + mwi_server_address_tooltip + Address of the MWI server that sends SIP notifications to display new voicemail indicators + Address of the MWI server that sends SIP notifications to display new voicemail indicators + + + account_settings_voicemail_uri_title - "URI de messagerie vocale" + "Voicemail address" Voicemail URI - account_settings_transport_title "Transport" - Transport + Transport + + + + voicemail_address_tooltip + SIP address dialed when clicking the voicemail button + SIP address dialed when clicking the voicemail button @@ -435,60 +444,60 @@ Registrar URI - + account_settings_sip_proxy_url_title Outbound SIP Proxy URI - + login_proxy_server_url_tooltip "If this field is filled, the outbound proxy will be enabled automatically. Leave it empty to disable it." If this field is filled, the outbound proxy will be enabled automatically. Leave it empty to disable it. - + account_settings_stun_server_url_title "Adresse du serveur STUN" STUN server address - + account_settings_enable_ice_title "Activer ICE" Enable ICE - + account_settings_avpf_title "AVPF" AVPF - + account_settings_bundle_mode_title "Mode bundle" Bundle mode - + account_settings_expire_title "Expiration (en seconde)" Expiration (in seconds) - + account_settings_conference_factory_uri_title "URI du serveur de conversations" Conference factory URI - + account_settings_audio_video_conference_factory_uri_title "URI du serveur de réunions" Video conference factory uri - + account_settings_lime_server_url_title "URL du serveur d’échange de clés de chiffrement" Lime server URL @@ -623,7 +632,7 @@ Create end to end encrypted meetings and group calls - + settings_advanced_hide_fps_title Hide FPS @@ -652,141 +661,141 @@ App - + remote_provisioning_dialog Voulez-vous télécharger et appliquer la configuration depuis cette adresse ? Do you want to download and apply remote provisioning from this address ? - - - + + + info_popup_error_title Error Error - - + + info_popup_configuration_failed_message Remote provisioning failed : %1 Remote provisioning failed : %1 - + info_popup_error_checking_update An error occured while trying to check update. Please try again later or contact support team. An error occured while trying to check update. Please try again later or contact support team. - + info_popup_new_version_download_label Download it ! - + info_popup_new_version_available_title New version available ! New version available ! - + info_popup_new_version_available_message A new version of Linphone (%1) is available. %2 A new version of Linphone (%1) is available at %1 - + info_popup_version_up_to_date_title Up to date - + info_popup_version_up_to_date_message Your version is up to date Up to date Your version is up to date - + configuration_error_detail not reachable not reachable - + application_description "A free and open source SIP video-phone." A free and open source SIP video-phone. - + command_line_arg_order "Send an order to the application towards a command line" Send an order to the application towards a command line - + command_line_option_show_help Show this help - + command_line_option_show_app_version Show app version - + command_line_option_config_to_fetch "Specify the linphone configuration file to be fetched. It will be merged with the current configuration." Specify the linphone configuration file to be fetched. It will be merged with the current configuration. - + command_line_option_config_to_fetch_arg "URL, path or file" URL, path or file - + command_line_option_minimized Minimize - + command_line_option_log_to_stdout Log to stdout some debug information while running - + command_line_option_print_app_logs_only "Print only logs from the application" Print only logs from the application - + hide_action "Cacher" "Afficher" Hide - + show_action Show - + quit_action "Quitter" Quit - + check_for_update Check for update Check for update - + mark_all_read_action Marquer tout comme lu @@ -811,19 +820,19 @@ Password - + cancel "Annuler Cancel - + assistant_account_login Connexion Connection - + assistant_account_login_missing_password Veuillez saisir un mot de passe Please enter a password @@ -832,76 +841,76 @@ CallCore - + call_record_end_message "Enregistrement terminé" Recording ended - + call_record_saved_in_file_message "L'appel a été enregistré dans le fichier : %1" Recording has been saved in file : %1 - - + + call_stats_codec_label "Codec: %1 / %2 kHz" Codec: %1 / %2 kHz - - + + call_stats_bandwidth_label "Bande passante : %1 %2 kbits/s %3 %4 kbits/s" Bandwidth : %1 %2 kbits/s %3 %4 kbits/s - - + + call_stats_loss_rate_label "Taux de perte: %1% %2%" Loss rate: %1% %2% - + call_stats_jitter_buffer_label "Tampon de gigue: %1 ms" Jitter buffer : %1 ms - + call_stats_resolution_label "Définition vidéo : %1 %2 %3 %4" Video resolution: %1 %2 %3 %4 - + call_stats_fps_label "FPS : %1 %2 %3 %4" FPS : %1 %2 %3 %4 - + media_encryption_dtls DTLS DTLS - + media_encryption_none None None - + media_encryption_srtp SRTP SRTP - + media_encryption_post_quantum "ZRTP - Post quantique" Post quantum ZRTP @@ -910,75 +919,68 @@ CallForwardSettingsLayout - + settings_call_forward_activate_title "Forward calls" Forward calls - + settings_call_forward_activate_subtitle "Enable call forwarding to voicemail or sip address" Forward calls to voicemail or a Number / SIP Address / number - + settings_call_forward_destination_choose Forward to destination Forward calls to: - - - + settings_call_forward_to_voicemail Voicemail - + settings_call_forward_to_sipaddress Number / SIP Address - + settings_call_forward_sipaddress_title SIP Address Number / SIP Address: - + settings_call_forward_sipaddress_placeholder John.doe - + settings_call_forward_address_cannot_be_empty A number or SIP address is mandatory - settings_call_forward_address_timeout - Unable to set call forward, request timeout + Unable to set call forward, request timeout - settings_call_forward_address_progress_disabling - Disabling call forward + Disabling call forward - settings_call_forward_address_progress_enabling - Enabling call forward to: + Enabling call forward to: - settings_call_forward_activation_success - Call forward activated to : + Call forward activated to : - settings_call_forward_deactivation_success - Call forward deactivated + Call forward deactivated @@ -1011,7 +1013,7 @@ CallHistoryListView - + call_name_accessible_button Call %1 Call %1 @@ -1140,81 +1142,81 @@ CallModel - + call_error_no_response_toast "No response" No response - + call_error_forbidden_resource_toast "403 : Forbidden resource" 403 : Forbidden resource - + call_error_not_answered_toast "Request timeout" Request timeout - + call_error_user_declined_toast "User declined the call" User declined the call - + call_error_user_not_found_toast "User was not found" User was not found - + call_error_user_busy_toast "User is busy" User is busy - + call_error_incompatible_media_params_toast "User can&apos;t accept your call" User can't accept your call - + call_error_io_error_toast "Unavailable service or network error" Unavailable service or network error - + call_error_do_not_disturb_toast "Le correspondant ne peut être dérangé" User cannot be disturbed - + call_error_temporarily_unavailable_toast "Temporarily unavailable" Temporarily unavailable - + call_error_server_timeout_toast - "Server tiemout" - Server tiemout + "Server timeout" + Server timeout CallPage - + call_forward_to_address_info Forward calls to: - + call_forward_to_address_info_voicemail Voicemail @@ -1231,170 +1233,170 @@ Empty call history - + history_dialog_delete_all_call_logs_title Supprimer l'historique d'appels ? Delete call history ? - + history_dialog_delete_all_call_logs_message "L'ensemble de votre historique d'appels sera définitivement supprimé." Call history will be permanently deleted. - + history_dialog_delete_call_logs_title Supprimer l'historique d'appels ? Delete call history ? - + history_dialog_delete_call_logs_message "L'ensemble de votre historique d'appels avec ce correspondant sera définitivement supprimé." Call history with this user will be permanently deleted. - + call_history_call_list_title "Appels" Calls - + call_history_options_accessible_name Call history options - - + + menu_delete_history "Supprimer l'historique" Delete history - + call_history_list_options_accessible_name Call history options Call history options - + create_new_call_accessible_name Create new call Create new call - + call_search_in_history "Rechercher un appel" Find call - + list_filter_no_result_found "Aucun résultat…" No result found… - + history_list_empty_history "Aucun appel dans votre historique" No call in history - + return_to_call_history_accessible_name Return to call history Return to call history - + call_action_start_new_call "Nouvel appel" New call - + call_start_group_call_title "Appel de groupe" Group call - + call_action_start_group_call "Lancer" Start - - - + + + information_popup_error_title Error - + group_call_error_must_have_name "Un nom doit être donné à l'appel de groupe A name must be provided for the call - + group_call_error_not_connected "Vous n'etes pas connecté" You are not connected - + menu_see_existing_contact "Show contact" Show contact - + menu_add_address_to_contacts "Add to contacts" Add to contacts - + menu_copy_sip_address "Copier l'adresse SIP" Copy SIP address - + sip_address_copied_to_clipboard_toast Adresse copiée SIP address copied - + sip_address_copied_to_clipboard_message L'adresse a été copié dans le presse_papiers The address has been copied to the clipboard - + sip_address_copy_to_clipboard_error "Erreur lors de la copie de l'adresse" Error copying address - + notification_missed_call_title "Appel manqué" Missed call - + call_outgoing "Appel sortant" Outgoing call - + call_audio_incoming "Appel entrant" Incoming call @@ -1507,236 +1509,236 @@ CallsWindow - + call_transfer_in_progress_toast "Transfert en cours, veuillez patienter" Transfer in progress, please wait - - + + information_popup_error_title Error - + call_transfer_failed_toast "Le transfert d'appel a échoué" Transfer failed - + conference_error_empty_uri "La conférence n'a pas pu démarrer en raison d'une erreur d'uri." Meeting could start due to URI error. - + call_close_window_dialog_title "Terminer tous les appels en cours ?" End all current calls ? - + call_close_window_dialog_message "La fenêtre est sur le point d'être fermée. Cela terminera tous les appels en cours." The window is about to be closed. This will end all current calls. - + call_can_be_trusted_toast "Appareil authentifié" Device trusted - + call_dir %1 call - + call_ended Appel terminé Call ended - + conference_paused Meeting paused Meeting paused - + call_paused Call paused Call paused - - + + call_srtp_point_to_point_encrypted Appel chiffré de point à point Point-to-point encrypted call - + call_zrtp_sas_validation_required Vérification nécessaire Validation required - + call_zrtp_end_to_end_encrypted Appel chiffré de bout en bout End-to-end encrypted call - + call_not_encrypted "Appel non chiffré" Unencrypted call - - + + call_waiting_for_encryption_info Waiting for encryption Waiting for encryption - + call_paused_by_remote Call paused by remote Call paused by remote - + conference_user_is_recording "You are recording the meeting" You are recording the meeting - + call_user_is_recording "You are recording the call" You are recording the call - + conference_remote_is_recording "Someone is recording the meeting" Someone is recording the meeting - + call_remote_recording "%1 is recording the call" %1 records the call - + call_stop_recording "Stop recording" Stop recording - + add Add - + call_transfer_current_call_title "Transférer %1 à…" Transfer %1 to… - - + + call_transfer_confirm_dialog_tittle "Confirmer le transfert" Confirm transfer - - + + call_transfer_confirm_dialog_message "Vous allez transférer %1 à %2." You are going to transfer %1 to %2. - + call_action_start_new_call "Nouvel appel" New call - - + + call_action_show_dialer "Pavé numérique" Dialer - + call_action_change_layout "Modifier la disposition" Change layout - + call_action_go_to_calls_list "Liste d'appel" Call list - + Merger tous les appels call_action_merge_calls Merge all calls - - + + call_action_go_to_settings "Paramètres" Settings - + conference_action_screen_sharing "Partage de votre écran" Share your screen - + conference_share_link_title Partager le lien de la réunion Share meeting link - + copied Copié Copied - + information_popup_meeting_address_copied_to_clipboard Le lien de la réunion a été copié dans le presse-papier Meeting link has been copied to the clipboard - - - + + + conference_participants_list_title "Participants (%1)" Participants (%1) - - + + group_call_participant_selected %1 selected participant @@ -1744,194 +1746,194 @@ - + meeting_schedule_add_participants_title Add participants - + call_encryption_title Chiffrement Encryption - + open_statistic_panel_accessible_name Open statistic panel - + conference_user_is_sharing_screen "You are sharing your screen" You are sharing your screen - + call_stop_screen_sharing "Stop sharing" Stop - + stop_recording_accessible_name Stop recording Stop recording - + stop_screen_sharing_accessible_name "Stop screen sharing" Stop screen sharing - + call_stats_title Statistiques Statistics - - + + call_action_end_call "Terminer l'appel" End call - - + + call_action_resume_call "Reprendre l'appel" Resume call - - + + call_action_pause_call "Mettre l'appel en pause" Pause call - - + + call_action_transfer_call "Transférer l'appel" Transfer call - - + + call_action_start_new_call_hint "Initier un nouvel appel" Start new call - - + + call_display_call_list_hint "Afficher la liste d'appels" View call list - - + + call_deactivate_video_hint "Désactiver la vidéo" "Activer la vidéo" Turn off video - - + + call_activate_video_hint Enable video - - + + call_activate_microphone "Activer le micro" Activate microphone - - + + call_deactivate_microphone "Désactiver le micro" Mute microphone - - + + call_share_screen_hint Partager l'écran… Share screen… - - + + call_open_chat_hint Open chat… Open conversation… - - + + call_rise_hand_hint "Lever la main" Rise hand - - + + call_send_reaction_hint "Envoyer une réaction" Send reaction - - + + call_manage_participants_hint "Gérer les participants" Manage participants - - + + call_more_options_hint "Plus d'options…" More options… - + call_action_change_conference_layout "Modifier la disposition" Change layout - + call_action_full_screen "Mode Plein écran" Full screen mode - + call_action_stop_recording "Terminer l'enregistrement" End recording - + call_action_record "Enregistrer l'appel" Record call - + call_activate_speaker_hint "Activer le son" Activate speaker - + call_deactivate_speaker_hint "Désactiver le son" Mute speaker @@ -1963,63 +1965,63 @@ Check that all information has been entered. - + information_popup_synchronization_success_title Success - + settings_contacts_carddav_synchronization_success_message "Le carnet d'adresse CardDAV est synchronisé." The CardDAV address book is synchronized. - + settings_contacts_carddav_popup_synchronization_error_title Error - + settings_contacts_carddav_popup_synchronization_error_message "Erreur de synchronisation : %1" Synchronization error : %1 - + settings_contacts_delete_carddav_server_title "Supprimer le carnet d'adresse CardDAV ?" Delete CardDAV address book? - + sip_address_display_name Nom d'affichage Display name - + settings_contacts_carddav_server_url_title "URL du serveur" Server URL - + username Username - + password Password - + settings_contacts_carddav_realm_title Domaine d’authentification Authentication realm - + settings_contacts_carddav_use_as_default_title "Stocker ici les contacts nouvellement crées" Store newly created contacts here @@ -2062,13 +2064,13 @@ ChatCore - + info_toast_deleted_title Deleted Deleted - + info_toast_deleted_message_history Message history has been deleted Message history has been deleted @@ -2077,13 +2079,13 @@ ChatDroppableTextArea - + chat_view_send_area_placeholder_text Say something… : placeholder text for sending message text area Say something… - + cannot_record_while_in_call_tooltip Cannot record a message while a call is ongoing Cannot record a message while a call is ongoing @@ -2092,65 +2094,65 @@ ChatListView - + chat_message_is_writing_info %1 is writing… %1 is writing… - + chat_message_draft_sending_text Draft : %1 - + chat_room_delete "Delete" Delete - + chat_room_mute Mute - + chat_room_unmute "Mute" Unmute - + chat_room_mark_as_read "Mark as read" Mark as read - + chat_room_leave "leave" Leave - + chat_list_leave_chat_popup_title leave the conversation ? Leave the conversation ? - + chat_list_leave_chat_popup_message You will not be able to send or receive messages in this conversation anymore. Do You want to continue ? You will not be able to send or receive messages in this conversation anymore. Do You want to continue ? - + chat_list_delete_chat_popup_title Delete the conversation ? Delete the conversation ? - + chat_list_delete_chat_popup_message This conversation and all its messages will be deleted. Do You want to continue ? This conversation and all its messages will be deleted. Do You want to continue ? @@ -2159,25 +2161,25 @@ ChatMessage - + chat_message_copy_selection "Copy selection" Copy selection - + chat_message_copy "Copy" Copy - + chat_message_copied_to_clipboard_title Copied Copied - + chat_message_copied_to_clipboard_toast "to clipboard" in clipboard @@ -2213,7 +2215,13 @@ You replied - + + chat_message_send_again + "Re-send" + Re-send + + + chat_message_reception_info "Reception info" Reception info @@ -2225,19 +2233,19 @@ Edit - + chat_message_reply Reply Reply - + chat_message_forward Forward Forward - + chat_message_delete "Delete" Delete @@ -2252,24 +2260,24 @@ ChatMessageContentCore - + download_file_default_error Error downloading file %1 Error downloading file %1 - + info_popup_error_titile Error - + popup_error_title Error Error - + popup_open_file_error_does_not_exist_message Could not open file : unknown path %1 Could not open file : unknown path %1 @@ -2329,37 +2337,37 @@ Error ChatMessageContentModel - + download_error_object_doesnt_exist - Internal error : message object does not exist anymore ! - Internal error : message object does not exist anymore ! + Internal error : message object associated to this content does not exist anymore ! + Internal error : message object associated to this content does not exist anymore ! - + download_file_server_error Error while trying to download content : %1 Error while trying to download content : %1 - + download_file_error_no_safe_file_path Unable to create safe file path for: %1 Unable to create safe file path for: %1 - + download_file_error_file_transfer_unavailable This file was already downloaded and is no more on the server. Your peer have to resend it if you want to get it This file was already downloaded and is no more on the server. Your peer have to resend it if you want to get it - + download_file_error_null_name Content name is null, can't download it ! Content name is null, can't download it ! - + download_file_error_unable_to_download Unable to download file of entry %1 Unable to download file of entry %1 @@ -2440,44 +2448,44 @@ Error ChatMessagesListView - - + + popup_info_find_message_title Find message Find message - + info_popup_no_result_message No result found No result found - + info_popup_first_result_message First result reached First result reached - + info_popup_last_result_message Last result reached Last result reached - + chat_message_list_encrypted_header_title End to end encrypted chat End to end encrypted chat - + unencrypted_conversation_warning This conversation is not encrypted ! This conversation is not encrypted ! - + chat_message_list_encrypted_header_message Messages in this conversation are e2e encrypted. Only your correspondent can decrypt them. @@ -2485,7 +2493,7 @@ Error Only your correspondent can decrypt them. - + chat_message_list_not_encrypted_header_message Messages are not end to end encrypted, may sure you don't share any sensitive information ! @@ -2493,7 +2501,7 @@ Only your correspondent can decrypt them. may sure you don't share any sensitive information ! - + chat_message_is_writing_info %1 is writing… %1 is writing… @@ -2550,115 +2558,115 @@ Only your correspondent can decrypt them. No conversation - + info_popup_error_title Error - + info_popup_chatroom_creation_failed Chat room creation failed ! Chat room creation failed ! - + loading_popup_chatroom_creation_pending_message Chat room is being created... Chat room is being created... - + chat_dialog_delete_chat_title Supprimer la conversation ? Delete conversation ? - + chat_dialog_delete_chat_message "La conversation et tous ses messages seront supprimés." This conversation and all its messages will be deleted. - + chat_list_title "Conversations" Conversations - + menu_mark_all_as_read "mark all as read" Mark all as read - + chat_search_in_history "Rechercher une conversation" Search for a chat - + list_filter_no_result_found "Aucun résultat…" No result… - + chat_list_empty_history "Aucune conversation dans votre historique" No conversation in history - + chat_action_start_new_chat "New chat" New conversation - + chat_start_group_chat_title "Nouveau groupe" New group - + chat_action_start_group_chat "Créer" Create - - - + + + information_popup_error_title Error - + information_popup_chat_creation_failed_message "La création a échoué" Creation failed - + group_chat_error_must_have_name "Un nom doit être donné au groupe A name must be set for the group - + group_chat_error_no_participant "Please select at least one participant Please select at least one participant - + group_call_error_not_connected "Vous n'etes pas connecté" You are not connected - + chat_creation_in_progress Creation de la conversation en cours … Chat creation pending… @@ -2721,13 +2729,13 @@ Only your correspondent can decrypt them. ConferenceInfoCore - + information_popup_error_title "Erreur" Error - + information_popup_disconnected_account_message "Votre compte est déconnecté" Your account is disconnected @@ -2736,19 +2744,19 @@ Only your correspondent can decrypt them. Contact - + information_popup_error_title Erreur Error - + information_popup_voicemail_address_undefined_message L'URI de messagerie vocale n'est pas définie. The voicemail URI is not defined. - + account_settings_name_accessible_name Account settings of %1 Account settings of %1 @@ -2757,145 +2765,145 @@ Only your correspondent can decrypt them. ContactEdition - + contact_editor_title "Modifier contact" Edit contact - + save "Enregistrer Save - - + + contact_editor_dialog_cancel_change_message "Les changements seront annulés. Souhaitez-vous continuer ?" Changes will be discarded. Do you wish to continue? - + close_accessible_name Close %1 Close %1 - + contact_editor_mandatory_first_name_or_company_not_filled "Veuillez saisir un prénom ou un nom d'entreprise" Please enter a first name or a company name - + contact_editor_mandatory_address_or_number_not_filled "Veuillez saisir une adresse ou un numéro de téléphone" Please enter a SIP address or phone number - + contact_editor_add_image_label "Ajouter une image" Add an image - + contact_details_edit "Modifier" Edit - + edit_contact_image_accessible_name "Edit contact image" Edit contact image - + contact_details_delete "Supprimer" Delete - + delete_contact_image_accessible_name "Delete contact image" Delete contact image - - + + contact_editor_first_name "Prénom" First name - - + + contact_editor_last_name "Nom" Last name - - + + contact_editor_company "Entreprise" Company - - + + contact_editor_job_title "Fonction" Job - - + + sip_address SIP address - + sip_address_number_accessible_name "SIP address number %1" SIP address number %1 - + remove_sip_address_accessible_name "Remove SIP address %1" Remove SIP address %1 - + new_sip_address_accessible_name "New SIP address" New SIP address - + phone_number_number_accessible_name "Phone number number %1" Phone number number %1 - + remove_phone_number_accessible_name Remove phone number %1 Remove phone number %1 - + new_phone_number_accessible_name "New phone number" New phone number - - + + phone "Téléphone" Phone @@ -2986,149 +2994,149 @@ Only your correspondent can decrypt them. No contact at the moment - + contact_new_title "Nouveau contact" New contact - + create Create - + contact_edit_title "Modifier contact" Edit contact - + save Save - + contact_dialog_delete_title Supprimer %1 ?" Delete %1? - + contact_dialog_delete_message Ce contact sera définitivement supprimé. This contact will be permanently deleted. - + contact_deleted_toast "Contact supprimé" Contact deleted - + contact_deleted_message "%1 a été supprimé" %1 has been deleted - + contact_dialog_devices_trust_popup_title "Augmenter la confiance" Increase trust level - + contact_dialog_devices_trust_popup_message "Pour augmenter le niveau de confiance vous devez appeler les différents appareils de votre contact et valider un code.<br><br>Vous êtes sur le point d’appeler “%1” voulez vous continuer ?" To increase trust level you must call your contact's devices and validate a code.<br><br>You are about to call "%1" do you want to continue? - + popup_do_not_show_again Ne plus afficher Do not show again - + cancel Cancel - + dialog_call "Appeler" Call - + contact_dialog_devices_trust_help_title "Niveau de confiance" Trust level - + contact_dialog_devices_trust_help_message "Vérifiez les appareils de votre contact pour confirmer que vos communications seront sécurisées et sans compromission. <br>Quand tous seront vérifiés, vous atteindrez le niveau de confiance maximal." Verify your contact's devices to confirm that your communications will be secure and uncompromised. When all are verified, you will reach the maximum trust level. - + dialog_ok "Ok" Ok - + bottom_navigation_contacts_label "Contacts" Contacts - + search_bar_look_for_contact_text Rechercher un contact Find contact - + list_filter_no_result_found Aucun résultat… No result… - + contact_list_empty Aucun contact pour le moment No contact at the moment - + expand_accessible_name Expand %1 Expand %1 - + shrink_accessible_name Shrink %1 Shrink %1 - + create_contact_accessible_name Create new contact Create new contact - + more_info_accessible_name More info %1 More info %1 - - + + contact_details_edit Edit ---------- @@ -3136,151 +3144,151 @@ Only your correspondent can decrypt them. Edit - + contact_call_action "Appel" Call - + contact_message_action "Message" Message - + contact_video_call_action "Appel vidéo" Video call - + contact_details_numbers_and_addresses_title "Coordonnées" Contact details - + call_adress_accessible_name Call address %1 Call address %1 - + contact_details_company_name "Société :" Company : - + contact_details_job_title "Poste :" Job : - + contact_details_medias_title "Medias" Medias - - + + contact_details_medias_subtitle "Afficher les medias partagés" Show shared media - + contact_details_trust_title "Confiance" Trust - + contact_dialog_devices_trust_title "Niveau de confiance - Appareils vérifiés" Trust Level - Verified Devices - + contact_details_no_device_found "Aucun appareil" No device - + contact_device_without_name "Appareil inconnu" Unknown device - + contact_make_call_check_device_trust "Vérifier" Verify - + verify_device_accessible_name Verify %1 device Verify %1 device - + contact_details_actions_title "Autres actions" Other actions - + contact_details_remove_from_favourites "Retirer des favoris" Remove from favorites - + contact_details_add_to_favourites "Ajouter aux favoris" Add to favorites - + contact_details_share "Partager" Share - + information_popup_error_title Error - + contact_details_share_error_mesage "La création du fichier vcard a échoué" VCard creation failed - + contact_details_share_success_title "VCard créée" VCard created - + contact_details_share_success_mesage "VCard du contact enregistrée dans %1" VCard has been saved in %1 - + contact_details_share_email_title "Partage de contact" Share contact - + contact_details_delete "Supprimer ce contact" Delete contact @@ -3362,18 +3370,18 @@ Only your correspondent can decrypt them. ContactsSettingsProviderLayout - + information_popup_success_title Success - + information_popup_changes_saved "Les changements ont été sauvegardés" Changes have been saved - + add "Ajouter" Add @@ -3382,138 +3390,138 @@ Only your correspondent can decrypt them. ConversationInfos - + one_one_infos_call "Appel" Call - + one_one_infos_unmute "Sourdine" Unmute - + one_one_infos_mute Mute - + group_infos_participants Participants (%1) - + group_infos_media_docs Medias & documents Medias & documents - + group_infos_shared_medias Shared medias Shared medias - + group_infos_shared_docs Shared documents Shared documents - + group_infos_other_actions Other actions Other actions - + group_infos_ephemerals Ephemeral messages : - + group_infos_enable_ephemerals Enable ephemeral messages - + group_infos_meeting Schedule a meeting Schedule a meeting - + group_infos_leave_room Leave chat room Leave Chat Room - + group_infos_leave_room_toast_title Leave Chat Room ? Leave Chat Room ? - + group_infos_leave_room_toast_message All the messages will be removed from the chat room. Do you want to continue ? All the messages will be removed from the chat room. Do you want to continue ? - + group_infos_delete_history Delete history Delete history - + group_infos_delete_history_toast_title Delete history ? Delete history ? - + group_infos_delete_history_toast_message All the messages will be removed from the chat room. Do you want to continue ? All the messages will be removed from the chat room. Do you want to continue ? - + one_one_infos_open_contact Show contact Show contact - + one_one_infos_create_contact Create contact Create contact - + one_one_infos_ephemerals Ephemeral messages : - + one_one_infos_enable_ephemerals Enable ephemeral messages - + one_one_infos_delete_history Delete history - + one_one_infos_delete_history_toast_title Delete history ? Delete history ? - + one_one_infos_delete_history_toast_message All the messages will be removed from the chat room. Do you want to continue ? All the messages will be removed from the chat room. Do you want to continue ? @@ -3522,12 +3530,12 @@ Only your correspondent can decrypt them. CoreModel - + info_popup_error_title Error - + fetching_config_failed_error_message "Remote provisioning cannot be retrieved" Remote provisioning cannot be retrieved @@ -3864,55 +3872,55 @@ Expiration : %1 GroupChatInfoParticipants - + group_infos_manage_participants_title "Gérer des participants" Manage participants - + group_infos_participant_is_admin Admin - + menu_see_existing_contact "Show contact" Show contact - + menu_add_address_to_contacts "Add to contacts" Add to contacts - + group_infos_give_admin_rights Give admin rights - + group_infos_remove_admin_rights Remove admin rights - + group_infos_copy_sip_address Copy SIP Address - + group_infos_remove_participant Remove participant - + group_infos_remove_participants_toast_title Remove participant ? - + group_infos_remove_participants_toast_message Participant will be removed from chat room. @@ -3927,7 +3935,7 @@ Expiration : %1 - + group_start_dialog_subject_hint "Nom du groupe" Group name @@ -4362,96 +4370,96 @@ Expiration : %1 Open meetings page - + searchbar_placeholder_text "Rechercher un contact, appeler %1" Find contact, call %1 - + searchbar_placeholder_text_chat_feature_enabled "ou envoyer un message …" or send message … - + do_not_disturb_accessible_name "Do not disturb" Do not disturb - - + + contact_presence_status_disable_do_not_disturb "Désactiver ne pas déranger" Disable do not disturb - + information_popup_error_title Error - + no_voicemail_uri_error_message "L'URI de messagerie vocale n'est pas définie." The voicemail URI is not defined. - + account_list_accessible_name "Account list" account list - + application_options_accessible_name "Application options" Application options - + drawer_menu_manage_account Mon compte My account - + contact_presence_status_enable_do_not_disturb "Activer ne pas déranger" Enable do not disturb - + settings_title Settings - + recordings_title "Enregistrements" Records - + help_title "Aide" Help - + help_quit_title "Quitter l'application" Quit the app - + quit_app_question "Quitter %1 ?" Quit %1 ? - + drawer_menu_add_account "Ajouter un compte" Add an account @@ -4490,56 +4498,67 @@ Expiration : %1 Your correspondent has been transferred to the selected contact - + information_popup_success_title Saved - + information_popup_changes_saved "Les changements ont été sauvegardés" Changes have been saved - + + oidc_connection_waiting_message + "Trying to connect to single sign on on web page ..." + + + + + cancel + Cancel + Cancel + + + captcha_validation_loading_message "Veuillez valider le captcha sur la page web" Please validate the captcha on the web page - + assistant_register_error_title "Erreur lors de la création" Error while creating - + assistant_register_success_title "Compte créé" Account created - + assistant_register_success_message "Le compte a été créé. Vous pouvez maintenant vous connecter" The account has been created. You can now log in. - + assistant_register_error_code "Erreur dans le code de validation" Error in validation code - information_popup_error_title - Error + Error ManageParticipants - + group_infos_manage_participants Participants @@ -4586,19 +4605,19 @@ Expiration : %1 MeetingListView - + meeting_info_cancelled "Réunion annulée" Meeting canceled - + meetings_list_no_meeting_for_today "Aucune réunion aujourd'hui" No meeting for today - + meeting_info_delete "Supprimer la réunion" Delete meeting @@ -4619,163 +4638,163 @@ Expiration : %1 No meeting - + meeting_schedule_cancel_dialog_message "Souhaitez-vous annuler et supprimer cette réunion ?" Would you like to cancel and delete this meeting? - + meeting_schedule_delete_dialog_message Souhaitez-vous supprimer cette réunion ? Would you like to delete this meeting? - + meeting_schedule_cancel_and_delete_action "Annuler et supprimer" Cancel and delete - + meeting_schedule_delete_only_action "Supprimer seulement" Delete only - + meeting_schedule_delete_action "Supprimer" Delete - + back_action Retour Back - + meetings_list_title Réunions Meetings - + meetings_search_hint "Rechercher une réunion" Find meeting - + list_filter_no_result_found "Aucun résultat…" No result… - + meetings_empty_list "Aucune réunion" No meeting - - + + meeting_schedule_title "Nouvelle réunion" New meeting - + create Create - - - - + + + - + + information_popup_error_title Error - - + + meeting_schedule_mandatory_field_not_filled_toast Veuillez saisir un titre et sélectionner au moins un participant Please fill the title and select at least one participant - - + + meeting_schedule_duration_error_toast "La fin de la conférence doit être plus récente que son début" The end of the conference must be more recent than its beginning - - + + meeting_schedule_creation_in_progress "Création de la réunion en cours …" Creation in progress… - + meeting_info_created_toast "Réunion planifiée avec succès" Meeting successfully created - + meeting_failed_to_schedule_toast "Échec de création de la réunion !" Failed to create meeting! - + save Save - - + + saved "Enregistré" Saved - + meeting_info_updated_toast "Réunion mise à jour" Meeting updated - + meeting_schedule_edit_in_progress "Modification de la réunion en cours…" Meeting update in progress… - + meeting_failed_to_edit_toast "Échec de la modification de la réunion !" Failed to update meeting ! - + meeting_schedule_add_participants_title "Ajouter des participants" Add participants - + meeting_schedule_add_participants_apply Apply - + group_call_participant_selected "%n participant(s) sélectionné(s)" @@ -4784,31 +4803,31 @@ Expiration : %1 - + meeting_info_delete "Supprimer la réunion" Delete meeting - + meeting_address_copied_to_clipboard_toast "Adresse de la réunion copiée" Meeting URI copied - + meeting_schedule_timezone_title "Fuseau horaire" Timezone - + meeting_info_organizer_label "Organisateur" Organizer - + meeting_info_join_title "Rejoindre la réunion" Join meeting @@ -4862,13 +4881,13 @@ Expiration : %1 MessageSharedFilesInfos - + no_shared_medias No media No media - + no_shared_documents No document No document @@ -4994,7 +5013,7 @@ Expiration : %1 new_voice_message - 'Voice message received!' : message to warn the user in a notofication for voice messages. + 'Voice message received!' : message to warn the user in a notification for voice messages. Voice message received! @@ -5029,108 +5048,109 @@ Expiration : %1 OAuthHttpServerReplyHandler is not listening - + + oidc_authentication_timeout_message Timeout: Not authenticated Timeout: Not authenticated - + oidc_authentication_granted_message Authentication granted Authentication granted - + oidc_authentication_not_authenticated_message Not authenticated Not authenticated - + oidc_authentication_refresh_message Refreshing token Refreshing token - + oidc_authentication_temporary_credentials_message Temporary credentials received Temporary credentials received - + oidc_authentication_network_error Network error Network error - + oidc_authentication_server_error Server error Server error - + oidc_authentication_token_not_found_error OAuth token not found OAuth token not found - + oidc_authentication_token_secret_not_found_error OAuth token secret not found OAuth token secret not found - + oidc_authentication_callback_not_verified_error OAuth callback not verified OAuth callback not verified - + oidc_authentication_request_auth_message Requesting authorization from browser Requesting authorization from browser - + oidc_authentication_no_token_found_error No token found - + oidc_authentication_request_token_message Requesting access token Requesting access token - + oidc_authentication_refresh_token_message Refreshing access token Refreshing access token - + oidc_authentication_request_authorization_message Requesting authorization Requesting authorization - + oidc_authentication_request_temporary_credentials_message Requesting temporary credentials Requesting temporary credentials - + oidc_authentication_no_auth_found_in_config_error No authorization endpoint found in OpenID configuration No authorization endpoint found in OpenID configuration - + oidc_authentication_no_token_found_in_config_error No token endpoint found in OpenID configuration No token endpoint found in OpenID configuration @@ -5154,13 +5174,13 @@ Expiration : %1 PhoneNumberInput - + prefix_phone_number_accessible_name %1 prefix %1 prefix - + number_phone_number_accessible_name %1 number %1 number @@ -5353,37 +5373,37 @@ Expiration : %1 RegisterCheckingPage - + email "email" email - + phone_number "numéro de téléphone" phone number - + confirm_register_title "Inscription | Confirmer votre %1" Register | Confirm your %1 - + assistant_account_creation_confirmation_explanation Nous vous avons envoyé un code de vérification sur votre %1 %2<br> Merci de le saisir ci-dessous We have sent you a verification code to your %1 %2<br> Please enter it below - + assistant_account_creation_confirmation_did_not_receive_code "Vous n'avez pas reçu le code ?" Didn't receive the code? - + assistant_account_creation_confirmation_resend_code "Renvoyer un code" Resend code @@ -5424,49 +5444,49 @@ Expiration : %1 Register with email - - + + username Username - - - - - + + + + + mandatory_field_accessible_name "%1 mandatory" %1 mandatory - + domain Domain - - - + + + phone_number "Numéro de téléphone" Phone number - - + + email Email - - + + password Password - - + + assistant_account_register_password_confirmation "Confirmation mot de passe" Password confirmation @@ -5490,37 +5510,37 @@ Expiration : %1 privacy policy - + assistant_account_create "Créer" Create - + assistant_account_create_missing_username_error "Veuillez entrer un nom d'utilisateur" Please enter a username - + assistant_account_create_missing_password_error "Veuillez entrer un mot de passe" Please enter a password - + assistant_account_create_confirm_password_error "Les mots de passe sont différents" Passwords do not match - + assistant_account_create_missing_number_error "Veuillez entrer un numéro de téléphone" Please enter a phone number - + assistant_account_create_missing_email_error "Veuillez entrer un email" Please enter an email @@ -5716,13 +5736,13 @@ To enable them in a commercial project, please contact us. SearchBar - + open_dialer_acccessibility_label "Open dialer" Open dialer - + clear_text_input_acccessibility_label "Clear text input" Clear text input @@ -5791,18 +5811,18 @@ To enable them in a commercial project, please contact us. SelectedChatView - + chat_view_group_call_toast_message Start a group call ? - + unencrypted_conversation_warning This conversation is not encrypted ! This conversation is not encrypted ! - + reply_to_label Reply to %1 Reply to %1 @@ -5814,25 +5834,25 @@ To enable them in a commercial project, please contact us. Message beeing edited - + shared_medias_title Shared medias Shared medias - + shared_documents_title Shared documents Shared documents - + forward_to_title Forward to… - Froward to… + Forward to… - + conversations_title Conversations Conversations @@ -5955,49 +5975,49 @@ To enable them in a commercial project, please contact us. ToolModel - + call_error_uninterpretable_sip_address "The calling address is not an interpretable SIP address : %1 The calling address is not an interpretable SIP address : %1 - + group_call_error_no_account No default account found, can't create group call - + group_call_error_participants_invite Couldn't invite participants to group call - + group_call_error_creation Group call couldn't be created - + voice_recording_duration "Voice recording (%1)" : %1 is the duration formated in mm:ss Voice recording (%1) - + unknown_audio_device_name Unknown device name - + conference_invitation Meeting invitation - + conference_invitation_cancelled Meeting cancellation - + conference_invitation_updated Meeting modification @@ -6017,7 +6037,7 @@ To enable them in a commercial project, please contact us. Utils - + nSeconds %1 second @@ -6025,7 +6045,7 @@ To enable them in a commercial project, please contact us. - + nMinute %1 minute @@ -6033,25 +6053,25 @@ To enable them in a commercial project, please contact us. - + chat_message_forward_error Cannot forward an invalid message Cannot forward an invalid message - + info_popup_forward_message_error Could not forward message : %1 Could not forward message : %1 - + info_popup_send_forward_message_error_message Failed to create forward message Failed to create forward message - + chat_message_reply_error Cannot reply to invalid message Cannot reply to invalid message @@ -6063,7 +6083,7 @@ To enable them in a commercial project, please contact us. Cannot modify invalid message - + info_popup_reply_message_error Could not send reply message : %1 Could not send reply message : %1 @@ -6075,7 +6095,7 @@ To enable them in a commercial project, please contact us. Could not send edited message : %1 - + info_popup_send_reply_message_error_message Failed to create reply message Failed to create reply message @@ -6087,7 +6107,7 @@ To enable them in a commercial project, please contact us. Failed to create edited message - + nHour %1 hour @@ -6095,8 +6115,8 @@ To enable them in a commercial project, please contact us. - - + + nDay %1 day @@ -6104,7 +6124,7 @@ To enable them in a commercial project, please contact us. - + nWeek %1 week @@ -6112,27 +6132,27 @@ To enable them in a commercial project, please contact us. - + contact_presence_status_available Available - + contact_presence_status_busy Busy - + contact_presence_status_do_not_disturb Do not disturb - + contact_presence_status_offline Offline - + contact_presence_status_away Idle/Away @@ -6145,12 +6165,9 @@ To enable them in a commercial project, please contact us. - - + information_popup_error_title - Error ----------- -Failed to create 1-1 conversation with %1 ! + Failed to create 1-1 conversation with %1 ! Error @@ -6228,44 +6245,42 @@ Failed to create 1-1 conversation with %1 ! ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 - - + information_popup_chatroom_creation_error_message - Failed to create 1-1 conversation with %1 ! Failed to create 1-1 conversation with %1 ! - + recorder_error Error with the recorder Error with the recorder - - - + + + chat_error Error in the chat - - - - - - + + + + + + info_popup_error_title Error Error - + info_popup_send_voice_message_error_message Could not send voice message : %1 Could not send voice message : %1 - + info_popup_send_voice_message_sending_error_message Failed to create message from record Failed to create message from record @@ -6274,32 +6289,32 @@ Failed to create 1-1 conversation with %1 ! WaitingRoom - + meeting_waiting_room_title Participer à : Join : - + meeting_waiting_room_join "Rejoindre" Join - - + + cancel Cancel Cancel - + meeting_waiting_room_joining_title "Connexion à la réunion" Connection to meeting - + meeting_waiting_room_joining_subtitle "Vous allez rejoindre la réunion dans quelques instants…" You will be joining the meeting in a few moments... @@ -6916,7 +6931,7 @@ Failed to create 1-1 conversation with %1 ! Italy - Italie + Italy diff --git a/Linphone/data/languages/es.ts b/Linphone/data/languages/es.ts index ddd4efe3b..d4a026856 100644 --- a/Linphone/data/languages/es.ts +++ b/Linphone/data/languages/es.ts @@ -361,23 +361,6 @@ settings_account_title - - - info_popup_invalid_registrar_uri_message - Registrar uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - - - - - info_popup_invalid_outbound_proxy_message - Outbound proxy uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - - - - - info_popup_error_title - - information_popup_success_title @@ -406,12 +389,6 @@ "URI de messagerie vocale" - - - account_settings_transport_title - "Transport" - - account_settings_registrar_uri_title @@ -891,31 +868,6 @@ settings_call_forward_address_cannot_be_empty - - - settings_call_forward_address_timeout - - - - - settings_call_forward_address_progress_disabling - - - - - settings_call_forward_address_progress_enabling - - - - - settings_call_forward_activation_success - - - - - settings_call_forward_deactivation_success - - CallHistoryLayout @@ -1138,7 +1090,7 @@ call_error_server_timeout_toast - "Server tiemout" + "Server timeout" @@ -4318,11 +4270,6 @@ Error "Erreur dans le code de validation" - - - information_popup_error_title - - ManageParticipants diff --git a/Linphone/data/languages/eu.ts b/Linphone/data/languages/eu.ts index ece0dfa03..1f5b250a6 100644 --- a/Linphone/data/languages/eu.ts +++ b/Linphone/data/languages/eu.ts @@ -164,7 +164,7 @@ set_outbound_proxy_uri_failed_error_message Unable to set outbound proxy uri, failed creating address from %1 - Ezin da irteerako proxy uri-a ezarri, huts egin du %1 (e)tik helbidea sortzean + Ezin da irteerako proxy uri-a ezarri, huts egin du %1 (e)tik helbidea sortzean @@ -304,6 +304,11 @@ "No information" Ez dago informaziorik + + + copied + Kopiatuta + AccountSettingsPage @@ -361,23 +366,6 @@ settings_account_title Kontuaren ezarpenak - - - info_popup_invalid_registrar_uri_message - Registrar uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - - - - - info_popup_invalid_outbound_proxy_message - Outbound proxy uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - - - - - info_popup_error_title - Errorea - information_popup_success_title @@ -406,12 +394,6 @@ "URI de messagerie vocale" Ahots-postontziaren URIa - - - account_settings_transport_title - "Transport" - Garraiatu - account_settings_registrar_uri_title @@ -891,31 +873,6 @@ settings_call_forward_address_cannot_be_empty - - - settings_call_forward_address_timeout - - - - - settings_call_forward_address_progress_disabling - - - - - settings_call_forward_address_progress_enabling - - - - - settings_call_forward_activation_success - - - - - settings_call_forward_deactivation_success - - CallHistoryLayout @@ -1138,8 +1095,8 @@ call_error_server_timeout_toast - "Server tiemout" - Zerbitzariaren denbora-muga + "Server timeout" + Zerbitzariaren denbora-muga @@ -1918,7 +1875,7 @@ settings_contacts_carddav_popup_synchronization_error_message "Erreur de synchronisation!" - Sinkronizazio-errorea! + Sinkronizazio-errorea! @@ -2484,6 +2441,11 @@ Error Creation de la conversation en cours … + + + info_popup_error_title + Errorea + ChatSettingsLayout @@ -4324,9 +4286,14 @@ Error Errorea baliozkotze kodean - - information_popup_error_title - Errorea + + oidc_connection_waiting_message + Ez da tokenik aurkitu + + + + cancel + Utzi @@ -6667,7 +6634,7 @@ Failed to create 1-1 conversation with %1 ! Italy - Italia + Italia diff --git a/Linphone/data/languages/fi.ts b/Linphone/data/languages/fi.ts index 68d861f00..fe84bfddf 100644 --- a/Linphone/data/languages/fi.ts +++ b/Linphone/data/languages/fi.ts @@ -361,23 +361,6 @@ settings_account_title - - - info_popup_invalid_registrar_uri_message - Registrar uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - - - - - info_popup_invalid_outbound_proxy_message - Outbound proxy uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - - - - - info_popup_error_title - - information_popup_success_title @@ -406,12 +389,6 @@ "URI de messagerie vocale" - - - account_settings_transport_title - "Transport" - - account_settings_registrar_uri_title @@ -891,31 +868,6 @@ settings_call_forward_address_cannot_be_empty - - - settings_call_forward_address_timeout - - - - - settings_call_forward_address_progress_disabling - - - - - settings_call_forward_address_progress_enabling - - - - - settings_call_forward_activation_success - - - - - settings_call_forward_deactivation_success - - CallHistoryLayout @@ -1138,7 +1090,7 @@ call_error_server_timeout_toast - "Server tiemout" + "Server timeout" @@ -4332,11 +4284,6 @@ Error "Erreur dans le code de validation" - - - information_popup_error_title - - ManageParticipants diff --git a/Linphone/data/languages/fr.ts b/Linphone/data/languages/fr.ts index e98876b80..d3ba60635 100644 --- a/Linphone/data/languages/fr.ts +++ b/Linphone/data/languages/fr.ts @@ -25,13 +25,13 @@ AbstractWindow - + contact_dialog_pick_phone_number_or_sip_address_title "Choisissez un numéro ou adresse SIP" Choisissez un numéro ou adresse SIP - + fps_counter %1 FPS @@ -39,45 +39,45 @@ AccountCore - + drawer_menu_account_connection_status_connected "Connecté" Connecté - + drawer_menu_account_connection_status_refreshing En cours de rafraîchissement… - + drawer_menu_account_connection_status_progress Connexion… - + drawer_menu_account_connection_status_failed Erreur - + drawer_menu_account_connection_status_cleared Désactivé - + manage_account_status_connected_summary "Vous êtes en ligne et joignable." Vous êtes en ligne et joignable. - + manage_account_status_failed_summary "Erreur de connexion, vérifiez vos paramètres." Erreur de connexion, vérifiez vos paramètres. - + manage_account_status_cleared_summary "Compte désactivé, vous ne recevrez ni appel ni message." Compte désactivé, vous ne recevrez ni appel ni message. @@ -95,7 +95,7 @@ AccountListView - + add_an_account Add an account Ajouter un compte @@ -104,43 +104,55 @@ AccountManager - + assistant_account_login_already_connected_error "The account is already connected" Le compte est déjà connecté - + + assistant_account_login_outbound_proxy_uri_error + Outbound proxy uri is invalid. Please make sure it matches the following format : sip:host>:<port>;transport=<transport> (:<port> is optional) + L'uri du proxy SIP sortant est invalide. Assurez-vous qu'elle corresponde format suivant : sip:host>:<port>;transport=<transport> (:<port> est optionnel) + + + assistant_account_login_proxy_address_error "Unable to create proxy address. Please check the domain name." Impossible de créer l'adresse proxy. Merci de vérifier le nom de domaine. - + assistant_account_login_address_configuration_error "Unable to configure address: `%1`." Impossible de configurer l'adresse : `%1`. - + + assistant_account_login_registrar_uri_error + Registrar uri is invalid. Please make sure it matches the following format : sip:host>:<port>;transport=<transport> (:<port> is optional) + La registrar uri est invalide. Assurez-vous qu'elle corresponde format suivant : sip:host>:<port>;transport=<transport> (:<port> est optionnel) + + + assistant_account_login_params_configuration_error "Unable to configure account settings." Impossible de configurer les paramètres du compte. - + assistant_account_login_forbidden_error "Username and password do not match" Le couple identifiant mot de passe ne correspond pas - + assistant_account_login_error "Error during connection, please verify your parameters" Erreur durant la connexion, veuillez vérifier vos paramètres - + assistant_account_add_error "Unable to add account." Impossible d'ajouter le compte. @@ -161,25 +173,25 @@ Impossible de définir l'adresse du serveur depuis l'adresse %1 - + set_outbound_proxy_uri_failed_error_message - Unable to set outbound proxy uri, failed creating address from %1 - Impossible de définir l'adresse du proxy sip sortant depuis l'adresse %1 + Unable to set outbound proxy uri from address %1. Please make sure it matches the following format : sip:host>:<port>;transport=<transport> (:<port> is optional) + Impossible de définir l'adresse du proxy sip sortant depuis l'adresse %1. Assurez-vous qu'elle corresponde au format suivant : sip:host>:<port>;transport=<transport> (:<port> est optionnel) - + set_conference_factory_address_failed_error_message "Unable to set the conversation server address, failed creating address from %1" Impossible de définir l'uri du serveur de conversations depuis l'adresse %1 - + set_audio_conference_factory_address_failed_error_message "Unable to set the meeting server address, failed creating address from %1" Impossible de définir l'uri du serveur de réunions depuis l'adresse %1 - + set_voicemail_address_failed_error_message Unable to set voicemail address, failed creating address from %1 Impossible de définir l'adresse de messagerie vocale depuis l'adresse %1 @@ -382,52 +394,44 @@ Paramètres de compte - info_popup_invalid_registrar_uri_message - Registrar uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - La registrar URI est invalide. Veuillez vous assurer qu'elle respecte le format suivant : sip:<host>:<port>;transport=<transport> (:<port> est facultatif) - - - info_popup_invalid_outbound_proxy_message - Outbound proxy uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - L'uri du proxy sip sortant est invalide. Veuillez vous assurer qu'elle respecte le format suivant : sip:<host>:<port>;transport=<transport> (:<port> est facultatif) - - - info_popup_error_title - Erreur - - - + information_popup_success_title Succès - + contact_editor_saved_changes_toast "Modifications sauvegardés" Modifications sauvegardés - + information_popup_error_title Erreur - + account_settings_mwi_uri_title - "URI du serveur de messagerie vocale" - URI du serveur de messagerie vocale + "MWI server address" + URI du serveur MWI (Message Waiting Indicator) - + + mwi_server_address_tooltip + Address of the MWI server that sends SIP notifications to display new voicemail indicators + Adresse du serveur MWI qui envoie les notifications SIP pour afficher l'indicateur de nouveaux messages vocaux + + + account_settings_voicemail_uri_title - "URI de messagerie vocale" - URI de messagerie vocale + "Voicemail address" + URI de la messagerie vocale - - account_settings_transport_title - "Transport" - Transport + + voicemail_address_tooltip + SIP address dialed when clicking the voicemail button + Adresse SIP appelée lors du clic sur le bouton messagerie @@ -435,60 +439,60 @@ Registrar URI - + account_settings_sip_proxy_url_title URL du proxy SIP sortant - + login_proxy_server_url_tooltip "If this field is filled, the outbound proxy will be enabled automatically. Leave it empty to disable it." Si ce champ est rempli, l’outbound proxy sera activé automatiquement. Laissez-le vide pour le désactiver. - + account_settings_stun_server_url_title "Adresse du serveur STUN" Adresse du serveur STUN - + account_settings_enable_ice_title "Activer ICE" Activer ICE - + account_settings_avpf_title "AVPF" AVPF - + account_settings_bundle_mode_title "Mode bundle" Mode bundle - + account_settings_expire_title "Expiration (en seconde)" Expiration (en seconde) - + account_settings_conference_factory_uri_title "URI du serveur de conversations" URI du serveur de conversations - + account_settings_audio_video_conference_factory_uri_title "URI du serveur de réunions" URI du serveur de réunions - + account_settings_lime_server_url_title "URL du serveur d’échange de clés de chiffrement" URL du serveur d’échange de clés de chiffrement @@ -623,7 +627,7 @@ Créer des appels de groupe et conférences chiffré(e)s de bout en bout - + settings_advanced_hide_fps_title Cacher les FPS @@ -652,141 +656,141 @@ App - + remote_provisioning_dialog Voulez-vous télécharger et appliquer la configuration depuis cette adresse ? Voulez-vous télécharger et appliquer la configuration depuis cette adresse ? - - - + + + info_popup_error_title Error Erreur - - + + info_popup_configuration_failed_message Remote provisioning failed : %1 La configuration distante a échoué : %1 - + info_popup_error_checking_update An error occured while trying to check update. Please try again later or contact support team. Une erreur est survenue lors de la recherche de mise à jour. Merci de réessayer plus tard ou de contacter l'équipe de support. - + info_popup_new_version_download_label - Téléchargez-là ! + Téléchargez-là ! - + info_popup_new_version_available_title New version available ! Nouvelle version disponible ! - + info_popup_new_version_available_message A new version of Linphone (%1) is available. %2 Une nouvelle version de Linphone (%1) est disponible. %2 - + info_popup_version_up_to_date_title À jour - + info_popup_version_up_to_date_message Your version is up to date Votre version est à jour - + configuration_error_detail not reachable indisponible - + application_description "A free and open source SIP video-phone." A free and open source SIP video-phone. - + command_line_arg_order "Send an order to the application towards a command line" Send an order to the application towards a command line - + command_line_option_show_help Show this help - + command_line_option_show_app_version Afficher la version de l'application - + command_line_option_config_to_fetch "Specify the linphone configuration file to be fetched. It will be merged with the current configuration." Specify the linphone configuration file to be fetched. It will be merged with the current configuration. - + command_line_option_config_to_fetch_arg "URL, path or file" URL, path or file - + command_line_option_minimized Minimiser - + command_line_option_log_to_stdout Log to stdout some debug information while running - + command_line_option_print_app_logs_only "Print only logs from the application" Print only logs from the application - + hide_action "Cacher" "Afficher" Cacher - + show_action Afficher - + quit_action "Quitter" Quitter - + check_for_update Check for update Rechercher une mise à jour - + mark_all_read_action Marquer tout comme lu @@ -811,19 +815,19 @@ Mot de passe - + cancel "Annuler Annuler - + assistant_account_login Connexion Connexion - + assistant_account_login_missing_password Veuillez saisir un mot de passe Veuillez saisir un mot de passe @@ -832,76 +836,76 @@ CallCore - + call_record_end_message "Enregistrement terminé" Enregistrement terminé - + call_record_saved_in_file_message "L'appel a été enregistré dans le fichier : %1" L'appel a été enregistré dans le fichier : %1 - - + + call_stats_codec_label "Codec: %1 / %2 kHz" Codec: %1 / %2 kHz - - + + call_stats_bandwidth_label "Bande passante : %1 %2 kbits/s %3 %4 kbits/s" Bande passante : %1 %2 kbits/s %3 %4 kbits/s - - + + call_stats_loss_rate_label "Taux de perte: %1% %2%" Taux de perte: %1% %2% - + call_stats_jitter_buffer_label "Tampon de gigue: %1 ms" Tampon de gigue: %1 ms - + call_stats_resolution_label "Définition vidéo : %1 %2 %3 %4" Définition vidéo : %1 %2 %3 %4 - + call_stats_fps_label "FPS : %1 %2 %3 %4" FPS : %1 %2 %3 %4 - + media_encryption_dtls DTLS DTLS - + media_encryption_none None None - + media_encryption_srtp SRTP SRTP - + media_encryption_post_quantum "ZRTP - Post quantique" ZRTP - Post quantique @@ -910,73 +914,46 @@ CallForwardSettingsLayout - - settings_call_forward_activation_success - Transfert d'appel activé vers : %1 - - - - - + settings_call_forward_to_voicemail Boîte vocale - - settings_call_forward_deactivation_success - Transfert d'appel désactivé - - - - settings_call_forward_address_timeout - Impossible d'établir le transfert d'appel, la requête a expiré - - - + settings_call_forward_address_cannot_be_empty Une adresse ou un numéro de téléphone est nécessaire - - settings_call_forward_address_progress_disabling - Désactiver le transfert d'appel - - - - settings_call_forward_address_progress_enabling - Activer le transfert d'appel pour : - - - + settings_call_forward_activate_title "Forward calls" Transférer les appels - + settings_call_forward_activate_subtitle "Enable call forwarding to voicemail or sip address" Transférer les appels vers une boîte vocale ou un numéro / une adresse SIP - + settings_call_forward_destination_choose Forward to destination Transférer les appels vers : - + settings_call_forward_to_sipaddress Adresse SIP - + settings_call_forward_sipaddress_title SIP Address Addresse SIP - + settings_call_forward_sipaddress_placeholder John.doe @@ -1011,7 +988,7 @@ CallHistoryListView - + call_name_accessible_button Call %1 Appeler %1 @@ -1140,69 +1117,69 @@ CallModel - + call_error_no_response_toast "No response" Pas de réponse - + call_error_forbidden_resource_toast "403 : Forbidden resource" 403 : Forbidden resource - + call_error_not_answered_toast "Request timeout" La requête a expiré - + call_error_user_declined_toast "User declined the call" Le correspondant a décliné l'appel - + call_error_user_not_found_toast "User was not found" Le correspondant n'a pas été trouvé - + call_error_user_busy_toast "User is busy" Le correspondant est occupé - + call_error_incompatible_media_params_toast "User can&apos;t accept your call" Le correspondant ne peut accepter votre appel - + call_error_io_error_toast "Unavailable service or network error" Service indisponible ou erreur réseau - + call_error_do_not_disturb_toast "Le correspondant ne peut être dérangé" Le correspondant ne peut être dérangé - + call_error_temporarily_unavailable_toast "Temporarily unavailable" Temporairement indisponible - + call_error_server_timeout_toast - "Server tiemout" + "Server timeout" Délai d'attente du serveur dépassé @@ -1221,180 +1198,180 @@ Historique d'appel vide - + history_dialog_delete_all_call_logs_title Supprimer l'historique d'appels ? Supprimer l'historique d'appels ? - + history_dialog_delete_all_call_logs_message "L'ensemble de votre historique d'appels sera définitivement supprimé." L'ensemble de votre historique d'appels sera définitivement supprimé. - + history_dialog_delete_call_logs_title Supprimer l'historique d'appels ? Supprimer l'historique d'appels ? - + history_dialog_delete_call_logs_message "L'ensemble de votre historique d'appels avec ce correspondant sera définitivement supprimé." L'ensemble de votre historique d'appels avec ce correspondant sera définitivement supprimé. - + call_history_call_list_title "Appels" Appels - + call_history_options_accessible_name Options de l' - - + + menu_delete_history "Supprimer l'historique" Supprimer l'historique - + call_history_list_options_accessible_name Call history options Options de la liste de l'historique d'appel - + create_new_call_accessible_name Create new call Créer un nouvel appel - + call_search_in_history "Rechercher un appel" Rechercher un appel - + call_forward_to_address_info Transférer l'appel à : - + call_forward_to_address_info_voicemail Boîte vocale - + list_filter_no_result_found "Aucun résultat…" Aucun résultat… - + history_list_empty_history "Aucun appel dans votre historique" Aucun appel dans votre historique - + return_to_call_history_accessible_name Return to call history Retourner à l'historique d'appels - + call_action_start_new_call "Nouvel appel" Nouvel appel - + call_start_group_call_title "Appel de groupe" Appel de groupe - + call_action_start_group_call "Lancer" Lancer - - - + + + information_popup_error_title Erreur - + group_call_error_must_have_name "Un nom doit être donné à l'appel de groupe Un nom doit être donné à l'appel de groupe - + group_call_error_not_connected "Vous n'etes pas connecté" Vous n'etes pas connecté - + menu_see_existing_contact "Show contact" Voir le contact - + menu_add_address_to_contacts "Add to contacts" Ajouter aux contacts - + menu_copy_sip_address "Copier l'adresse SIP" Copier l'adresse SIP - + sip_address_copied_to_clipboard_toast Adresse copiée Adresse copiée - + sip_address_copied_to_clipboard_message L'adresse a été copié dans le presse_papiers L'adresse a été copié dans le presse-papiers - + sip_address_copy_to_clipboard_error "Erreur lors de la copie de l'adresse" Erreur lors de la copie de l'adresse - + notification_missed_call_title "Appel manqué" Appel manqué - + call_outgoing "Appel sortant" Appel sortant - + call_audio_incoming "Appel entrant" Appel entrant @@ -1507,236 +1484,236 @@ CallsWindow - + call_transfer_in_progress_toast "Transfert en cours, veuillez patienter" Transfert en cours, veuillez patienter - - + + information_popup_error_title Erreur - + call_transfer_failed_toast "Le transfert d'appel a échoué" Le transfert d'appel a échoué - + conference_error_empty_uri "La conférence n'a pas pu démarrer en raison d'une erreur d'uri." La conférence n'a pas pu démarrer en raison d'une erreur d'uri. - + call_close_window_dialog_title "Terminer tous les appels en cours ?" Terminer tous les appels en cours ? - + call_close_window_dialog_message "La fenêtre est sur le point d'être fermée. Cela terminera tous les appels en cours." La fenêtre est sur le point d'être fermée. Cela terminera tous les appels en cours. - + call_can_be_trusted_toast "Appareil authentifié" Appareil authentifié - + call_dir Appel %1 - + call_ended Appel terminé Appel terminé - + conference_paused Meeting paused Réunion mise en pause - + call_paused Call paused Appel mis en pause - - + + call_srtp_point_to_point_encrypted Appel chiffré de point à point Appel chiffré de point à point - + call_zrtp_sas_validation_required Vérification nécessaire Vérification nécessaire - + call_zrtp_end_to_end_encrypted Appel chiffré de bout en bout Appel chiffré de bout en bout - + call_not_encrypted "Appel non chiffré" Appel non chiffré - - + + call_waiting_for_encryption_info Waiting for encryption En attente de chiffrement - + call_paused_by_remote Call paused by remote Appel mis en pause par votre correspondant - + conference_user_is_recording "You are recording the meeting" Vous enregistrez la réunion - + call_user_is_recording "You are recording the call" Vous enregistrez l'appel - + conference_remote_is_recording "Someone is recording the meeting" Un participant enregistre la réunion - + call_remote_recording "%1 is recording the call" %1 enregistre l'appel - + call_stop_recording "Stop recording" Arrêter l'enregistrement - + add Ajouter - + call_transfer_current_call_title "Transférer %1 à…" Transférer %1 à… - - + + call_transfer_confirm_dialog_tittle "Confirmer le transfert" Confirmer le transfert - - + + call_transfer_confirm_dialog_message "Vous allez transférer %1 à %2." Vous allez transférer %1 à %2. - + call_action_start_new_call "Nouvel appel" Nouvel appel - - + + call_action_show_dialer "Pavé numérique" Pavé numérique - + call_action_change_layout "Modifier la disposition" Modifier la disposition - + call_action_go_to_calls_list "Liste d'appel" Liste d'appel - + Merger tous les appels call_action_merge_calls Merger tous les appels - - + + call_action_go_to_settings "Paramètres" Paramètres - + conference_action_screen_sharing "Partage de votre écran" Partage de votre écran - + conference_share_link_title Partager le lien de la réunion Partager le lien de la réunion - + copied Copié Copié - + information_popup_meeting_address_copied_to_clipboard Le lien de la réunion a été copié dans le presse-papier Le lien de la réunion a été copié dans le presse-papier - - - + + + conference_participants_list_title "Participants (%1)" Participants (%1) - - + + group_call_participant_selected %1 participant sélectionné @@ -1744,194 +1721,194 @@ - + meeting_schedule_add_participants_title Ajouter des participants - + call_encryption_title Chiffrement Chiffrement - + open_statistic_panel_accessible_name Ouvrir le panneau de statistiques - + conference_user_is_sharing_screen "You are sharing your screen" Vous partagez votre écran - + call_stop_screen_sharing "Stop sharing" Arrêter le partage - + stop_recording_accessible_name Stop recording Arrêter l'enregistrement - + stop_screen_sharing_accessible_name "Stop screen sharing" Arrêter le partage d'écran - + call_stats_title Statistiques Statistiques - - + + call_action_end_call "Terminer l'appel" Terminer l'appel - - + + call_action_resume_call "Reprendre l'appel" Reprendre l'appel - - + + call_action_pause_call "Mettre l'appel en pause" Mettre l'appel en pause - - + + call_action_transfer_call "Transférer l'appel" Transférer l'appel - - + + call_action_start_new_call_hint "Initier un nouvel appel" Initier un nouvel appel - - + + call_display_call_list_hint "Afficher la liste d'appels" Afficher la liste d'appels - - + + call_deactivate_video_hint "Désactiver la vidéo" "Activer la vidéo" Désactiver la vidéo - - + + call_activate_video_hint Activer la vidéo - - + + call_activate_microphone "Activer le micro" Activer le micro - - + + call_deactivate_microphone "Désactiver le micro" Désactiver le micro - - + + call_share_screen_hint Partager l'écran… Partager l'écran… - - + + call_open_chat_hint Open chat… Ouvrir le chat… - - + + call_rise_hand_hint "Lever la main" Lever la main - - + + call_send_reaction_hint "Envoyer une réaction" Envoyer une réaction - - + + call_manage_participants_hint "Gérer les participants" Gérer les participants - - + + call_more_options_hint "Plus d'options…" Plus d'options… - + call_action_change_conference_layout "Modifier la disposition" Modifier la disposition - + call_action_full_screen "Mode Plein écran" Mode Plein écran - + call_action_stop_recording "Terminer l'enregistrement" Terminer l'enregistrement - + call_action_record "Enregistrer l'appel" Enregistrer l'appel - + call_activate_speaker_hint "Activer le son" Activer le son - + call_deactivate_speaker_hint "Désactiver le son" Désactiver le son @@ -1963,63 +1940,63 @@ Vérifiez que toutes les informations ont été saisies. - + information_popup_synchronization_success_title Succès - + settings_contacts_carddav_synchronization_success_message "Le carnet d'adresse CardDAV est synchronisé." Le carnet d'adresse CardDAV est synchronisé. - + settings_contacts_carddav_popup_synchronization_error_title Erreur - + settings_contacts_carddav_popup_synchronization_error_message "Erreur de synchronisation : %1" Erreur de synchronisation : %1 - + settings_contacts_delete_carddav_server_title "Supprimer le carnet d'adresse CardDAV ?" Supprimer le carnet d'adresse CardDAV ? - + sip_address_display_name Nom d'affichage Nom d'affichage - + settings_contacts_carddav_server_url_title "URL du serveur" URL du serveur - + username Nom d'utilisateur - + password Mot de passe - + settings_contacts_carddav_realm_title Domaine d’authentification Domaine d’authentification - + settings_contacts_carddav_use_as_default_title "Stocker ici les contacts nouvellement crées" Stocker ici les contacts nouvellement crées @@ -2062,13 +2039,13 @@ ChatCore - + info_toast_deleted_title Deleted Supprimé - + info_toast_deleted_message_history Message history has been deleted L'historique des messages a été supprimé @@ -2077,13 +2054,13 @@ ChatDroppableTextArea - + chat_view_send_area_placeholder_text Say something… : placeholder text for sending message text area Dites quelque chose… - + cannot_record_while_in_call_tooltip Cannot record a message while a call is ongoing Impossible d'enregistrer un message vocal pendant un appel @@ -2092,65 +2069,65 @@ ChatListView - + chat_message_is_writing_info %1 is writing… %1 est en train d'écrire… - + chat_message_draft_sending_text Brouillon : %1 - + chat_room_delete "Delete" Supprimer - + chat_room_mute Mettre en sourdine - + chat_room_unmute "Mute" Enlever la sourdine - + chat_room_mark_as_read "Mark as read" Marquer comme lu - + chat_room_leave "leave" Quitter la conversation - + chat_list_leave_chat_popup_title leave the conversation ? Quitter la conversation ? - + chat_list_leave_chat_popup_message You will not be able to send or receive messages in this conversation anymore. Do You want to continue ? Vous ne pourrez plus envoyer ou recevoir de messages dans cette conversation. Souhaitez-vous continuer ? - + chat_list_delete_chat_popup_title Delete the conversation ? Supprimer la conversation ? - + chat_list_delete_chat_popup_message This conversation and all its messages will be deleted. Do You want to continue ? La conversation et tous ses messages seront supprimés. Souhaitez-vous continuer ? @@ -2159,25 +2136,25 @@ ChatMessage - + chat_message_copy_selection "Copy selection" Copier la sélection - + chat_message_copy "Copy" Copier - + chat_message_copied_to_clipboard_title Copied Copié - + chat_message_copied_to_clipboard_toast "to clipboard" dans le presse-papiers @@ -2213,7 +2190,13 @@ Vous avez répondu - + + chat_message_send_again + "Re-send" + Ré-envoyer + + + chat_message_reception_info "Reception info" Info de réception @@ -2225,19 +2208,19 @@ Modifier - + chat_message_reply Reply Répondre - + chat_message_forward Forward Transférer - + chat_message_delete "Delete" Supprimer @@ -2252,24 +2235,24 @@ ChatMessageContentCore - + download_file_default_error Error downloading file %1 Erreur de téléchargement du fichier %1 - + info_popup_error_titile Erreur - + popup_error_title Error Erreur - + popup_open_file_error_does_not_exist_message Could not open file : unknown path %1 Impossible d'ouvrir le fichier : chemin inconnu (%1) @@ -2329,37 +2312,37 @@ Error ChatMessageContentModel - + download_error_object_doesnt_exist - Internal error : message object does not exist anymore ! - Erreur interne : l'objet ChatMessage n'existe plus ! + Internal error : message object associated to this content does not exist anymore ! + Erreur interne : le message associé à ce contenu n'existe plus ! - + download_file_server_error Error while trying to download content : %1 Erreur en tentant de télécharger le contenu : %1 - + download_file_error_no_safe_file_path Unable to create safe file path for: %1 Impossible de créer le chemin : %1 - + download_file_error_file_transfer_unavailable This file was already downloaded and is no more on the server. Your peer have to resend it if you want to get it Le fichier a déjà été téléchargé et n'est plus disponible sur le serveur. Votre correspondant devra vous le renvoyez si vous souhaitez l'obtenir - + download_file_error_null_name Content name is null, can't download it ! Le nom du contenu est nul, impossible de le télécharger ! - + download_file_error_unable_to_download Unable to download file of entry %1 Impossible de télécharger le fichier : %1 @@ -2440,44 +2423,44 @@ Error ChatMessagesListView - - + + popup_info_find_message_title Find message Trouver un message - + info_popup_no_result_message No result found Aucun résultat trouvé - + info_popup_first_result_message First result reached Premier résultat atteint - + info_popup_last_result_message Last result reached Dernier résultat atteint - + chat_message_list_encrypted_header_title End to end encrypted chat Conversation chiffrée de bout en bout - + unencrypted_conversation_warning This conversation is not encrypted ! Cette conversation n'est pas chiffrée ! - + chat_message_list_encrypted_header_message Messages in this conversation are e2e encrypted. Only your correspondent can decrypt them. @@ -2485,7 +2468,7 @@ Error en bout. Seul votre correspondant peut les déchiffrer. - + chat_message_list_not_encrypted_header_message Messages are not end to end encrypted, may sure you don't share any sensitive information ! @@ -2493,7 +2476,7 @@ en bout. Seul votre correspondant peut les déchiffrer. assurez-vous de ne pas partager d’informations sensibles ! - + chat_message_is_writing_info %1 is writing… %1 est en train d'écrire… @@ -2550,115 +2533,115 @@ en bout. Seul votre correspondant peut les déchiffrer. Aucune conversation - + info_popup_error_title Erreur - + info_popup_chatroom_creation_failed Chat room creation failed ! La création de la conversation a échoué ! - + loading_popup_chatroom_creation_pending_message Chat room is being created... Création de la conversation en cours... - + chat_dialog_delete_chat_title Supprimer la conversation ? Supprimer la conversation ? - + chat_dialog_delete_chat_message "La conversation et tous ses messages seront supprimés." La conversation et tous ses messages seront supprimés. - + chat_list_title "Conversations" Conversations - + menu_mark_all_as_read "mark all as read" Tout marquer comme lu - + chat_search_in_history "Rechercher une conversation" Rechercher une conversation - + list_filter_no_result_found "Aucun résultat…" Aucun résultat… - + chat_list_empty_history "Aucune conversation dans votre historique" Aucune conversation dans votre historique - + chat_action_start_new_chat "New chat" Nouvelle conversation - + chat_start_group_chat_title "Nouveau groupe" Nouveau groupe - + chat_action_start_group_chat "Créer" Créer - - - + + + information_popup_error_title Erreur - + information_popup_chat_creation_failed_message "La création a échoué" La création a échoué - + group_chat_error_must_have_name "Un nom doit être donné au groupe Un nom doit être donné au groupe - + group_chat_error_no_participant "Please select at least one participant Veuillez sélectionner au moins un participant - + group_call_error_not_connected "Vous n'etes pas connecté" Vous n'êtes pas connecté - + chat_creation_in_progress Creation de la conversation en cours … Création de la conversation en cours… @@ -2721,13 +2704,13 @@ en bout. Seul votre correspondant peut les déchiffrer. ConferenceInfoCore - + information_popup_error_title "Erreur" Erreur - + information_popup_disconnected_account_message "Votre compte est déconnecté" Votre compte est déconnecté @@ -2736,19 +2719,19 @@ en bout. Seul votre correspondant peut les déchiffrer. Contact - + information_popup_error_title Erreur Erreur - + information_popup_voicemail_address_undefined_message L'URI de messagerie vocale n'est pas définie. L'URI de messagerie vocale n'est pas définie. - + account_settings_name_accessible_name Account settings of %1 Paramaètres de compte de %1 @@ -2757,145 +2740,145 @@ en bout. Seul votre correspondant peut les déchiffrer. ContactEdition - + contact_editor_title "Modifier contact" Modifier contact - + save "Enregistrer Enregistrer - - + + contact_editor_dialog_cancel_change_message "Les changements seront annulés. Souhaitez-vous continuer ?" Les changements seront annulés. Souhaitez-vous continuer ? - + close_accessible_name Close %1 Fermer %1 - + contact_editor_mandatory_first_name_or_company_not_filled "Veuillez saisir un prénom ou un nom d'entreprise" Veuillez saisir un prénom ou un nom d'entreprise - + contact_editor_mandatory_address_or_number_not_filled "Veuillez saisir une adresse ou un numéro de téléphone" Veuillez saisir une adresse ou un numéro de téléphone - + contact_editor_add_image_label "Ajouter une image" Ajouter une image - + contact_details_edit "Modifier" Modifier - + edit_contact_image_accessible_name "Edit contact image" - + contact_details_delete "Supprimer" Supprimer - + delete_contact_image_accessible_name "Delete contact image" Supprimer l'image du contact - - + + contact_editor_first_name "Prénom" Prénom - - + + contact_editor_last_name "Nom" Nom - - + + contact_editor_company "Entreprise" Entreprise - - + + contact_editor_job_title "Fonction" Fonction - - + + sip_address Adresse SIP - + sip_address_number_accessible_name "SIP address number %1" Adresse SIP numéro %1 - + remove_sip_address_accessible_name "Remove SIP address %1" Retirer l'adresse SIP %1 - + new_sip_address_accessible_name "New SIP address" Nouvelle adresse SIP - + phone_number_number_accessible_name "Phone number number %1" Numéro de téléphone numéro - + remove_phone_number_accessible_name Remove phone number %1 Retirer le numéro de téléphone %1 - + new_phone_number_accessible_name "New phone number" Nouveau numéro de téléphone - - + + phone "Téléphone" Téléphone @@ -2986,149 +2969,149 @@ en bout. Seul votre correspondant peut les déchiffrer. Aucun contact pour le moment - + contact_new_title "Nouveau contact" Nouveau contact - + create Créer - + contact_edit_title "Modifier contact" Modifier contact - + save Enregistrer - + contact_dialog_delete_title Supprimer %1 ?" Supprimer %1 ? - + contact_dialog_delete_message Ce contact sera définitivement supprimé. Ce contact sera définitivement supprimé. - + contact_deleted_toast "Contact supprimé" Contact supprimé - + contact_deleted_message "%1 a été supprimé" %1 a été supprimé - + contact_dialog_devices_trust_popup_title "Augmenter la confiance" Augmenter la confiance - + contact_dialog_devices_trust_popup_message "Pour augmenter le niveau de confiance vous devez appeler les différents appareils de votre contact et valider un code.<br><br>Vous êtes sur le point d’appeler “%1” voulez vous continuer ?" Pour augmenter le niveau de confiance vous devez appeler les différents appareils de votre contact et valider un code.<br><br>Vous êtes sur le point d’appeler “%1” voulez vous continuer ? - + popup_do_not_show_again Ne plus afficher Ne plus afficher - + cancel Annuler - + dialog_call "Appeler" Appeler - + contact_dialog_devices_trust_help_title "Niveau de confiance" Niveau de confiance - + contact_dialog_devices_trust_help_message "Vérifiez les appareils de votre contact pour confirmer que vos communications seront sécurisées et sans compromission. <br>Quand tous seront vérifiés, vous atteindrez le niveau de confiance maximal." Vérifiez les appareils de votre contact pour confirmer que vos communications seront sécurisées et sans compromission. <br>Quand tous seront vérifiés, vous atteindrez le niveau de confiance maximal. - + dialog_ok "Ok" Ok - + bottom_navigation_contacts_label "Contacts" Contacts - + search_bar_look_for_contact_text Rechercher un contact Rechercher un contact - + list_filter_no_result_found Aucun résultat… Aucun résultat… - + contact_list_empty Aucun contact pour le moment Aucun contact pour le moment - + expand_accessible_name Expand %1 Étendre %1 - + shrink_accessible_name Shrink %1 Réduire %1 - + create_contact_accessible_name Create new contact Créer un nouveau contact - + more_info_accessible_name More info %1 Plus d'information %1 - - + + contact_details_edit Edit ---------- @@ -3136,151 +3119,151 @@ en bout. Seul votre correspondant peut les déchiffrer. Éditer - + contact_call_action "Appel" Appel - + contact_message_action "Message" Message - + contact_video_call_action "Appel vidéo" Appel vidéo - + contact_details_numbers_and_addresses_title "Coordonnées" Coordonnées - + call_adress_accessible_name Call address %1 Appeller l'adresse %1 - + contact_details_company_name "Société :" Société : - + contact_details_job_title "Poste :" Poste : - + contact_details_medias_title "Medias" Medias - - + + contact_details_medias_subtitle "Afficher les medias partagés" Afficher les medias partagés - + contact_details_trust_title "Confiance" Confiance - + contact_dialog_devices_trust_title "Niveau de confiance - Appareils vérifiés" Niveau de confiance - Appareils vérifiés - + contact_details_no_device_found "Aucun appareil" Aucun appareil - + contact_device_without_name "Appareil inconnu" Appareil inconnu - + contact_make_call_check_device_trust "Vérifier" Vérifier - + verify_device_accessible_name Verify %1 device Vérifier l'appareil %1 - + contact_details_actions_title "Autres actions" Autres actions - + contact_details_remove_from_favourites "Retirer des favoris" Retirer des favoris - + contact_details_add_to_favourites "Ajouter aux favoris" Ajouter aux favoris - + contact_details_share "Partager" Partager - + information_popup_error_title Erreur - + contact_details_share_error_mesage "La création du fichier vcard a échoué" La création du fichier vcard a échoué - + contact_details_share_success_title "VCard créée" VCard créée - + contact_details_share_success_mesage "VCard du contact enregistrée dans %1" VCard du contact enregistrée dans %1 - + contact_details_share_email_title "Partage de contact" Partage de contact - + contact_details_delete "Supprimer ce contact" Supprimer ce contact @@ -3362,18 +3345,18 @@ en bout. Seul votre correspondant peut les déchiffrer. ContactsSettingsProviderLayout - + information_popup_success_title Succès - + information_popup_changes_saved "Les changements ont été sauvegardés" Les changements ont été sauvegardés - + add "Ajouter" Ajouter @@ -3382,138 +3365,138 @@ en bout. Seul votre correspondant peut les déchiffrer. ConversationInfos - + one_one_infos_call "Appel" Appel - + one_one_infos_unmute "Sourdine" Réactiver les notifications - + one_one_infos_mute Sourdine - + group_infos_participants Participants (%1) - + group_infos_media_docs Medias & documents Medias & documents - + group_infos_shared_medias Shared medias Médias partagés - + group_infos_shared_docs Shared documents Documents partagés - + group_infos_other_actions Other actions Autres actions - + group_infos_ephemerals Messages éphémères : - + group_infos_enable_ephemerals Activer les messages éphémères - + group_infos_meeting Schedule a meeting Programmer une réunion - + group_infos_leave_room Leave chat room Quitter la conversation - + group_infos_leave_room_toast_title Leave Chat Room ? Quitter la conversation ? - + group_infos_leave_room_toast_message All the messages will be removed from the chat room. Do you want to continue ? Vous ne recevrez ni pourrez envoyer des messages dans cette conversation, quitter ? - + group_infos_delete_history Delete history Supprimer l'historique - + group_infos_delete_history_toast_title Delete history ? Supprimer l'historique ? - + group_infos_delete_history_toast_message All the messages will be removed from the chat room. Do you want to continue ? Tous les messages seront supprimés. Souhaitez-vous continuer ? - + one_one_infos_open_contact Show contact Voir le contact - + one_one_infos_create_contact Create contact Créer un contact - + one_one_infos_ephemerals Messages éphémères : - + one_one_infos_enable_ephemerals Activer les messages éphémères - + one_one_infos_delete_history Supprimer l'historique - + one_one_infos_delete_history_toast_title Delete history ? Supprimer l'historique ? - + one_one_infos_delete_history_toast_message All the messages will be removed from the chat room. Do you want to continue ? Tous les messages seront supprimés. Souhaitez-vous continuer ? @@ -3522,12 +3505,12 @@ en bout. Seul votre correspondant peut les déchiffrer. CoreModel - + info_popup_error_title Erreur - + fetching_config_failed_error_message "Remote provisioning cannot be retrieved" La configuration distante n'a pas pu être récupérée @@ -3864,55 +3847,55 @@ Expiration : %1 GroupChatInfoParticipants - + group_infos_manage_participants_title "Gérer des participants" Gérer les participants - + group_infos_participant_is_admin Admin - + menu_see_existing_contact "Show contact" Voir le contact - + menu_add_address_to_contacts "Add to contacts" Ajouter aux contacts - + group_infos_give_admin_rights Donner les droits admins - + group_infos_remove_admin_rights Retirer les droits admins - + group_infos_copy_sip_address Copier l’adresse SIP - + group_infos_remove_participant Retirer le participant - + group_infos_remove_participants_toast_title Retirer le participant ? - + group_infos_remove_participants_toast_message La participant sere retiré de la conversation @@ -3927,7 +3910,7 @@ Expiration : %1 - + group_start_dialog_subject_hint "Nom du groupe" Nom du groupe @@ -4362,96 +4345,96 @@ Expiration : %1 Ouvrir la page des réunions - + searchbar_placeholder_text "Rechercher un contact, appeler %1" Rechercher un contact, appeler %1 - + searchbar_placeholder_text_chat_feature_enabled "ou envoyer un message …" ou envoyer un message … - + do_not_disturb_accessible_name "Do not disturb" Ne pas déranger - - + + contact_presence_status_disable_do_not_disturb "Désactiver ne pas déranger" Désactiver ne pas déranger - + information_popup_error_title Erreur - + no_voicemail_uri_error_message "L'URI de messagerie vocale n'est pas définie." L'URI de messagerie vocale n'est pas définie. - + account_list_accessible_name "Account list" liste des comptes - + application_options_accessible_name "Application options" Options de l'application - + drawer_menu_manage_account Mon compte Mon compte - + contact_presence_status_enable_do_not_disturb "Activer ne pas déranger" Activer ne pas déranger - + settings_title Paramètres - + recordings_title "Enregistrements" Enregistrements - + help_title "Aide" Aide - + help_quit_title "Quitter l'application" Quitter l'application - + quit_app_question "Quitter %1 ?" Quitter %1 ? - + drawer_menu_add_account "Ajouter un compte" Ajouter un compte @@ -4490,56 +4473,63 @@ Expiration : %1 Votre correspondant a été transféré au contact sélectionné - + information_popup_success_title Enregistré - + information_popup_changes_saved "Les changements ont été sauvegardés" Les changements ont été sauvegardés - + + oidc_connection_waiting_message + "Trying to connect to single sign on on web page ..." + Tentative de connexion distante ... + + + + cancel + Cancel + Annuler + + + captcha_validation_loading_message "Veuillez valider le captcha sur la page web" Veuillez valider le captcha sur la page web - + assistant_register_error_title "Erreur lors de la création" Erreur lors de la création - + assistant_register_success_title "Compte créé" Compte créé - + assistant_register_success_message "Le compte a été créé. Vous pouvez maintenant vous connecter" Le compte a été créé. Vous pouvez maintenant vous connecter - + assistant_register_error_code "Erreur dans le code de validation" Erreur dans le code de validation - - - information_popup_error_title - Erreur - ManageParticipants - + group_infos_manage_participants Participants @@ -4586,19 +4576,19 @@ Expiration : %1 MeetingListView - + meeting_info_cancelled "Réunion annulée" Réunion annulée - + meetings_list_no_meeting_for_today "Aucune réunion aujourd'hui" Aucune réunion aujourd'hui - + meeting_info_delete "Supprimer la réunion" Supprimer la réunion @@ -4619,163 +4609,163 @@ Expiration : %1 Aucune réunion - + meeting_schedule_cancel_dialog_message "Souhaitez-vous annuler et supprimer cette réunion ?" Souhaitez-vous annuler et supprimer cette réunion ? - + meeting_schedule_delete_dialog_message Souhaitez-vous supprimer cette réunion ? Souhaitez-vous supprimer cette réunion ? - + meeting_schedule_cancel_and_delete_action "Annuler et supprimer" Annuler et supprimer - + meeting_schedule_delete_only_action "Supprimer seulement" Supprimer seulement - + meeting_schedule_delete_action "Supprimer" Supprimer - + back_action Retour Retour - + meetings_list_title Réunions Réunions - + meetings_search_hint "Rechercher une réunion" Rechercher une réunion - + list_filter_no_result_found "Aucun résultat…" Aucun résultat… - + meetings_empty_list "Aucune réunion" Aucune réunion - - + + meeting_schedule_title "Nouvelle réunion" Nouvelle réunion - + create Créer - - - - + + + - + + information_popup_error_title Erreur - - + + meeting_schedule_mandatory_field_not_filled_toast Veuillez saisir un titre et sélectionner au moins un participant Veuillez saisir un titre et sélectionner au moins un participant - - + + meeting_schedule_duration_error_toast "La fin de la conférence doit être plus récente que son début" La fin de la conférence doit être plus récente que son début - - + + meeting_schedule_creation_in_progress "Création de la réunion en cours …" Création de la réunion en cours… - + meeting_info_created_toast "Réunion planifiée avec succès" Réunion planifiée avec succès - + meeting_failed_to_schedule_toast "Échec de création de la réunion !" Échec de création de la réunion ! - + save Enregistrer - - + + saved "Enregistré" Enregistré - + meeting_info_updated_toast "Réunion mise à jour" Réunion mise à jour - + meeting_schedule_edit_in_progress "Modification de la réunion en cours…" Modification de la réunion en cours… - + meeting_failed_to_edit_toast "Échec de la modification de la réunion !" Échec de la modification de la réunion ! - + meeting_schedule_add_participants_title "Ajouter des participants" Ajouter des participants - + meeting_schedule_add_participants_apply Appliquer - + group_call_participant_selected "%n participant(s) sélectionné(s)" @@ -4784,31 +4774,31 @@ Expiration : %1 - + meeting_info_delete "Supprimer la réunion" Supprimer la réunion - + meeting_address_copied_to_clipboard_toast "Adresse de la réunion copiée" Adresse de la réunion copiée - + meeting_schedule_timezone_title "Fuseau horaire" Fuseau horaire - + meeting_info_organizer_label "Organisateur" Organisateur - + meeting_info_join_title "Rejoindre la réunion" Rejoindre la réunion @@ -4862,13 +4852,13 @@ Expiration : %1 MessageSharedFilesInfos - + no_shared_medias No media Aucun média - + no_shared_documents No document Aucun document @@ -4994,7 +4984,7 @@ Expiration : %1 new_voice_message - 'Voice message received!' : message to warn the user in a notofication for voice messages. + 'Voice message received!' : message to warn the user in a notification for voice messages. Message vocal reçu ! @@ -5029,108 +5019,109 @@ Expiration : %1 OAuthHttpServerReplyHandler n'est pas disponible - + + oidc_authentication_timeout_message Timeout: Not authenticated Timeout : non authentifié - + oidc_authentication_granted_message Authentication granted Authentification accordée - + oidc_authentication_not_authenticated_message Not authenticated Non authentifié - + oidc_authentication_refresh_message Refreshing token Token en cours de rafraîchissement - + oidc_authentication_temporary_credentials_message Temporary credentials received Identifiants temporaires reçus - + oidc_authentication_network_error Network error Erreur réseau - + oidc_authentication_server_error Server error Erreur de serveur - + oidc_authentication_token_not_found_error OAuth token not found Token OAuth non trouvé - + oidc_authentication_token_secret_not_found_error OAuth token secret not found Token OAuth secret non trouvé - + oidc_authentication_callback_not_verified_error OAuth callback not verified Retour OAuth non vérifié - + oidc_authentication_request_auth_message Requesting authorization from browser En attente d'autorisation du navigateur - + oidc_authentication_no_token_found_error Token non trouvé - + oidc_authentication_request_token_message Requesting access token En attente du token d'accès - + oidc_authentication_refresh_token_message Refreshing access token Token en cours de rafraîchissement - + oidc_authentication_request_authorization_message Requesting authorization Autorisation en cours - + oidc_authentication_request_temporary_credentials_message Requesting temporary credentials En attente d'identifiants temporaires - + oidc_authentication_no_auth_found_in_config_error No authorization endpoint found in OpenID configuration Pas d'autorisation trouvé dans la configuration OpenID - + oidc_authentication_no_token_found_in_config_error No token endpoint found in OpenID configuration Pas de token trouvé dans la configuration OpenID @@ -5154,13 +5145,13 @@ Expiration : %1 PhoneNumberInput - + prefix_phone_number_accessible_name %1 prefix %1 préfix - + number_phone_number_accessible_name %1 number %1 indicatif téléphonique @@ -5353,37 +5344,37 @@ Expiration : %1 RegisterCheckingPage - + email "email" email - + phone_number "numéro de téléphone" numéro de téléphone - + confirm_register_title "Inscription | Confirmer votre %1" Inscription | Confirmer votre %1 - + assistant_account_creation_confirmation_explanation Nous vous avons envoyé un code de vérification sur votre %1 %2<br> Merci de le saisir ci-dessous Nous vous avons envoyé un code de vérification sur votre %1 %2<br> Merci de le saisir ci-dessous - + assistant_account_creation_confirmation_did_not_receive_code "Vous n'avez pas reçu le code ?" Vous n'avez pas reçu le code ? - + assistant_account_creation_confirmation_resend_code "Renvoyer un code" Renvoyer un code @@ -5424,49 +5415,49 @@ Expiration : %1 S'inscrire avec un email - - + + username Nom d'utilisateur - - - - - + + + + + mandatory_field_accessible_name "%1 mandatory" %1 requit - + domain Domaine - - - + + + phone_number "Numéro de téléphone" Numéro de téléphone - - + + email Email - - + + password Mot de passe - - + + assistant_account_register_password_confirmation "Confirmation mot de passe" Confirmation mot de passe @@ -5490,37 +5481,37 @@ Expiration : %1 politique de confidentialité - + assistant_account_create "Créer" Créer - + assistant_account_create_missing_username_error "Veuillez entrer un nom d'utilisateur" Veuillez entrer un nom d'utilisateur - + assistant_account_create_missing_password_error "Veuillez entrer un mot de passe" Veuillez entrer un mot de passe - + assistant_account_create_confirm_password_error "Les mots de passe sont différents" Les mots de passe sont différents - + assistant_account_create_missing_number_error "Veuillez entrer un numéro de téléphone" Veuillez entrer un numéro de téléphone - + assistant_account_create_missing_email_error "Veuillez entrer un email" Veuillez entrer un email @@ -5716,13 +5707,13 @@ Pour les activer dans un projet commercial, merci de nous contacter. SearchBar - + open_dialer_acccessibility_label "Open dialer" Ouvrir le numéroteur - + clear_text_input_acccessibility_label "Clear text input" Supprimer le texte saisi @@ -5791,18 +5782,18 @@ Pour les activer dans un projet commercial, merci de nous contacter. SelectedChatView - + chat_view_group_call_toast_message Démarrer un appel de groupe ? - + unencrypted_conversation_warning This conversation is not encrypted ! Cette conversation n'est pas chiffrée ! - + reply_to_label Reply to %1 Réponse à %1 @@ -5814,25 +5805,25 @@ Pour les activer dans un projet commercial, merci de nous contacter.Modification du message - + shared_medias_title Shared medias Médias partagés - + shared_documents_title Shared documents Documents partagés - + forward_to_title Forward to… Transférer à… - + conversations_title Conversations Conversations @@ -5955,49 +5946,49 @@ Pour les activer dans un projet commercial, merci de nous contacter. ToolModel - + call_error_uninterpretable_sip_address "The calling address is not an interpretable SIP address : %1 L'adresse n'est pas interprétable comme une adresse SIP - + group_call_error_no_account Impossible de créer l'appel de groupe, le compte par défaut n'est pas défini - + group_call_error_participants_invite Impossible d'inviter les participants à l'appel de groupe - + group_call_error_creation L'appel de groupe n'a pas pu être créé - + voice_recording_duration "Voice recording (%1)" : %1 is the duration formated in mm:ss Message vocal (%1) - + unknown_audio_device_name Appareil inconnu - + conference_invitation Invitation à une réunion - + conference_invitation_cancelled Annulation d'une réunion - + conference_invitation_updated Modification d'une réunion @@ -6017,7 +6008,7 @@ Pour les activer dans un projet commercial, merci de nous contacter. Utils - + nMinute %1 minute @@ -6025,7 +6016,7 @@ Pour les activer dans un projet commercial, merci de nous contacter. - + nHour %1 heure @@ -6033,8 +6024,8 @@ Pour les activer dans un projet commercial, merci de nous contacter. - - + + nDay %1 jour @@ -6042,7 +6033,7 @@ Pour les activer dans un projet commercial, merci de nous contacter. - + nWeek %1 semaine @@ -6050,7 +6041,7 @@ Pour les activer dans un projet commercial, merci de nous contacter. - + nSeconds %1 seconde @@ -6058,27 +6049,27 @@ Pour les activer dans un projet commercial, merci de nous contacter. - + contact_presence_status_available Disponible - + contact_presence_status_busy Occupé - + contact_presence_status_do_not_disturb Ne pas déranger - + contact_presence_status_offline Hors ligne - + contact_presence_status_away Inactif/Absent @@ -6091,12 +6082,9 @@ Pour les activer dans un projet commercial, merci de nous contacter. - - + information_popup_error_title - Error ----------- -Failed to create 1-1 conversation with %1 ! + Failed to create 1-1 conversation with %1 ! Erreur @@ -6174,56 +6162,54 @@ Failed to create 1-1 conversation with %1 ! ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 - - + information_popup_chatroom_creation_error_message - Failed to create 1-1 conversation with %1 ! Erreur lors de la création de la conversation avec %1 - + recorder_error Error with the recorder Erreur avec l'enregistreur - - - + + + chat_error Erreur dans le chat - + chat_message_forward_error Cannot forward an invalid message Impossible de transférer : message invalide - - - - - - + + + + + + info_popup_error_title Error Erreur - + info_popup_forward_message_error Could not forward message : %1 Impossible de transférer le message : %1 - + info_popup_send_forward_message_error_message Failed to create forward message Impossible de créer le message - + chat_message_reply_error Cannot reply to invalid message Impossible de répondre : message invalide @@ -6235,7 +6221,7 @@ Failed to create 1-1 conversation with %1 ! Impossible de modifier le message : message invalide - + info_popup_reply_message_error Could not send reply message : %1 Impossible d'envoyer la réponse : %1 @@ -6247,7 +6233,7 @@ Failed to create 1-1 conversation with %1 ! Impossible d'envoyer le message modifié : %1 - + info_popup_send_reply_message_error_message Failed to create reply message Impossible de créer le message @@ -6259,13 +6245,13 @@ Failed to create 1-1 conversation with %1 ! Impossible de créer le message modifié - + info_popup_send_voice_message_error_message Could not send voice message : %1 Impossible d'envoyer le message vocal : %1 - + info_popup_send_voice_message_sending_error_message Failed to create message from record Impossible de créer le message vocal @@ -6274,32 +6260,32 @@ Failed to create 1-1 conversation with %1 ! WaitingRoom - + meeting_waiting_room_title Participer à : Participer à : - + meeting_waiting_room_join "Rejoindre" Rejoindre - - + + cancel Cancel Annuler - + meeting_waiting_room_joining_title "Connexion à la réunion" Connexion à la réunion - + meeting_waiting_room_joining_subtitle "Vous allez rejoindre la réunion dans quelques instants…" Vous allez rejoindre la réunion dans quelques instants… diff --git a/Linphone/data/languages/hu.ts b/Linphone/data/languages/hu.ts index 9233ba8af..2c03e71c9 100644 --- a/Linphone/data/languages/hu.ts +++ b/Linphone/data/languages/hu.ts @@ -361,23 +361,6 @@ settings_account_title - - - info_popup_invalid_registrar_uri_message - Registrar uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - - - - - info_popup_invalid_outbound_proxy_message - Outbound proxy uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - - - - - info_popup_error_title - - information_popup_success_title @@ -406,12 +389,6 @@ "URI de messagerie vocale" - - - account_settings_transport_title - "Transport" - - account_settings_registrar_uri_title @@ -891,31 +868,6 @@ settings_call_forward_address_cannot_be_empty - - - settings_call_forward_address_timeout - - - - - settings_call_forward_address_progress_disabling - - - - - settings_call_forward_address_progress_enabling - - - - - settings_call_forward_activation_success - - - - - settings_call_forward_deactivation_success - - CallHistoryLayout @@ -1138,7 +1090,7 @@ call_error_server_timeout_toast - "Server tiemout" + "Server timeout" @@ -4318,11 +4270,6 @@ Error "Erreur dans le code de validation" - - - information_popup_error_title - - ManageParticipants diff --git a/Linphone/data/languages/mk.ts b/Linphone/data/languages/mk.ts index 19c3d4189..0be387f15 100644 --- a/Linphone/data/languages/mk.ts +++ b/Linphone/data/languages/mk.ts @@ -361,23 +361,6 @@ settings_account_title - - - info_popup_invalid_registrar_uri_message - Registrar uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - - - - - info_popup_invalid_outbound_proxy_message - Outbound proxy uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - - - - - info_popup_error_title - - information_popup_success_title @@ -406,12 +389,6 @@ "URI de messagerie vocale" - - - account_settings_transport_title - "Transport" - - account_settings_registrar_uri_title @@ -891,31 +868,6 @@ settings_call_forward_address_cannot_be_empty - - - settings_call_forward_address_timeout - - - - - settings_call_forward_address_progress_disabling - - - - - settings_call_forward_address_progress_enabling - - - - - settings_call_forward_activation_success - - - - - settings_call_forward_deactivation_success - - CallHistoryLayout @@ -1138,7 +1090,7 @@ call_error_server_timeout_toast - "Server tiemout" + "Server timeout" @@ -4318,11 +4270,6 @@ Error "Erreur dans le code de validation" - - - information_popup_error_title - - ManageParticipants diff --git a/Linphone/data/languages/nl.ts b/Linphone/data/languages/nl.ts index 38c5c9c28..bf2f7644b 100644 --- a/Linphone/data/languages/nl.ts +++ b/Linphone/data/languages/nl.ts @@ -361,23 +361,6 @@ settings_account_title - - - info_popup_invalid_registrar_uri_message - Registrar uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - - - - - info_popup_invalid_outbound_proxy_message - Outbound proxy uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - - - - - info_popup_error_title - - information_popup_success_title @@ -406,12 +389,6 @@ "URI de messagerie vocale" - - - account_settings_transport_title - "Transport" - - account_settings_registrar_uri_title @@ -891,31 +868,6 @@ settings_call_forward_address_cannot_be_empty - - - settings_call_forward_address_timeout - - - - - settings_call_forward_address_progress_disabling - - - - - settings_call_forward_address_progress_enabling - - - - - settings_call_forward_activation_success - - - - - settings_call_forward_deactivation_success - - CallHistoryLayout @@ -1138,7 +1090,7 @@ call_error_server_timeout_toast - "Server tiemout" + "Server timeout" @@ -4318,11 +4270,6 @@ Error "Erreur dans le code de validation" - - - information_popup_error_title - - ManageParticipants diff --git a/Linphone/data/languages/pl.ts b/Linphone/data/languages/pl.ts new file mode 100644 index 000000000..24eee6f03 --- /dev/null +++ b/Linphone/data/languages/pl.ts @@ -0,0 +1,7571 @@ + + + + + AbstractSettingsLayout + + + return_accessible_name + Return + + + + + save + "Enregistrer" + Zapisz + + + + save_settings_accessible_name + Save %1 settings + + + + + AbstractWindow + + + contact_dialog_pick_phone_number_or_sip_address_title + "Choisissez un numéro ou adresse SIP" + Wybierz numer SIP lub adres + + + + fps_counter + %1 FPS + + + + AccountCore + + + drawer_menu_account_connection_status_connected + "Connecté" + Połączono + + + + drawer_menu_account_connection_status_refreshing + Odświeżanie… + + + + drawer_menu_account_connection_status_progress + Łączenie… + + + + drawer_menu_account_connection_status_failed + Błąd + + + + drawer_menu_account_connection_status_cleared + Wyłączone + + + + manage_account_status_connected_summary + "Vous êtes en ligne et joignable." + Jesteś online i dostępny. + + + + manage_account_status_failed_summary + "Erreur de connexion, vérifiez vos paramètres." + Błąd połączenia, sprawdź swoje ustawienia. + + + + manage_account_status_cleared_summary + "Compte désactivé, vous ne recevrez ni appel ni message." + Konto jest wyłączone, nie będziesz odbierać połączeń ani wiadomości. + + + + AccountDeviceList + + + manage_account_no_device_found_error_message + "Erreur lors de la récupération des appareils" + Błąd podczas pobierania urządzeń + + + + AccountListView + + + add_an_account + Add an account + Dodaj konto + + + + AccountManager + + + assistant_account_login_already_connected_error + "The account is already connected" + Konto jest już połączone. + + + + assistant_account_login_proxy_address_error + "Unable to create proxy address. Please check the domain name." + Nie można utworzyć adresu proxy. Sprawdź nazwę domeny. + + + + assistant_account_login_address_configuration_error + "Unable to configure address: `%1`." + Nie można skonfigurować adresu: `%1`. + + + + assistant_account_login_params_configuration_error + "Unable to configure account settings." + Nie można skonfigurować ustawień konta. + + + + assistant_account_login_forbidden_error + "Username and password do not match" + Nazwa użytkownika i hasło nie pasują do siebie + + + + assistant_account_login_error + "Error during connection, please verify your parameters" + Błąd podczas połączenia + + + + assistant_account_add_error + "Unable to add account." + Nie można dodać konta. + + + + AccountModel + + + set_mwi_server_address_failed_error_message + "Unable to set voicemail server address, failed creating address from %1" : %1 is address + + + + + set_server_address_failed_error_message + "Unable to set server address, failed creating address from %1" + + + + + set_outbound_proxy_uri_failed_error_message + Unable to set outbound proxy uri, failed creating address from %1 + + + + + set_conference_factory_address_failed_error_message + "Unable to set the conversation server address, failed creating address from %1" + + + + + set_audio_conference_factory_address_failed_error_message + "Unable to set the meeting server address, failed creating address from %1" + + + + + set_voicemail_address_failed_error_message + Unable to set voicemail address, failed creating address from %1 + + + + + AccountSettingsGeneralLayout + + + manage_account_details_title + "Détails" + Szczegóły + + + + manage_account_details_subtitle + Éditer les informations de votre compte. + Edytuj informacje o swoim koncie. + + + + manage_account_devices_title + "Vos appareils" + Twoje urządzenia + + + + manage_account_devices_subtitle + "La liste des appareils connectés à votre compte. Vous pouvez retirer les appareils que vous n’utilisez plus." + Lista urządzeń podłączonych do Twojego konta. Możesz usunąć urządzenia, których już nie używasz. + + + + manage_account_add_picture + "Ajouter une image" + Dodaj obraz + + + + manage_account_edit_picture + "Modifier l'image" + Edytuj obraz + + + + manage_account_remove_picture + "Supprimer l'image" + Usuń obraz + + + + sip_address + SIP address + Adres SIP + + + + copied + Copied + Skopiowano + + + + account_settings_sip_address_copied_message + Your SIP address has been copied in the clipboard + + + + + account_settings_sip_address_copied_error_message + Error copying your SIP address + + + + + sip_address_display_name + "Nom d'affichage + Wyświetlana nazwa + + + + sip_address_display_name_explaination + "Le nom qui sera affiché à vos correspondants lors de vos échanges." + Nazwa wyświetlana Twoim kontaktom podczas wymiany wiadomości. + + + + manage_account_international_prefix + Indicatif international* + Kod międzynarodowy* + + + + manage_account_delete + "Déconnecter mon compte" + Odłącz moje konto + + + + manage_account_delete_message + Twoje konto zostanie usunięte z tego klienta Linphone, ale pozostaniesz połączony z innymi klientami. + + + + manage_account_dialog_remove_account_title + "Se déconnecter du compte ?" + Wylogować się z konta? + + + + manage_account_dialog_remove_account_message + Si vous souhaitez supprimer définitivement votre compte rendez-vous sur : https://sip.linphone.org + Jeśli chcesz trwale usunąć swoje konto, przejdź do: https://sip.linphone.org + + + + + error + Erreur + Błąd + + + + manage_account_device_remove + "Supprimer" + Usuń + + + + manage_account_device_remove_confirm_dialog + Usunąć %1? + + + + manage_account_device_last_connection + "Dernière connexion:" + Ostatnie logowanie: + + + + device_last_updated_time_no_info + "No information" + + + + + AccountSettingsPage + + + drawer_menu_manage_account + "Mon compte" + Moje konto + + + + settings_general_title + "Général" + Ogólne + + + + settings_account_title + "Paramètres de compte" + Ustawienia konta + + + + contact_editor_popup_abort_confirmation_title + "Modifications non enregistrées" + Niezapisane zmiany + + + + contact_editor_popup_abort_confirmation_message + "Vous avez des modifications non enregistrées. Si vous quittez cette page, vos changements seront perdus. Voulez-vous enregistrer vos modifications avant de continuer ?" + Masz niezapisane zmiany. Jeśli opuścisz tę stronę, Twoje zmiany zostaną utracone. Czy chcesz zapisać zmiany zanim kontynuujesz? + + + + contact_editor_dialog_abort_confirmation_do_not_save + "Ne pas enregistrer" "Enregistrer" + Nie zapisuj + + + + contact_editor_dialog_abort_confirmation_save + Zapisz + + + + AccountSettingsParametersLayout + + + settings_title + Ustawienia + + + + settings_account_title + Ustawienia konta + + + info_popup_invalid_registrar_uri_message + Registrar uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) + Registrar uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) + + + info_popup_invalid_outbound_proxy_message + Outbound proxy uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) + Outbound proxy uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) + + + info_popup_error_title + Error + + + + information_popup_success_title + Sukces + + + + contact_editor_saved_changes_toast + "Modifications sauvegardés" + Zapisano zmiany + + + + information_popup_error_title + Błąd + + + + account_settings_mwi_uri_title + "URI du serveur de messagerie vocale" + URI serwera poczty głosowej + + + + account_settings_voicemail_uri_title + "URI de messagerie vocale" + URI poczty głosowej + + + + account_settings_registrar_uri_title + + + + + account_settings_sip_proxy_url_title + + + + + login_proxy_server_url_tooltip + "If this field is filled, the outbound proxy will be enabled automatically. Leave it empty to disable it." + + + + + account_settings_stun_server_url_title + "Adresse du serveur STUN" + Adres serwera STUN + + + + account_settings_enable_ice_title + "Activer ICE" + Aktywuj ICE + + + + account_settings_avpf_title + "AVPF" + AVPF + + + + account_settings_bundle_mode_title + "Mode bundle" + + + + + account_settings_expire_title + "Expiration (en seconde)" + Wygaśnięcie (w sekundach) + + + + account_settings_conference_factory_uri_title + "URI du serveur de conversations" + URI konferencji + + + + account_settings_audio_video_conference_factory_uri_title + "URI du serveur de réunions" + URI wideokonferencji + + + + account_settings_lime_server_url_title + "URL du serveur d’échange de clés de chiffrement" + URL serwera Lime + + + + AddParticipantsForm + + + search_bar_search_contacts_placeholder + "Rechercher des contacts" + Znajdź kontakty + + + + add_participant_selected_count + 0 + "%n participant(s) sélectionné(s)" + + + + + + + + + remove_participant_accessible_name + Remove participant %1 + + + + + list_filter_no_result_found + "Aucun contact" + Nie znaleziono wyników… + + + + contact_list_empty + Brak kontaktów na ten moment + + + + AdvancedSettingsLayout + + + settings_system_title + System + System + + + + settings_remote_provisioning_title + Remote provisioning + Zdalne dostarczanie + + + + settings_security_title + Security / Encryption + Bezpieczeństwo / Szyfrowanie + + + + settings_advanced_audio_codecs_title + Audio codecs + Kodeki Audio + + + + settings_advanced_video_codecs_title + Video codecs + Kodeki Wideo + + + + settings_advanced_auto_start_title + Auto start %1 + Auto start %1 + + + + settings_advanced_remote_provisioning_url + Remote provisioning URL + URL zdalnego dostarczania + + + + settings_advanced_download_apply_remote_provisioning + Download and apply + Pobierz i zastosuj + + + + information_popup_error_title + Invalid URL format + Błąd + + + + settings_advanced_invalid_url_message + Błędny format adresu URL + + + + download_apply_remote_provisioning_accessible_name + "Download and apply remote provisioning" + + + + + + settings_advanced_media_encryption_title + Media encryption + Szyfrowanie multimediów + + + + settings_advanced_media_encryption_mandatory_title + Media encryption mandatory + Obowiązkowe szyfrowanie multimediów + + + + settings_advanced_create_endtoend_encrypted_meetings_title + Create end to end encrypted meetings and group calls + Twórz szyfrowane end-to-end spotkania i rozmowy grupowe + + + + settings_advanced_hide_fps_title + Ukryj FPS + + + + AllContactListView + + + car_favorites_contacts_title + "Favoris" + Ulubione + + + + generic_address_picker_contacts_list_title + 'Contacts' + Kontakty + + + + generic_address_picker_suggestions_list_title + "Suggestions" + Sugestie + + + + App + + + remote_provisioning_dialog + Voulez-vous télécharger et appliquer la configuration depuis cette adresse ? + Czy chcesz pobrać i zastosować zdalne dostarczanie z tego adresu? + + + + + + info_popup_error_title + Error + Błąd + + + + + info_popup_configuration_failed_message + Remote provisioning failed : %1 + Nie udało się zdalnie dostarczyć: %1 + + + + info_popup_error_checking_update + An error occured while trying to check update. Please try again later or contact support team. + + + + + info_popup_new_version_download_label + + + + + info_popup_new_version_available_title + New version available ! + + + + + info_popup_new_version_available_message + A new version of Linphone (%1) is available. %2 + + + + + info_popup_version_up_to_date_title + + + + + info_popup_version_up_to_date_message + Your version is up to date + + + + + configuration_error_detail + not reachable + nieosiągalny + + + + application_description + "A free and open source SIP video-phone." + Bezpłatny i otwartoźródłowy wideotelefon SIP. + + + + command_line_arg_order + "Send an order to the application towards a command line" + Wyślij polecenie do aplikacji za pomocą wiersza poleceń + + + + command_line_option_show_help + Wyświetl tę pomoc + + + + command_line_option_show_app_version + Wyświetl wersję aplikacji + + + + command_line_option_config_to_fetch + "Specify the linphone configuration file to be fetched. It will be merged with the current configuration." + Określ plik konfiguracyjny linphone, który ma zostać pobrany. Zostanie on połączony z bieżącą konfiguracją. + + + + command_line_option_config_to_fetch_arg + "URL, path or file" + URL, ścieżka lub plik + + + + command_line_option_minimized + Minimalizuj + + + + command_line_option_log_to_stdout + W trakcie działania zapisz w logu informacje debugowania na stdout + + + + command_line_option_print_app_logs_only + "Print only logs from the application" + Drukuj tylko logi z aplikacji + + + + hide_action + "Cacher" "Afficher" + Ukryj + + + + show_action + Pokaż + + + + quit_action + "Quitter" + Wyjście + + + + check_for_update + Check for update + + + + + mark_all_read_action + + + + + AuthenticationDialog + + + account_settings_dialog_invalid_password_title + "Authentification requise" + Wymagane uwierzytelnienie + + + + account_settings_dialog_invalid_password_message + La connexion a échoué pour le compte %1. Vous pouvez renseigner votre mot de passe à nouveau ou bien vérifier les options de configuration de votre compte. + Logowanie nie powiodło się dla konta %1. Możesz ponownie wprowadzić hasło lub sprawdzić ustawienia konta. + + + + password + Hasło + + + + cancel + "Annuler + Anuluj + + + + assistant_account_login + Connexion + Połączenie + + + + assistant_account_login_missing_password + Veuillez saisir un mot de passe + Proszę wprowadzić hasło + + + + CallCore + + + call_record_end_message + "Enregistrement terminé" + Nagrywanie zakończone + + + + call_record_saved_in_file_message + "L'appel a été enregistré dans le fichier : %1" + Nagranie zostało zapisane do pliku: %1 + + + + + call_stats_codec_label + "Codec: %1 / %2 kHz" + Kodek: %1 / %2 kHz + + + + + call_stats_bandwidth_label + "Bande passante : %1 %2 kbits/s %3 %4 kbits/s" + Przepustowość: %1 %2 kbits/s %3 %4 kbits/s + + + + + call_stats_loss_rate_label + "Taux de perte: %1% %2%" + Utracono: %1% %2% + + + + call_stats_jitter_buffer_label + "Tampon de gigue: %1 ms" + Bufor Jittera: %1 ms + + + + call_stats_resolution_label + "Définition vidéo : %1 %2 %3 %4" + Rozdzielczość wideo: %1 %2 %3 %4 + + + + call_stats_fps_label + "FPS : %1 %2 %3 %4" + FPS : %1 %2 %3 %4 + + + + media_encryption_dtls + DTLS + DTLS + + + + media_encryption_none + None + Żaden + + + + media_encryption_srtp + SRTP + SRTP + + + + media_encryption_post_quantum + "ZRTP - Post quantique" + Postkwantowy ZRTP + + + + CallForwardSettingsLayout + + + settings_call_forward_activate_title + "Forward calls" + + + + + settings_call_forward_activate_subtitle + "Enable call forwarding to voicemail or sip address" + + + + + settings_call_forward_destination_choose + Forward to destination + + + + + + + settings_call_forward_to_voicemail + + + + + settings_call_forward_to_sipaddress + + + + + settings_call_forward_sipaddress_title + SIP Address + + + + + settings_call_forward_sipaddress_placeholder + + + + + settings_call_forward_address_cannot_be_empty + + + + + CallHistoryLayout + + + meeting_info_join_title + "Rejoindre la réunion" + Dołącz do spotkania + + + + contact_call_action + "Appel" + Zadzwoń + + + + contact_message_action + "Message" + Wyślij wiadomość + + + + contact_video_call_action + "Appel Video" + Wideorozmowa + + + + CallHistoryListView + + + call_name_accessible_button + Call %1 + + + + + CallLayout + + + meeting_event_conference_destroyed + "Vous avez quitté la conférence" + Opuściłeś spotkanie + + + + call_ended_by_user + "Vous avez terminé l'appel" + Zakończyłeś rozmowę + + + + call_ended_by_remote + "Votre correspondant a terminé l'appel" + Twój rozmówca zakończył rozmowę + + + + conference_call_empty + "En attente d'autres participants…" + Oczekiwanie na innych uczestników… + + + + conference_share_link_title + "Partager le lien" + Udostępnij link + + + + copied + Skopiowano + + + + information_popup_meeting_address_copied_to_clipboard + Le lien de la réunion a été copié dans le presse-papier + Link do spotkania został skopiowany do schowka + + + + CallList + + + remote_group_call + Remote group call + + + + + local_group_call + + + + + info_popup_error_title + Błąd + + + + info_popup_merge_calls_failed_message + Failed to merge calls ! + + + + + CallListView + + + meeting + "Réunion + Spotkanie + + + + call + "Appel" + Zadzwoń + + + + paused_call_or_meeting + "%1 en pause" + %1 wstrzymano + + + + ongoing_call_or_meeting + "%1 en cours" + Trwa %1 + + + + transfer_call_name_accessible_name + Transfer call %1 + + + + + resume_call_name_accessible_name + Resume %1 call + + + + + pause_call_name_accessible_name + Pause %1 call + + + + + end_call_name_accessible_name + End %1 call + + + + + CallModel + + + call_error_no_response_toast + "No response" + + + + + call_error_forbidden_resource_toast + "403 : Forbidden resource" + + + + + call_error_not_answered_toast + "Request timeout" + + + + + call_error_user_declined_toast + "User declined the call" + Użytkownik odrzucił połączenie + + + + call_error_user_not_found_toast + "User was not found" + Użytkownik nie został znaleziony + + + + call_error_user_busy_toast + "User is busy" + Użytkownik jest zajęty + + + + call_error_incompatible_media_params_toast + "User can&apos;t accept your call" + Użytkownik nie może odebrać połączenia + + + + call_error_io_error_toast + "Unavailable service or network error" + Usługa niedostępna lub błąd sieci + + + + call_error_do_not_disturb_toast + "Le correspondant ne peut être dérangé" + + + + + call_error_temporarily_unavailable_toast + "Temporarily unavailable" + + + + + call_error_server_timeout_toast + "Server timeout" + Przekroczono limit czasu serwera + + + + CallPage + + + call_forward_to_address_info + + + + + call_forward_to_address_info_voicemail + + + + + history_call_start_title + "Nouvel appel" + Nowe połączenie + + + + call_history_empty_title + "Historique d'appel vide" + Pusta historia połączeń + + + + history_dialog_delete_all_call_logs_title + Supprimer l'historique d'appels ? + Usunąć historię połączeń? + + + + history_dialog_delete_all_call_logs_message + "L'ensemble de votre historique d'appels sera définitivement supprimé." + Historia połączeń zostanie trwale usunięta. + + + + history_dialog_delete_call_logs_title + Supprimer l'historique d'appels ? + Usunąć historię połączeń? + + + + history_dialog_delete_call_logs_message + "L'ensemble de votre historique d'appels avec ce correspondant sera définitivement supprimé." + Historia połączeń z tym użytkownikiem zostanie trwale usunięta. + + + + call_history_call_list_title + "Appels" + Połączenia + + + + call_history_options_accessible_name + + + + + + menu_delete_history + "Supprimer l'historique" + Usuń historię + + + + call_history_list_options_accessible_name + Call history options + + + + + create_new_call_accessible_name + Create new call + + + + + call_search_in_history + "Rechercher un appel" + Znajdź połączenie + + + + list_filter_no_result_found + "Aucun résultat…" + Nie znaleziono wyników… + + + + history_list_empty_history + "Aucun appel dans votre historique" + Brak połączeń w historii + + + + return_to_call_history_accessible_name + Return to call history + + + + + call_action_start_new_call + "Nouvel appel" + Nowe połączenie + + + + call_start_group_call_title + "Appel de groupe" + Rozmowa grupowa + + + + call_action_start_group_call + "Lancer" + Rozpocznij + + + + + + information_popup_error_title + Błąd + + + + group_call_error_must_have_name + "Un nom doit être donné à l'appel de groupe + Należy podać nazwę połączenia + + + + group_call_error_not_connected + "Vous n'etes pas connecté" + Nie jesteś połączony + + + + menu_see_existing_contact + "Show contact" + Pokaż kontakt + + + + menu_add_address_to_contacts + "Add to contacts" + Dodaj do kontaktów + + + + menu_copy_sip_address + "Copier l'adresse SIP" + Kopiuj adres SIP + + + + sip_address_copied_to_clipboard_toast + Adresse copiée + Adres SIP skopiowany + + + + sip_address_copied_to_clipboard_message + L'adresse a été copié dans le presse_papiers + Adres został skopiowany do schowka + + + + sip_address_copy_to_clipboard_error + "Erreur lors de la copie de l'adresse" + Błąd kopiowania adresu + + + + notification_missed_call_title + "Appel manqué" + Nieodebrane połączenie + + + + call_outgoing + "Appel sortant" + Połączenie Wychodzące + + + + call_audio_incoming + "Appel entrant" + Połączenie Przychodzące + + + + CallSettingsLayout + + + settings_call_devices_title + "Périphériques" + Urządzenia + + + + settings_call_devices_subtitle + "Vous pouvez modifier les périphériques de sortie audio, le microphone et la caméra de capture." + Możesz zmienić urządzenia wyjściowe audio, mikrofon i kamerę. + + + + settings_calls_echo_canceller_title + "Annulateur d'écho" + Redukcja Echa + + + + settings_calls_echo_canceller_subtitle + "Évite que de l'écho soit entendu par votre correspondant" + Zapobiega słyszeniu echa przez rozmówcę. + + + + settings_calls_auto_record_title + "Activer l’enregistrement automatique des appels" + Włącz automatyczne nagrywanie rozmów + + + + settings_call_enable_tones_title + Tonalités + Tony + + + + settings_call_enable_tones_subtitle + Activer les tonalités + Włącz tony + + + + settings_calls_enable_video_title + "Autoriser la vidéo" + Włącz wideo + + + + settings_calls_command_line_title + Command line + + + + + settings_calls_command_line_title_place_holder + + + + + settings_calls_change_ringtone_title + "Change ringtone" + + + + + settings_calls_current_ringtone_filename + Current ringtone : + + + + + choose_ringtone_file_accessible_name + Choose ringtone file + + + + + CallSettingsPanel + + + close_name_panel_accessible_button + Close %1 panel + + + + + CallStatistics + + + call_stats_audio_title + "Audio" + Audio + + + + call_stats_video_title + "Vidéo" + Wideo + + + + CallsWindow + + + call_transfer_in_progress_toast + "Transfert en cours, veuillez patienter" + Transfer w toku, proszę czekać + + + + + information_popup_error_title + Błąd + + + + call_transfer_failed_toast + "Le transfert d'appel a échoué" + Transfer nie powiódł się + + + + conference_error_empty_uri + "La conférence n'a pas pu démarrer en raison d'une erreur d'uri." + Nie można rozpocząć konferencji z powodu błędu URI. + + + + call_close_window_dialog_title + "Terminer tous les appels en cours ?" + Zakończyć wszystkie bieżące połączenia? + + + + call_close_window_dialog_message + "La fenêtre est sur le point d'être fermée. Cela terminera tous les appels en cours." + Okno zostanie wkrótce zamknięte. Spowoduje to zakończenie wszystkich bieżących połączeń. + + + + call_can_be_trusted_toast + "Appareil authentifié" + Urządzenie zaufane + + + + call_dir + %1 połączenie + + + + call_ended + Appel terminé + Połączenie zakończone + + + + conference_paused + Meeting paused + Spotkanie wstrzymane + + + + call_paused + Call paused + Rozmowa wstrzymana + + + + + call_srtp_point_to_point_encrypted + Appel chiffré de point à point + Połączenie szyfrowane typu punkt-punkt + + + + call_zrtp_sas_validation_required + Vérification nécessaire + Walidacja wymagana + + + + call_zrtp_end_to_end_encrypted + Appel chiffré de bout en bout + Połączenie szyfrowane typu end-to-end + + + + call_not_encrypted + "Appel non chiffré" + Połączenie nieszyfrowane + + + + + call_waiting_for_encryption_info + Waiting for encryption + Oczekiwanie na szyfrowanie + + + + call_paused_by_remote + Call paused by remote + Połączenie wstrzymane przez drugą stronę + + + + conference_user_is_recording + "You are recording the meeting" + Nagrywasz spotkanie + + + + call_user_is_recording + "You are recording the call" + Nagrywasz rozmowę + + + + conference_remote_is_recording + "Someone is recording the meeting" + Ktoś nagrywa spotkanie + + + + call_remote_recording + "%1 is recording the call" + %1 nagrywa połączenie + + + + call_stop_recording + "Stop recording" + Zatrzymaj nagrywanie + + + + add + Dodaj + + + + call_transfer_current_call_title + "Transférer %1 à…" + Transfer %1 do… + + + + + call_transfer_confirm_dialog_tittle + "Confirmer le transfert" + Potwierdź transfer + + + + + call_transfer_confirm_dialog_message + "Vous allez transférer %1 à %2." + Zamierzasz przesłać %1 do %2. + + + + call_action_start_new_call + "Nouvel appel" + Nowe połączenie + + + + + call_action_show_dialer + "Pavé numérique" + Klawiatura numeryczna + + + + call_action_change_layout + "Modifier la disposition" + Zmień layout + + + + call_action_go_to_calls_list + "Liste d'appel" + Lista połączeń + + + + Merger tous les appels + call_action_merge_calls + Połącz wszystkie połączenia + + + + + call_action_go_to_settings + "Paramètres" + Ustawienia + + + + conference_action_screen_sharing + "Partage de votre écran" + Udostępnij swój ekran + + + + conference_share_link_title + Partager le lien de la réunion + Udostępnij link do spotkania + + + + copied + Copié + Skopiowano + + + + information_popup_meeting_address_copied_to_clipboard + Le lien de la réunion a été copié dans le presse-papier + Link do spotkania został skopiowany do schowka + + + + + + conference_participants_list_title + "Participants (%1)" + Uczestnicy (%1) + + + + + group_call_participant_selected + + + + + + + + + meeting_schedule_add_participants_title + Dodaj uczestników + + + + call_encryption_title + Chiffrement + Szyfrowanie + + + + open_statistic_panel_accessible_name + + + + + conference_user_is_sharing_screen + "You are sharing your screen" + + + + + call_stop_screen_sharing + "Stop sharing" + + + + + stop_recording_accessible_name + Stop recording + Zatrzymaj nagrywanie + + + + stop_screen_sharing_accessible_name + "Stop screen sharing" + + + + + call_stats_title + Statistiques + Statystyki + + + + + call_action_end_call + "Terminer l'appel" + Zakończ połączenie + + + + + call_action_resume_call + "Reprendre l'appel" + Wznów połączenie + + + + + call_action_pause_call + "Mettre l'appel en pause" + Wstrzymaj połączenie + + + + + call_action_transfer_call + "Transférer l'appel" + Przekieruj połączenie + + + + + call_action_start_new_call_hint + "Initier un nouvel appel" + Rozpocznij nowe połączenie + + + + + call_display_call_list_hint + "Afficher la liste d'appels" + Pokaż listę połączeń + + + + + call_deactivate_video_hint + "Désactiver la vidéo" "Activer la vidéo" + Wyłącz wideo + + + + + call_activate_video_hint + Włącz wideo + + + + + call_activate_microphone + "Activer le micro" + Aktywuj mikrofon + + + + + call_deactivate_microphone + "Désactiver le micro" + Wycisz mikrofon + + + + + call_share_screen_hint + Partager l'écran… + Udostępnij ekran… + + + + + call_open_chat_hint + Open chat… + + + + + + call_rise_hand_hint + "Lever la main" + Podnieś rękę + + + + + call_send_reaction_hint + "Envoyer une réaction" + Wyślij reakcję + + + + + call_manage_participants_hint + "Gérer les participants" + Zarządzaj uczestnikami + + + + + call_more_options_hint + "Plus d'options…" + Więcej opcji… + + + + call_action_change_conference_layout + "Modifier la disposition" + Zmień layout + + + + call_action_full_screen + "Mode Plein écran" + Tryb pełnoekranowy + + + + call_action_stop_recording + "Terminer l'enregistrement" + Zakończ nagrywanie + + + + call_action_record + "Enregistrer l'appel" + Nagraj połączenie + + + + call_activate_speaker_hint + "Activer le son" + Aktywuj głośnik + + + + call_deactivate_speaker_hint + "Désactiver le son" + Wycisz głośnik + + + + CarddavSettingsLayout + + + settings_contacts_carddav_title + Carnet d'adresse CardDAV + Książka adresowa CardDAV + + + + settings_contacts_carddav_subtitle + "Ajouter un carnet d’adresse CardDAV pour synchroniser vos contacts Linphone avec un carnet d’adresse tiers." + Dodaj książkę adresową CardDAV, aby zsynchronizować kontakty Linphone z książką adresową innej firmy. + + + + information_popup_error_title + Błąd + + + + settings_contacts_carddav_popup_invalid_error + "Vérifiez que toutes les informations ont été saisies." + Sprawdź czy wszystkie informacje zostały wprowadzone. + + + + information_popup_synchronization_success_title + Sukces + + + + settings_contacts_carddav_synchronization_success_message + "Le carnet d'adresse CardDAV est synchronisé." + Książka adresowa CardDAV została zsynchronizowana. + + + + settings_contacts_carddav_popup_synchronization_error_title + Błąd + + + + settings_contacts_carddav_popup_synchronization_error_message + "Erreur de synchronisation : %1" + + + + + settings_contacts_delete_carddav_server_title + "Supprimer le carnet d'adresse CardDAV ?" + Usunąć książkę adresową CardDAV? + + + + sip_address_display_name + Nom d'affichage + Wyświetlana nazwa + + + + settings_contacts_carddav_server_url_title + "URL du serveur" + URL serwera + + + + username + Nazwa użytkownika + + + + password + Hasło + + + + settings_contacts_carddav_realm_title + Domaine d’authentification + Obszar uwierzytelniania + + + + settings_contacts_carddav_use_as_default_title + "Stocker ici les contacts nouvellement crées" + Przechowuj tutaj nowo utworzone kontakty + + + + ChangeLayoutForm + + + conference_layout_grid + Siatka + + + + conference_layout_active_speaker + Głośnik aktywny + + + + conference_layout_audio_only + Tylko Audio + + + + ChatAudioContent + + + + information_popup_error_title + Error + Błąd + + + + information_popup_voice_message_error_message + Failed to create voice message : error in recorder + + + + + ChatCore + + + info_toast_deleted_title + Deleted + + + + + info_toast_deleted_message_history + Message history has been deleted + + + + + ChatDroppableTextArea + + + chat_view_send_area_placeholder_text + Say something… : placeholder text for sending message text area + + + + + cannot_record_while_in_call_tooltip + Cannot record a message while a call is ongoing + + + + + ChatListView + + + chat_message_is_writing_info + %1 is writing… + + + + + chat_message_draft_sending_text + + + + + chat_room_delete + "Delete" + Usuń + + + + chat_room_mute + + + + + chat_room_unmute + "Mute" + + + + + chat_room_mark_as_read + "Mark as read" + + + + + chat_room_leave + "leave" + + + + + chat_list_leave_chat_popup_title + leave the conversation ? + + + + + chat_list_leave_chat_popup_message + You will not be able to send or receive messages in this conversation anymore. Do You want to continue ? + + + + + chat_list_delete_chat_popup_title + Delete the conversation ? + + + + + chat_list_delete_chat_popup_message + This conversation and all its messages will be deleted. Do You want to continue ? + + + + + ChatMessage + + + chat_message_copy_selection + "Copy selection" + + + + + chat_message_copy + "Copy" + + + + + chat_message_copied_to_clipboard_title + Copied + Skopiowano + + + + chat_message_copied_to_clipboard_toast + "to clipboard" + + + + + chat_message_remote_replied + %1 replied + + + + + chat_message_forwarded + Forwarded + + + + + chat_message_remote_replied_to + %1 replied to %2 + + + + + chat_message_user_replied_to + You replied to %1 + + + + + chat_message_user_replied + You replied + + + + + chat_message_reception_info + "Reception info" + + + + + chat_message_reply + Reply + + + + + chat_message_forward + Forward + + + + + chat_message_delete + "Delete" + Usuń + + + + ChatMessageContentCore + + + download_file_default_error + Error downloading file %1 + + + + + info_popup_error_titile + Błąd + + + + popup_error_title + Error + Błąd + + + + popup_open_file_error_does_not_exist_message + Could not open file : unknown path %1 + + + + + ChatMessageContentList + + + + + + popup_error_title + Error adding file +---------- +Error + + + + + popup_error_path_does_not_exist_message + File was not found: %1 + + + + + popup_error_nb_files_not_found_message + + + + + popup_error_max_files_count_message + You can send 12 files maximum at a time. %n files were ignored + + + + + + + + + popup_error_file_too_big_message + %n files were ignored cause they exceed the maximum size. (Size limit=%2) + + + + + popup_error_unsupported_files_message + + + + + popup_error_unsupported_file_message + Unable to get supported mime type for: `%1`. + + + + + ChatMessageContentModel + + + download_error_object_doesnt_exist + Internal error : message object does not exist anymore ! + + + + + download_file_server_error + Error while trying to download content : %1 + + + + + download_file_error_no_safe_file_path + Unable to create safe file path for: %1 + + + + + download_file_error_file_transfer_unavailable + This file was already downloaded and is no more on the server. Your peer have to resend it if you want to get it + + + + + download_file_error_null_name + Content name is null, can't download it ! + + + + + download_file_error_unable_to_download + Unable to download file of entry %1 + + + + + ChatMessageCore + + + all_reactions_label + "Reactions": all reactions for one message label + + + + + info_toast_deleted_title + Deleted + + + + + info_toast_deleted_message + The message has been deleted + + + + + ChatMessageInvitationBubble + + + ics_bubble_meeting_from + + + + + ics_bubble_meeting_to + + + + + ics_bubble_meeting_modified + Meeting has been updated + + + + + ics_bubble_meeting_cancelled + Meeting has been canceled + + + + + + + + + + ics_bubble_description_title + Description + + + + + ics_bubble_join + "Rejoindre" + + + + + ics_bubble_participants + %n participant(s) + + + + + + + + + ChatMessagesListView + + + + popup_info_find_message_title + Find message + + + + + info_popup_no_result_message + No result found + + + + + info_popup_first_result_message + First result reached + + + + + info_popup_last_result_message + Last result reached + + + + + chat_message_list_encrypted_header_title + End to end encrypted chat + + + + + unencrypted_conversation_warning + This conversation is not encrypted ! + + + + + chat_message_list_encrypted_header_message + Messages in this conversation are e2e encrypted. + Only your correspondent can decrypt them. + + + + + chat_message_list_not_encrypted_header_message + Messages are not end to end encrypted, + may sure you don't share any sensitive information ! + + + + + chat_message_is_writing_info + %1 is writing… + + + + + ChatPage + + + chat_start_title + "Nouvelle conversation" + + + + + chat_empty_title + "Aucune conversation" + + + + + info_popup_error_title + Błąd + + + + info_popup_chatroom_creation_failed + Chat room creation failed ! + + + + + loading_popup_chatroom_creation_pending_message + Chat room is being created... + + + + + chat_dialog_delete_chat_title + Supprimer la conversation ? + + + + + chat_dialog_delete_chat_message + "La conversation et tous ses messages seront supprimés." + + + + + chat_list_title + "Conversations" + Konwersacje + + + + menu_mark_all_as_read + "mark all as read" + + + + + chat_search_in_history + "Rechercher une conversation" + + + + + list_filter_no_result_found + "Aucun résultat…" + Brak wyników… + + + + chat_list_empty_history + "Aucune conversation dans votre historique" + + + + + chat_action_start_new_chat + "New chat" + + + + + chat_start_group_chat_title + "Nouveau groupe" + + + + + chat_action_start_group_chat + "Créer" + Utwórz + + + + + + information_popup_error_title + Błąd + + + + information_popup_chat_creation_failed_message + "La création a échoué" + + + + + group_chat_error_must_have_name + "Un nom doit être donné au groupe + + + + + group_chat_error_no_participant + "Please select at least one participant + + + + + group_call_error_not_connected + "Vous n'etes pas connecté" + Nie jesteś połączony + + + + chat_creation_in_progress + Creation de la conversation en cours … + + + + + ChatSettingsLayout + + + settings_chat_attached_files_title + Attached files + + + + + settings_chat_attached_files_auto_download_title + "Automatic download" + + + + + settings_chat_attached_files_auto_download_subtitle + "Automatically download transferred or received files in conversations" + + + + + CliModel + + + show_function_description + Pokaż + + + + fetch_config_function_description + Pobierz konfigurację + + + + call_function_description + Zadzwoń + + + + bye_function_description + Odrzuć + + + + accept_function_description + Akceptuj + + + + decline_function_description + Odmów + + + + ConferenceInfoCore + + + information_popup_error_title + "Erreur" + Błąd + + + + information_popup_disconnected_account_message + "Votre compte est déconnecté" + Twoje konto jest rozłączone + + + + Contact + + + information_popup_error_title + Erreur + Błąd + + + + information_popup_voicemail_address_undefined_message + L'URI de messagerie vocale n'est pas définie. + URI poczty głosowej nie jest zdefiniowany. + + + + account_settings_name_accessible_name + Account settings of %1 + + + + + ContactEdition + + + contact_editor_title + "Modifier contact" + Edytuj kontakt + + + + save + "Enregistrer + Zapisz + + + + + contact_editor_dialog_cancel_change_message + "Les changements seront annulés. Souhaitez-vous continuer ?" + Zmiany nie zostaną zapisane. Kontynuować? + + + + close_accessible_name + Close %1 + + + + + contact_editor_mandatory_first_name_or_company_not_filled + "Veuillez saisir un prénom ou un nom d'entreprise" + + + + + contact_editor_mandatory_address_or_number_not_filled + "Veuillez saisir une adresse ou un numéro de téléphone" + Proszę wprowadzić adres SIP lub numer telefonu + + + + contact_editor_add_image_label + "Ajouter une image" + Dodaj obraz + + + + contact_details_edit + "Modifier" + Edytuj + + + + edit_contact_image_accessible_name + "Edit contact image" + + + + + contact_details_delete + "Supprimer" + Usuń + + + + delete_contact_image_accessible_name + "Delete contact image" + + + + + + contact_editor_first_name + "Prénom" + Imię + + + + + contact_editor_last_name + "Nom" + Nazwisko + + + + + contact_editor_company + "Entreprise" + Firma + + + + + contact_editor_job_title + "Fonction" + Zawód + + + + + sip_address + Adres SIP + + + + sip_address_number_accessible_name + "SIP address number %1" + + + + + remove_sip_address_accessible_name + "Remove SIP address %1" + + + + + new_sip_address_accessible_name + "New SIP address" + + + + + phone_number_number_accessible_name + "Phone number number %1" + + + + + remove_phone_number_accessible_name + Remove phone number %1 + + + + + new_phone_number_accessible_name + "New phone number" + + + + + + phone + "Téléphone" + Telefon + + + + ContactListItem + + + contact_details_remove_from_favourites + "Enlever des favoris" + Usuń z ulubionych + + + + contact_details_add_to_favourites + "Ajouter aux favoris" + Dodaj do ulubionych + + + + Partager + Udostępnij + + + + information_popup_error_title + Błąd + + + + information_popup_vcard_creation_error + La création du fichier vcard a échoué + Błąd tworzenia VCard + + + + information_popup_vcard_creation_title + VCard créée + VCard utworzony + + + + information_popup_vcard_creation_success + "VCard du contact enregistrée dans %1" + VCard został zapisany w %1 + + + + contact_sharing_email_title + Partage de contact + Udostępnij kontakt + + + + contact_details_delete + "Supprimer" + Usuń + + + + ContactListView + + + shrink_accessible_name + Shrink %1 + + + + + expand_accessible_name + Expand %1 + + + + + ContactPage + + + contacts_add + "Ajouter un contact" + Dodaj kontakt + + + + contacts_list_empty + "Aucun contact pour le moment" + Brak kontaktów na ten moment... + + + + contact_new_title + "Nouveau contact" + Nowy kontakt + + + + create + Utwórz + + + + contact_edit_title + "Modifier contact" + Edytuj kontakt + + + + save + Zapisz + + + + contact_dialog_delete_title + Supprimer %1 ?" + Usunąć %1? + + + + contact_dialog_delete_message + Ce contact sera définitivement supprimé. + Kontakt został trwale usunięty. + + + + contact_deleted_toast + "Contact supprimé" + Kontakt usunięty + + + + contact_deleted_message + "%1 a été supprimé" + %1 został usunięty + + + + contact_dialog_devices_trust_popup_title + "Augmenter la confiance" + Zwiększ poziom zaufania + + + + contact_dialog_devices_trust_popup_message + "Pour augmenter le niveau de confiance vous devez appeler les différents appareils de votre contact et valider un code.<br><br>Vous êtes sur le point d’appeler “%1” voulez vous continuer ?" + Aby zwiększyć poziom zaufania, musisz zadzwonić na urządzenia swoich kontaktów i zweryfikować kod.<br><br>Zamierzasz zadzwonić do „%1”. Czy chcesz kontynuować? + + + + popup_do_not_show_again + Ne plus afficher + Nie pokazuj ponownie + + + + cancel + Anuluj + + + + dialog_call + "Appeler" + Zadzwoń + + + + contact_dialog_devices_trust_help_title + "Niveau de confiance" + Poziom zaufania + + + + contact_dialog_devices_trust_help_message + "Vérifiez les appareils de votre contact pour confirmer que vos communications seront sécurisées et sans compromission. <br>Quand tous seront vérifiés, vous atteindrez le niveau de confiance maximal." + Zweryfikuj urządzenia swoich kontaktów, aby upewnić się, że Twoja komunikacja będzie bezpieczna i nie zostanie naruszona. Po zweryfikowaniu wszystkich urządzeń osiągniesz maksymalny poziom zaufania. + + + + dialog_ok + "Ok" + OK + + + + bottom_navigation_contacts_label + "Contacts" + Kontakty + + + + search_bar_look_for_contact_text + Rechercher un contact + Znajdź kontakt + + + + list_filter_no_result_found + Aucun résultat… + Brak wyników… + + + + contact_list_empty + Aucun contact pour le moment + Brak kontaktów na ten moment... + + + + expand_accessible_name + Expand %1 + + + + + shrink_accessible_name + Shrink %1 + + + + + create_contact_accessible_name + Create new contact + + + + + more_info_accessible_name + More info %1 + + + + + + contact_details_edit + Edit +---------- +"Éditer" + Edytuj + + + + contact_call_action + "Appel" + Zadzwoń + + + + contact_message_action + "Message" + Wyślij wiadomość + + + + contact_video_call_action + "Appel vidéo" + Wideorozmowa + + + + contact_details_numbers_and_addresses_title + "Coordonnées" + Szczegóły kontaktu + + + + call_adress_accessible_name + Call address %1 + + + + + contact_details_company_name + "Société :" + Firma : + + + + contact_details_job_title + "Poste :" + Stanowisko : + + + + contact_details_medias_title + "Medias" + Media + + + + + contact_details_medias_subtitle + "Afficher les medias partagés" + Pokaż udostępnione multimedia + + + + contact_details_trust_title + "Confiance" + Zaufanie + + + + contact_dialog_devices_trust_title + "Niveau de confiance - Appareils vérifiés" + Poziom Zaufania - Urządzenia zweryfikowane + + + + contact_details_no_device_found + "Aucun appareil" + Brak urządzeń + + + + contact_device_without_name + "Appareil inconnu" + Nieznane urządzenie + + + + contact_make_call_check_device_trust + "Vérifier" + Weryfikuj + + + + verify_device_accessible_name + Verify %1 device + + + + + contact_details_actions_title + "Autres actions" + Inne akcje + + + + contact_details_remove_from_favourites + "Retirer des favoris" + Usuń z ulubionych + + + + contact_details_add_to_favourites + "Ajouter aux favoris" + Dodaj do ulubionych + + + + contact_details_share + "Partager" + Udostępnij + + + + information_popup_error_title + Błąd + + + + contact_details_share_error_mesage + "La création du fichier vcard a échoué" + Błąd tworzenia VCard + + + + contact_details_share_success_title + "VCard créée" + VCard utworzony + + + + contact_details_share_success_mesage + "VCard du contact enregistrée dans %1" + VCard został zapisany w %1 + + + + contact_details_share_email_title + "Partage de contact" + Udostępnij kontakt + + + + contact_details_delete + "Supprimer ce contact" + Usuń kontakt + + + + ContactsSettingsLayout + + + settings_contacts_ldap_title + Annuaires LDAP + Serwery LDAP + + + + settings_contacts_ldap_subtitle + "Ajouter vos annuaires LDAP pour pouvoir effectuer des recherches dans la barre de recherche." + Dodaj serwery LDAP, aby móc wyszukiwać w magicznym pasku wyszukiwania. + + + + settings_contacts_carddav_title + Książka adresowa CardDAV + + + + settings_contacts_carddav_subtitle + Dodaj książkę adresową CardDAV, aby zsynchronizować kontakty Linphone z książką adresową innej firmy. + + + + settings_contacts_add_ldap_server_title + "Ajouter un annuaire LDAP" + Dodaj serwer LDAP + + + + settings_contacts_edit_ldap_server_title + "Modifier un annuaire LDAP" + Edytuj serwer LDAP + + + + edit_ldap_server_accessible_name + "Editer le serveur LDAP %1" + + + + + use_ldap_server_accessible_name + "Utiliser le serveur LDAP %1" + + + + + settings_contacts_add_carddav_server_title + "Ajouter un carnet d'adresse CardDAV" + Dodaj książkę adresową CardDAV + + + + settings_contacts_edit_carddav_server_title + "Modifier un carnet d'adresse CardDAV" + Edytuj książkę adresową CardDAV + + + + edit_cardav_server_accessible_name + "Editer le carnet d'adresses CardDAV %1" + + + + + use_cardav_server_accessible_name + "Utiliser le d'adresses CardDAV %1" + + + + + ContactsSettingsProviderLayout + + + information_popup_success_title + Sukces + + + + information_popup_changes_saved + "Les changements ont été sauvegardés" + Zmiany zostały zapisane + + + + add + "Ajouter" + Dodaj + + + + ConversationInfos + + + one_one_infos_call + "Appel" + Zadzwoń + + + + one_one_infos_unmute + "Sourdine" + + + + + one_one_infos_mute + + + + + group_infos_participants + Uczestnicy (%1) + + + + group_infos_media_docs + Medias & documents + + + + + group_infos_shared_medias + Shared medias + + + + + group_infos_shared_docs + Shared documents + + + + + group_infos_other_actions + Other actions + Inne akcje + + + + group_infos_ephemerals + + + + + group_infos_enable_ephemerals + + + + + group_infos_meeting + Schedule a meeting + + + + + group_infos_leave_room + Leave chat room + + + + + group_infos_leave_room_toast_title + Leave Chat Room ? + + + + + group_infos_leave_room_toast_message + All the messages will be removed from the chat room. Do you want to continue ? + + + + + group_infos_delete_history + Delete history + Usuń historię + + + + group_infos_delete_history_toast_title + Delete history ? + + + + + group_infos_delete_history_toast_message + All the messages will be removed from the chat room. Do you want to continue ? + + + + + one_one_infos_open_contact + Show contact + Pokaż kontakt + + + + one_one_infos_create_contact + Create contact + + + + + one_one_infos_ephemerals + + + + + one_one_infos_enable_ephemerals + + + + + one_one_infos_delete_history + Usuń historię + + + + one_one_infos_delete_history_toast_title + Delete history ? + + + + + one_one_infos_delete_history_toast_message + All the messages will be removed from the chat room. Do you want to continue ? + + + + + CoreModel + + + info_popup_error_title + Błąd + + + + fetching_config_failed_error_message + "Remote provisioning cannot be retrieved" + + + + + CreationFormLayout + + + search_bar_look_for_contact_text + "Rechercher un contact" + Znajdź kontakt + + + + DebugSettingsLayout + + + settings_debug_clean_logs_message + "Les traces de débogage seront supprimées. Souhaitez-vous continuer ?" + Ślady debugowania zostaną usunięte. Czy chcesz kontynuować? + + + + settings_debug_share_logs_message + "Les traces de débogage ont été téléversées. Comment souhaitez-vous partager le lien ? " + Ślady debugowania zostały przesłane. Jak chcesz udostępnić link? + + + + settings_debug_clipboard + "Presse-papier" + Schowek + + + + settings_debug_email + "E-Mail" + E-Mail + + + + debug_settings_trace + "Traces %1" + %1 śladów + + + + information_popup_email_sharing_failed + "Le partage par mail a échoué. Veuillez envoyer le lien %1 directement à l'adresse %2." + Udostępnianie e-mailem nie powiodło się. Wyślij link %1 bezpośrednio do %2. + + + + + information_popup_error_title + Une erreur est survenue. + Wystąpił błąd. + + + + settings_debug_enable_logs_title + "Activer les traces de débogage" + Włącz ślady debugowania + + + + settings_debug_enable_full_logs_title + "Activer les traces de débogage intégrales" + Włącz pełne logi + + + + settings_debug_delete_logs_title + "Supprimer les traces" + Usuń ślady debugowania + + + + settings_debug_share_logs_title + "Partager les traces" + Udostępnij ślady debugowania + + + + settings_debug_share_logs_loading_message + "Téléversement des traces en cours …" + Przesyłanie śladów… + + + + settings_debug_app_version_title + "Version de l'application" + Wersja aplikacji + + + + settings_debug_sdk_version_title + "Version du SDK" + Wersja SDK + + + + settings_debug_qt_version_title + "Qt Version" + + + + + settings_debug_share_logs_error + "Le téléversement des traces a échoué. Vous pouvez partager les fichiers de trace directement depuis le répertoire suivant : %1" + Wysyłanie śladów nie powiodło się. Pliki śladów można udostępniać bezpośrednio z następującego katalogu: %1 + + + + DecoratedTextField + + + textfield_error_message_cannot_be_empty + "ne peut être vide" + nie może być puste + + + + textfield_error_message_unknown_format + "Format non reconnu" + Nieznany format + + + + Dialog + + + + dialog_confirm + "Confirmer" + Potwierdź + + + + + dialog_cancel + "Annuler" + Anuluj + + + + EncryptionSettings + + + call_stats_media_encryption_title + "Encryption :" + Szyfrowanie : + + + + call_stats_media_encryption + Media encryption : %1 + Szyfrowanie multimediów: %1%2 + + + + call_stats_zrtp_cipher_algo + "Algorithme de chiffrement : %1" + Algorytm szyfrowania : %1 + + + + call_stats_zrtp_key_agreement_algo + "Algorithme d'accord de clé : %1" + Algorytm uzgadniania kluczy: %1 + + + + call_stats_zrtp_hash_algo + "Algorithme de hachage : %1" + Algorytm hashujący: %1 + + + + call_stats_zrtp_auth_tag_algo + "Algorithme d'authentification : %1" + Algorytm uwierzytelniania: %1 + + + + call_stats_zrtp_sas_algo + "Algorithme SAS : %1" + Algorytm SAS: %1 + + + + call_zrtp_validation_button_label + "Validation chiffrement" + Walidacja szyfrowania + + + + EphemeralSettings + + + title + + + + + explanations + + + + + one_minute + + + + + one_hour + + + + + one_day + + + + + one_week + + + + + disabled + Wyłączone + + + + custom + + + + + EventLogCore + + + conference_created_event + + + + + conference_created_terminated + + + + + conference_participant_added_event + + + + + conference_participant_removed_event + + + + + conference_participant_set_admin_event + + + + + conference_participant_unset_admin_event + + + + + + conference_security_event + + + + + conference_ephemeral_message_enabled_event + + + + + conference_ephemeral_message_disabled_event + + + + + conference_subject_changed_event + + + + + conference_ephemeral_message_lifetime_changed_event + + + + + FriendCore + + + + + + + sip_address + "Adresse SIP" + Adres SIP + + + + + + device_id + "Téléphone" + Telefon + + + + information_popup_error_title + Błąd + + + + information_popup_invalid_address_message + "Adresse invalide" + Niepoprawny adres + + + + GroupChatInfoParticipants + + + group_infos_manage_participants_title + "Gérer des participants" + Zarządzaj uczestnikami + + + + group_infos_participant_is_admin + + + + + menu_see_existing_contact + "Show contact" + Pokaż kontakt + + + + menu_add_address_to_contacts + "Add to contacts" + Dodaj do kontaktów + + + + group_infos_give_admin_rights + + + + + group_infos_remove_admin_rights + + + + + group_infos_copy_sip_address + + + + + group_infos_remove_participant + + + + + group_infos_remove_participants_toast_title + + + + + group_infos_remove_participants_toast_message + + + + + GroupCreationFormLayout + + + return_accessible_name + Return + + + + + + group_start_dialog_subject_hint + "Nom du groupe" + Nazwa grupy + + + + required + "Requis" + Wymagane + + + + HelpPage + + + help_title + "Aide" + Pomoc + + + + + help_about_title + "À propos de %1" + O %1 + + + + help_about_privacy_policy_title + "Règles de confidentialité" + Polityka Prywatności + + + + help_about_privacy_policy_subtitle + Quelles informations %1 collecte et utilise + Jakie informacje gromadzi i wykorzystuje %1 + + + + help_about_version_title + "Version" + Wersja + + + + help_check_for_update_button_label + Check update + + + + + help_about_gpl_licence_title + "Licences GPLv3" + Licencje GPLv3 + + + + help_about_contribute_translations_title + "Contribuer à la traduction de %1" + Przyczyń się do tłumaczenia %1 + + + + help_troubleshooting_title + "Dépannage" + Rozwiązywanie problemów + + + + LdapSettingsLayout + + + settings_contacts_ldap_title + Serwery LDAP + + + + settings_contacts_ldap_subtitle + Dodaj serwery LDAP, aby móc wyszukiwać w magicznym pasku wyszukiwania. + + + + information_popup_success_title + Sukces + + + + settings_contacts_ldap_success_toast + "L'annuaire LDAP a été sauvegardé" + Serwer LDAP został zapisany + + + + settings_contacts_ldap_error_toast + "Une erreur s'est produite, la configuration LDAP n'a pas été sauvegardée !" + Wystąpił błąd, konfiguracja LDAP nie została zapisana! + + + + information_popup_error_title + Błąd + + + + settings_contacts_ldap_delete_confirmation_message + "Supprimer l'annuaire LDAP ?" + Usunąć serwer LDAP? + + + + delete_ldap_server_accessible_name + Delete LDAP server + + + + + settings_contacts_ldap_server_url_title + "URL du serveur (ne peut être vide)" + URL serwera (nie może być pusty) + + + + settings_contacts_ldap_bind_dn_title + "Bind DN" + Powiąż DN + + + + settings_contacts_ldap_password_title + "Mot de passe" + Hasło + + + + settings_contacts_ldap_use_tls_title + "Utiliser TLS" + Używaj TLS + + + + settings_contacts_ldap_search_base_title + "Base de recherche (ne peut être vide)" + Baza wiedzy (nie może być pusta) + + + + settings_contacts_ldap_search_filter_title + "Filtre" + Filtr + + + + settings_contacts_ldap_max_results_title + "Nombre maximum de résultats" + Max wyników + + + + settings_contacts_ldap_request_delay_title + "Délai entre 2 requêtes (en millisecondes)" + Opóźnienie między dwoma zapytaniami (w milisekundach) + + + + settings_contacts_ldap_request_timeout_title + "Durée maximun (en secondes)" + Limit czasu (w sekundach) + + + + settings_contacts_ldap_min_characters_title + "Nombre minimum de caractères pour la requête" + Minimalna liczba znaków dla zapytania + + + + settings_contacts_ldap_name_attributes_title + "Attributs de nom" + Atrybuty nazwy + + + + settings_contacts_ldap_sip_attributes_title + "Attributs SIP" + Atrybuty SIP + + + + settings_contacts_ldap_sip_domain_title + "Domaine SIP" + Domena SIP + + + + settings_contacts_ldap_debug_title + "Débogage" + Debug + + + + LoadingPopup + + + cancel + Anuluj + + + + LoginForm + + + + username + Nom d'utilisateur : username + Nazwa użytkownika + + + + + password + Mot de passe + Hasło + + + + mandatory_field_accessible_name + "%1 mandatory" + + + + + + assistant_account_login + "Connexion" + Połączenie + + + + assistant_account_login_missing_username + "Veuillez saisir un nom d'utilisateur" + Proszę wprowadzić nazwę użytkownika + + + + assistant_account_login_missing_password + "Veuillez saisir un mot de passe" + Proszę wprowadzić hasło + + + + assistant_forgotten_password + "Mot de passe oublié ?" + Zapomniałeś hasła? + + + + LoginLayout + + + + help_about_title + À propos de %1 + O %1 + + + + help_about_privacy_policy_title + "Politique de confidentialité" + Polityka prywatności + + + + help_about_privacy_policy_link + "Visiter notre potilique de confidentialité" + Odwiedź naszą politykę prywatności + + + + help_about_version_title + "Version" + Wersja + + + + help_about_licence_title + "Licence" + Licencja + + + + help_about_copyright_title + "Copyright + Wszystkie prawa zastrzeżone + + + + close + "Fermer" + Zamknij + + + + LoginPage + + + return_accessible_name + Return + + + + + assistant_account_login + Connexion + Połączenie + + + + assistant_no_account_yet + "Pas encore de compte ?" + Nie masz jeszcze konta? + + + + assistant_account_register + "S'inscrire" + Zarejestruj się + + + + assistant_login_third_party_sip_account_title + "Compte SIP tiers" + Konto SIP innej firmy + + + + assistant_login_remote_provisioning + "Configuration distante" + Zdalne dostarczanie + + + + assistant_login_download_remote_config + "Télécharger une configuration distante" + Pobierz zdalną konfigurację + + + + assistant_login_remote_provisioning_url + 'Veuillez entrer le lien de configuration qui vous a été fourni :' + Wprowadź link konfiguracyjny, który Ci dostarczono: + + + + cancel + Anuluj + + + + validate + "Valider" + Potwierdź + + + + settings_advanced_remote_provisioning_url + 'Lien de configuration distante' + Link do dostarczania zasobów + + + + default_account_connection_state_error_toast + Błąd podczas połączenia + + + + MagicSearchList + + + device_id + Telefon + + + + MainLayout + + + bottom_navigation_calls_label + "Appels" + Połączenia + + + + open_calls_page_accessible_name + "Open calls page" + + + + + bottom_navigation_contacts_label + "Contacts" + Kontakty + + + + open_contacts_page_accessible_name + "Open contacts page" + + + + + bottom_navigation_conversations_label + "Conversations" + Konwersacje + + + + open_conversations_page_accessible_name + "Open conversations page" + + + + + bottom_navigation_meetings_label + "Réunions" + Spotkania + + + + open_contact_page_accessible_name + "Open meetings page" + + + + + searchbar_placeholder_text + "Rechercher un contact, appeler %1" + Znajdź kontakt, połącz %1 + + + + searchbar_placeholder_text_chat_feature_enabled + "ou envoyer un message …" + lub wyślij wiadomość … + + + + do_not_disturb_accessible_name + "Do not disturb" + Nie przeszkadzać + + + + + contact_presence_status_disable_do_not_disturb + "Désactiver ne pas déranger" + Wyłącz tryb "nie przeszkadzać" + + + + information_popup_error_title + Błąd + + + + no_voicemail_uri_error_message + "L'URI de messagerie vocale n'est pas définie." + URI poczty głosowej nie jest zdefiniowany. + + + + account_list_accessible_name + "Account list" + + + + + application_options_accessible_name + "Application options" + + + + + drawer_menu_manage_account + Mon compte + Moje konto + + + + contact_presence_status_enable_do_not_disturb + "Activer ne pas déranger" + Włącz tryb "nie przeszkadzać" + + + + settings_title + Ustawienia + + + + recordings_title + "Enregistrements" + Nagrania + + + + help_title + "Aide" + Pomoc + + + + help_quit_title + "Quitter l'application" + Zamknij aplikację + + + + quit_app_question + "Quitter %1 ?" + Zamknąć %1 ? + + + + drawer_menu_add_account + "Ajouter un compte" + Dodaj konto + + + + MainWindow + + + information_popup_connexion_succeed_title + "Connexion réussie" + Nawiązano połączenie + + + + information_popup_connexion_succeed_message + "Vous êtes connecté en mode %1" + Jesteś zalogowany w trybie %1 + + + + interoperable + interopérable + interoperacyjny + + + + call_transfer_successful_toast_title + "Appel transféré" + Przekierowanie połączenia + + + + call_transfer_successful_toast_message + "Votre correspondant a été transféré au contact sélectionné" + Twój korespondent został przeniesiony do wybranego kontaktu. + + + + information_popup_success_title + Zapisano + + + + information_popup_changes_saved + "Les changements ont été sauvegardés" + Zmiany zostały zapisane + + + + captcha_validation_loading_message + "Veuillez valider le captcha sur la page web" + Proszę rozwiązać captcha na stronie internetowej + + + + assistant_register_error_title + "Erreur lors de la création" + Błąd podczas tworzenia + + + + assistant_register_success_title + "Compte créé" + Konto utworzone + + + + assistant_register_success_message + "Le compte a été créé. Vous pouvez maintenant vous connecter" + Konto zostało utworzone. Możesz się teraz zalogować. + + + + assistant_register_error_code + "Erreur dans le code de validation" + Błąd walidacji kodu + + + + cancel + Anuluj + + + + ManageParticipants + + + group_infos_manage_participants + + + + + MeetingForm + + + meeting_schedule_meeting_label + "Réunion" + Spotkanie + + + + meeting_schedule_broadcast_label + "Webinar" + Webinar + + + + meeting_schedule_subject_hint + "Ajouter un titre" + Dodaj tytuł + + + + meeting_schedule_description_hint + "Ajouter une description" + Dodaj opis + + + + meeting_schedule_add_participants_title + "Ajouter des participants" + Dodaj uczestników + + + + meeting_schedule_send_invitations_title + "Envoyer une invitation aux participants" + Wyślij zaproszenie do uczestników + + + + MeetingListView + + + meeting_info_cancelled + "Réunion annulée" + Spotkanie odwołane + + + + meetings_list_no_meeting_for_today + "Aucune réunion aujourd'hui" + Brak spotkań na dziś + + + + meeting_info_delete + "Supprimer la réunion" + Usuń spotkanie + + + + MeetingPage + + + meetings_add + "Créer une réunion" + Utwórz spotkanie + + + + meetings_list_empty + "Aucune réunion" + Brak spotkań + + + + meeting_schedule_cancel_dialog_message + "Souhaitez-vous annuler et supprimer cette réunion ?" + Czy chcesz anulować i usunąć to spotkanie? + + + + meeting_schedule_delete_dialog_message + Souhaitez-vous supprimer cette réunion ? + Czy chcesz usunąć to spotkanie? + + + + meeting_schedule_cancel_and_delete_action + "Annuler et supprimer" + Anuluj i usuń + + + + meeting_schedule_delete_only_action + "Supprimer seulement" + Tylko usuń + + + + meeting_schedule_delete_action + "Supprimer" + Usuń + + + + back_action + Retour + Wstecz + + + + meetings_list_title + Réunions + Spotkania + + + + meetings_search_hint + "Rechercher une réunion" + Znajdź spotkanie + + + + list_filter_no_result_found + "Aucun résultat…" + Brak wyników… + + + + meetings_empty_list + "Aucune réunion" + Brak spotkań + + + + + meeting_schedule_title + "Nouvelle réunion" + Nowe spotkanie + + + + create + Utwórz + + + + + + + + + information_popup_error_title + Błąd + + + + + meeting_schedule_mandatory_field_not_filled_toast + Veuillez saisir un titre et sélectionner au moins un participant + Proszę wpisać tytuł i wybrać co najmniej jednego uczestnika. + + + + + meeting_schedule_duration_error_toast + "La fin de la conférence doit être plus récente que son début" + Koniec konferencji musi być później niż jej początek. + + + + + meeting_schedule_creation_in_progress + "Création de la réunion en cours …" + Tworzenie w toku… + + + + meeting_info_created_toast + "Réunion planifiée avec succès" + Spotkanie zostało pomyślnie utworzone + + + + meeting_failed_to_schedule_toast + "Échec de création de la réunion !" + Nie udało się utworzyć spotkania! + + + + save + Zapisz + + + + + saved + "Enregistré" + Zapisano + + + + meeting_info_updated_toast + "Réunion mise à jour" + Zaktualizowano spotkanie + + + + meeting_schedule_edit_in_progress + "Modification de la réunion en cours…" + Aktualizacja spotkania w toku… + + + + meeting_failed_to_edit_toast + "Échec de la modification de la réunion !" + Aktualizacja spotkania nie powiodła się ! + + + + meeting_schedule_add_participants_title + "Ajouter des participants" + Dodaj uczestników + + + + meeting_schedule_add_participants_apply + + + + + group_call_participant_selected + "%n participant(s) sélectionné(s)" + + + + + + + + + meeting_info_delete + "Supprimer la réunion" + Usuń spotkanie + + + + meeting_address_copied_to_clipboard_toast + "Adresse de la réunion copiée" + URI spotkania skopiowany + + + + meeting_schedule_timezone_title + "Fuseau horaire" + Strefa czasowa + + + + meeting_info_organizer_label + "Organisateur" + Organizator + + + + meeting_info_join_title + "Rejoindre la réunion" + Dołącz do spotkania + + + + MeetingsSettingsLayout + + + settings_meetings_display_title + "Affichage" + Wyświetlacz + + + + settings_meetings_default_layout_title + "Mode d’affichage par défaut" + + + + + settings_meetings_default_layout_subtitle + "Le mode d’affichage des participants en réunions" + + + + + MessageImdnStatusInfos + + + message_details_status_title + Message status + + + + + MessageReactionsInfos + + + message_details_reactions_title + Reactions + + + + + click_to_delete_reaction_info + Click to delete + + + + + MessageSharedFilesInfos + + + no_shared_medias + No media + + + + + no_shared_documents + No document + + + + + MultimediaSettings + + + + multimedia_settings_ringer_title + Ringtone - Incoming calls + + + + + + + + choose_something_accessible_name + Choose %1 + + + + + + + multimedia_settings_speaker_title + "Haut-parleurs" + + + + + + device_volume_accessible_name + %1 volume + + + + + + + multimedia_settings_microphone_title + "Microphone" + + + + + + multimedia_settings_camera_title + "Caméra" + + + + + NetworkSettingsLayout + + + settings_network_title + "Réseau" + + + + + settings_network_allow_ipv6 + "Autoriser l'IPv6" + + + + + NewCallForm + + + call_transfer_active_calls_label + "Appels en cours" + + + + + call_start_group_call_title + Appel de groupe + Rozmowa grupowa + + + + NewChatForm + + + chat_start_group_chat_title + Nouveau groupe + + + + + NotificationReceivedCall + + + call_audio_incoming + "Appel entrant" + Połączenie Przychodzące + + + + dialog_accept + "Accepter" + Akceptuj + + + + dialog_deny + "Refuser + Odmów + + + + Notifier + + + new_call_alert_accessible_name + New call from %1 + + + + + new_voice_message + 'Voice message received!' : message to warn the user in a notofication for voice messages. + + + + + new_file_message + + + + + new_conference_invitation + 'Conference invitation received!' : Notification about receiving an invitation to a conference. + + + + + new_chat_room_messages + 'New messages received!' Notification that warn the user of new messages. + + + + + new_message_alert_accessible_name + New message on chatroom %1 + + + + + OIDCModel + + + OAuthHttpServerReplyHandler is not listening + + + + + oidc_authentication_timeout_message + Timeout: Not authenticated + + + + + oidc_authentication_granted_message + Authentication granted + + + + + oidc_authentication_not_authenticated_message + Not authenticated + + + + + oidc_authentication_refresh_message + Refreshing token + + + + + oidc_authentication_temporary_credentials_message + Temporary credentials received + + + + + oidc_authentication_network_error + Network error + + + + + oidc_authentication_server_error + Server error + + + + + oidc_authentication_token_not_found_error + OAuth token not found + + + + + oidc_authentication_token_secret_not_found_error + OAuth token secret not found + + + + + oidc_authentication_callback_not_verified_error + OAuth callback not verified + + + + + oidc_authentication_request_auth_message + Requesting authorization from browser + + + + + oidc_authentication_no_token_found_error + + + + + oidc_authentication_request_token_message + Requesting access token + + + + + oidc_authentication_refresh_token_message + Refreshing access token + + + + + oidc_authentication_request_authorization_message + Requesting authorization + + + + + oidc_authentication_request_temporary_credentials_message + Requesting temporary credentials + + + + + oidc_authentication_no_auth_found_in_config_error + No authorization endpoint found in OpenID configuration + + + + + oidc_authentication_no_token_found_in_config_error + No token endpoint found in OpenID configuration + + + + + ParticipantListView + + + meeting_participant_is_admin_label + "Admin" + + + + + meeting_add_participants_title + "Ajouter des participants" + Dodaj uczestników + + + + PhoneNumberInput + + + prefix_phone_number_accessible_name + %1 prefix + + + + + number_phone_number_accessible_name + %1 number + + + + + PopupButton + + + close_popup_panel_accessible_name + "Close %1 popup" + + + + + open_popup_panel_accessible_name + "Open %1" popup + + + + + Presence + + + contact_presence_reset_status + + + + + contact_presence_button_set_custom_status + + + + + contact_presence_button_edit_custom_status + Edytuj + + + + contact_presence_button_delete_custom_status + Usuń + + + + contact_presence_custom_status + + + + + PresenceNoteLayout + + + contact_presence_note_title + + + + + PresenceSetCustomStatus + + + contact_presence_button_set_custom_status_title + + + + + contact_presence_button_save_custom_status + Zapisz + + + + QObject + + + media_encryption_dtls + DTLS + + + + media_encryption_none + Żaden + + + + media_encryption_srtp + SRTP + + + + media_encryption_post_quantum + "ZRTP - Post quantique" + Postkwantowy ZRTP + + + + message_state_idle + "idle" + + + + + message_state_in_progress + "delivery in progress" + + + + + message_state_delivered + sent + + + + + message_state_not_delivered + error + + + + + message_state_file_transfer_error + cannot get file from server + + + + + message_state_file_transfer_done + file transfer has been completed successfully + + + + + message_state_delivered_to_user + received + + + + + message_state_displayed + read + + + + + message_state_file_transfer__in_progress + file transfer in progress + + + + + message_state_pending_delivery + pending delivery + + + + + message_state_file_transfer_cancelling + file transfer canceled + + + + + incoming + "Entrant" + + + + + outgoing + "Sortant" + + + + + conference_layout_active_speaker + "Participant actif" + Głośnik aktywny + + + + conference_layout_grid + "Mosaïque" + Siatka + + + + conference_layout_audio_only + "Audio uniquement" + Tylko Audio + + + + RegisterCheckingPage + + + email + "email" + + + + + phone_number + "numéro de téléphone" + + + + + confirm_register_title + "Inscription | Confirmer votre %1" + + + + + assistant_account_creation_confirmation_explanation + Nous vous avons envoyé un code de vérification sur votre %1 %2<br> Merci de le saisir ci-dessous + + + + + assistant_account_creation_confirmation_did_not_receive_code + "Vous n'avez pas reçu le code ?" + + + + + assistant_account_creation_confirmation_resend_code + "Renvoyer un code" + + + + + RegisterPage + + + return_accessible_name + Return + + + + + assistant_account_register + "Inscription + Zarejestruj się + + + + assistant_already_have_an_account + + + + + assistant_account_login + Połączenie + + + + assistant_account_register_with_phone_number + + + + + assistant_account_register_with_email + + + + + + username + Nazwa użytkownika + + + + + + + + mandatory_field_accessible_name + "%1 mandatory" + + + + + domain + + + + + + + phone_number + "Numéro de téléphone" + + + + + + email + + + + + + password + Hasło + + + + + assistant_account_register_password_confirmation + "Confirmation mot de passe" + + + + + assistant_dialog_cgu_and_privacy_policy_message + "J'accepte les %1 et la %2" + + + + + assistant_dialog_general_terms_label + "conditions d'utilisation" + + + + + assistant_dialog_privacy_policy_label + "politique de confidentialité" + + + + + assistant_account_create + "Créer" + Utwórz + + + + assistant_account_create_missing_username_error + "Veuillez entrer un nom d'utilisateur" + Proszę wprowadzić nazwę użytkownika + + + + assistant_account_create_missing_password_error + "Veuillez entrer un mot de passe" + Proszę wprowadzić hasło + + + + assistant_account_create_confirm_password_error + "Les mots de passe sont différents" + + + + + assistant_account_create_missing_number_error + "Veuillez entrer un numéro de téléphone" + + + + + assistant_account_create_missing_email_error + "Veuillez entrer un email" + + + + + SIPLoginPage + + + return_accessible_name + Return + + + + + assistant_login_third_party_sip_account_title + Compte SIP tiers + Konto SIP innej firmy + + + + assistant_no_account_yet + Pas encore de compte ? + Nie masz jeszcze konta? + + + + assistant_account_register + S'inscrire + Zarejestruj się + + + + Certaines fonctionnalités telles que les conversations de groupe, les vidéo-conférences, etc… nécessitent un compte %1. + +Ces fonctionnalités seront masquées si vous utilisez un compte SIP tiers. + +Pour les activer dans un projet commercial, merci de nous contacter. + + + + + assistant_third_party_sip_account_create_linphone_account + "Créer un compte linphone" + + + + + assistant_third_party_sip_account_warning_ok + "Je comprends" + + + + + + username + "Nom d'utilisateur" + Nazwa użytkownika + + + + + mandatory_field_accessible_name + "%1 mandatory" + + + + + + password + Hasło + + + + + sip_address_domain + "Domaine" + + + + + + sip_address_display_name + Nom d'affichage + Wyświetlana nazwa + + + + + transport + "Transport" + Transport + + + + + assistant_account_login + Połączenie + + + + assistant_account_login_missing_username + Proszę wprowadzić nazwę użytkownika + + + + assistant_account_login_missing_password + Proszę wprowadzić hasło + + + + assistant_account_login_missing_domain + "Veuillez saisir un nom de domaine + + + + + login_advanced_parameters_label + Advanced parameters + + + + + + login_proxy_server_url + "Outbound SIP Proxy URI" + + + + + login_proxy_server_url_tooltip + "If this field is filled, the outbound proxy will be enabled automatically. Leave it empty to disable it." + + + + + + login_registrar_uri + "Registrar URI" + + + + + + login_id + "Authentication ID (if different)" + + + + + ScreencastSettings + + + screencast_settings_choose_window_text + "Veuillez choisir l’écran ou la fenêtre que vous souihaitez partager au autres participants" + + + + + screencast_settings_all_screen_label + "Ecran entier" + + + + + screencast_settings_one_window_label + "Fenêtre" + + + + + screencast_settings_screen + "Ecran %1" + + + + + stop + "Stop + + + + + share + "Partager" + Udostępnij + + + + SearchBar + + + open_dialer_acccessibility_label + "Open dialer" + + + + + clear_text_input_acccessibility_label + "Clear text input" + + + + + SecurityModePage + + + manage_account_choose_mode_title + "Choisir votre mode" + + + + + manage_account_choose_mode_message + "Vous pourrez changer de mode plus tard." + + + + + manage_account_e2e_encrypted_mode_default_title + "Chiffrement de bout en bout" + + + + + manage_account_e2e_encrypted_mode_default_summary + "Ce mode vous garanti la confidentialité de tous vos échanges. Notre technologie de chiffrement de bout en bout assure un niveau de sécurité maximal pour tous vos échanges." + + + + + manage_account_e2e_encrypted_mode_interoperable_title + "Interoperable" + + + + + manage_account_e2e_encrypted_mode_interoperable_summary + "Ce mode vous permet de profiter de toute les fonctionnalités de Linphone, toute en restant interopérable avec n’importe qu’elle autre service SIP." + + + + + dialog_continue + "Continuer" + + + + + SecuritySettingsLayout + + + settings_security_enable_vfs_title + "Chiffrer tous les fichiers" + + + + + settings_security_enable_vfs_subtitle + "Attention, vous ne pourrez pas revenir en arrière !" + + + + + SelectedChatView + + + chat_view_group_call_toast_message + + + + + unencrypted_conversation_warning + This conversation is not encrypted ! + + + + + reply_to_label + Reply to %1 + + + + + shared_medias_title + Shared medias + + + + + shared_documents_title + Shared documents + + + + + forward_to_title + Forward to… + + + + + conversations_title + Conversations + Konwersacje + + + + SettingsMenuItem + + + setting_tab_accessible_name + %1 settings + + + + + SettingsPage + + + settings_title + "Paramètres" + Ustawienia + + + + settings_calls_title + "Appels" + Połączenia + + + + settings_call_forward + "Transfert d'appel" + + + + + settings_conversations_title + "Conversations" + Konwersacje + + + + settings_contacts_title + "Contacts" + Kontakty + + + + settings_meetings_title + "Réunions" + Spotkania + + + + settings_network_title + "Affichage" "Security" "Réseau" + + + + + settings_advanced_title + "Paramètres avancés" + + + + + contact_editor_popup_abort_confirmation_title + Modifications non enregistrées + Niezapisane zmiany + + + + contact_editor_popup_abort_confirmation_message + Vous avez des modifications non enregistrées. Si vous quittez cette page, vos changements seront perdus. Voulez-vous enregistrer vos modifications avant de continuer ? + Masz niezapisane zmiany. Jeśli opuścisz tę stronę, Twoje zmiany zostaną utracone. Czy chcesz zapisać zmiany zanim kontynuujesz? + + + + contact_editor_dialog_abort_confirmation_do_not_save + "Ne pas enregistrer" + Nie zapisuj + + + + contact_editor_dialog_abort_confirmation_save + "Enregistrer" + Zapisz + + + + Sticker + + + conference_participant_joining_text + "rejoint…" + + + + + conference_participant_paused_text + "En pause" + + + + + TextField + + + show_accessible_name + Show %1 + + + + + hide_accessible_name + Hide %1 + + + + + ToolModel + + + call_error_uninterpretable_sip_address + "The calling address is not an interpretable SIP address : %1 + + + + + group_call_error_no_account + + + + + group_call_error_participants_invite + + + + + group_call_error_creation + + + + + voice_recording_duration + "Voice recording (%1)" : %1 is the duration formated in mm:ss + + + + + unknown_audio_device_name + + + + + conference_invitation + + + + + conference_invitation_cancelled + + + + + conference_invitation_updated + + + + + Utils + + + nSeconds + + + + + + + + + nMinute + + + + + + + + + chat_message_forward_error + Cannot forward an invalid message + + + + + info_popup_forward_message_error + Could not forward message : %1 + + + + + info_popup_send_forward_message_error_message + Failed to create forward message + + + + + chat_message_reply_error + Cannot reply to invalid message + + + + + info_popup_reply_message_error + Could not send reply message : %1 + + + + + info_popup_send_reply_message_error_message + Failed to create reply message + + + + + nHour + + + + + + + + + + nDay + + + + + + + + + nWeek + + + + + + + + + contact_presence_status_available + + + + + contact_presence_status_busy + Zajęty + + + + contact_presence_status_do_not_disturb + Nie przeszkadzać + + + + contact_presence_status_offline + Offline + + + + contact_presence_status_away + + + + + information_popup_call_not_created_message + "L'appel n'a pas pu être créé" + + + + + + + + information_popup_error_title + Error +---------- +Failed to create 1-1 conversation with %1 ! + Błąd + + + + information_popup_group_call_not_created_message + + + + + number_of_years + %n an(s) + + + + + + + + + number_of_month + "%n mois" + + + + + + + + + number_of_weeks + %n semaine(s) + + + + + + + + + number_of_days + %n jour(s) + + + + + + + + + today + "Aujourd'hui" + + + + + yesterday + "Hier + + + + + duration_tomorrow + Tomorrow + + + + + duration_number_of_days + %1 jour(s) + + + + + + + + + call_zrtp_token_verification_possible_characters + "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + + + + + + information_popup_chatroom_creation_error_message + Failed to create 1-1 conversation with %1 ! + + + + + recorder_error + Error with the recorder + + + + + + + chat_error + + + + + + + + + + info_popup_error_title + Error + Błąd + + + + info_popup_send_voice_message_error_message + Could not send voice message : %1 + + + + + info_popup_send_voice_message_sending_error_message + Failed to create message from record + + + + + WaitingRoom + + + meeting_waiting_room_title + Participer à : + + + + + meeting_waiting_room_join + "Rejoindre" + + + + + + cancel + Cancel + Anuluj + + + + meeting_waiting_room_joining_title + "Connexion à la réunion" + + + + + meeting_waiting_room_joining_subtitle + "Vous allez rejoindre la réunion dans quelques instants…" + + + + + WelcomePage + + + welcome_page_title + "Bienvenue" + + + + + welcome_page_subtitle + "sur %1" + + + + + welcome_carousel_skip + "Passer" + + + + + welcome_page_1_message + "Une application de communication <b>sécurisée</b>,<br> <b>open source</b> et <b>française</b>." + + + + + welcome_page_2_title + "Sécurisé" + + + + + welcome_page_2_message + "Vos communications sont en sécurité grâce aux <br><b>Chiffrement de bout en bout</b>." + + + + + welcome_page_3_title + "Open Source" + + + + + welcome_page_3_message + "Une application open source et un <b>service gratuit</b> <br>depuis <b>2001</b>" + + + + + next + "Suivant" + + + + + start + "Commencer" + Rozpocznij + + + + ZrtpAuthenticationDialog + + + call_dialog_zrtp_validate_trust_title + Vérification de sécurité + + + + + call_zrtp_sas_validation_skip + "Passer" + + + + + call_dialog_zrtp_validate_trust_warning_message + "Pour garantir le chiffrement, nous avons besoin de réauthentifier l’appareil de votre correspondant. Echangez vos codes :" + + + + + call_dialog_zrtp_validate_trust_message + "Pour garantir le chiffrement, nous avons besoin d’authentifier l’appareil de votre correspondant. Veuillez échanger vos codes : " + + + + + call_dialog_zrtp_validate_trust_local_code_label + "Votre code :" + + + + + call_dialog_zrtp_validate_trust_remote_code_label + "Code correspondant :" + + + + + call_dialog_zrtp_validate_trust_letters_do_not_match_text + "Le code fourni ne correspond pas." + + + + + call_dialog_zrtp_security_alert_message + "La confidentialité de votre appel peut être compromise !" + + + + + call_dialog_zrtp_validate_trust_letters_do_not_match + "Aucune correspondance" + + + + + call_action_hang_up + "Raccrocher" + Odrzuć + + + + country + + + Afghanistan + + + + + Albania + + + + + Algeria + + + + + AmericanSamoa + + + + + Andorra + + + + + Angola + + + + + Anguilla + + + + + AntiguaAndBarbuda + + + + + Argentina + + + + + Armenia + + + + + Aruba + + + + + Australia + + + + + Austria + + + + + Azerbaijan + + + + + Bahamas + + + + + Bahrain + + + + + Bangladesh + + + + + Barbados + + + + + Belarus + + + + + Belgium + + + + + Belize + + + + + Benin + + + + + Bermuda + + + + + Bhutan + + + + + Bolivia + + + + + BosniaAndHerzegowina + + + + + Botswana + + + + + Brazil + + + + + Brunei + + + + + Bulgaria + + + + + BurkinaFaso + + + + + Burundi + + + + + Cambodia + + + + + Cameroon + + + + + Canada + + + + + CapeVerde + + + + + CaymanIslands + + + + + CentralAfricanRepublic + + + + + Chad + + + + + Chile + + + + + China + + + + + Colombia + + + + + Comoros + + + + + PeoplesRepublicOfCongo + + + + + CookIslands + + + + + CostaRica + + + + + IvoryCoast + + + + + Croatia + + + + + Cuba + + + + + Cyprus + + + + + CzechRepublic + + + + + Denmark + + + + + Djibouti + + + + + Dominica + + + + + DominicanRepublic + + + + + Ecuador + + + + + Egypt + + + + + ElSalvador + + + + + EquatorialGuinea + + + + + Eritrea + + + + + Estonia + + + + + Ethiopia + + + + + FalklandIslands + + + + + FaroeIslands + + + + + Fiji + + + + + Finland + + + + + France + + + + + FrenchGuiana + + + + + FrenchPolynesia + + + + + Gabon + + + + + Gambia + + + + + Georgia + + + + + Germany + + + + + Ghana + + + + + Gibraltar + + + + + Greece + + + + + Greenland + + + + + Grenada + + + + + Guadeloupe + + + + + Guam + + + + + Guatemala + + + + + Guinea + + + + + GuineaBissau + + + + + Guyana + + + + + Haiti + + + + + Honduras + + + + + DemocraticRepublicOfCongo + + + + + HongKong + + + + + Hungary + + + + + Iceland + + + + + India + + + + + Indonesia + + + + + Iran + + + + + Iraq + + + + + Ireland + + + + + Israel + + + + + Italy + + + + + Jamaica + + + + + Japan + + + + + Jordan + + + + + Kazakhstan + + + + + Kenya + + + + + Kiribati + + + + + DemocraticRepublicOfKorea + + + + + RepublicOfKorea + + + + + Kuwait + + + + + Kyrgyzstan + + + + + Laos + + + + + Latvia + + + + + Lebanon + + + + + Lesotho + + + + + Liberia + + + + + Libya + + + + + Liechtenstein + + + + + Lithuania + + + + + Luxembourg + + + + + Macau + + + + + Macedonia + + + + + Madagascar + + + + + Malawi + + + + + Malaysia + + + + + Maldives + + + + + Mali + + + + + Malta + + + + + MarshallIslands + + + + + Martinique + + + + + Mauritania + + + + + Mauritius + + + + + Mayotte + + + + + Mexico + + + + + Micronesia + + + + + Moldova + + + + + Monaco + + + + + Mongolia + + + + + Montenegro + + + + + Montserrat + + + + + Morocco + + + + + Mozambique + + + + + Myanmar + + + + + Namibia + + + + + NauruCountry + + + + + Nepal + + + + + Netherlands + + + + + NewCaledonia + + + + + NewZealand + + + + + Nicaragua + + + + + Niger + + + + + Nigeria + + + + + Niue + + + + + NorfolkIsland + + + + + NorthernMarianaIslands + + + + + Norway + + + + + Oman + + + + + Pakistan + + + + + Palau + + + + + PalestinianTerritories + + + + + Panama + + + + + PapuaNewGuinea + + + + + Paraguay + + + + + Peru + + + + + Philippines + + + + + Poland + + + + + Portugal + + + + + PuertoRico + + + + + Qatar + + + + + Reunion + + + + + Romania + + + + + RussianFederation + + + + + Rwanda + + + + + SaintHelena + + + + + SaintKittsAndNevis + + + + + SaintLucia + + + + + SaintPierreAndMiquelon + + + + + SaintVincentAndTheGrenadines + + + + + Samoa + + + + + SanMarino + + + + + SaoTomeAndPrincipe + + + + + SaudiArabia + + + + + Senegal + + + + + Serbia + + + + + Seychelles + + + + + SierraLeone + + + + + Singapore + + + + + Slovakia + + + + + Slovenia + + + + + SolomonIslands + + + + + Somalia + + + + + SouthAfrica + + + + + Spain + + + + + SriLanka + + + + + Sudan + + + + + Suriname + + + + + Swaziland + + + + + Sweden + + + + + Switzerland + + + + + Syria + + + + + Taiwan + + + + + Tajikistan + + + + + Tanzania + + + + + Thailand + + + + + Togo + + + + + Tokelau + + + + + Tonga + + + + + TrinidadAndTobago + + + + + Tunisia + + + + + Turkey + + + + + Turkmenistan + + + + + TurksAndCaicosIslands + + + + + Tuvalu + + + + + Uganda + + + + + Ukraine + + + + + UnitedArabEmirates + + + + + UnitedKingdom + + + + + UnitedStates + + + + + Uruguay + + + + + Uzbekistan + + + + + Vanuatu + + + + + Venezuela + + + + + Vietnam + + + + + WallisAndFutunaIslands + + + + + Yemen + + + + + Zambia + + + + + Zimbabwe + + + + + utils + + + formatYears + '%1 year' + + + + + + + + + formatMonths + '%1 month' + + + + + + + + + formatWeeks + '%1 week' + + + + + + + + + formatDays + '%1 day' + + + + + + + + + formatHours + '%1 hour' + + + + + + + + + formatMinutes + '%1 minute' + + + + + + + + + formatSeconds + '%1 second' + + + + + + + + + codec_install + "Installation de codec" + + + + + download_codec + "Télécharger le codec %1 (%2) ?" + + + + + information_popup_success_title + "Succès" + Sukces + + + + information_popup_codec_install_success_text + "Le codec a été installé avec succès." + + + + + + + information_popup_error_title + Błąd + + + + information_popup_codec_install_error_text + "Le codec n'a pas pu être installé." + + + + + information_popup_codec_save_error_text + "Le codec n'a pas pu être sauvegardé." + + + + + information_popup_codec_download_error_text + "Le codec n'a pas pu être téléchargé." + + + + + loading_popup_codec_install_progress + "Téléchargement en cours …" + + + + + okButton + OK + + + diff --git a/Linphone/data/languages/pt.ts b/Linphone/data/languages/pt.ts index 65de2cf2a..c4c2704d6 100644 --- a/Linphone/data/languages/pt.ts +++ b/Linphone/data/languages/pt.ts @@ -304,6 +304,11 @@ "No information" + + + copied + Copiado + AccountSettingsPage @@ -361,23 +366,6 @@ settings_account_title Parâmetros de conta - - - info_popup_invalid_registrar_uri_message - Registrar uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - - - - - info_popup_invalid_outbound_proxy_message - Outbound proxy uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - - - - - info_popup_error_title - Erro - information_popup_success_title @@ -406,12 +394,6 @@ "URI de messagerie vocale" URI Correio de voz - - - account_settings_transport_title - "Transport" - Transporte - account_settings_registrar_uri_title @@ -891,31 +873,6 @@ settings_call_forward_address_cannot_be_empty - - - settings_call_forward_address_timeout - - - - - settings_call_forward_address_progress_disabling - - - - - settings_call_forward_address_progress_enabling - - - - - settings_call_forward_activation_success - - - - - settings_call_forward_deactivation_success - - CallHistoryLayout @@ -1138,7 +1095,7 @@ call_error_server_timeout_toast - "Server tiemout" + "Server timeout" @@ -2484,6 +2441,11 @@ Error Creation de la conversation en cours … + + + info_popup_error_title + Erro + ChatSettingsLayout @@ -4324,9 +4286,9 @@ Error - - information_popup_error_title - Erro + + cancel + Cancelar diff --git a/Linphone/data/languages/pt_BR.ts b/Linphone/data/languages/pt_BR.ts index aebcd953f..547a82c4c 100644 --- a/Linphone/data/languages/pt_BR.ts +++ b/Linphone/data/languages/pt_BR.ts @@ -113,7 +113,7 @@ assistant_account_login_proxy_address_error "Unable to create proxy address. Please check the domain name." - Impossível criar endereço do proxy. Por favor verifique o nome do domínio. + Impossível criar endereço do proxy. Por favor, verifique o nome do domínio. @@ -145,6 +145,16 @@ "Unable to add account." Impossível adicionar conta. + + + assistant_account_login_registrar_uri_error + Registrar url ié nválida. Por favor, certifique-se de que ela segue o formato: sip:host>:<port>;transport=<transport> (:<port> é opcional) + + + + assistant_account_login_outbound_proxy_uri_error + Url do proxy de saída inválida. Por favor, certifique-se de que ela segue o formato: sip:host>:<port>;transport=<transport> (:<port> é opcional) + AccountModel @@ -164,7 +174,7 @@ set_outbound_proxy_uri_failed_error_message Unable to set outbound proxy uri, failed creating address from %1 - Não foi possível definir URI do proxy de saída, falha ao criar endereço de %1 + Não foi possível definir URI do proxy de saída, falha ao criar endereço de %1 @@ -304,6 +314,21 @@ "No information" Sem informação + + + copied + Copiado + + + + account_settings_sip_address_copied_error_message + Erro copiando seu endereço SIP + + + + account_settings_sip_address_copied_message + O seu endereço SIP foi copiado para a área de transferência + AccountSettingsPage @@ -361,23 +386,6 @@ settings_account_title Configurações de conta - - - info_popup_invalid_registrar_uri_message - Registrar uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - - - - - info_popup_invalid_outbound_proxy_message - Outbound proxy uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - O URI do proxy de saída está inválido. Por favor, certifique-se que ele está no seguinte formato: sip:<host>:<porta>;transport=<transporte> (:<porta> é opcional) - - - - info_popup_error_title - Erro - information_popup_success_title @@ -406,12 +414,6 @@ "URI de messagerie vocale" URI do correio de voz - - - account_settings_transport_title - "Transport" - Transporte - account_settings_registrar_uri_title @@ -891,31 +893,6 @@ settings_call_forward_address_cannot_be_empty Um número ou endereço SIP é exigido - - - settings_call_forward_address_timeout - Não foi possível definir encaminhamento de chamada, tempo de requisição esgotado - - - - settings_call_forward_address_progress_disabling - Desabilitando encaminhamento de chamada - - - - settings_call_forward_address_progress_enabling - Habilitando encaminhamento de chamada para: - - - - settings_call_forward_activation_success - Encaminhamento de chamada ativado para: - - - - settings_call_forward_deactivation_success - Encaminhamento de chamada desativado - CallHistoryLayout @@ -1138,8 +1115,8 @@ call_error_server_timeout_toast - "Server tiemout" - Tempo esgotado do servidor + "Server timeout" + Tempo esgotado do servidor @@ -1918,7 +1895,7 @@ settings_contacts_carddav_popup_synchronization_error_message "Erreur de synchronisation!" - Erro de sincronização! + Erro de sincronização! @@ -2486,6 +2463,11 @@ Apenas seu correspondente pode descriptografá-las. Creation de la conversation en cours … Criando conversa… + + + info_popup_error_title + Erro + ChatSettingsLayout @@ -4326,9 +4308,9 @@ Apenas seu correspondente pode descriptografá-las. Erro no código de validação - - information_popup_error_title - Erro + + cancel + Cancelar @@ -5612,7 +5594,7 @@ Para habilitá-las em um projeto comercial, por favor, entre em contato conosco. forward_to_title Forward to… - Encaminhar para… + Encaminhar para… @@ -6669,7 +6651,7 @@ Failed to create 1-1 conversation with %1 ! Italy - Itália + Itália @@ -7317,9 +7299,9 @@ Failed to create 1-1 conversation with %1 ! formatMonths '%1 month' - - - + + um mês + %1 meses diff --git a/Linphone/data/languages/ru.ts b/Linphone/data/languages/ru.ts index 245f37034..445869602 100644 --- a/Linphone/data/languages/ru.ts +++ b/Linphone/data/languages/ru.ts @@ -304,6 +304,11 @@ "No information" + + + copied + Скопированный + AccountSettingsPage @@ -361,23 +366,6 @@ settings_account_title Настройки учётной записи - - - info_popup_invalid_registrar_uri_message - Registrar uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - - - - - info_popup_invalid_outbound_proxy_message - Outbound proxy uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - - - - - info_popup_error_title - Ошибка - information_popup_success_title @@ -406,12 +394,6 @@ "URI de messagerie vocale" URI голосовой почты - - - account_settings_transport_title - "Transport" - Транспорт - account_settings_registrar_uri_title @@ -892,31 +874,6 @@ settings_call_forward_address_cannot_be_empty - - - settings_call_forward_address_timeout - - - - - settings_call_forward_address_progress_disabling - - - - - settings_call_forward_address_progress_enabling - - - - - settings_call_forward_activation_success - - - - - settings_call_forward_deactivation_success - - CallHistoryLayout @@ -1139,8 +1096,8 @@ call_error_server_timeout_toast - "Server tiemout" - Тайм-аут сервера + "Server timeout" + Тайм-аут сервера @@ -1920,7 +1877,7 @@ settings_contacts_carddav_popup_synchronization_error_message "Erreur de synchronisation!" - Ошибка синхронизации! + Ошибка синхронизации! @@ -2488,6 +2445,11 @@ Error Creation de la conversation en cours … + + + info_popup_error_title + Ошибка + ChatSettingsLayout @@ -4328,9 +4290,9 @@ Error Ошибка в коде проверки - - information_popup_error_title - Ошибка + + cancel + Отмена @@ -6682,7 +6644,7 @@ Failed to create 1-1 conversation with %1 ! Italy - Италия + Италия diff --git a/Linphone/data/languages/sk.ts b/Linphone/data/languages/sk.ts index 558c38d6d..665a06a48 100644 --- a/Linphone/data/languages/sk.ts +++ b/Linphone/data/languages/sk.ts @@ -361,23 +361,6 @@ settings_account_title - - - info_popup_invalid_registrar_uri_message - Registrar uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - - - - - info_popup_invalid_outbound_proxy_message - Outbound proxy uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - - - - - info_popup_error_title - - information_popup_success_title @@ -406,12 +389,6 @@ "URI de messagerie vocale" - - - account_settings_transport_title - "Transport" - - account_settings_registrar_uri_title @@ -892,31 +869,6 @@ settings_call_forward_address_cannot_be_empty - - - settings_call_forward_address_timeout - - - - - settings_call_forward_address_progress_disabling - - - - - settings_call_forward_address_progress_enabling - - - - - settings_call_forward_activation_success - - - - - settings_call_forward_deactivation_success - - CallHistoryLayout @@ -1139,7 +1091,7 @@ call_error_server_timeout_toast - "Server tiemout" + "Server timeout" @@ -4322,11 +4274,6 @@ Error "Erreur dans le code de validation" - - - information_popup_error_title - - ManageParticipants diff --git a/Linphone/data/languages/uk.ts b/Linphone/data/languages/uk.ts index dc540ce80..f6cfe4535 100644 --- a/Linphone/data/languages/uk.ts +++ b/Linphone/data/languages/uk.ts @@ -304,6 +304,11 @@ "No information" + + + copied + Скопійовано + AccountSettingsPage @@ -361,23 +366,6 @@ settings_account_title Налаштування облікового запису - - - info_popup_invalid_registrar_uri_message - Registrar uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - - - - - info_popup_invalid_outbound_proxy_message - Outbound proxy uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - - - - - info_popup_error_title - Помилка - information_popup_success_title @@ -406,12 +394,6 @@ "URI de messagerie vocale" URI голосової пошти - - - account_settings_transport_title - "Transport" - Транспорт - account_settings_registrar_uri_title @@ -892,31 +874,6 @@ settings_call_forward_address_cannot_be_empty - - - settings_call_forward_address_timeout - - - - - settings_call_forward_address_progress_disabling - - - - - settings_call_forward_address_progress_enabling - - - - - settings_call_forward_activation_success - - - - - settings_call_forward_deactivation_success - - CallHistoryLayout @@ -1139,8 +1096,8 @@ call_error_server_timeout_toast - "Server tiemout" - Час очікування сервера + "Server timeout" + Час очікування сервера @@ -1920,7 +1877,7 @@ settings_contacts_carddav_popup_synchronization_error_message "Erreur de synchronisation!" - Помилка синхронізації! + Помилка синхронізації! @@ -2488,6 +2445,11 @@ Error Creation de la conversation en cours … + + + info_popup_error_title + Помилка + ChatSettingsLayout @@ -4328,9 +4290,9 @@ Error Помилка в коді перевірки - - information_popup_error_title - Помилка + + cancel + Скасувати @@ -6682,7 +6644,7 @@ Failed to create 1-1 conversation with %1 ! Italy - Італія + Італія diff --git a/Linphone/data/languages/zh_Hans.ts b/Linphone/data/languages/zh_Hans.ts index 7e4f33e58..9713a3abb 100644 --- a/Linphone/data/languages/zh_Hans.ts +++ b/Linphone/data/languages/zh_Hans.ts @@ -164,7 +164,7 @@ set_outbound_proxy_uri_failed_error_message Unable to set outbound proxy uri, failed creating address from %1 - 无法设置出站代理 URI,无法从 %1 创建地址 + 无法设置出站代理 URI,无法从 %1 创建地址 @@ -304,6 +304,11 @@ "No information" 无信息 + + + copied + 已复制 + AccountSettingsPage @@ -361,23 +366,6 @@ settings_account_title 账户设置 - - - info_popup_invalid_registrar_uri_message - Registrar uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - - - - - info_popup_invalid_outbound_proxy_message - Outbound proxy uri is invalid. Please make sure it matches the following format : sip:<host>:<port>;transport=<transport> (:<port> is optional) - - - - - info_popup_error_title - 错误 - information_popup_success_title @@ -406,12 +394,6 @@ "URI de messagerie vocale" 语音邮箱服务器URI - - - account_settings_transport_title - "Transport" - 传输 - account_settings_registrar_uri_title @@ -728,7 +710,7 @@ info_popup_new_version_download_label - + @@ -895,31 +877,6 @@ settings_call_forward_address_cannot_be_empty - - - settings_call_forward_address_timeout - - - - - settings_call_forward_address_progress_disabling - - - - - settings_call_forward_address_progress_enabling - - - - - settings_call_forward_activation_success - - - - - settings_call_forward_deactivation_success - - CallHistoryLayout @@ -1142,8 +1099,8 @@ call_error_server_timeout_toast - "Server tiemout" - 服务器连接超时 + "Server timeout" + 服务器连接超时 @@ -1921,7 +1878,7 @@ settings_contacts_carddav_popup_synchronization_error_message "Erreur de synchronisation!" - 同步错误! + 同步错误! @@ -2485,6 +2442,11 @@ Error Creation de la conversation en cours … + + + info_popup_error_title + 错误 + ChatSettingsLayout @@ -4325,9 +4287,14 @@ Error 验证码错误 - - information_popup_error_title - 错误 + + oidc_connection_waiting_message + + + + + cancel + 取消 @@ -6657,7 +6624,7 @@ Failed to create 1-1 conversation with %1 ! Italy - 意大利 + 意大利 diff --git a/Linphone/main.cpp b/Linphone/main.cpp index 92939ba45..b791ff183 100644 --- a/Linphone/main.cpp +++ b/Linphone/main.cpp @@ -3,6 +3,8 @@ #include "core/App.hpp" #include "core/logger/QtLogger.hpp" +#include "core/path/Paths.hpp" + #include #include #include @@ -22,6 +24,7 @@ FILE *gStream = NULL; #define ACCESSBILITY_WORKAROUND #include #include + void DummyUpdateHandler(QAccessibleEvent *event) { } void DummyRootObjectHandler(QObject *) { @@ -39,6 +42,7 @@ void cleanStream() { } int main(int argc, char *argv[]) { + /* #if defined _WIN32 // log in console only if launched from console @@ -48,6 +52,7 @@ int main(int argc, char *argv[]) { } #endif */ + // Useful to share camera on Fullscreen (other context) or multiscreens lDebug() << "[Main] Setting ShareOpenGLContexts"; QApplication::setAttribute(Qt::AA_ShareOpenGLContexts); @@ -63,6 +68,7 @@ int main(int argc, char *argv[]) { setlocale(LC_CTYPE, ".UTF8"); lDebug() << "[Main] Creating application"; auto app = QSharedPointer::create(argc, argv); + #ifdef ACCESSBILITY_WORKAROUND QAccessible::installUpdateHandler(DummyUpdateHandler); QAccessible::installRootObjectHandler(DummyRootObjectHandler); diff --git a/Linphone/model/account/AccountManager.cpp b/Linphone/model/account/AccountManager.cpp index 068d87fa1..f745a04c8 100644 --- a/Linphone/model/account/AccountManager.cpp +++ b/Linphone/model/account/AccountManager.cpp @@ -31,6 +31,7 @@ #include "core/path/Paths.hpp" #include "model/core/CoreModel.hpp" +#include "model/setting/SettingsModel.hpp" #include "model/tool/ToolModel.hpp" #include "tool/Utils.hpp" @@ -96,17 +97,10 @@ bool AccountManager::login(QString username, } if (!displayName.isEmpty()) identity->setDisplayName(Utils::appStringToCoreString(displayName)); - if (!registrarUri.isEmpty()) { - auto linRegistrarUri = ToolModel::interpretUrl(registrarUri); - params->setServerAddress(linRegistrarUri); - } - if (!outboundProxyAddress.isEmpty()) { - auto linOutboundProxyAddress = ToolModel::interpretUrl(outboundProxyAddress); - if (linOutboundProxyAddress) params->setRoutesAddresses({linOutboundProxyAddress}); - } + if (!domain.isEmpty()) { identity->setDomain(Utils::appStringToCoreString(domain)); - if (QString::compare(domain, "sip.linphone.org")) { + if (QString::compare(domain, SettingsModel::getInstance()->getDefaultDomain())) { params->setLimeServerUrl(""); auto computedServerAddress = factory->createAddress(Utils::appStringToCoreString(QStringLiteral("sip:%1").arg(domain))); @@ -130,6 +124,28 @@ bool AccountManager::login(QString username, return false; } + if (!registrarUri.isEmpty()) { + auto linRegistrarUri = factory->createAddress(Utils::appStringToCoreString(registrarUri)); + if (linRegistrarUri) params->setServerAddress(linRegistrarUri); + else { + //: Registrar uri is invalid. Please make sure it matches the following format : + //: sip:host>:;transport= (: is optional) + *errorMessage = tr("assistant_account_login_registrar_uri_error"); + return false; + } + } + + if (!outboundProxyAddress.isEmpty()) { + auto linOutboundProxyAddress = factory->createAddress(Utils::appStringToCoreString(outboundProxyAddress)); + if (linOutboundProxyAddress) params->setRoutesAddresses({linOutboundProxyAddress}); + else { + //: Outbound proxy uri is invalid. Please make sure it matches the following format : + //: sip:host>:;transport= (: is optional) + *errorMessage = tr("assistant_account_login_outbound_proxy_uri_error"); + return false; + } + } + if (account->setParams(params)) { //: "Unable to configure account settings." *errorMessage = tr("assistant_account_login_params_configuration_error"); @@ -169,6 +185,7 @@ bool AccountManager::login(QString username, } emit registrationStateChanged(state, account->getError(), errorMessage); }); + auto status = core->addAccount(account); if (status == -1) { //: "Unable to add account." @@ -340,10 +357,11 @@ void AccountManager::linkNewAccountUsingCode(const QString &code, const std::string &errorMessage, const std::shared_ptr ¶meterErrors) { if (request->getType() == linphone::AccountManagerServicesRequest::Type::LinkEmailUsingCode) { lInfo() << "[AccountManager] error linking email to account" << errorMessage; + emit linkingNewAccountWithCodeFailed(Utils::coreStringToAppString(errorMessage)); } else if (request->getType() == linphone::AccountManagerServicesRequest::Type::LinkPhoneNumberUsingCode) { lInfo() << "[AccountManager] error linking phone number to account" << errorMessage; + emit linkingNewAccountWithCodeFailed(Utils::coreStringToAppString(errorMessage)); } - emit linkingNewAccountWithCodeFailed(Utils::coreStringToAppString(errorMessage)); }); if (registerType == RegisterType::Email) mAccountManagerServicesModel->linkEmailToAccountUsingCode(sipIdentityAddress, diff --git a/Linphone/model/account/AccountModel.cpp b/Linphone/model/account/AccountModel.cpp index 7852e5d55..9d32fa534 100644 --- a/Linphone/model/account/AccountModel.cpp +++ b/Linphone/model/account/AccountModel.cpp @@ -125,7 +125,7 @@ void AccountModel::setPictureUri(QString uri) { // Hack because Account doesn't provide callbacks on updated data // emit pictureUriChanged(uri); auto core = CoreModel::getInstance()->getCore(); - emit CoreModel::getInstance() -> defaultAccountChanged(core, core->getDefaultAccount()); + emit CoreModel::getInstance()->defaultAccountChanged(core, core->getDefaultAccount()); } void AccountModel::onDefaultAccountChanged() { @@ -182,7 +182,7 @@ void AccountModel::setDisplayName(QString displayName) { // Hack because Account doesn't provide callbacks on updated data // emit displayNameChanged(displayName); auto core = CoreModel::getInstance()->getCore(); - emit CoreModel::getInstance() -> defaultAccountChanged(core, core->getDefaultAccount()); + emit CoreModel::getInstance()->defaultAccountChanged(core, core->getDefaultAccount()); } void AccountModel::setDialPlan(int index) { @@ -302,9 +302,11 @@ QString AccountModel::getOutboundProxyUri() const { } void AccountModel::setOutboundProxyUri(QString value) { - auto linOutboundProxyAddress = ToolModel::interpretUrl(value); + auto linOutboundProxyAddress = linphone::Factory::get()->createAddress(Utils::appStringToCoreString(value)); if (!linOutboundProxyAddress) { - //: Unable to set outbound proxy uri, failed creating address from %1 + //: Unable to set outbound proxy uri from address %1. + //: Please make sure it matches the following format : + //: sip:host>:;transport= (: is optional) emit setValueFailed(tr("set_outbound_proxy_uri_failed_error_message").arg(value)); return; } else { @@ -526,6 +528,11 @@ void AccountModel::setPresence(LinphoneEnums::Presence presence, QString presenceNote) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + if (!mMonitor->getParams()->publishEnabled()) { + lDebug() << log().arg("cannot set presence as publish is disabled in account params, return"); + return; + } + lDebug() << log().arg("presence set request to: " + LinphoneEnums::toString(presence) + " | user initiated? " + (userInitiated ? "true" : "false") + " | reset to auto? " + (resetToAuto ? "true" : "false")); @@ -561,16 +568,8 @@ void AccountModel::setPresence(LinphoneEnums::Presence presence, core->getConfig()->sync(); } - if (!presenceNote.isEmpty()) { - core->getConfig()->setString(accountSection, "presence_note", Utils::appStringToCoreString(presenceNote)); - core->getConfig()->sync(); - } - - if (!mMonitor->getParams()->publishEnabled()) { - auto params = mMonitor->getParams()->clone(); - params->enablePublish(true); - mMonitor->setParams(params); - } + core->getConfig()->setString(accountSection, "presence_note", Utils::appStringToCoreString(presenceNote)); + core->getConfig()->sync(); auto presenceModel = ToolModel::appPresenceToCorePresenceModel(presence, presenceNote); core->setPresenceModel(presenceModel); // No api (yet) at the account level diff --git a/Linphone/model/auth/OIDCModel.cpp b/Linphone/model/auth/OIDCModel.cpp index 4f3b04cd9..6659fe498 100644 --- a/Linphone/model/auth/OIDCModel.cpp +++ b/Linphone/model/auth/OIDCModel.cpp @@ -102,6 +102,7 @@ OIDCModel::OIDCModel(const std::shared_ptr &authInfo, QObjec lWarning() << log().arg("Timeout reached for OpenID connection."); dynamic_cast(mOidc.replyHandler())->close(); CoreModel::getInstance()->getCore()->abortAuthentication(mAuthInfo); + emit timeoutTimerStopped(); //: Timeout: Not authenticated emit statusChanged(tr("oidc_authentication_timeout_message")); emit finished(); @@ -120,14 +121,14 @@ OIDCModel::OIDCModel(const std::shared_ptr &authInfo, QObjec connect(&mOidc, &QOAuth2AuthorizationCodeFlow::statusChanged, [=](QAbstractOAuth::Status status) { switch (status) { case QAbstractOAuth::Status::Granted: { - mTimeout.stop(); + stopTimeoutTimer(); //: Authentication granted emit statusChanged(tr("oidc_authentication_granted_message")); emit authenticated(); break; } case QAbstractOAuth::Status::NotAuthenticated: { - mTimeout.stop(); + stopTimeoutTimer(); //: Not authenticated emit statusChanged(tr("oidc_authentication_not_authenticated_message")); emit finished(); @@ -149,7 +150,7 @@ OIDCModel::OIDCModel(const std::shared_ptr &authInfo, QObjec }); connect(&mOidc, &QOAuth2AuthorizationCodeFlow::requestFailed, [=](QAbstractOAuth::Error error) { - mTimeout.stop(); + stopTimeoutTimer(); const QMetaObject metaObject = QAbstractOAuth::staticMetaObject; int index = metaObject.indexOfEnumerator("Error"); @@ -183,12 +184,16 @@ OIDCModel::OIDCModel(const std::shared_ptr &authInfo, QObjec }); connect(&mOidc, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser, [this](const QUrl &url) { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); qDebug() << "Browser authentication url : " << url; //: Requesting authorization from browser emit statusChanged(tr("oidc_authentication_request_auth_message")); mTimeout.start(); + emit timeoutTimerStarted(); QDesktopServices::openUrl(url); }); + mTimeout.start(); + emit timeoutTimerStarted(); connect(&mOidc, &QOAuth2AuthorizationCodeFlow::finished, [this](QNetworkReply *reply) { connect(reply, &QNetworkReply::errorOccurred, @@ -261,6 +266,29 @@ OIDCModel::OIDCModel(const std::shared_ptr &authInfo, QObjec connect(reply, &QNetworkReply::finished, this, &OIDCModel::openIdConfigReceived); } +void OIDCModel::forceTimeout() { + lWarning() << log().arg("Froce timeout for OpenID connection."); + stopTimeoutTimer(); + dynamic_cast(mOidc.replyHandler())->close(); + CoreModel::getInstance()->getCore()->abortAuthentication(mAuthInfo); + //: Timeout: Not authenticated + emit statusChanged(tr("oidc_authentication_timeout_message")); + emit finished(); +} + +bool OIDCModel::isTimerRunning() const { + return mTimeout.isActive(); +} + +int OIDCModel::getRemainingTimeBeforeTimeOut() { + return mTimeout.remainingTime(); +} + +void OIDCModel::stopTimeoutTimer() { + mTimeout.stop(); + emit timeoutTimerStopped(); +} + void OIDCModel::openIdConfigReceived() { auto reply = dynamic_cast(sender()); auto document = QJsonDocument::fromJson(reply->readAll()); diff --git a/Linphone/model/auth/OIDCModel.hpp b/Linphone/model/auth/OIDCModel.hpp index 84dcb9ee9..108f61128 100644 --- a/Linphone/model/auth/OIDCModel.hpp +++ b/Linphone/model/auth/OIDCModel.hpp @@ -36,12 +36,18 @@ public: void openIdConfigReceived(); void setBearers(); + void forceTimeout(); + bool isTimerRunning() const; + int getRemainingTimeBeforeTimeOut(); + void stopTimeoutTimer(); signals: void authenticated(); void requestFailed(const QString &error); void statusChanged(const QString &status); void finished(); + void timeoutTimerStarted(); + void timeoutTimerStopped(); private: /** diff --git a/Linphone/model/call/CallModel.cpp b/Linphone/model/call/CallModel.cpp index 5e906d3be..db951ff13 100644 --- a/Linphone/model/call/CallModel.cpp +++ b/Linphone/model/call/CallModel.cpp @@ -69,7 +69,7 @@ void CallModel::accept(bool withVideo) { activateLocalVideo(params, withVideo); mMonitor->acceptWithParams(params); emit localVideoEnabledChanged(withVideo); - emit cameraEnabledChanged(params->cameraEnabled()); + emit cameraEnabledChanged(withVideo && params->cameraEnabled()); } void CallModel::decline() { @@ -300,6 +300,7 @@ void CallModel::changeConferenceVideoLayout(LinphoneEnums::ConferenceLayout layo auto params = coreManager->getCore()->createCallParams(mMonitor); params->setConferenceVideoLayout(LinphoneEnums::toLinphone(layout)); params->enableVideo(layout != LinphoneEnums::ConferenceLayout::AudioOnly); + params->enableCamera(layout != LinphoneEnums::ConferenceLayout::AudioOnly); if (!params->videoEnabled() && params->screenSharingEnabled()) { params->enableScreenSharing(false); // Deactivate screensharing if going to audio only. } @@ -408,7 +409,7 @@ void CallModel::updateCallErrorFromReason(linphone::Reason reason) { error = tr("call_error_temporarily_unavailable_toast"); break; case linphone::Reason::ServerTimeout: - //: "Server tiemout" + //: "Server timeout" error = tr("call_error_server_timeout_toast"); break; default: @@ -459,9 +460,11 @@ void CallModel::onStateChanged(const std::shared_ptr &call, auto videoDirection = params->getVideoDirection(); auto remoteVideoDirection = call->getRemoteParams()->getVideoDirection(); lInfo() << log().arg("Camera enabled changed") << params->cameraEnabled(); - emit cameraEnabledChanged(params->cameraEnabled()); emit localVideoEnabledChanged(videoDirection == linphone::MediaDirection::SendOnly || videoDirection == linphone::MediaDirection::SendRecv); + emit cameraEnabledChanged((videoDirection == linphone::MediaDirection::SendOnly || + videoDirection == linphone::MediaDirection::SendRecv) && + params->cameraEnabled()); emit remoteVideoEnabledChanged(remoteVideoDirection == linphone::MediaDirection::SendOnly || remoteVideoDirection == linphone::MediaDirection::SendRecv); updateConferenceVideoLayout(); diff --git a/Linphone/model/chat/message/ChatMessageModel.cpp b/Linphone/model/chat/message/ChatMessageModel.cpp index cf0f57c34..ac09cb6da 100644 --- a/Linphone/model/chat/message/ChatMessageModel.cpp +++ b/Linphone/model/chat/message/ChatMessageModel.cpp @@ -44,6 +44,15 @@ ChatMessageModel::ChatMessageModel(const std::shared_ptr mEphemeralTimer.stop(); deleteMessageFromChatRoom(false); }); + // We need to force this signal sending because there is no callback to know when a message has been read + connect(CoreModel::getInstance().get(), &CoreModel::chatRoomRead, this, + [this](const std::shared_ptr &core, const std::shared_ptr &chatRoom) { + if (chatRoom == mMonitor->getChatRoom()) { + if (mMonitor->isRead()) { + emit messageRead(mMonitor); + } + } + }); } ChatMessageModel::~ChatMessageModel() { @@ -91,8 +100,8 @@ bool ChatMessageModel::isRead() const { void ChatMessageModel::markAsRead() { mMonitor->markAsRead(); - emit messageRead(); - emit CoreModel::getInstance() -> messageReadInChatRoom(mMonitor->getChatRoom()); + emit messageRead(mMonitor); + emit CoreModel::getInstance()->messageReadInChatRoom(mMonitor->getChatRoom()); } void ChatMessageModel::deleteMessageFromChatRoom(bool deletedByUser) { diff --git a/Linphone/model/chat/message/ChatMessageModel.hpp b/Linphone/model/chat/message/ChatMessageModel.hpp index 56c9419fc..95cfefe38 100644 --- a/Linphone/model/chat/message/ChatMessageModel.hpp +++ b/Linphone/model/chat/message/ChatMessageModel.hpp @@ -66,7 +66,7 @@ public: signals: void messageDeleted(bool deletedByUser); - void messageRead(); + void messageRead(const std::shared_ptr &chatMessage); void msgStateChanged(const std::shared_ptr &message, linphone::ChatMessage::State state); void newMessageReaction(const std::shared_ptr &message, @@ -102,6 +102,7 @@ signals: private: linphone::ChatMessage::State mMessageState; QTimer mEphemeralTimer; + std::weak_ptr mChatRoom; DECLARE_ABSTRACT_OBJECT diff --git a/Linphone/model/chat/message/content/ChatMessageContentModel.cpp b/Linphone/model/chat/message/content/ChatMessageContentModel.cpp index 2345c56fd..d598b5d70 100644 --- a/Linphone/model/chat/message/content/ChatMessageContentModel.cpp +++ b/Linphone/model/chat/message/content/ChatMessageContentModel.cpp @@ -57,6 +57,10 @@ ChatMessageContentModel::~ChatMessageContentModel() { mustBeInLinphoneThread("~" + getClassName()); } +std::shared_ptr ChatMessageContentModel::getChatMessageModel() const { + return mChatMessageModel; +} + // Create a thumbnail from the first content that have a file void ChatMessageContentModel::createThumbnail() { auto path = Utils::coreStringToAppString(mContent->getFilePath()); @@ -75,8 +79,11 @@ void ChatMessageContentModel::removeDownloadedFile(QString filePath) { } bool ChatMessageContentModel::downloadFile(const QString &name, QString *error) { + const QString filepath = Utils::getSafeFilePath( + QStringLiteral("%1%2").arg(App::getInstance()->getSettings()->getDownloadFolder()).arg(name), nullptr); + qDebug() << "try to download" << filepath; if (!mChatMessageModel) { - //: Internal error : message object does not exist anymore ! + //: Internal error : message object associated to this content does not exist anymore ! if (error) *error = tr("download_error_object_doesnt_exist"); return false; } diff --git a/Linphone/model/chat/message/content/ChatMessageContentModel.hpp b/Linphone/model/chat/message/content/ChatMessageContentModel.hpp index b552f536f..2df57d222 100644 --- a/Linphone/model/chat/message/content/ChatMessageContentModel.hpp +++ b/Linphone/model/chat/message/content/ChatMessageContentModel.hpp @@ -43,6 +43,7 @@ public: void setThumbnail(const QString &data); void setWasDownloaded(bool wasDownloaded); + std::shared_ptr getChatMessageModel() const; void createThumbnail(); void removeDownloadedFile(QString filePath); diff --git a/Linphone/model/conference/ConferenceInfoModel.cpp b/Linphone/model/conference/ConferenceInfoModel.cpp index 50af52070..4542c17a1 100644 --- a/Linphone/model/conference/ConferenceInfoModel.cpp +++ b/Linphone/model/conference/ConferenceInfoModel.cpp @@ -32,7 +32,8 @@ DEFINE_ABSTRACT_OBJECT(ConferenceInfoModel) ConferenceInfoModel::ConferenceInfoModel(const std::shared_ptr &conferenceInfo, QObject *parent) - : mConferenceInfo(conferenceInfo) { + // TODO : remove cloning when a fix will be done in SDK (#SDK-1001 ticket) + : mConferenceInfo(conferenceInfo->clone()) { mustBeInLinphoneThread(getClassName()); } @@ -127,7 +128,7 @@ QString ConferenceInfoModel::getOrganizerAddress() const { QString ConferenceInfoModel::getDescription() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); - return Utils::coreStringToAppString(mConferenceInfo->getSubject()); + return Utils::coreStringToAppString(mConferenceInfo->getDescription()); } QString ConferenceInfoModel::getUri() const { diff --git a/Linphone/model/core/CoreModel.cpp b/Linphone/model/core/CoreModel.cpp index fff5a3d21..ff7e30d63 100644 --- a/Linphone/model/core/CoreModel.cpp +++ b/Linphone/model/core/CoreModel.cpp @@ -75,30 +75,17 @@ void CoreModel::start() { linphone::Factory::get()->createCore(Utils::appStringToCoreString(Paths::getConfigFilePath(mConfigPath)), Utils::appStringToCoreString(Paths::getFactoryConfigFilePath()), nullptr); setMonitor(mCore); - mCore->enableRecordAware(true); - mCore->setVideoDisplayFilter("MSQOGL"); mCore->usePreviewWindow(true); // Force capture/display. // 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", 0); - config->setInt("video", "display", 0); - } + // if (!mCore->videoSupported()) { + // config->setInt("video", "capture", 0); + // config->setInt("video", "display", 0); + // } - // TODO : set the real transport type when sdk will be updated - // for now, we need to let the OS choose the port to listen on - // so that the user can be connected to linphone and another softphone - // at the same time (otherwise it tries to listen on the same port as - // the other software) - auto transports = mCore->getTransports(); - transports->setTcpPort(-2); - transports->setUdpPort(-2); - transports->setTlsPort(-2); - mCore->setTransports(transports); - mCore->enableVideoPreview(false); // SDK doesn't write the state in configuration if not ready. - config->setInt("video", "show_local", 0); // So : write ourself to turn off camera before starting the core. + mCore->enableVideoPreview(false); // SDK doesn't write the state in configuration if not ready. + auto config = mCore->getConfig(); QString userAgent = ToolModel::computeUserAgent(config); mCore->setUserAgent(Utils::appStringToCoreString(userAgent), LINPHONESDK_VERSION); mCore->start(); @@ -114,10 +101,6 @@ void CoreModel::start() { 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 = new QTimer(this); mIterateTimer->setInterval(20); connect(mIterateTimer, &QTimer::timeout, [this]() { mCore->iterate(); }); @@ -162,6 +145,14 @@ void CoreModel::setConfigPath(QString path) { } } +void CoreModel::refreshOidcRemainingTime() { + for (auto oidc : mOpenIdConnections) { + if (oidc && oidc->isTimerRunning()) { + emit oidcRemainingTimeBeforeTimeoutChanged(oidc->getRemainingTimeBeforeTimeOut()); + } + } +} + //------------------------------------------------------------------------------- // PATHS //------------------------------------------------------------------------------- @@ -422,7 +413,22 @@ void CoreModel::onAuthenticationRequested(const std::shared_ptr << realm << " at " << serverUrl; QString key = username + '@' + realm + ' ' + serverUrl; if (mOpenIdConnections.contains(key)) mOpenIdConnections[key]->deleteLater(); - mOpenIdConnections[key] = new OIDCModel(authInfo, this); + auto oidcModel = new OIDCModel(authInfo, this); + mOpenIdConnections[key] = oidcModel; + connect(oidcModel, &OIDCModel::timeoutTimerStarted, this, [this] { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + emit timeoutTimerStarted(); + qDebug() << "start refresh timer"; + }); + if (oidcModel->isTimerRunning()) { + emit timeoutTimerStarted(); + } + connect(oidcModel, &OIDCModel::timeoutTimerStopped, this, [this] { emit timeoutTimerStopped(); }); + connect(this, &CoreModel::forceOidcTimeout, oidcModel, [this, oidcModel] { + if (oidcModel->isTimerRunning()) { + oidcModel->forceTimeout(); + } + }); } } emit authenticationRequested(core, authInfo, method); @@ -463,7 +469,9 @@ void CoreModel::onCallStateChanged(const std::shared_ptr &core, SettingsModel::getInstance()->setCallToneIndicationsEnabled(false); } App::postModelAsync([core]() { - for (int i = 0; i < App::getInstance()->getAccountList()->rowCount(); ++i) { + auto accounts = App::getInstance()->getAccountList(); + if (!accounts) return; + for (int i = 0; i < accounts->rowCount(); ++i) { auto accountCore = App::getInstance()->getAccountList()->getAt(i); emit accountCore->lSetPresence(core->getCallsNb() == 0 ? LinphoneEnums::Presence::Online : LinphoneEnums::Presence::Busy, diff --git a/Linphone/model/core/CoreModel.hpp b/Linphone/model/core/CoreModel.hpp index 94caad489..85eca27c2 100644 --- a/Linphone/model/core/CoreModel.hpp +++ b/Linphone/model/core/CoreModel.hpp @@ -61,6 +61,8 @@ public: void start(); void setConfigPath(QString path); + void refreshOidcRemainingTime(); + QString getFetchConfig(QString filePath, bool *error); void useFetchConfig(QString filePath); bool setFetchConfig(QString filePath); @@ -77,6 +79,9 @@ public: bool mEnd = false; linphone::ConfiguringState mConfigStatus; QString mConfigMessage; + // Used to get a chatroom created by user when trying to add + // one to the chat room list, so we can automatically select it + std::shared_ptr mChatRoomBeingCreated; std::shared_ptr mCore; std::shared_ptr mLogger; @@ -93,6 +98,10 @@ signals: void enabledLdapAddressBookSaved(); void magicSearchResultReceived(QString filter); void messageReadInChatRoom(std::shared_ptr chatRoom); + void oidcRemainingTimeBeforeTimeoutChanged(int remainingTime); + void forceOidcTimeout(); + void timeoutTimerStarted(); + void timeoutTimerStopped(); private: QString mConfigPath; diff --git a/Linphone/model/logger/LoggerListener.hpp b/Linphone/model/logger/LoggerListener.hpp index 2386da293..8eb4be0bc 100644 --- a/Linphone/model/logger/LoggerListener.hpp +++ b/Linphone/model/logger/LoggerListener.hpp @@ -27,17 +27,19 @@ #include // ============================================================================= -class LoggerListener : public QObject, public linphone::LoggingServiceListener { -Q_OBJECT +class LoggerListener : public QObject, public linphone::LoggingServiceListener { + Q_OBJECT public: LoggerListener(); - - static QString printLog(bool isAppLog, const std::string &domain, linphone::LogLevel level, const std::string &message); + + static QString + printLog(bool isAppLog, const std::string &domain, linphone::LogLevel level, const std::string &message); signals: void logReceived(const std::shared_ptr &logService, - const std::string &domain, - linphone::LogLevel level, - const std::string &message); + const std::string &domain, + linphone::LogLevel level, + const std::string &message); + private: virtual void onLogMessageWritten(const std::shared_ptr &logService, const std::string &domain, diff --git a/Linphone/model/participant/ParticipantModel.cpp b/Linphone/model/participant/ParticipantModel.cpp index 9a7e77d5e..53bbca859 100644 --- a/Linphone/model/participant/ParticipantModel.cpp +++ b/Linphone/model/participant/ParticipantModel.cpp @@ -52,6 +52,10 @@ QString ParticipantModel::getSipAddress() const { return Utils::coreStringToAppString(mParticipant->getAddress()->asString()); } +std::shared_ptr ParticipantModel::getAddress() const { + return mParticipant ? mParticipant->getAddress() : nullptr; +} + QDateTime ParticipantModel::getCreationTime() const { return QDateTime::fromSecsSinceEpoch(mParticipant->getCreationTime()); } diff --git a/Linphone/model/participant/ParticipantModel.hpp b/Linphone/model/participant/ParticipantModel.hpp index ac1a8329c..4ceec6047 100644 --- a/Linphone/model/participant/ParticipantModel.hpp +++ b/Linphone/model/participant/ParticipantModel.hpp @@ -40,6 +40,7 @@ public: ~ParticipantModel(); QString getSipAddress() const; + std::shared_ptr getAddress() const; QDateTime getCreationTime() const; bool getAdminStatus() const; bool isFocus() const; diff --git a/Linphone/model/search/MagicSearchModel.cpp b/Linphone/model/search/MagicSearchModel.cpp index 721fbcaef..237d9c9d9 100644 --- a/Linphone/model/search/MagicSearchModel.cpp +++ b/Linphone/model/search/MagicSearchModel.cpp @@ -155,5 +155,5 @@ void MagicSearchModel::updateFriendListWithFriend(const std::shared_ptraddFriend(linphoneFriend); - emit CoreModel::getInstance() -> friendCreated(linphoneFriend); + emit CoreModel::getInstance()->friendCreated(linphoneFriend); } diff --git a/Linphone/model/setting/SettingsModel.cpp b/Linphone/model/setting/SettingsModel.cpp index 6eecf9f12..4581cc58f 100644 --- a/Linphone/model/setting/SettingsModel.cpp +++ b/Linphone/model/setting/SettingsModel.cpp @@ -25,6 +25,9 @@ #include "model/tool/ToolModel.hpp" // #include "model/tool/VfsUtils.hpp" #include "tool/Utils.hpp" +#ifdef HAVE_CRASH_HANDLER +#include "tool/crash_reporter/CrashReporter.hpp" +#endif // ============================================================================= @@ -70,10 +73,21 @@ SettingsModel::SettingsModel() { CoreModel::getInstance().get(), &CoreModel::defaultAccountChanged, this, [this](const std::shared_ptr &core, const std::shared_ptr account) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); - setDisableMeetingsFeature(account && !account->getParams()->getAudioVideoConferenceFactoryAddress()); + if (!getDisableMeetingsFeature() && account && + !account->getParams()->getAudioVideoConferenceFactoryAddress()) + setDisableMeetingsFeature(true); + else if (getDisableMeetingsFeature() && account && + account->getParams()->getAudioVideoConferenceFactoryAddress()) + setDisableMeetingsFeature(false); }); auto defaultAccount = core->getDefaultAccount(); - setDisableMeetingsFeature(defaultAccount && !defaultAccount->getParams()->getAudioVideoConferenceFactoryAddress()); + if (!getDisableMeetingsFeature() && defaultAccount && + !defaultAccount->getParams()->getAudioVideoConferenceFactoryAddress()) + setDisableMeetingsFeature(true); + else if (getDisableMeetingsFeature() && defaultAccount && + defaultAccount->getParams()->getAudioVideoConferenceFactoryAddress()) + setDisableMeetingsFeature(false); + // Media cards must not be used twice (capture card + call) else we will get latencies issues and bad echo // calibrations in call. QObject::connect(CoreModel::getInstance().get(), &CoreModel::firstCallStarted, this, @@ -242,10 +256,9 @@ float SettingsModel::getMicVolume() { } else { auto call = CoreModel::getInstance()->getCore()->getCurrentCall(); if (call) { - v = call->getRecordVolume(); + v = static_cast(pow(10.0, call->getRecordVolume() / 10.0)); } } - emit micVolumeChanged(v); return v; } @@ -306,8 +319,6 @@ 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; @@ -321,8 +332,6 @@ QVariantList SettingsModel::getPlaybackDevices() const { for (const auto &device : core->getExtendedAudioDevices()) { if (device->hasCapability(linphone::AudioDevice::Capabilities::CapabilityPlay)) { list << ToolModel::createVariant(device); - } else if (device->hasCapability(linphone::AudioDevice::Capabilities::CapabilityAll)) { - list << ToolModel::createVariant(device); } } @@ -620,16 +629,32 @@ void SettingsModel::setFullLogsEnabled(bool status) { emit fullLogsEnabledChanged(status); } +bool SettingsModel::getCrashReporterEnabled() const { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + return getCrashReporterEnabled(mConfig); +} + +void SettingsModel::setCrashReporterEnabled(bool status) { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + mConfig->setInt(AppSection, "send_logs_to_crashlytics", status); +#ifdef HAVE_CRASH_HANDLER + CrashReporter::enable(status); +#endif + emit crashReporterEnabledChanged(status); +} + bool SettingsModel::getLogsEnabled(const shared_ptr &config) { - mustBeInLinphoneThread(sLog().arg(Q_FUNC_INFO)); return config ? config->getInt(UiSection, "logs_enabled", false) : true; } bool SettingsModel::getFullLogsEnabled(const shared_ptr &config) { - mustBeInLinphoneThread(sLog().arg(Q_FUNC_INFO)); return config ? config->getInt(UiSection, "full_logs_enabled", false) : false; } +bool SettingsModel::getCrashReporterEnabled(const shared_ptr &config) { + return config ? config->getInt(AppSection, "send_logs_to_crashlytics", true) : true; +} + QString SettingsModel::getLogsFolder() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); return getLogsFolder(mConfig); diff --git a/Linphone/model/setting/SettingsModel.hpp b/Linphone/model/setting/SettingsModel.hpp index 7af530681..3329a70b5 100644 --- a/Linphone/model/setting/SettingsModel.hpp +++ b/Linphone/model/setting/SettingsModel.hpp @@ -143,8 +143,12 @@ public: bool getFullLogsEnabled() const; void setFullLogsEnabled(bool status); + bool getCrashReporterEnabled() const; + void setCrashReporterEnabled(bool status); + static bool getLogsEnabled(const std::shared_ptr &config); static bool getFullLogsEnabled(const std::shared_ptr &config); + static bool getCrashReporterEnabled(const std::shared_ptr &config); QString getLogsFolder() const; void setLogsFolder(const QString &folder); @@ -272,6 +276,7 @@ signals: void logsEnabledChanged(bool status); void fullLogsEnabledChanged(bool status); + void crashReporterEnabledChanged(bool status); void dndChanged(bool value); diff --git a/Linphone/model/tool/ToolModel.cpp b/Linphone/model/tool/ToolModel.cpp index 215c2e5eb..017c6e0c1 100644 --- a/Linphone/model/tool/ToolModel.cpp +++ b/Linphone/model/tool/ToolModel.cpp @@ -131,7 +131,21 @@ QString ToolModel::getDisplayName(QString address) { return nameSplitted.join(" "); } +QString ToolModel::boldTextPart(const QString &text, const QString ®ex) { + int regexIndex = text.indexOf(regex, 0, Qt::CaseInsensitive); + if (regex.isEmpty() || regexIndex == -1) return text; + QString result; + QStringList splittedText = text.split(regex, Qt::KeepEmptyParts, Qt::CaseInsensitive); + for (int i = 0; i < splittedText.size() - 1; ++i) { + result.append(splittedText[i]); + result.append("" + regex + ""); + } + if (splittedText.size() > 0) result.append(splittedText[splittedText.size() - 1]); + return result; +} + QString ToolModel::encodeTextToQmlRichFormat(const QString &text, + const QString &textPartToBold, const QVariantMap &options, std::shared_ptr chatRoom) { QStringList formattedText; @@ -146,11 +160,13 @@ QString ToolModel::encodeTextToQmlRichFormat(const QString &text, for (int i = 0; i < iriParsed.size(); ++i) { QString iri = iriParsed[i] - .second.replace('&', "&") + .second + // .replace('&', "&") .replace('<', "\u2063<") - .replace('>', "\u2063>") - .replace('"', """) - .replace('\'', "'"); + .replace('\n', "
"); + // .replace('>', "\u2063>") + // .replace('"', """) + // .replace('\'', "'"); if (!iriParsed[i].first) { if (lastWasUrl) { lastWasUrl = false; @@ -230,7 +246,11 @@ QString ToolModel::encodeTextToQmlRichFormat(const QString &text, } } } - return "

" + formattedText.join(""); + QString finalText = formattedText.join(""); + if (!textPartToBold.isEmpty()) { + finalText = boldTextPart(finalText, textPartToBold); + } + return finalText; } std::shared_ptr ToolModel::findFriendByAddress(const QString &address) { @@ -746,6 +766,7 @@ std::shared_ptr ToolModel::createChatForAddress(std::shared_ return nullptr; } auto chatRoom = core->createChatRoom(params, participants); + CoreModel::getInstance()->mChatRoomBeingCreated = chatRoom; return chatRoom; } @@ -773,6 +794,7 @@ ToolModel::createGroupChatRoom(QString subject, std::listcreateChatRoom(params, participantsAddresses); if (!chatRoom) lWarning() << ("[ToolModel] Failed to create group chat"); + CoreModel::getInstance()->mChatRoomBeingCreated = chatRoom; return chatRoom; } diff --git a/Linphone/model/tool/ToolModel.hpp b/Linphone/model/tool/ToolModel.hpp index e778414e2..307761416 100644 --- a/Linphone/model/tool/ToolModel.hpp +++ b/Linphone/model/tool/ToolModel.hpp @@ -50,9 +50,11 @@ public: static QString getDisplayName(const std::shared_ptr &address); static QString getDisplayName(QString address); + static QString boldTextPart(const QString &text, const QString ®ex); static QString encodeTextToQmlRichFormat(const QString &text, - const QVariantMap &options, - std::shared_ptr chatRoom); + const QString &textPartToBold = QString(), + const QVariantMap &options = QVariantMap(), + std::shared_ptr chatRoom = nullptr); static std::shared_ptr findFriendByAddress(const QString &address); static std::shared_ptr diff --git a/Linphone/tool/CMakeLists.txt b/Linphone/tool/CMakeLists.txt index aafe1a72b..2f0da9850 100644 --- a/Linphone/tool/CMakeLists.txt +++ b/Linphone/tool/CMakeLists.txt @@ -1,58 +1,45 @@ -list(APPEND _LINPHONEAPP_SOURCES - tool/Constants.cpp - tool/EnumsToString.cpp - tool/Utils.cpp - tool/UriTools.cpp - tool/QExifImageHeader.cpp +list(APPEND _LINPHONEAPP_SOURCES tool / Constants.cpp tool / EnumsToString.cpp tool / Utils.cpp tool / + UriTools.cpp tool / + QExifImageHeader.cpp - tool/LinphoneEnums.cpp - tool/thread/SafeSharedPointer.hpp - tool/thread/SafeConnection.hpp - tool/thread/Thread.cpp - tool/providers/AvatarProvider.cpp - tool/providers/EmojiProvider.cpp - tool/providers/ExternalImageProvider.cpp - tool/providers/ImageProvider.cpp - tool/providers/ScreenProvider.cpp - tool/providers/ThumbnailProvider.cpp - tool/providers/VideoFrameGrabber.cpp - - tool/native/DesktopTools.hpp + tool / + LinphoneEnums.cpp tool / thread / SafeSharedPointer.hpp tool / thread / SafeConnection.hpp tool / thread / + Thread.cpp tool / providers / AvatarProvider.cpp tool / providers / EmojiProvider.cpp tool / providers / + ExternalImageProvider.cpp tool / providers / ImageProvider.cpp tool / providers / ScreenProvider.cpp tool / + providers / + ThumbnailProvider.cpp +#tool / providers / VideoFrameGrabber.cpp - tool/request/RequestDialog.cpp - tool/request/CallbackHelper.cpp + tool / + native / + DesktopTools.hpp - tool/file/FileDownloader.cpp - tool/file/FileExtractor.cpp - tool/file/TemporaryFile.cpp + tool / + request / RequestDialog.cpp tool / request / + CallbackHelper.cpp - tool/ui/DashRectangle.cpp + tool / + file / FileDownloader.cpp tool / file / FileExtractor.cpp tool / file / + TemporaryFile.cpp - tool/accessibility/AccessibilityHelper.cpp - tool/accessibility/KeyboardShortcuts.cpp - tool/accessibility/FocusHelper.cpp + tool / + ui / + DashRectangle.cpp -) + tool / + accessibility / AccessibilityHelper.cpp tool / accessibility / KeyboardShortcuts.cpp tool / accessibility / + FocusHelper.cpp -if (APPLE) - list(APPEND _LINPHONEAPP_SOURCES - tool/native/DesktopToolsMacOs.cpp - tool/native/DesktopToolsMacOsNative.mm - tool/native/screen-saver/ScreenSaverMacOs.m - tool/native/state-process/StateProcessMacOs.mm - ) -elseif (WIN32) - list(APPEND _LINPHONEAPP_SOURCES - tool/native/DesktopToolsWindows.cpp - ) -else () - list(APPEND _LINPHONEAPP_SOURCES - tool/native/DesktopToolsLinux.cpp - tool/native/screen-saver/ScreenSaverDBus.cpp - tool/native/screen-saver/ScreenSaverXdg.cpp - ) -endif () + ) -set(_LINPHONEAPP_SOURCES ${_LINPHONEAPP_SOURCES} PARENT_SCOPE) + if (APPLE) list(APPEND _LINPHONEAPP_SOURCES tool / native / DesktopToolsMacOs.cpp tool / native / + DesktopToolsMacOsNative.mm tool / native / screen - + saver / ScreenSaverMacOs.m tool / native / state - process / StateProcessMacOs.mm) elseif(WIN32) + list(APPEND _LINPHONEAPP_SOURCES tool / native / DesktopToolsWindows.cpp) else() + list(APPEND _LINPHONEAPP_SOURCES tool / native / DesktopToolsLinux.cpp tool / native / screen - + saver / ScreenSaverDBus.cpp tool / native / screen - saver / ScreenSaverXdg.cpp) endif() + if (HAVE_CRASH_HANDLER) list(APPEND _LINPHONEAPP_SOURCES tool / crash_reporter / CrashReporter.cpp) + endif() + set(_LINPHONEAPP_SOURCES ${_LINPHONEAPP_SOURCES} PARENT_SCOPE) diff --git a/Linphone/tool/Constants.cpp b/Linphone/tool/Constants.cpp index e8b4431f2..769b4388c 100644 --- a/Linphone/tool/Constants.cpp +++ b/Linphone/tool/Constants.cpp @@ -59,6 +59,10 @@ constexpr char Constants::PathCaptures[]; constexpr char Constants::PathCodecs[]; constexpr char Constants::PathTools[]; constexpr char Constants::PathLogs[]; +#ifdef HAVE_CRASH_HANDLER +constexpr char Constants::PathCrashpad[]; +constexpr char Constants::PathCrashpadHandler[]; +#endif #ifdef APPLE constexpr char Constants::PathPlugins[]; #else diff --git a/Linphone/tool/Constants.hpp b/Linphone/tool/Constants.hpp index c0e9b7762..0a1514ef0 100644 --- a/Linphone/tool/Constants.hpp +++ b/Linphone/tool/Constants.hpp @@ -145,13 +145,21 @@ public: static constexpr char PathTools[] = "/tools/"; static constexpr char PathLogs[] = "/logs/"; static constexpr char PathVCards[] = "/vcards/"; + static constexpr char PathMetrics[] = "/metrics/"; #ifdef APPLE static constexpr char PathPlugins[] = "/Plugins/"; #else static constexpr char PathPlugins[] = "/plugins/"; #endif static constexpr char PathPluginsApp[] = "app/"; + static constexpr char PathBin[] = "/bin/"; + static constexpr char PathSounds[] = "/sounds/linphone"; static constexpr char PathUserCertificates[] = "/usr-crt/"; +#ifdef HAVE_CRASH_HANDLER + static constexpr char PathCrashpad[] = "/crashpad/"; + static constexpr char PathCrashpadHandler[] = CRASHPAD_EXECUTABLE_NAME; + static constexpr char BugsplatUrl[] = "https://Linphone.bugsplat.com/post/bp/crash/crashpad.php"; +#endif static constexpr char PathCallHistoryList[] = "/call-history.db"; static constexpr char PathConfig[] = "/linphonerc"; diff --git a/Linphone/tool/QExifImageHeader.hpp b/Linphone/tool/QExifImageHeader.hpp index edd729ee7..6e89f8ee7 100644 --- a/Linphone/tool/QExifImageHeader.hpp +++ b/Linphone/tool/QExifImageHeader.hpp @@ -44,12 +44,12 @@ #ifndef QEXIFIMAGEHEADER_H_ #define QEXIFIMAGEHEADER_H_ -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include typedef QPair QExifURational; typedef QPair QExifSRational; @@ -58,72 +58,66 @@ class QExifValuePrivate; class QExifValue { public: - enum Type { - Byte = 1, - Ascii = 2, - Short = 3, - Long = 4, - Rational = 5, - Undefined = 7, - SignedLong = 9, - SignedRational = 10 - }; + enum Type { + Byte = 1, + Ascii = 2, + Short = 3, + Long = 4, + Rational = 5, + Undefined = 7, + SignedLong = 9, + SignedRational = 10 + }; - enum TextEncoding { - NoEncoding, - AsciiEncoding, - JisEncoding, - UnicodeEncoding, - UndefinedEncoding - }; + enum TextEncoding { NoEncoding, AsciiEncoding, JisEncoding, UnicodeEncoding, UndefinedEncoding }; - QExifValue (); - QExifValue (quint8 value); - QExifValue (const QVector &value); - QExifValue (const QString &value, TextEncoding encoding = NoEncoding); - QExifValue (quint16 value); - QExifValue (const QVector &value); - QExifValue (quint32 value); - QExifValue (const QVector &value); - QExifValue (const QExifURational &value); - QExifValue (const QVector &value); - QExifValue (const QByteArray &value); - QExifValue (qint32 value); - QExifValue (const QVector &value); - QExifValue (const QExifSRational &value); - QExifValue (const QVector &value); - QExifValue (const QDateTime &value); - QExifValue (const QExifValue &other); - QExifValue &operator= (const QExifValue &other); - ~QExifValue (); + QExifValue(); + QExifValue(quint8 value); + QExifValue(const QVector &value); + QExifValue(const QString &value, TextEncoding encoding = NoEncoding); + QExifValue(quint16 value); + QExifValue(const QVector &value); + QExifValue(quint32 value); + QExifValue(const QVector &value); + QExifValue(const QExifURational &value); + QExifValue(const QVector &value); + QExifValue(const QByteArray &value); + QExifValue(qint32 value); + QExifValue(const QVector &value); + QExifValue(const QExifSRational &value); + QExifValue(const QVector &value); + QExifValue(const QDateTime &value); + QExifValue(const QExifValue &other); + QExifValue &operator=(const QExifValue &other); + ~QExifValue(); - bool operator== (const QExifValue &other) const; + bool operator==(const QExifValue &other) const; - bool isNull () const; + bool isNull() const; - int type () const; - int count () const; + int type() const; + int count() const; - TextEncoding encoding () const; + TextEncoding encoding() const; - quint8 toByte () const; - QVector toByteVector () const; - QString toString () const; - quint16 toShort () const; - QVector toShortVector () const; - quint32 toLong () const; - QVector toLongVector () const; - QExifURational toRational () const; - QVector toRationalVector () const; - QByteArray toByteArray () const; - qint32 toSignedLong () const; - QVector toSignedLongVector () const; - QExifSRational toSignedRational () const; - QVector toSignedRationalVector () const; - QDateTime toDateTime () const; + quint8 toByte() const; + QVector toByteVector() const; + QString toString() const; + quint16 toShort() const; + QVector toShortVector() const; + quint32 toLong() const; + QVector toLongVector() const; + QExifURational toRational() const; + QVector toRationalVector() const; + QByteArray toByteArray() const; + qint32 toSignedLong() const; + QVector toSignedLongVector() const; + QExifSRational toSignedRational() const; + QVector toSignedRationalVector() const; + QDateTime toDateTime() const; private: - QExplicitlySharedDataPointer d; + QExplicitlySharedDataPointer d; }; struct ExifIfdHeader; @@ -131,206 +125,206 @@ struct ExifIfdHeader; class QExifImageHeaderPrivate; class QExifImageHeader { - Q_DISABLE_COPY(QExifImageHeader) + Q_DISABLE_COPY(QExifImageHeader) public: - enum ImageTag { - ImageWidth = 0x0100, - ImageLength = 0x0101, - BitsPerSample = 0x0102, - Compression = 0x0103, - PhotometricInterpretation = 0x0106, - Orientation = 0x0112, - SamplesPerPixel = 0x0115, - PlanarConfiguration = 0x011C, - YCbCrSubSampling = 0x0212, - XResolution = 0x011A, - YResolution = 0x011B, - ResolutionUnit = 0x0128, - StripOffsets = 0x0111, - RowsPerStrip = 0x0116, - StripByteCounts = 0x0117, - TransferFunction = 0x012D, - WhitePoint = 0x013E, - PrimaryChromaciticies = 0x013F, - YCbCrCoefficients = 0x0211, - ReferenceBlackWhite = 0x0214, - DateTime = 0x0132, - ImageDescription = 0x010E, - Make = 0x010F, - Model = 0x0110, - Software = 0x0131, - Artist = 0x013B, - Copyright = 0x8298 - }; + enum ImageTag { + ImageWidth = 0x0100, + ImageLength = 0x0101, + BitsPerSample = 0x0102, + Compression = 0x0103, + PhotometricInterpretation = 0x0106, + Orientation = 0x0112, + SamplesPerPixel = 0x0115, + PlanarConfiguration = 0x011C, + YCbCrSubSampling = 0x0212, + XResolution = 0x011A, + YResolution = 0x011B, + ResolutionUnit = 0x0128, + StripOffsets = 0x0111, + RowsPerStrip = 0x0116, + StripByteCounts = 0x0117, + TransferFunction = 0x012D, + WhitePoint = 0x013E, + PrimaryChromaciticies = 0x013F, + YCbCrCoefficients = 0x0211, + ReferenceBlackWhite = 0x0214, + DateTime = 0x0132, + ImageDescription = 0x010E, + Make = 0x010F, + Model = 0x0110, + Software = 0x0131, + Artist = 0x013B, + Copyright = 0x8298 + }; - enum ExifExtendedTag { - ExifVersion = 0x9000, - FlashPixVersion = 0xA000, - ColorSpace = 0xA001, - ComponentsConfiguration = 0x9101, - CompressedBitsPerPixel = 0x9102, - PixelXDimension = 0xA002, - PixelYDimension = 0xA003, - MakerNote = 0x927C, - UserComment = 0x9286, - RelatedSoundFile = 0xA004, - DateTimeOriginal = 0x9003, - DateTimeDigitized = 0x9004, - SubSecTime = 0x9290, - SubSecTimeOriginal = 0x9291, - SubSecTimeDigitized = 0x9292, - ImageUniqueId = 0xA420, - ExposureTime = 0x829A, - FNumber = 0x829D, - ExposureProgram = 0x8822, - SpectralSensitivity = 0x8824, - ISOSpeedRatings = 0x8827, - Oecf = 0x8828, - ShutterSpeedValue = 0x9201, - ApertureValue = 0x9202, - BrightnessValue = 0x9203, - ExposureBiasValue = 0x9204, - MaxApertureValue = 0x9205, - SubjectDistance = 0x9206, - MeteringMode = 0x9207, - LightSource = 0x9208, - Flash = 0x9209, - FocalLength = 0x920A, - SubjectArea = 0x9214, - FlashEnergy = 0xA20B, - SpatialFrequencyResponse = 0xA20C, - FocalPlaneXResolution = 0xA20E, - FocalPlaneYResolution = 0xA20F, - FocalPlaneResolutionUnit = 0xA210, - SubjectLocation = 0xA214, - ExposureIndex = 0xA215, - SensingMethod = 0xA217, - FileSource = 0xA300, - SceneType = 0xA301, - CfaPattern = 0xA302, - CustomRendered = 0xA401, - ExposureMode = 0xA402, - WhiteBalance = 0xA403, - DigitalZoomRatio = 0xA404, - FocalLengthIn35mmFilm = 0xA405, - SceneCaptureType = 0xA406, - GainControl = 0xA407, - Contrast = 0xA408, - Saturation = 0xA409, - Sharpness = 0xA40A, - DeviceSettingDescription = 0xA40B, - SubjectDistanceRange = 0x40C - }; + enum ExifExtendedTag { + ExifVersion = 0x9000, + FlashPixVersion = 0xA000, + ColorSpace = 0xA001, + ComponentsConfiguration = 0x9101, + CompressedBitsPerPixel = 0x9102, + PixelXDimension = 0xA002, + PixelYDimension = 0xA003, + MakerNote = 0x927C, + UserComment = 0x9286, + RelatedSoundFile = 0xA004, + DateTimeOriginal = 0x9003, + DateTimeDigitized = 0x9004, + SubSecTime = 0x9290, + SubSecTimeOriginal = 0x9291, + SubSecTimeDigitized = 0x9292, + ImageUniqueId = 0xA420, + ExposureTime = 0x829A, + FNumber = 0x829D, + ExposureProgram = 0x8822, + SpectralSensitivity = 0x8824, + ISOSpeedRatings = 0x8827, + Oecf = 0x8828, + ShutterSpeedValue = 0x9201, + ApertureValue = 0x9202, + BrightnessValue = 0x9203, + ExposureBiasValue = 0x9204, + MaxApertureValue = 0x9205, + SubjectDistance = 0x9206, + MeteringMode = 0x9207, + LightSource = 0x9208, + Flash = 0x9209, + FocalLength = 0x920A, + SubjectArea = 0x9214, + FlashEnergy = 0xA20B, + SpatialFrequencyResponse = 0xA20C, + FocalPlaneXResolution = 0xA20E, + FocalPlaneYResolution = 0xA20F, + FocalPlaneResolutionUnit = 0xA210, + SubjectLocation = 0xA214, + ExposureIndex = 0xA215, + SensingMethod = 0xA217, + FileSource = 0xA300, + SceneType = 0xA301, + CfaPattern = 0xA302, + CustomRendered = 0xA401, + ExposureMode = 0xA402, + WhiteBalance = 0xA403, + DigitalZoomRatio = 0xA404, + FocalLengthIn35mmFilm = 0xA405, + SceneCaptureType = 0xA406, + GainControl = 0xA407, + Contrast = 0xA408, + Saturation = 0xA409, + Sharpness = 0xA40A, + DeviceSettingDescription = 0xA40B, + SubjectDistanceRange = 0x40C + }; - enum GpsTag { - GpsVersionId = 0x0000, - GpsLatitudeRef = 0x0001, - GpsLatitude = 0x0002, - GpsLongitudeRef = 0x0003, - GpsLongitude = 0x0004, - GpsAltitudeRef = 0x0005, - GpsAltitude = 0x0006, - GpsTimeStamp = 0x0007, - GpsSatellites = 0x0008, - GpsStatus = 0x0009, - GpsMeasureMode = 0x000A, - GpsDop = 0x000B, - GpsSpeedRef = 0x000C, - GpsSpeed = 0x000D, - GpsTrackRef = 0x000E, - GpsTrack = 0x000F, - GpsImageDirectionRef = 0x0010, - GpsImageDirection = 0x0011, - GpsMapDatum = 0x0012, - GpsDestLatitudeRef = 0x0013, - GpsDestLatitude = 0x0014, - GpsDestLongitudeRef = 0x0015, - GpsDestLongitude = 0x0016, - GpsDestBearingRef = 0x0017, - GpsDestBearing = 0x0018, - GpsDestDistanceRef = 0x0019, - GpsDestDistance = 0x001A, - GpsProcessingMethod = 0x001B, - GpsAreaInformation = 0x001C, - GpsDateStamp = 0x001D, - GpsDifferential = 0x001E - }; + enum GpsTag { + GpsVersionId = 0x0000, + GpsLatitudeRef = 0x0001, + GpsLatitude = 0x0002, + GpsLongitudeRef = 0x0003, + GpsLongitude = 0x0004, + GpsAltitudeRef = 0x0005, + GpsAltitude = 0x0006, + GpsTimeStamp = 0x0007, + GpsSatellites = 0x0008, + GpsStatus = 0x0009, + GpsMeasureMode = 0x000A, + GpsDop = 0x000B, + GpsSpeedRef = 0x000C, + GpsSpeed = 0x000D, + GpsTrackRef = 0x000E, + GpsTrack = 0x000F, + GpsImageDirectionRef = 0x0010, + GpsImageDirection = 0x0011, + GpsMapDatum = 0x0012, + GpsDestLatitudeRef = 0x0013, + GpsDestLatitude = 0x0014, + GpsDestLongitudeRef = 0x0015, + GpsDestLongitude = 0x0016, + GpsDestBearingRef = 0x0017, + GpsDestBearing = 0x0018, + GpsDestDistanceRef = 0x0019, + GpsDestDistance = 0x001A, + GpsProcessingMethod = 0x001B, + GpsAreaInformation = 0x001C, + GpsDateStamp = 0x001D, + GpsDifferential = 0x001E + }; - QExifImageHeader (); - explicit QExifImageHeader (const QString &fileName); - ~QExifImageHeader (); + QExifImageHeader(); + explicit QExifImageHeader(const QString &fileName); + ~QExifImageHeader(); - bool loadFromJpeg (const QString &fileName); - bool loadFromJpeg (QIODevice *device); - bool saveToJpeg (const QString &fileName) const; - bool saveToJpeg (QIODevice *device) const; + bool loadFromJpeg(const QString &fileName); + bool loadFromJpeg(QIODevice *device); + bool saveToJpeg(const QString &fileName) const; + bool saveToJpeg(QIODevice *device) const; - bool read (QIODevice *device); - qint64 write (QIODevice *device) const; + bool read(QIODevice *device); + qint64 write(QIODevice *device) const; - qint64 size () const; + qint64 size() const; - QSysInfo::Endian byteOrder () const; + QSysInfo::Endian byteOrder() const; - void clear (); + void clear(); - QList imageTags () const; - QList extendedTags () const; - QList gpsTags () const; + QList imageTags() const; + QList extendedTags() const; + QList gpsTags() const; - bool contains (ImageTag tag) const; - bool contains (ExifExtendedTag tag) const; - bool contains (GpsTag tag) const; + bool contains(ImageTag tag) const; + bool contains(ExifExtendedTag tag) const; + bool contains(GpsTag tag) const; - void remove (ImageTag tag); - void remove (ExifExtendedTag tag); - void remove (GpsTag tag); + void remove(ImageTag tag); + void remove(ExifExtendedTag tag); + void remove(GpsTag tag); - QExifValue value (ImageTag tag) const; - QExifValue value (ExifExtendedTag tag) const; - QExifValue value (GpsTag tag) const; + QExifValue value(ImageTag tag) const; + QExifValue value(ExifExtendedTag tag) const; + QExifValue value(GpsTag tag) const; - void setValue (ImageTag tag, const QExifValue &value); - void setValue (ExifExtendedTag tag, const QExifValue &value); - void setValue (GpsTag tag, const QExifValue &value); + void setValue(ImageTag tag, const QExifValue &value); + void setValue(ExifExtendedTag tag, const QExifValue &value); + void setValue(GpsTag tag, const QExifValue &value); - QImage thumbnail () const; - void setThumbnail (const QImage &thumbnail); + QImage thumbnail() const; + void setThumbnail(const QImage &thumbnail); private: - enum PrivateTag { - ExifIfdPointer = 0x8769, - GpsInfoIfdPointer = 0x8825, - InteroperabilityIfdPointer = 0xA005, - JpegInterchangeFormat = 0x0201, - JpegInterchangeFormatLength = 0x0202 - }; + enum PrivateTag { + ExifIfdPointer = 0x8769, + GpsInfoIfdPointer = 0x8825, + InteroperabilityIfdPointer = 0xA005, + JpegInterchangeFormat = 0x0201, + JpegInterchangeFormatLength = 0x0202 + }; - QByteArray extractExif (QIODevice *device) const; + QByteArray extractExif(QIODevice *device) const; - QList readIfdHeaders (QDataStream &stream) const; + QList readIfdHeaders(QDataStream &stream) const; - QExifValue readIfdValue (QDataStream &stream, int startPos, const ExifIfdHeader &header) const; - template - QMap readIfdValues (QDataStream &stream, int startPos, const QList &headers) const; - template - QMap readIfdValues (QDataStream &stream, int startPos, const QExifValue &pointer) const; + QExifValue readIfdValue(QDataStream &stream, int startPos, const ExifIfdHeader &header) const; + template + QMap readIfdValues(QDataStream &stream, int startPos, const QList &headers) const; + template + QMap readIfdValues(QDataStream &stream, int startPos, const QExifValue &pointer) const; - quint32 writeExifHeader (QDataStream &stream, quint16 tag, const QExifValue &value, quint32 offset) const; - void writeExifValue (QDataStream &stream, const QExifValue &value) const; + quint32 writeExifHeader(QDataStream &stream, quint16 tag, const QExifValue &value, quint32 offset) const; + void writeExifValue(QDataStream &stream, const QExifValue &value) const; - template - quint32 writeExifHeaders (QDataStream &stream, const QMap &values, quint32 offset) const; - template - void writeExifValues (QDataStream &target, const QMap &values) const; + template + quint32 writeExifHeaders(QDataStream &stream, const QMap &values, quint32 offset) const; + template + void writeExifValues(QDataStream &target, const QMap &values) const; - quint32 sizeOf (const QExifValue &value) const; + quint32 sizeOf(const QExifValue &value) const; - template - quint32 calculateSize (const QMap &values) const; + template + quint32 calculateSize(const QMap &values) const; - QExifImageHeaderPrivate *d; + QExifImageHeaderPrivate *d; }; #endif // ifndef QEXIFIMAGEHEADER_H_ diff --git a/Linphone/tool/Utils.cpp b/Linphone/tool/Utils.cpp index 630f40a26..a99161e35 100644 --- a/Linphone/tool/Utils.cpp +++ b/Linphone/tool/Utils.cpp @@ -1397,6 +1397,11 @@ bool Utils::isCurrentMonth(QDate date) { return date.month() == currentDate.month() && date.year() == currentDate.year(); } +bool Utils::isCurrentYear(QDate date) { + auto currentDate = QDate::currentDate(); + return date.year() == currentDate.year(); +} + bool Utils::datesAreEqual(const QDate &a, const QDate &b) { return a.month() == b.month() && a.year() == b.year() && a.day() == b.day(); } @@ -1587,25 +1592,21 @@ VariantObject *Utils::getCurrentCallChat(CallGui *call) { auto linphoneChatRoom = ToolModel::lookupCurrentCallChat(callModel); if (linphoneChatRoom) { auto chatCore = ChatCore::create(linphoneChatRoom); - return QVariant::fromValue(new ChatGui(chatCore)); + return chatCore ? QVariant::fromValue(new ChatGui(chatCore)) : QVariant(); } else { - lInfo() << "[Utils] Did not find existing chat room, create one"; - linphoneChatRoom = ToolModel::createCurrentCallChat(callModel); + // Only try to create chatroom if 1-1 call + if (!callModel->getConference()) { + lInfo() << "[Utils] Did not find existing chat room, create one"; + linphoneChatRoom = ToolModel::createCurrentCallChat(callModel); + } if (linphoneChatRoom != nullptr) { lInfo() << "[Utils] Chatroom created with" << callModel->getRemoteAddress()->asStringUriOnly(); auto id = linphoneChatRoom->getIdentifier(); auto chatCore = ChatCore::create(linphoneChatRoom); - return QVariant::fromValue(new ChatGui(chatCore)); + return chatCore ? QVariant::fromValue(new ChatGui(chatCore)) : QVariant(); } else { lWarning() << "[Utils] Failed to create 1-1 conversation with" << callModel->getRemoteAddress()->asStringUriOnly() << "!"; - data->mConnection->invokeToCore([] { - //: Error - showInformationPopup(tr("information_popup_error_title"), - //: Failed to create 1-1 conversation with %1 ! - tr("information_popup_chatroom_creation_error_message"), false, - getOrCreateCallsWindow()); - }); return QVariant(); } } @@ -1624,14 +1625,14 @@ VariantObject *Utils::getChatForAddress(QString address) { auto linphoneChatRoom = ToolModel::lookupChatForAddress(linAddr); if (linphoneChatRoom) { auto chatCore = ChatCore::create(linphoneChatRoom); - return QVariant::fromValue(new ChatGui(chatCore)); + return chatCore ? QVariant::fromValue(new ChatGui(chatCore)) : QVariant(); } else { lInfo() << "[Utils] Did not find existing chat room, create one"; linphoneChatRoom = ToolModel::createChatForAddress(linAddr); if (linphoneChatRoom != nullptr) { lInfo() << "[Utils] Chatroom created with" << linAddr->asStringUriOnly(); auto chatCore = ChatCore::create(linphoneChatRoom); - return QVariant::fromValue(new ChatGui(chatCore)); + return chatCore ? QVariant::fromValue(new ChatGui(chatCore)) : QVariant(); } else { lWarning() << "[Utils] Failed to create 1-1 conversation with" << linAddr->asStringUriOnly() << "!"; //: Failed to create 1-1 conversation with %1 ! @@ -1661,7 +1662,7 @@ VariantObject *Utils::createGroupChat(QString subject, QStringList participantAd auto linphoneChatRoom = ToolModel::createGroupChatRoom(subject, addresses); if (linphoneChatRoom) { auto chatCore = ChatCore::create(linphoneChatRoom); - return QVariant::fromValue(new ChatGui(chatCore)); + return chatCore ? QVariant::fromValue(new ChatGui(chatCore)) : QVariant(); } else { return QVariant(); } @@ -1865,7 +1866,10 @@ QString Utils::getPresenceStatus(LinphoneEnums::Presence presence) { return presenceStatus; } -VariantObject *Utils::encodeTextToQmlRichFormat(const QString &text, const QVariantMap &options, ChatGui *chat) { +VariantObject *Utils::encodeTextToQmlRichFormat(const QString &text, + const QString &textPartToBold, + const QVariantMap &options, + ChatGui *chat) { /*QString images; QStringList imageFormat; for(auto format : QImageReader::supportedImageFormats()) @@ -1874,99 +1878,10 @@ VariantObject *Utils::encodeTextToQmlRichFormat(const QString &text, const QVari VariantObject *data = new VariantObject("encodeTextToQmlRichFormat"); if (!data) return nullptr; auto primaryColor = getDefaultStyleColor("info_500_main"); - data->makeRequest([text, options, chat, primaryColor] { - QStringList formattedText; - bool lastWasUrl = false; - - if (options.contains("noLink") && options["noLink"].toBool()) { - formattedText.append(encodeEmojiToQmlRichFormat(text)); - } else { - - auto iriParsed = UriTools::parseIri(text); - - for (int i = 0; i < iriParsed.size(); ++i) { - QString iri = iriParsed[i] - .second.replace('&', "&") - .replace('<', "\u2063<") - .replace('>', "\u2063>") - .replace('"', """) - .replace('\'', "'"); - if (!iriParsed[i].first) { - if (lastWasUrl) { - lastWasUrl = false; - if (iri.front() != ' ') iri.push_front(' '); - } - formattedText.append(encodeEmojiToQmlRichFormat(iri)); - } else { - QString uri = - iriParsed[i].second.left(3) == "www" ? "http://" + iriParsed[i].second : iriParsed[i].second; - /* TODO : preview from link - int extIndex = iriParsed[i].second.lastIndexOf('.'); - QString ext; - if( extIndex >= 0) - ext = iriParsed[i].second.mid(extIndex+1).toUpper(); - if(imageFormat.contains(ext.toLatin1())){// imagesHeight is not used because of bugs on display - (blank image if set without width) images += ""+uri+""; - }else{ - */ - formattedText.append("" + iri + - ""); - lastWasUrl = true; - /*}*/ - } - } - } - if (lastWasUrl && formattedText.last().back() != ' ') { - formattedText.push_back(" "); - } - if (chat && chat->mCore) { - auto participants = chat->mCore->getParticipants(); - auto mentionsParsed = UriTools::parseMention(formattedText.join("")); - formattedText.clear(); - - for (int i = 0; i < mentionsParsed.size(); ++i) { - QString mention = mentionsParsed[i].second; - - if (mentionsParsed[i].first) { - QString mentions = mentionsParsed[i].second; - QStringList finalMentions; - QStringList parts = mentions.split(" "); - for (auto part : parts) { - if (part.startsWith("@")) { // mention - QString username = part; - username.removeFirst(); - auto it = std::find_if( - participants.begin(), participants.end(), - [username](QSharedPointer p) { return username == p->getUsername(); }); - if (it != participants.end()) { - auto foundParticipant = participants.at(std::distance(participants.begin(), it)); - auto address = foundParticipant->getSipAddress(); - auto isFriend = ToolModel::findFriendByAddress(address); - if (isFriend) - part = "@" + Utils::coreStringToAppString(isFriend->getAddress()->getDisplayName()); - QString participantLink = "" + part + ""; - finalMentions.append(participantLink); - } else { - finalMentions.append(part); - } - } else { - finalMentions.append(part); - } - } - formattedText.push_back(finalMentions.join(" ")); - } else { - formattedText.push_back(mentionsParsed[i].second); - } - } - } - return "

" + formattedText.join(""); + data->makeRequest([text, options, chat, primaryColor, textPartToBold] { + auto chatroom = + chat && chat->mCore && chat->mCore->getModel() ? chat->mCore->getModel()->getMonitor() : nullptr; + return ToolModel::encodeTextToQmlRichFormat(text, textPartToBold, options, chatroom); }); data->requestValue(); return data; diff --git a/Linphone/tool/Utils.hpp b/Linphone/tool/Utils.hpp index 6f6fad91f..2650e2ff9 100644 --- a/Linphone/tool/Utils.hpp +++ b/Linphone/tool/Utils.hpp @@ -117,6 +117,7 @@ public: Q_INVOKABLE static bool isCurrentDay(QDateTime date); Q_INVOKABLE static bool isCurrentDay(QDate date); Q_INVOKABLE static bool isCurrentMonth(QDate date); + Q_INVOKABLE static bool isCurrentYear(QDate date); Q_INVOKABLE static bool datesAreEqual(const QDate &a, const QDate &b); Q_INVOKABLE static bool dateisInMonth(const QDate &a, int month, int year); Q_INVOKABLE static QDateTime createDateTime(const QDate &date, int hour, int min); @@ -142,10 +143,10 @@ public: Q_INVOKABLE static void useFetchConfig(const QString &configUrl); Q_INVOKABLE void playDtmf(const QString &dtmf); Q_INVOKABLE bool isInteger(const QString &text); - Q_INVOKABLE QString boldTextPart(const QString &text, const QString ®ex); + Q_INVOKABLE static QString boldTextPart(const QString &text, const QString ®ex); Q_INVOKABLE static QString getFileChecksum(const QString &filePath); - Q_INVOKABLE QList append(const QList a, const QList b); - Q_INVOKABLE QString getAddressToDisplay(QVariantList addressList, QString filter, QString defaultAddress); + Q_INVOKABLE static QList append(const QList a, const QList b); + Q_INVOKABLE static QString getAddressToDisplay(QVariantList addressList, QString filter, QString defaultAddress); Q_INVOKABLE static QColor getPresenceColor(LinphoneEnums::Presence presence); Q_INVOKABLE static QUrl getPresenceIcon(LinphoneEnums::Presence presence); Q_INVOKABLE static QString getPresenceStatus(LinphoneEnums::Presence presence); @@ -155,8 +156,10 @@ public: Q_INVOKABLE static VariantObject *createGroupChat(QString subject, QStringList participantAddresses); Q_INVOKABLE static void openChat(ChatGui *chat); Q_INVOKABLE static bool isEmptyMessage(QString message); - Q_INVOKABLE static VariantObject * - encodeTextToQmlRichFormat(const QString &text, const QVariantMap &options = QVariantMap(), ChatGui *chat = nullptr); + Q_INVOKABLE static VariantObject *encodeTextToQmlRichFormat(const QString &text, + const QString &textPartToBold = QString(), + const QVariantMap &options = QVariantMap(), + ChatGui *chat = nullptr); Q_INVOKABLE static QString encodeEmojiToQmlRichFormat(const QString &body); Q_INVOKABLE static bool isOnlyEmojis(const QString &text); Q_INVOKABLE static void openContactAtAddress(const QString &address); @@ -247,6 +250,16 @@ public: static QUrl getAppIcon(const QString &iconName); static QUrl getRegistrationStateIcon(LinphoneEnums::RegistrationState state); +#ifdef Q_OS_WINDOWS + static inline std::wstring getNativeString(const QString &s) { + return s.toStdWString(); + } +#else + static std::string getNativeString(const QString &s) { + return s.toStdString(); + } +#endif + private: DECLARE_ABSTRACT_OBJECT }; diff --git a/Linphone/tool/accessibility/AccessibilityHelper.cpp b/Linphone/tool/accessibility/AccessibilityHelper.cpp index 170e49724..1b3df0ee1 100644 --- a/Linphone/tool/accessibility/AccessibilityHelper.cpp +++ b/Linphone/tool/accessibility/AccessibilityHelper.cpp @@ -28,6 +28,7 @@ DEFINE_ABSTRACT_OBJECT(AccessibilityHelper) void AccessibilityHelper::announceMessage(const QString &message, QObject *context, bool assertive) { QObject *target = context ? context : static_cast(Utils::getMainWindow()); + if (!target) return; QAccessibleAnnouncementEvent event(target, message); event.setPoliteness(assertive ? QAccessible::AnnouncementPoliteness::Assertive : QAccessible::AnnouncementPoliteness::Polite); diff --git a/Linphone/tool/crash_reporter/CrashReporter.cpp b/Linphone/tool/crash_reporter/CrashReporter.cpp new file mode 100644 index 000000000..7a5a6ed9e --- /dev/null +++ b/Linphone/tool/crash_reporter/CrashReporter.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2010-2026 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "CrashReporter.hpp" +#include "config.h" +#include "core/path/Paths.hpp" +#include "model/setting/SettingsModel.hpp" +#include "tool/Constants.hpp" +#include "tool/Utils.hpp" + +#include "client/crash_report_database.h" +#include "client/settings.h" +#include + +CrashReporter *CrashReporter::gHandler = nullptr; + +CrashReporter::CrashReporter(QObject *parent) : QObject(parent) { + mHandlerPath = base::FilePath(Utils::getNativeString(Paths::getCrashpadHandlerFilePath())); + mDatabasePath = base::FilePath(Utils::getNativeString(Paths::getCrashpadDirPath())); + mMetricsPath = base::FilePath(Utils::getNativeString(Paths::getMetricsDirPath())); + mBugsplatUrl = Constants::BugsplatUrl; + + mAnnotations["format"] = "minidump"; // Required: Crashpad setting to save crash as a minidump + mAnnotations["database"] = BUGSPLAT_DATABASE; // Required: BugSplat database + mAnnotations["product"] = APPLICATION_NAME; // Required: BugSplat appName + mAnnotations["version"] = APPLICATION_SEMVER; + // annotations["key"] = "Sample key"; // Optional: BugSplat key field + // annotations["user"] = "fred@bugsplat.com"; // Optional: BugSplat user email + // annotations["list_annotations"] = "Sample comment"; // Optional: BugSplat crash description + + // Disable crashpad rate limiting so that all crashes have dmp files + mArguments.push_back("--no-rate-limit"); + auto config = + linphone::Factory::get()->createConfig(Utils::appStringToCoreString(Paths::getConfigFilePath("", true))); + + // Attachments to be uploaded alongside the crash - default bundle size limit is 20MB + // Crashpad doesn't manage folders. We have to guess the files to be uploaded. + QString logFiles = Paths::getLogsDirPath() + QDir::separator() + EXECUTABLE_NAME; + mAttachments.push_back(base::FilePath(Utils::getNativeString(logFiles + ".log"))); + mAttachments.push_back(base::FilePath(Utils::getNativeString(logFiles + "1.log"))); + mAttachments.push_back(base::FilePath(Utils::getNativeString(logFiles + "2.log"))); +} + +void CrashReporter::start() { + auto config = + linphone::Factory::get()->createConfig(Utils::appStringToCoreString(Paths::getConfigFilePath("", true))); + CrashReporter::enable(SettingsModel::getCrashReporterEnabled(config)); +} + +void CrashReporter::run() { + // crashpad::CrashpadClient *client = new crashpad::CrashpadClient(); + bool status = mClient.StartHandler(mHandlerPath, mDatabasePath, mMetricsPath, mBugsplatUrl.toStdString(), + mAnnotations.toStdMap(), mArguments, true, true, mAttachments); + + if (!status) { + lWarning() << "[CrashReporter] Failed to start Crashpad handler. Crashes will not be logged."; + } else { + lInfo() << "[CrashReporter] Started Crashpad handler. Database at " << Paths::getCrashpadDirPath(); + } +} + +void CrashReporter::enable(const bool &on) { + if (!gHandler) gHandler = new CrashReporter(); + std::unique_ptr database = + crashpad::CrashReportDatabase::Initialize(gHandler->mDatabasePath); + if (database == NULL) return; + crashpad::Settings *settings = database->GetSettings(); + if (settings == NULL) return; + settings->SetUploadsEnabled(on); + if (on) gHandler->run(); + else { + lInfo() << "[CrashReporter] Crashpad has been deactivated by user."; + } +} diff --git a/Linphone/tool/crash_reporter/CrashReporter.hpp b/Linphone/tool/crash_reporter/CrashReporter.hpp new file mode 100644 index 000000000..f40ac99bc --- /dev/null +++ b/Linphone/tool/crash_reporter/CrashReporter.hpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2010-2026 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef CRASH_REPORTER_H_ +#define CRASH_REPORTER_H_ + +#include +#include +#include +#include + +#include "client/crashpad_client.h" + +// ============================================================================= +class CrashReporter : public QObject { +public: + CrashReporter(QObject *parent = nullptr); + + static void start(); + static void enable(const bool &on); + void run(); + + crashpad::CrashpadClient mClient; + std::vector mAttachments; + QMap mAnnotations; + std::vector mArguments; + base::FilePath mHandlerPath; + base::FilePath mDatabasePath; + base::FilePath mMetricsPath; + base::FilePath mLogsPath; + QString mBugsplatUrl; + static CrashReporter *gHandler; +}; + +#endif \ No newline at end of file diff --git a/Linphone/tool/providers/ThumbnailProvider.cpp b/Linphone/tool/providers/ThumbnailProvider.cpp index def19a9bd..79af4c960 100644 --- a/Linphone/tool/providers/ThumbnailProvider.cpp +++ b/Linphone/tool/providers/ThumbnailProvider.cpp @@ -36,7 +36,7 @@ const QString ThumbnailProvider::ProviderId = "thumbnail"; ThumbnailAsyncImageResponse::ThumbnailAsyncImageResponse(const QString &id, const QSize &requestedSize) { mPath = id; - connect(&mListener, &VideoFrameGrabberListener::imageGrabbed, this, &ThumbnailAsyncImageResponse::imageGrabbed); + // connect(&mListener, &VideoFrameGrabberListener::imageGrabbed, this, &ThumbnailAsyncImageResponse::imageGrabbed); if (QFileInfo(mPath).isFile()) { bool removeExportedFile = SettingsModel::getInstance()->getVfsEncrypted(); @@ -53,11 +53,12 @@ ThumbnailAsyncImageResponse::ThumbnailAsyncImageResponse(const QString &id, cons if (!format.isEmpty()) { originalImage = QImage(mPath, format); } else if (Utils::isVideo(mPath)) { - VideoFrameGrabber *grabber = new VideoFrameGrabber(removeExportedFile); - removeExportedFile = false; - connect(grabber, &VideoFrameGrabber::grabFinished, &mListener, - &VideoFrameGrabberListener::imageGrabbed); - grabber->requestFrame(mPath); + originalImage = QImage(mPath); + // VideoFrameGrabber *grabber = new VideoFrameGrabber(removeExportedFile); + // removeExportedFile = false; + // connect(grabber, &VideoFrameGrabber::grabFinished, &mListener, + // &VideoFrameGrabberListener::imageGrabbed); + // grabber->requestFrame(mPath); } } if (removeExportedFile) QFile(mPath).remove(); diff --git a/Linphone/tool/providers/ThumbnailProvider.hpp b/Linphone/tool/providers/ThumbnailProvider.hpp index 9314030b8..b935f3dfa 100644 --- a/Linphone/tool/providers/ThumbnailProvider.hpp +++ b/Linphone/tool/providers/ThumbnailProvider.hpp @@ -23,7 +23,7 @@ #include -#include "VideoFrameGrabber.hpp" +// #include "VideoFrameGrabber.hpp" #include "tool/AbstractObject.hpp" // Thumbnails are created asynchronously with QQuickAsyncImageProvider and not QQuickImageProvider. @@ -43,7 +43,7 @@ public: QImage mImage; QString mPath; - VideoFrameGrabberListener mListener; + // VideoFrameGrabberListener mListener; private: DECLARE_ABSTRACT_OBJECT diff --git a/Linphone/tool/thread/SafeConnection.hpp b/Linphone/tool/thread/SafeConnection.hpp index 15fd91ffd..854c67934 100644 --- a/Linphone/tool/thread/SafeConnection.hpp +++ b/Linphone/tool/thread/SafeConnection.hpp @@ -66,7 +66,7 @@ template class SafeConnection : public QObject { // Use create functions. protected: - SafeConnection(QSharedPointer a, std::shared_ptr b) + SafeConnection(const QSharedPointer &a, std::shared_ptr b) : mCore(a), mModel(b), mCoreObject(a.get()), mModelObject(b.get()) { } SafeConnection(QSharedPointer a, QSharedPointer b) diff --git a/Linphone/tool/thread/Thread.cpp b/Linphone/tool/thread/Thread.cpp index 3319c29bd..894c0aeb5 100644 --- a/Linphone/tool/thread/Thread.cpp +++ b/Linphone/tool/thread/Thread.cpp @@ -26,7 +26,7 @@ Thread::Thread(QObject *parent) : QThread(parent) { } void Thread::run() { - qInfo () << "Thread is running"; + qInfo() << "Thread is running"; mThreadId = new QObject(); setlocale(LC_CTYPE, ".UTF8"); int toExit = false; @@ -48,6 +48,7 @@ bool Thread::isInLinphoneThread() { } bool Thread::mustBeInLinphoneThread(const QString &context) { + if (!App::getInstance()) return true; bool isLinphoneThread = isInLinphoneThread(); if (!isLinphoneThread) { // Bracket to easier debugging. lCritical() << "[Thread] Not processing in Linphone thread from " << context; diff --git a/Linphone/view/Control/Button/CountryIndicatorCombobox.qml b/Linphone/view/Control/Button/CountryIndicatorCombobox.qml index 5fe2c5dae..df9b03bd1 100644 --- a/Linphone/view/Control/Button/CountryIndicatorCombobox.qml +++ b/Linphone/view/Control/Button/CountryIndicatorCombobox.qml @@ -3,12 +3,18 @@ import QtQuick.Controls.Basic as Control import QtQuick.Layouts import QtQuick.Effects import Linphone +import CustomControls 1.0 import "qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js" as Utils Control.ComboBox { id: mainItem property string defaultCallingCode: "" property bool enableBackgroundColors: false + onKeyboardFocusChanged: console.log("keyboard focus combobox", keyboardFocus) + property bool keyboardFocus: FocusHelper.keyboardFocus + property color keyboardFocusedBorderColor: DefaultStyle.main2_900 + property real borderWidth: Utils.getSizeWithScreenRatio(1) + property real keyboardFocusedBorderWidth: Utils.getSizeWithScreenRatio(3) property string text: combobox.model.getAt(combobox.currentIndex) ? combobox.model.getAt(combobox.currentIndex).countryCallingCode : "" currentIndex: phoneNumberModel.count > 0 ? Math.max(0, phoneNumberModel.findIndexByCountryCallingCode(defaultCallingCode)) : -1 Accessible.name: mainItem.Accessible.name @@ -19,13 +25,16 @@ Control.ComboBox { anchors.fill: parent radius: Utils.getSizeWithScreenRatio(63) color: mainItem.enableBackgroundColor ? DefaultStyle.grey_100 : "transparent" - border.color: mainItem.enableBackgroundColors - ? (mainItem.errorMessage.length > 0 - ? DefaultStyle.danger_500_main - : textField.activeFocus - ? DefaultStyle.main1_500_main - : DefaultStyle.grey_200) - : "transparent" + border.color: mainItem.keyboardFocus + ? mainItem.keyboardFocusedBorderColor + : mainItem.enableBackgroundColors + ? (mainItem.errorMessage.length > 0 + ? DefaultStyle.danger_500_main + : mainItem.activeFocus || textField.activeFocus + ? DefaultStyle.main1_500_main + : DefaultStyle.grey_200) + : "transparent" + border.width: mainItem.keyboardFocus ? mainItem.keyboardFocusedBorderWidth : mainItem.borderWidth } contentItem: RowLayout { readonly property var currentItem: combobox.model.getAt(combobox.currentIndex) @@ -95,8 +104,6 @@ Control.ComboBox { maximumFlickVelocity: 1500 spacing: Utils.getSizeWithScreenRatio(10) highlight: Rectangle { - anchors.left: parent.left - anchors.right: parent.right width: listView.width height: listView.height color: DefaultStyle.main2_300 diff --git a/Linphone/view/Control/Button/Settings/SwitchSetting.qml b/Linphone/view/Control/Button/Settings/SwitchSetting.qml index af4ca4aa3..bf74d6080 100644 --- a/Linphone/view/Control/Button/Settings/SwitchSetting.qml +++ b/Linphone/view/Control/Button/Settings/SwitchSetting.qml @@ -12,12 +12,10 @@ RowLayout { property var propertyOwner property var propertyOwnerGui property bool enabled: true + property alias checked: switchButton.checked spacing : Utils.getSizeWithScreenRatio(20) - signal checkedChanged(bool checked) - function setChecked(value) { - switchButton.checked = value - } + signal toggled() ColumnLayout { Layout.minimumHeight: Utils.getSizeWithScreenRatio(32) @@ -44,8 +42,10 @@ RowLayout { checked: propertyOwnerGui ? propertyOwnerGui.core[mainItem.propertyName] : propertyOwner ? propertyOwner[mainItem.propertyName] : false enabled: mainItem.enabled - onCheckedChanged: mainItem.checkedChanged(checked) - onToggled: binding.when = true + onToggled: { + binding.when = true + mainItem.toggled() + } implicitHeight: Utils.getSizeWithScreenRatio(30) Accessible.name: "%1 %2".arg(mainItem.titleText).arg(mainItem.subTitleText) } diff --git a/Linphone/view/Control/Container/Contact/ContactLayout.qml b/Linphone/view/Control/Container/Contact/ContactLayout.qml index 52b1abb95..495121243 100644 --- a/Linphone/view/Control/Container/Contact/ContactLayout.qml +++ b/Linphone/view/Control/Container/Contact/ContactLayout.qml @@ -71,7 +71,7 @@ ColumnLayout { } PresenceNoteLayout { visible: contact?.core.presenceNote.length > 0 && mainItem.useVerticalLayout - friendCore: contact?.core || null + friendGui: contact Layout.preferredWidth: Utils.getSizeWithScreenRatio(412) Layout.preferredHeight: Utils.getSizeWithScreenRatio(85) } @@ -96,7 +96,7 @@ ColumnLayout { visible: contact && contact.core.presenceNote.length > 0 && !mainItem.useVerticalLayout PresenceNoteLayout { anchors.centerIn: parent - friendCore: contact?.core || null + friendGui: contact width: Utils.getSizeWithScreenRatio(412) height: Utils.getSizeWithScreenRatio(85) } diff --git a/Linphone/view/Control/Container/Contact/PresenceNoteLayout.qml b/Linphone/view/Control/Container/Contact/PresenceNoteLayout.qml index 4e8c8a773..5d05f105f 100644 --- a/Linphone/view/Control/Container/Contact/PresenceNoteLayout.qml +++ b/Linphone/view/Control/Container/Contact/PresenceNoteLayout.qml @@ -10,7 +10,7 @@ import "qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js" as Utils Rectangle { id: mainItem - property var friendCore + property FriendGui friendGui color: DefaultStyle.grey_0 radius: Utils.getSizeWithScreenRatio(20) border.color: DefaultStyle.main2_200 @@ -45,7 +45,7 @@ Rectangle { Text { font: Typography.p3 color: DefaultStyle.main2_500_main - text: mainItem.friendCore?.presenceNote || "" + text: mainItem.friendGui?.core.presenceNote || "" wrapMode: Text.Wrap Layout.fillWidth: true } diff --git a/Linphone/view/Control/Container/FormItemLayout.qml b/Linphone/view/Control/Container/FormItemLayout.qml index fceaf1296..6997d3c53 100644 --- a/Linphone/view/Control/Container/FormItemLayout.qml +++ b/Linphone/view/Control/Container/FormItemLayout.qml @@ -12,6 +12,7 @@ FocusScope{ property string labelIndication property string tooltip: "" property bool mandatory: false + property int errorTextTopMargin: 0// property alias errorTextItem: errorText property alias errorMessage: errorText.text @@ -85,7 +86,7 @@ FocusScope{ Item { Layout.preferredHeight: childrenRect.height Layout.fillWidth: true - StackLayout { + FocusScope { id: contentItem height: childrenRect.height anchors.left: parent.left @@ -94,6 +95,7 @@ FocusScope{ TemporaryText { id: errorText anchors.top: contentItem.bottom + anchors.topMargin: mainItem.errorTextTopMargin color: DefaultStyle.danger_500_main } } diff --git a/Linphone/view/Control/Container/GroupCreationFormLayout.qml b/Linphone/view/Control/Container/GroupCreationFormLayout.qml index e2c1c80d3..930549284 100644 --- a/Linphone/view/Control/Container/GroupCreationFormLayout.qml +++ b/Linphone/view/Control/Container/GroupCreationFormLayout.qml @@ -81,6 +81,7 @@ FocusScope { Layout.fillWidth: true Layout.preferredHeight: Utils.getSizeWithScreenRatio(49) focus: true + isError: groupNameItem.errorMessage !== "" KeyNavigation.down: addParticipantsLayout //participantList.count > 0 ? participantList : searchbar Accessible.name: qsTr("group_start_dialog_subject_hint") } diff --git a/Linphone/view/Control/Container/VerticalTabBar.qml b/Linphone/view/Control/Container/VerticalTabBar.qml index 329ece59e..cad6ac008 100644 --- a/Linphone/view/Control/Container/VerticalTabBar.qml +++ b/Linphone/view/Control/Container/VerticalTabBar.qml @@ -16,11 +16,19 @@ Control.TabBar { readonly property alias cornerRadius: bottomLeftCorner.radius property AccountGui defaultAccount + + property int visibleCount: 0 // Call it after model is ready. If done before, Repeater will not be updated function initButtons(){ actionsRepeater.model = mainItem.model } + function updateVisibleCount() { + mainItem.visibleCount = 0 + contentChildren.forEach(child => { + if (child.visible) mainItem.visibleCount = mainItem.visibleCount + 1 + }) + } onDefaultAccountChanged: { if (defaultAccount) defaultAccount.core?.lRefreshNotifications() @@ -86,6 +94,7 @@ Control.TabBar { topInset: Utils.getSizeWithScreenRatio(32) hoverEnabled: true visible: modelData?.visible != undefined ? modelData.visible : true + onVisibleChanged: mainItem.updateVisibleCount() text: modelData.accessibilityLabel property bool keyboardFocus: FocusHelper.keyboardFocus UnreadNotification { diff --git a/Linphone/view/Control/Display/Call/CallHistoryListView.qml b/Linphone/view/Control/Display/Call/CallHistoryListView.qml index 167676433..89c023c2d 100644 --- a/Linphone/view/Control/Display/Call/CallHistoryListView.qml +++ b/Linphone/view/Control/Display/Call/CallHistoryListView.qml @@ -21,19 +21,16 @@ ListView { onResultsReceived: { loading = false - // contentY = 0 } model: CallHistoryProxy { id: callHistoryProxy - Component.onCompleted: { - loading = true - } onListAboutToBeReset: loading = true filterText: mainItem.searchText onFilterTextChanged: maxDisplayItems = initialDisplayItems initialDisplayItems: Math.max(20, Math.round(2 * mainItem.height / Utils.getSizeWithScreenRatio(56))) displayItemsStep: 3 * initialDisplayItems / 2 + onModelAboutToBeReset: loading = true onModelReset: { mainItem.resultsReceived() } @@ -51,13 +48,12 @@ ListView { Component.onCompleted: cacheBuffer = Math.max(mainItem.height,0) //contentHeight>0 ? contentHeight : 0// cache all items // remove binding loop - onContentHeightChanged: Qt.callLater(function () { - if (mainItem) - mainItem.cacheBuffer = Math?.max(contentHeight, 0) || 0 - }) + // onContentHeightChanged: Qt.callLater(function () { + // if (mainItem) + // mainItem.cacheBuffer = Math?.max(contentHeight, 0) || 0 + // }) - onActiveFocusChanged: if (activeFocus && currentIndex < 0 && count > 0) - currentIndex = 0 + onActiveFocusChanged: if (activeFocus && currentIndex < 0 && count > 0) currentIndex = 0 onCountChanged: { if (currentIndex < 0 && count > 0) { mainItem.currentIndex = 0 // Select first item after loading model @@ -88,8 +84,8 @@ ListView { // Update position only if we are moving to current item and its position is changing. property var _currentItemY: currentItem?.y on_CurrentItemYChanged: if (_currentItemY && moveAnimation.running) { - moveToCurrentItem() - } + moveToCurrentItem() + } Behavior on contentY { NumberAnimation { id: moveAnimation @@ -100,10 +96,6 @@ ListView { } //---------------------------------------------------------------- - onVisibleChanged: { -// if (!visible) -// currentIndex = -1 - } BusyIndicator { anchors.horizontalCenter: mainItem.horizontalCenter @@ -196,6 +188,7 @@ ListView { } } BigButton { + visible: !modelData.core.isConference || !SettingsCpp.disableMeetingsFeature style: ButtonStyle.noBackground icon.source: AppIcons.phone focus: true diff --git a/Linphone/view/Control/Display/Chat/AnimatedImageFileView.qml b/Linphone/view/Control/Display/Chat/AnimatedImageFileView.qml index 161da0e23..a867711f3 100644 --- a/Linphone/view/Control/Display/Chat/AnimatedImageFileView.qml +++ b/Linphone/view/Control/Display/Chat/AnimatedImageFileView.qml @@ -1,7 +1,6 @@ import QtQuick import QtQuick.Controls as Control import QtQuick.Layouts -import QtMultimedia import Linphone import UtilsCpp @@ -25,7 +24,8 @@ AnimatedImage { autoTransform: true fillMode: Image.PreserveAspectFit source: contentGui && UtilsCpp.isAnimatedImage(contentGui.core.filePath) ? ('file:/'+ contentGui.core.filePath) : "" - + // sourceSize.width: implicitWidth + // sourceSize.height: implicitHeight states: State { name: 'hovered' } diff --git a/Linphone/view/Control/Display/Chat/ChatListView.qml b/Linphone/view/Control/Display/Chat/ChatListView.qml index 1291b6d5e..60b7bd217 100644 --- a/Linphone/view/Control/Display/Chat/ChatListView.qml +++ b/Linphone/view/Control/Display/Chat/ChatListView.qml @@ -15,6 +15,7 @@ ListView { property SearchBar searchBar property bool loading: false property string searchText: searchBar?.text + property alias chatProxy: chatProxy property real busyIndicatorSize: Utils.getSizeWithScreenRatio(60) property ChatGui currentChatGui: model.getAt(currentIndex) || null @@ -38,9 +39,6 @@ ListView { model: ChatProxy { id: chatProxy - Component.onCompleted: { - loading = true - } filterText: mainItem.searchText onFilterTextChanged: { chatToSelectLater = currentChatGui @@ -50,6 +48,19 @@ ListView { } onModelReset: { loading = false + if (mainItem.chatToSelectLater) { + selectChat(mainItem.chatToSelectLater) + mainItem.chatToSelectLater = null + } + else if (mainItem.chatToSelect) { + selectChat(mainItem.chatToSelect) + mainItem.chatToSelect = null + } else { + selectChat(mainItem.currentChatGui) + } + } + onChatAdded: (chat) => { + mainItem.chatToSelect = chat } onRowsRemoved: { var index = mainItem.currentIndex @@ -76,7 +87,9 @@ ListView { function selectChat(chatGui, force) { var index = chatProxy.findChatIndex(chatGui) - // force adding chat to list if not in list for now + // force adding chat to list if it already exists + // but has not been added to the list yet because + // it is empty and hide_empty_chatrooms is set if (index === -1 && force === true && chatGui) { if (chatProxy.addChatInList(chatGui)) { var index = chatProxy.findChatIndex(chatGui) @@ -85,7 +98,6 @@ ListView { mainItem.currentIndex = index } - Component.onCompleted: cacheBuffer = Math.max(contentHeight, 0) //contentHeight>0 ? contentHeight : 0// cache all items // remove binding loop onContentHeightChanged: Qt.callLater(function () { if (mainItem) @@ -264,7 +276,8 @@ ListView { Item{Layout.fillWidth: true} Text { color: DefaultStyle.main2_500_main - text: modelData ? UtilsCpp.formatDate(modelData.core.lastUpdatedTime, true, false) : "" + property string format: modelData && UtilsCpp.isCurrentYear(modelData.core.lastUpdatedTime) ? "dd/MM" : "dd/MM/yy" + text: modelData ? UtilsCpp.formatDate(modelData.core.lastUpdatedTime, true, false, format) : "" font { pixelSize: Typography.p3.pixelSize weight: Typography.p3.weight diff --git a/Linphone/view/Control/Display/Chat/ChatMessage.qml b/Linphone/view/Control/Display/Chat/ChatMessage.qml index 0c7402e2d..b5cccece5 100644 --- a/Linphone/view/Control/Display/Chat/ChatMessage.qml +++ b/Linphone/view/Control/Display/Chat/ChatMessage.qml @@ -27,7 +27,7 @@ Control.Control { property var msgState: chatMessage ? chatMessage.core.messageState : LinphoneEnums.ChatMessageState.StateIdle hoverEnabled: true property bool linkHovered: false - property real maxWidth: parent?.width || Utils.getSizeWithScreenRatio(300) + property real maxWidth: parent ? parent.width : Utils.getSizeWithScreenRatio(300) leftPadding: isRemoteMessage ? Utils.getSizeWithScreenRatio(5) : 0 @@ -422,6 +422,18 @@ Control.Control { popup.padding: 0 popup.contentItem: ColumnLayout { spacing: 0 + IconLabelButton { + visible: mainItem.msgStatev === LinphoneEnums.ChatMessageState.StateNotDelivered + inverseLayout: true + //: "Re-send" + text: qsTr("chat_message_send_again") + icon.source: AppIcons.chatTeardropText + Layout.fillWidth: true + Layout.preferredHeight: Utils.getSizeWithScreenRatio(45) + onClicked: { + mainItem.chatMessage.lSend() + } + } IconLabelButton { inverseLayout: true //: "Reception info" diff --git a/Linphone/view/Control/Display/Chat/ChatMessageContent.qml b/Linphone/view/Control/Display/Chat/ChatMessageContent.qml index 7751fd8e2..e983b0f95 100644 --- a/Linphone/view/Control/Display/Chat/ChatMessageContent.qml +++ b/Linphone/view/Control/Display/Chat/ChatMessageContent.qml @@ -2,7 +2,6 @@ import QtQuick import QtQuick.Effects import QtQuick.Layouts import QtQuick.Controls.Basic as Control -import QtMultimedia import Linphone import UtilsCpp @@ -82,6 +81,7 @@ ColumnLayout { // SINGLE FILE ImageFileView { id: singleImageFile + cache: false visible: mainItem.filescontentProxy.count === 1 && source !== "" && UtilsCpp.isImage(contentGui.core.filePath) contentGui: mainItem.filescontentProxy.count === 1 ? mainItem.filescontentProxy.getChatMessageContentAtIndex(0) @@ -100,6 +100,22 @@ ColumnLayout { Layout.preferredHeight: paintedHeight Layout.alignment: Qt.AlignHCenter fillMode: Image.PreserveAspectFit + cache: false + property int initialSourceWidth + property int initialSourceHeight + property bool initialization: true + onStatusChanged: { + if (status == Image.Ready) { + if (singleAnimatedImageFile.initialization) { + initialSourceWidth = sourceSize.width + initialSourceHeight = sourceSize.height + singleAnimatedImageFile.initialization = false + } + var sourceW = Math.min(initialSourceWidth, mainItem.maxWidth) + sourceSize.height = Math.round((sourceW/initialSourceWidth) * initialSourceHeight) + sourceSize.width = sourceW + } + } } VideoFileView { id: singleVideoFile @@ -110,10 +126,10 @@ ColumnLayout { Layout.fillWidth: true width: Math.min(Utils.getSizeWithScreenRatio(285), mainItem.maxWidth) height: Math.min(Utils.getSizeWithScreenRatio(285), mainItem.maxWidth) - Layout.preferredWidth: videoOutput.contentRect.width - Layout.preferredHeight: videoOutput.contentRect.height + Layout.preferredWidth: width + Layout.preferredHeight: height Layout.alignment: Qt.AlignHCenter - fillMode: VideoOutput.PreserveAspectFit + // fillMode: VideoOutput.PreserveAspectFit } // FILES diff --git a/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml b/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml index 343e5b9f3..c41cc7d8c 100644 --- a/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml +++ b/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml @@ -18,7 +18,7 @@ ListView { property real busyIndicatorSize: Utils.getSizeWithScreenRatio(60) property bool loading: false property bool isEncrypted: chat && chat.core.isEncrypted - highlightFollowsCurrentItem: false + highlightFollowsCurrentItem: true verticalLayoutDirection: ListView.BottomToTop signal showReactionsForMessageRequested(ChatMessageGui chatMessage) @@ -74,7 +74,7 @@ ListView { onAtYBeginningChanged: if (atYBeginning && count !== 0) { eventLogProxy.displayMore() } - onAtYEndChanged: if (atYEnd && chat) { + onAtYEndChanged: if (atYEnd && chat && count !== 0) { chat.core.lMarkAsRead() } @@ -87,10 +87,13 @@ ListView { onModelAboutToBeReset: { loading = true } - onModelReset: { + onModelUpdated: { loading = false var index = eventLogProxy.findFirstUnreadIndex() - mainItem.positionViewAtIndex(index, ListView.Contain) + var itemToSelect = mainItem.itemAtIndex(index) + mainItem.positionViewAtIndex(index, ListView.Beginning) + var lastMessage = itemAtIndex(0) + mainItem.lastItemVisible = lastMessage.isFullyVisible eventLogProxy.markIndexAsRead(index) } onEventInsertedByUser: (index) => { @@ -225,6 +228,7 @@ ListView { indicatorWidth: mainItem.busyIndicatorSize indicatorColor: DefaultStyle.main1_500_main } + Dialog { id: messageDeletionDialog diff --git a/Linphone/view/Control/Display/Chat/ChatTextContent.qml b/Linphone/view/Control/Display/Chat/ChatTextContent.qml index d592d87a6..e907cb0d4 100644 --- a/Linphone/view/Control/Display/Chat/ChatTextContent.qml +++ b/Linphone/view/Control/Display/Chat/ChatTextContent.qml @@ -15,6 +15,9 @@ TextEdit { property string lastTextSelected : '' property string searchedTextPart color: DefaultStyle.main2_700 + // force restoring cursor in case we click on a mention, otherwise + // the cursor stays IBeam + onVisibleChanged: if (!visible) UtilsCpp.restoreGlobalCursor() font { pixelSize: (contentGui && UtilsCpp.isOnlyEmojis(contentGui.core.text)) ? Typography.h1.pixelSize : Typography.p1.pixelSize weight: Typography.p1.weight @@ -26,9 +29,10 @@ TextEdit { readOnly: true selectByMouse: true - text: searchedTextPart !== "" - ? UtilsCpp.boldTextPart(contentGui.core.richFormatText, searchedTextPart) - : contentGui.core.richFormatText + text: contentGui.core.richFormatText + onSearchedTextPartChanged: { + contentGui.core.setSearchedTextPart(searchedTextPart) + } textFormat: Text.RichText // To supports links and imgs. wrapMode: TextEdit.Wrap @@ -66,7 +70,11 @@ TextEdit { propagateComposedEvents: true hoverEnabled: true scrollGestureEnabled: false - cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.IBeamCursor + onContainsMouseChanged: { + if (containsMouse) UtilsCpp.setGlobalCursor(Qt.IBeamCursor) + else UtilsCpp.restoreGlobalCursor() + } + cursorShape: mainItem.hoveredLink ? Qt.PointingHandCursor : Qt.IBeamCursor acceptedButtons: Qt.LeftButton onPressed: (mouse) => { // if(!keepLastSelection) { diff --git a/Linphone/view/Control/Display/Chat/EphemeralEvent.qml b/Linphone/view/Control/Display/Chat/EphemeralEvent.qml index 9bec8bec7..68db83cb4 100644 --- a/Linphone/view/Control/Display/Chat/EphemeralEvent.qml +++ b/Linphone/view/Control/Display/Chat/EphemeralEvent.qml @@ -7,8 +7,7 @@ import "qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js" as Utils Rectangle { anchors.centerIn: parent property EventLogGui eventLogGui - property var eventLogCore: eventLogGui.core - visible: eventLogCore.handled + visible: eventLogGui.core.handled height: row.height + Utils.getSizeWithScreenRatio(15) width: row.width + Utils.getSizeWithScreenRatio(15) radius: Utils.getSizeWithScreenRatio(10) @@ -27,7 +26,7 @@ Rectangle { } Text { id: message - text: eventLogCore.eventDetails + text: eventLogGui.core.eventDetails font: Typography.p3 color: DefaultStyle.main2_400 } diff --git a/Linphone/view/Control/Display/Chat/Event.qml b/Linphone/view/Control/Display/Chat/Event.qml index eaa131149..f6be58be2 100644 --- a/Linphone/view/Control/Display/Chat/Event.qml +++ b/Linphone/view/Control/Display/Chat/Event.qml @@ -5,11 +5,10 @@ import UtilsCpp import "qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js" as Utils RowLayout { - id: mainLayout + id: mainItem height: Utils.getSizeWithScreenRatio(40) - visible: eventLogCore.handled + visible: eventLogGui.core.handled property EventLogGui eventLogGui - property var eventLogCore: eventLogGui.core Rectangle { height: 1 @@ -24,15 +23,15 @@ RowLayout { Text { id: message - text: eventLogCore.eventDetails + text: mainItem.eventLogGui.core.eventDetails font: Typography.p3 - color: eventLogCore.important ? DefaultStyle.danger_500_main : DefaultStyle.main2_500_main + color: mainItem.eventLogGui.core.important ? DefaultStyle.danger_500_main : DefaultStyle.main2_500_main horizontalAlignment: Text.AlignHCenter Layout.alignment: Qt.AlignHCenter } Text { id: date - text: UtilsCpp.toDateTimeString(eventLogCore.timestamp) + text: UtilsCpp.toDateTimeString(mainItem.eventLogGui.core.timestamp) font: Typography.p3 color: DefaultStyle.main2_500_main horizontalAlignment: Text.AlignHCenter diff --git a/Linphone/view/Control/Display/Chat/FileView.qml b/Linphone/view/Control/Display/Chat/FileView.qml index f9725f992..adb930908 100644 --- a/Linphone/view/Control/Display/Chat/FileView.qml +++ b/Linphone/view/Control/Display/Chat/FileView.qml @@ -1,7 +1,6 @@ import QtQuick import QtQuick.Controls as Control import QtQuick.Layouts -import QtMultimedia import Linphone import UtilsCpp @@ -42,7 +41,7 @@ Item { Connections { enabled: contentGui - target: contentGui.core + target: contentGui ? contentGui.core : null function onMsgStateChanged(state) { mainItem.isTransferring = state === LinphoneEnums.ChatMessageState.StateFileTransferInProgress || state === LinphoneEnums.ChatMessageState.StateInProgress @@ -107,35 +106,35 @@ Item { visible: mainItem.isVideo color: DefaultStyle.grey_1000 anchors.fill: parent - Video { - id: videoThumbnail - anchors.fill: parent - position: 100 - source: mainItem.isVideo ? "file:///" + mainItem.filePath : "" - fillMode: playbackState === MediaPlayer.PlayingState ? VideoOutput.PreserveAspectFit : VideoOutput.PreserveAspectCrop + // Video { + // id: videoThumbnail + // anchors.fill: parent + // position: 100 + // source: mainItem.isVideo ? "file:///" + mainItem.filePath : "" + // fillMode: playbackState === MediaPlayer.PlayingState ? VideoOutput.PreserveAspectFit : VideoOutput.PreserveAspectCrop EffectImage { anchors.centerIn: parent - visible: videoThumbnail.playbackState !== MediaPlayer.PlayingState + // visible: videoThumbnail.playbackState !== MediaPlayer.PlayingState width: Utils.getSizeWithScreenRatio(24) height: Utils.getSizeWithScreenRatio(24) imageSource: AppIcons.playFill colorizationColor: DefaultStyle.main2_0 } - Text { - z: parent.z + 1 - property int timeDisplayed: videoThumbnail.playbackState === MediaPlayer.PlayingState ? videoThumbnail.position : videoThumbnail.duration - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.bottomMargin: Utils.getSizeWithScreenRatio(6) - anchors.leftMargin: Utils.getSizeWithScreenRatio(6) - text: UtilsCpp.formatDuration(timeDisplayed) - color: DefaultStyle.grey_0 - font { - pixelSize: Typography.d1.pixelSize - weight: Typography.d1.weight - } - } - } + // Text { + // z: parent.z + 1 + // property int timeDisplayed: videoThumbnail.playbackState === MediaPlayer.PlayingState ? videoThumbnail.position : videoThumbnail.duration + // anchors.bottom: parent.bottom + // anchors.left: parent.left + // anchors.bottomMargin: Utils.getSizeWithScreenRatio(6) + // anchors.leftMargin: Utils.getSizeWithScreenRatio(6) + // text: UtilsCpp.formatDuration(timeDisplayed) + // color: DefaultStyle.grey_0 + // font { + // pixelSize: Typography.d1.pixelSize + // weight: Typography.d1.weight + // } + // } + // } } } } @@ -271,7 +270,7 @@ Item { EffectImage { z: parent.z + 1 anchors.centerIn: parent - imageSource: defaultViewStack.imageSource + imageSource: mainItem.imageSource width: Utils.getSizeWithScreenRatio(22) height: width colorizationColor: DefaultStyle.main2_600 diff --git a/Linphone/view/Control/Display/Chat/ImageFileView.qml b/Linphone/view/Control/Display/Chat/ImageFileView.qml index 4adc01790..49269386c 100644 --- a/Linphone/view/Control/Display/Chat/ImageFileView.qml +++ b/Linphone/view/Control/Display/Chat/ImageFileView.qml @@ -1,7 +1,6 @@ import QtQuick import QtQuick.Controls as Control import QtQuick.Layouts -import QtMultimedia import Linphone import UtilsCpp diff --git a/Linphone/view/Control/Display/Chat/VideoFileView.qml b/Linphone/view/Control/Display/Chat/VideoFileView.qml index e24208377..6f48eb739 100644 --- a/Linphone/view/Control/Display/Chat/VideoFileView.qml +++ b/Linphone/view/Control/Display/Chat/VideoFileView.qml @@ -1,56 +1,55 @@ import QtQuick import QtQuick.Controls as Control import QtQuick.Layouts -import QtMultimedia +// import QtMultimedia import Linphone import UtilsCpp import 'qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js' as Utils // ============================================================================= - Rectangle { id: mainItem color: DefaultStyle.grey_1000 property ChatMessageContentGui contentGui property string filePath: contentGui && contentGui.core.filePath - property var fillMode: playbackState === MediaPlayer.PlayingState ? VideoOutput.PreserveAspectFit : VideoOutput.PreserveAspectCrop - property alias videoOutput: output - property string source: mediaPlayer.source + // property var fillMode: playbackState === MediaPlayer.PlayingState ? VideoOutput.PreserveAspectFit : VideoOutput.PreserveAspectCrop + // property alias videoOutput: output + // property string source: mediaPlayer.source - MediaPlayer { - id: mediaPlayer - source: UtilsCpp.isVideo(mainItem.filePath) ? "file:///" + mainItem.filePath : "" - position: 100 - videoOutput: output - } - VideoOutput { - id: output - fillMode: mainItem.fillMode - endOfStreamPolicy: VideoOutput.KeepLastFrame - width: mainItem.width - height: mainItem.height - Component.onCompleted: { - // We need to start the video so the content rect of the - // video output is updated - mediaPlayer.play() - mediaPlayer.pause() - } - Text { - z: parent.z + 1 - property int timeDisplayed: mediaPlayer.playbackState === MediaPlayer.PlayingState ? mediaPlayer.position : mediaPlayer.duration - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.bottomMargin: Utils.getSizeWithScreenRatio(6) - anchors.leftMargin: Utils.getSizeWithScreenRatio(6) - text: UtilsCpp.formatDuration(timeDisplayed) - color: DefaultStyle.grey_0 - font { - pixelSize: Typography.d1.pixelSize - weight: Typography.d1.weight - } - } - } + // MediaPlayer { + // id: mediaPlayer + // source: UtilsCpp.isVideo(mainItem.filePath) ? "file:///" + mainItem.filePath : "" + // position: 100 + // videoOutput: output + // } + // VideoOutput { + // id: output + // fillMode: mainItem.fillMode + // endOfStreamPolicy: VideoOutput.KeepLastFrame + // width: mainItem.width + // height: mainItem.height + // Component.onCompleted: { + // // We need to start the video so the content rect of the + // // video output is updated + // mediaPlayer.play() + // mediaPlayer.pause() + // } + // Text { + // z: parent.z + 1 + // property int timeDisplayed: mediaPlayer.playbackState === MediaPlayer.PlayingState ? mediaPlayer.position : mediaPlayer.duration + // anchors.bottom: parent.bottom + // anchors.left: parent.left + // anchors.bottomMargin: Utils.getSizeWithScreenRatio(6) + // anchors.leftMargin: Utils.getSizeWithScreenRatio(6) + // text: UtilsCpp.formatDuration(timeDisplayed) + // color: DefaultStyle.grey_0 + // font { + // pixelSize: Typography.d1.pixelSize + // weight: Typography.d1.weight + // } + // } + // } MouseArea { propagateComposedEvents: false enabled: mainItem.visible @@ -71,7 +70,7 @@ Rectangle { } EffectImage { anchors.centerIn: parent - visible: mediaPlayer.playbackState !== MediaPlayer.PlayingState + // visible: mediaPlayer.playbackState !== MediaPlayer.PlayingState width: Utils.getSizeWithScreenRatio(24) height: Utils.getSizeWithScreenRatio(24) imageSource: AppIcons.playFill diff --git a/Linphone/view/Control/Display/Contact/Avatar.qml b/Linphone/view/Control/Display/Contact/Avatar.qml index 301f771c0..735f95381 100644 --- a/Linphone/view/Control/Display/Contact/Avatar.qml +++ b/Linphone/view/Control/Display/Contact/Avatar.qml @@ -146,7 +146,7 @@ Loader{ width: height Rectangle { id: initialItem - property string initials: mainItem.isConference ? "" : UtilsCpp.getInitials(mainItem.displayNameVal) + property string initials: mainItem.isConference || (mainItem.displayNameVal && mainItem.displayNameVal[0] === "+") ? "" : UtilsCpp.getInitials(mainItem.displayNameVal) radius: width / 2 color: DefaultStyle.main2_200 height: stackView.height @@ -165,7 +165,7 @@ Loader{ } EffectImage { id: initialImg - visible: initialItem.initials === "" + visible: initialItem.initials === "" || initialItem.initials[0] === "+" width: stackView.width/2 height: width colorizationColor: DefaultStyle.main2_600 diff --git a/Linphone/view/Control/Display/Contact/Contact.qml b/Linphone/view/Control/Display/Contact/Contact.qml index 9ee7b2349..a256bda69 100644 --- a/Linphone/view/Control/Display/Contact/Contact.qml +++ b/Linphone/view/Control/Display/Contact/Contact.qml @@ -102,6 +102,7 @@ Control.Control{ height: contactStatusPopup.height ContactStatusPopup{ id: contactStatusPopup + visible: mainItem.account.core.publishEnabled } MouseArea { anchors.fill: contactStatusPopup diff --git a/Linphone/view/Control/Display/Contact/ContactListItem.qml b/Linphone/view/Control/Display/Contact/ContactListItem.qml index c9ffc5dba..89ab3b238 100644 --- a/Linphone/view/Control/Display/Contact/ContactListItem.qml +++ b/Linphone/view/Control/Display/Contact/ContactListItem.qml @@ -3,7 +3,7 @@ import QtQuick.Layouts import QtQuick.Controls.Basic as Control import Linphone -import UtilsCpp 1.0 +import UtilsCpp import ConstantsCpp import SettingsCpp import "qrc:/qt/qml/Linphone/view/Style/buttonStyle.js" as ButtonStyle @@ -36,7 +36,7 @@ FocusScope { property real itemsRightMargin: Utils.getSizeWithScreenRatio(39) property var displayName: searchResultItem? searchResultItem.core.fullName : "" - property var initial: displayName != "" ? displayName[0].toLocaleLowerCase(ConstantsCpp.DefaultLocale) : '' + property var initial: displayName.length > 0 ? displayName[0].toLocaleLowerCase(AppCpp.localeAsString) : '' signal clicked(var mouse) signal contactDeletionRequested(FriendGui contact) diff --git a/Linphone/view/Control/Display/Contact/ContactStatusPopup.qml b/Linphone/view/Control/Display/Contact/ContactStatusPopup.qml index c209ca22d..6d2f0509b 100644 --- a/Linphone/view/Control/Display/Contact/ContactStatusPopup.qml +++ b/Linphone/view/Control/Display/Contact/ContactStatusPopup.qml @@ -73,7 +73,7 @@ PopupButton { visible: !presenceAndRegistrationItem.editCustomStatus anchors.fill: parent anchors.margins: Utils.getSizeWithScreenRatio(20) - accountCore: mainItem.account.core + accountGui: mainItem.account onSetCustomStatusClicked: { presenceAndRegistrationItem.editCustomStatus = true } @@ -84,7 +84,7 @@ PopupButton { visible: presenceAndRegistrationItem.editCustomStatus anchors.fill: parent anchors.margins: Utils.getSizeWithScreenRatio(20) - accountCore: mainItem.account.core + accountGui: mainItem.account onVisibleChanged: { if (!visible) { presenceAndRegistrationItem.editCustomStatus = false diff --git a/Linphone/view/Control/Display/Contact/Presence.qml b/Linphone/view/Control/Display/Contact/Presence.qml index 9ce75ef86..feefca37f 100644 --- a/Linphone/view/Control/Display/Contact/Presence.qml +++ b/Linphone/view/Control/Display/Contact/Presence.qml @@ -8,20 +8,20 @@ import 'qrc:/qt/qml/Linphone/view/Style/buttonStyle.js' as ButtonStyle ColumnLayout { id: mainItem - property var accountCore + property var accountGui signal setCustomStatusClicked signal isSet spacing: Utils.getSizeWithScreenRatio(8) - PresenceStatusItem { presence: LinphoneEnums.Presence.Online; accountCore: mainItem.accountCore; onClick: mainItem.isSet()} - PresenceStatusItem { presence: LinphoneEnums.Presence.Away; accountCore: mainItem.accountCore; onClick: mainItem.isSet()} - PresenceStatusItem { presence: LinphoneEnums.Presence.Busy; accountCore: mainItem.accountCore; onClick: mainItem.isSet()} - PresenceStatusItem { presence: LinphoneEnums.Presence.DoNotDisturb; accountCore: mainItem.accountCore; onClick: mainItem.isSet()} - PresenceStatusItem { presence: LinphoneEnums.Presence.Offline; accountCore: mainItem.accountCore; onClick: mainItem.isSet()} + PresenceStatusItem { presence: LinphoneEnums.Presence.Online; accountGui: mainItem.accountGui; onClick: mainItem.isSet()} + PresenceStatusItem { presence: LinphoneEnums.Presence.Away; accountGui: mainItem.accountGui; onClick: mainItem.isSet()} + PresenceStatusItem { presence: LinphoneEnums.Presence.Busy; accountGui: mainItem.accountGui; onClick: mainItem.isSet()} + PresenceStatusItem { presence: LinphoneEnums.Presence.DoNotDisturb; accountGui: mainItem.accountGui; onClick: mainItem.isSet()} + PresenceStatusItem { presence: LinphoneEnums.Presence.Offline; accountGui: mainItem.accountGui; onClick: mainItem.isSet()} RowLayout { spacing: 0 - visible: accountCore.explicitPresence != LinphoneEnums.Presence.Undefined + visible: accountGui.core.explicitPresence != LinphoneEnums.Presence.Undefined Layout.alignment: Qt.AlignLeft Layout.topMargin: Utils.getSizeWithScreenRatio(3) Layout.bottomMargin: Utils.getSizeWithScreenRatio(3) @@ -39,7 +39,7 @@ ColumnLayout { icon.width: Utils.getSizeWithScreenRatio(17) icon.height: Utils.getSizeWithScreenRatio(17) icon.source: AppIcons.reloadArrow - onClicked: accountCore.resetToAutomaticPresence() + onClicked: accountGui.core.resetToAutomaticPresence() } } HorizontalBar { @@ -53,16 +53,16 @@ ColumnLayout { Layout.alignment: Qt.AlignLeft Text { font: Typography.p1 - text: accountCore.presenceNote.length > 0 ? accountCore.presenceNote : qsTr("contact_presence_custom_status") + text: accountGui.core.presenceNote.length > 0 ? accountGui.core.presenceNote : qsTr("contact_presence_custom_status") color: DefaultStyle.main2_600 wrapMode: Text.WordWrap - Layout.preferredWidth: Utils.getSizeWithScreenRatio(accountCore.presenceNote.length == 0 ? 175 : 230) + Layout.preferredWidth: Utils.getSizeWithScreenRatio(accountGui.core.presenceNote.length == 0 ? 175 : 230) } Item { Layout.fillWidth: true } SmallButton { - visible: accountCore.presenceNote.length == 0 + visible: accountGui.core.presenceNote.length == 0 style: ButtonStyle.secondary text: qsTr("contact_presence_button_set_custom_status") onClicked: { @@ -71,7 +71,7 @@ ColumnLayout { } } RowLayout { - visible: accountCore.presenceNote.length > 0 + visible: accountGui.core.presenceNote.length > 0 spacing: Utils.getSizeWithScreenRatio(10) Item { Layout.fillWidth: true @@ -85,10 +85,10 @@ ColumnLayout { } SmallButton { style: ButtonStyle.secondary - visible: accountCore.presenceNote.length > 0 + visible: accountGui.core.presenceNote.length > 0 text: qsTr("contact_presence_button_delete_custom_status") onClicked: { - mainItem.accountCore.presenceNote = "" + mainItem.accountGui.core.presenceNote = "" } } } diff --git a/Linphone/view/Control/Display/Contact/PresenceSetCustomStatus.qml b/Linphone/view/Control/Display/Contact/PresenceSetCustomStatus.qml index 8d14de162..9d009ed9f 100644 --- a/Linphone/view/Control/Display/Contact/PresenceSetCustomStatus.qml +++ b/Linphone/view/Control/Display/Contact/PresenceSetCustomStatus.qml @@ -10,7 +10,7 @@ Column { id: mainItem spacing: Utils.getSizeWithScreenRatio(20) anchors.centerIn: parent - property var accountCore + property var accountGui signal isSet Text { @@ -39,9 +39,9 @@ Column { Layout.fillHeight: true Layout.fillWidth: true property string previoustext: "" - text: mainItem.accountCore.presenceNote + text: mainItem.accountGui.core.presenceNote onTextChanged: { - if (statusMessage.text.length > accountCore.maxPresenceNoteSize) { + if (statusMessage.text.length > accountGui.core.maxPresenceNoteSize) { statusMessage.text = previoustext statusMessage.cursorPosition = statusMessage.text.length } else { @@ -54,7 +54,7 @@ Column { } Text { Layout.fillWidth: true - text: statusMessage.text.length + " / " + accountCore.maxPresenceNoteSize + text: statusMessage.text.length + " / " + accountGui.core.maxPresenceNoteSize font: Typography.p1 color: DefaultStyle.main2_400 horizontalAlignment: Text.AlignRight @@ -71,7 +71,7 @@ Column { text: qsTr("contact_presence_button_save_custom_status") enabled: statusMessage.text.length > 0 onClicked: { - mainItem.accountCore.presenceNote = statusMessage.text + mainItem.accountGui.core.presenceNote = statusMessage.text mainItem.isSet() } } diff --git a/Linphone/view/Control/Display/Contact/PresenceStatusItem.qml b/Linphone/view/Control/Display/Contact/PresenceStatusItem.qml index 7d2668a65..f030283a3 100644 --- a/Linphone/view/Control/Display/Contact/PresenceStatusItem.qml +++ b/Linphone/view/Control/Display/Contact/PresenceStatusItem.qml @@ -10,7 +10,7 @@ import "qrc:/qt/qml/Linphone/view/Style/buttonStyle.js" as ButtonStyle IconLabelButton { id: mainItem - property var accountCore + property var accountGui property var presence signal click() @@ -29,7 +29,7 @@ IconLabelButton { padding: 0 onClicked: { - mainItem.accountCore.presence = mainItem.presence + mainItem.accountGui.core.presence = mainItem.presence mainItem.click() } } diff --git a/Linphone/view/Control/Display/Meeting/MeetingListView.qml b/Linphone/view/Control/Display/Meeting/MeetingListView.qml index 4983b0567..a1b728884 100644 --- a/Linphone/view/Control/Display/Meeting/MeetingListView.qml +++ b/Linphone/view/Control/Display/Meeting/MeetingListView.qml @@ -16,6 +16,7 @@ ListView { property bool hoverEnabled: true property var delegateButtons property ConferenceInfoGui selectedConference + property ConferenceInfoGui confToBeSelected: null property bool _moveToIndex: false property bool loading: false property real busyIndicatorSize: Utils.getSizeWithScreenRatio(60) @@ -26,6 +27,10 @@ ListView { spacing: Utils.getSizeWithScreenRatio(8) highlightFollowsCurrentItem: false + onCurrentIndexChanged: if(currentIndex === -1) { + resetSelections() + } + signal meetingDeletionRequested(ConferenceInfoGui confInfo, bool canCancel) function selectIndex(index){ @@ -52,7 +57,6 @@ ListView { moveToCurrentItem() if(currentItem) { mainItem.selectedConference = currentItem.itemGui - currentItem.forceActiveFocus() } } // Update position only if we are moving to current item and its position is changing. @@ -101,15 +105,14 @@ ListView { filterType: ConferenceInfoProxy.None initialDisplayItems: Math.max(20, Math.round(2 * mainItem.height / Utils.getSizeWithScreenRatio(63))) displayItemsStep: initialDisplayItems/2 - Component.onCompleted: { - mainItem.loading = false - } onModelAboutToBeReset: { + mainItem.confToBeSelected = mainItem.selectedConference mainItem.loading = true } onModelReset: { mainItem.loading = false - selectData(getCurrentDateConfInfo()) + if (mainItem.confToBeSelected) selectData(mainItem.confToBeSelected) + else selectData(getCurrentDateConfInfo()) } function selectData(confInfoGui){ mainItem.currentIndex = loadUntil(confInfoGui) @@ -243,7 +246,7 @@ ListView { anchors.fill: parent anchors.rightMargin: 5 // margin to avoid clipping shadows at right radius: Utils.getSizeWithScreenRatio(10) - visible: itemDelegate.haveModel || itemDelegate.activeFocus + visible: itemDelegate.haveModel || mainItem.currentIndex === itemDelegate.index color: itemDelegate.isSelected ? DefaultStyle.main2_200 : DefaultStyle.grey_0 // mainItem.currentIndex === index ColumnLayout { anchors.fill: parent diff --git a/Linphone/view/Control/Display/Participant/ParticipantInfoListView.qml b/Linphone/view/Control/Display/Participant/ParticipantInfoListView.qml index 32851d00f..57d47627a 100644 --- a/Linphone/view/Control/Display/Participant/ParticipantInfoListView.qml +++ b/Linphone/view/Control/Display/Participant/ParticipantInfoListView.qml @@ -15,6 +15,7 @@ ListView { property ChatGui chatGui height: contentHeight + property int delegateHoverRectangleRadius: 0 signal participantClicked(string username) @@ -60,6 +61,7 @@ ListView { visible: mousearea.containsMouse color: DefaultStyle.main2_200 opacity: 0.5 + radius: mainItem.delegateHoverRectangleRadius } } } diff --git a/Linphone/view/Control/Form/Call/ChangeLayoutForm.qml b/Linphone/view/Control/Form/Call/ChangeLayoutForm.qml index 8148117e7..d3c8887ea 100644 --- a/Linphone/view/Control/Form/Call/ChangeLayoutForm.qml +++ b/Linphone/view/Control/Form/Call/ChangeLayoutForm.qml @@ -31,6 +31,7 @@ FocusScope { ] RadioButton { id: radiobutton + enabled: mainItem.call && !mainItem.call.core.paused checkOnClick: false color: DefaultStyle.main1_500_main indicatorSize: Utils.getSizeWithScreenRatio(20) diff --git a/Linphone/view/Control/Input/Chat/ChatDroppableTextArea.qml b/Linphone/view/Control/Input/Chat/ChatDroppableTextArea.qml index d5a2295af..19fd372b3 100644 --- a/Linphone/view/Control/Input/Chat/ChatDroppableTextArea.qml +++ b/Linphone/view/Control/Input/Chat/ChatDroppableTextArea.qml @@ -26,6 +26,8 @@ Control.Control { property bool isEditing: false property ChatGui chat + + signal focusTextArea() // --------------------------------------------------------------------------- @@ -178,6 +180,9 @@ Control.Control { function onSendMessage() { sendingTextArea.clear() } + function onFocusTextArea() { + sendingTextArea.forceActiveFocus() + } } } } diff --git a/Linphone/view/Control/Input/DigitInput.qml b/Linphone/view/Control/Input/DigitInput.qml index 43a7ee957..93e583c5b 100644 --- a/Linphone/view/Control/Input/DigitInput.qml +++ b/Linphone/view/Control/Input/DigitInput.qml @@ -6,6 +6,7 @@ import "qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js" as Utils Control.TextField { id: mainItem property real inputSize: Utils.getSizeWithScreenRatio(100) + property bool isError: false color: activeFocus ? DefaultStyle.main1_500_main : DefaultStyle.main2_500_main validator: IntValidator{bottom: 0; top: 9} @@ -34,7 +35,11 @@ Control.TextField { Rectangle { id: background border.width: Utils.getSizeWithScreenRatio(1) - border.color: mainItem.activeFocus ? DefaultStyle.main1_500_main : DefaultStyle.main2_500_main + border.color: mainItem.isError + ? DefaultStyle.danger_500_main + : mainItem.activeFocus + ? DefaultStyle.main1_500_main + : DefaultStyle.main2_500_main radius: mainItem.inputSize * 0.15 width: mainItem.inputSize * 0.9 height: mainItem.inputSize diff --git a/Linphone/view/Control/Input/NumericPad.qml b/Linphone/view/Control/Input/NumericPad.qml index a84d3bef1..074c7c3cb 100644 --- a/Linphone/view/Control/Input/NumericPad.qml +++ b/Linphone/view/Control/Input/NumericPad.qml @@ -41,50 +41,69 @@ FocusScope { } } + function handleKeyPadEvent(event) { + if (event.key === Qt.Key_0) { + keypadKeyPressedAtIndex(10) + event.accepted = true + } + if (event.key === Qt.Key_1) { + keypadKeyPressedAtIndex(0) + event.accepted = true + } + if (event.key === Qt.Key_2) { + keypadKeyPressedAtIndex(1) + event.accepted = true + } + if (event.key === Qt.Key_3) { + keypadKeyPressedAtIndex(2) + event.accepted = true + } + if (event.key === Qt.Key_4) { + keypadKeyPressedAtIndex(3) + event.accepted = true + } + if (event.key === Qt.Key_5) { + keypadKeyPressedAtIndex(4) + event.accepted = true + } + if (event.key === Qt.Key_6) { + keypadKeyPressedAtIndex(5) + event.accepted = true + } + if (event.key === Qt.Key_7) { + keypadKeyPressedAtIndex(6) + event.accepted = true + } + if (event.key === Qt.Key_8) { + keypadKeyPressedAtIndex(7) + event.accepted = true + } + if (event.key === Qt.Key_9) { + keypadKeyPressedAtIndex(8) + event.accepted = true + } + if (event.key === Qt.Key_Asterisk) { + keypadKeyPressedAtIndex(9) + event.accepted = true + } + if (event.key === Qt.Key_Plus) { + mainItem.buttonPressed("+") + event.accepted = true + } + if (event.key === Qt.Key_Enter) { + mainItem.launchCall() + event.accepted = true + } + } + Keys.onPressed: (event) => { + event.accepted = false if (event.modifiers & Qt.KeypadModifier) { - if (event.key === Qt.Key_0) { - keypadKeyPressedAtIndex(10) - } - if (event.key === Qt.Key_1) { - keypadKeyPressedAtIndex(0) - } - if (event.key === Qt.Key_2) { - keypadKeyPressedAtIndex(1) - } - if (event.key === Qt.Key_3) { - keypadKeyPressedAtIndex(2) - } - if (event.key === Qt.Key_4) { - keypadKeyPressedAtIndex(3) - } - if (event.key === Qt.Key_5) { - keypadKeyPressedAtIndex(4) - } - if (event.key === Qt.Key_6) { - keypadKeyPressedAtIndex(5) - } - if (event.key === Qt.Key_7) { - keypadKeyPressedAtIndex(6) - } - if (event.key === Qt.Key_8) { - keypadKeyPressedAtIndex(7) - } - if (event.key === Qt.Key_9) { - keypadKeyPressedAtIndex(8) - } - if (event.key === Qt.Key_Asterisk) { - keypadKeyPressedAtIndex(9) - } - if (event.key === Qt.Key_Plus) { - mainItem.buttonPressed("+") - } - if (event.key === Qt.Key_Enter) { - mainItem.launchCall() - } + handleKeyPadEvent(event) } if (event.key === Qt.Key_Backspace) { mainItem.wipe() + event.accepted = true } } diff --git a/Linphone/view/Control/Input/PhoneNumberInput.qml b/Linphone/view/Control/Input/PhoneNumberInput.qml index 25a883e5f..72be8086e 100644 --- a/Linphone/view/Control/Input/PhoneNumberInput.qml +++ b/Linphone/view/Control/Input/PhoneNumberInput.qml @@ -18,7 +18,10 @@ ColumnLayout { readonly property string phoneNumber: textField.text readonly property string countryCode: combobox.text property string defaultCallingCode - property bool keyboardFocus: FocusHelper.keyboardFocus + property bool keyboardFocus: combobox.keyboardFocus || textField.keyboardFocus + property color keyboardFocusedBorderColor: DefaultStyle.main2_900 + property real borderWidth: Utils.getSizeWithScreenRatio(1) + property real keyboardFocusedBorderWidth: Utils.getSizeWithScreenRatio(3) spacing: Utils.getSizeWithScreenRatio(5) @@ -26,7 +29,7 @@ ColumnLayout { visible: label.length > 0 verticalAlignment: Text.AlignVCenter text: mainItem.label + (mainItem.mandatory ? "*" : "") - color: (combobox.hasActiveFocus || textField.hasActiveFocus) ? DefaultStyle.main1_500_main : DefaultStyle.main2_600 + color: (combobox.activeFocus || textField.activeFocus) ? DefaultStyle.main1_500_main : DefaultStyle.main2_600 font { pixelSize: Typography.p2.pixelSize weight: Typography.p2.weight @@ -36,25 +39,25 @@ ColumnLayout { Control.Control { Layout.preferredWidth: mainItem.width Layout.preferredHeight: Utils.getSizeWithScreenRatio(49) - leftPadding: Utils.getSizeWithScreenRatio(16) background: Rectangle { id: contentBackground anchors.fill: parent radius: Utils.getSizeWithScreenRatio(63) color: DefaultStyle.grey_100 border.color: mainItem.errorMessage.length > 0 - ? DefaultStyle.danger_500_main - : (textField.hasActiveFocus || combobox.hasActiveFocus) - ? DefaultStyle.main1_500_main - : DefaultStyle.grey_200 + ? DefaultStyle.danger_500_main + : (textField.activeFocus || combobox.activeFocus) + ? DefaultStyle.main1_500_main + : DefaultStyle.grey_200 + border.width: mainItem.borderWidth } contentItem: RowLayout { CountryIndicatorCombobox { id: combobox implicitWidth: Utils.getSizeWithScreenRatio(110) + leftPadding: Utils.getSizeWithScreenRatio(16) Layout.fillHeight: true defaultCallingCode: mainItem.defaultCallingCode - property bool keyboardFocus: FocusHelper.keyboardFocus //: %1 prefix Accessible.name: qsTr("prefix_phone_number_accessible_name").arg(mainItem.Accessible.name) } @@ -69,10 +72,15 @@ ColumnLayout { id: textField Layout.fillWidth: true placeholderText: mainItem.placeholderText - background: Item{} + background: Rectangle { + visible: textField.keyboardFocus + radius: Utils.getSizeWithScreenRatio(63) + color: "transparent" + border.color: mainItem.keyboardFocusedBorderColor + border.width: mainItem.keyboardFocusedBorderWidth + } initialText: initialPhoneNumber validator: RegularExpressionValidator{ regularExpression: /[0-9]+/} - property bool keyboardFocus: FocusHelper.keyboardFocus //: %1 number Accessible.name: qsTr("number_phone_number_accessible_name").arg(mainItem.Accessible.name) } diff --git a/Linphone/view/Control/Input/SearchBar.qml b/Linphone/view/Control/Input/SearchBar.qml index 0d2378f19..f335db3d0 100644 --- a/Linphone/view/Control/Input/SearchBar.qml +++ b/Linphone/view/Control/Input/SearchBar.qml @@ -104,6 +104,13 @@ FocusScope { repeat: false onTriggered: textField.searchText = textField.text } + Keys.onPressed: (event) => { + event.accepted = false + if (mainItem.numericPadPopup && mainItem.numericPadPopup.opened && (event.modifiers & Qt.KeypadModifier)) { + mainItem.numericPadPopup.keyPadKeyPressed(event) + event.accepted = true + } + } } Button { id: dialerButton diff --git a/Linphone/view/Control/Popup/Dialog/AuthenticationDialog.qml b/Linphone/view/Control/Popup/Dialog/AuthenticationDialog.qml index cd2cf2696..74484fd24 100644 --- a/Linphone/view/Control/Popup/Dialog/AuthenticationDialog.qml +++ b/Linphone/view/Control/Popup/Dialog/AuthenticationDialog.qml @@ -72,6 +72,7 @@ Dialog { contentItem: TextField { id: passwordEdit hidden: true + width: parent.width isError: passwordItem.errorTextVisible KeyNavigation.down: cancelButton } diff --git a/Linphone/view/Control/Popup/NumericPadPopup.qml b/Linphone/view/Control/Popup/NumericPadPopup.qml index 65fce495e..56c763ee3 100644 --- a/Linphone/view/Control/Popup/NumericPadPopup.qml +++ b/Linphone/view/Control/Popup/NumericPadPopup.qml @@ -20,6 +20,10 @@ Control.Popup { property var currentCall onOpened: numPad.forceActiveFocus() signal buttonPressed(string text) + signal keyPadKeyPressed(KeyEvent event) + onKeyPadKeyPressed: (event) => { + numPad.handleKeyPadEvent(event) + } signal launchCall() signal wipe() @@ -72,7 +76,6 @@ Control.Popup { lastRowVisible: mainItem.lastRowVisible currentCall: mainItem.currentCall onButtonPressed: (text) => { - console.log("BUTTON PRESSED NUMPAD") mainItem.buttonPressed(text) } onLaunchCall: mainItem.launchCall() diff --git a/Linphone/view/Control/Tool/Prototype/CallPrototype.qml b/Linphone/view/Control/Tool/Prototype/CallPrototype.qml index 5553271db..95b902a01 100644 --- a/Linphone/view/Control/Tool/Prototype/CallPrototype.qml +++ b/Linphone/view/Control/Tool/Prototype/CallPrototype.qml @@ -93,7 +93,6 @@ Window { height: 20 width: accountList.width Text{ - text: modelData.core.identityAddress } } diff --git a/Linphone/view/Page/Form/Chat/SelectedChatView.qml b/Linphone/view/Page/Form/Chat/SelectedChatView.qml index 9784284c6..34c3a265f 100644 --- a/Linphone/view/Page/Form/Chat/SelectedChatView.qml +++ b/Linphone/view/Page/Form/Chat/SelectedChatView.qml @@ -22,6 +22,7 @@ FocusScope { property alias callHeaderContent: splitPanel.header.contentItem property bool replyingToMessage: false property bool editingMessage: false + property string lastChar enum PanelType { MessageReactions, SharedFiles, Medias, ImdnStatus, ForwardToList, ManageParticipants, EphemeralSettings, None} signal oneOneCall(bool video) @@ -54,8 +55,7 @@ FocusScope { let addresses = []; for (let i = 0; i < sourceList.length; ++i) { const participantGui = sourceList[i] - const participantCore = participantGui.core - addresses.push(participantCore.sipAddress) + addresses.push(participantGui.core.sipAddress) } UtilsCpp.createGroupCall(mainItem.chat?.core.title, addresses) } @@ -104,13 +104,27 @@ FocusScope { Layout.preferredWidth: Utils.getSizeWithScreenRatio(45) Layout.preferredHeight: Utils.getSizeWithScreenRatio(45) } - Text { - text: mainItem.chat?.core.title || "" - color: DefaultStyle.main2_600 - maximumLineCount: 1 - font { - pixelSize: Typography.h4.pixelSize - weight: Utils.getSizeWithScreenRatio(400) + ColumnLayout { + Text { + text: mainItem.chat?.core.title || "" + color: DefaultStyle.main2_600 + maximumLineCount: 1 + font { + pixelSize: Typography.h4.pixelSize + weight: Utils.getSizeWithScreenRatio(400) + } + } + RowLayout { + visible: mainItem.chat?.core.ephemeralEnabled || false + EffectImage { + colorizationColor: DefaultStyle.main1_500_main + Layout.preferredWidth: Utils.getSizeWithScreenRatio(14) + Layout.preferredHeight: Utils.getSizeWithScreenRatio(14) + imageSource: AppIcons.clockCountDown + } + Text { + text: mainItem.chat ? UtilsCpp.getEphemeralFormatedTime(mainItem.chat.core.ephemeralLifetime) : "" + } } } RowLayout { @@ -328,8 +342,9 @@ FocusScope { Control.Control { id: participantListPopup width: parent.width - height: visible ? Math.min(participantInfoList.height, Utils.getSizeWithScreenRatio(200)) : 0 - visible: false + height: Math.min(participantInfoList.height, Utils.getSizeWithScreenRatio(200)) + visible: mainItem.lastChar === "@" + onVisibleChanged: console.log("participant list visible changed", visible, height) anchors.bottom: chatMessagesListView.bottom anchors.left: chatMessagesListView.left anchors.right: chatMessagesListView.right @@ -367,6 +382,7 @@ FocusScope { height: contentHeight width: participantListPopup.width chatGui: mainItem.chat + delegateHoverRectangleRadius: Utils.getSizeWithScreenRatio(20) onParticipantClicked: (username) => { messageSender.text = messageSender.text + username + " " messageSender.textArea.cursorPosition = messageSender.text.length @@ -510,12 +526,10 @@ FocusScope { if (chat) messageSender.text = mainItem.chat.core.sendingText } onTextChanged: { - if (text !== "" && mainItem.chat.core.composingName !== "") { + if (text !== "") { mainItem.chat.core.lCompose() } - var lastChar = text.slice(-1) - if (lastChar == "@") participantListPopup.visible = true - else participantListPopup.visible = false + mainItem.lastChar = text.slice(-1) mainItem.chat.core.sendingText = text } onSendMessage: { @@ -536,6 +550,13 @@ FocusScope { onDropped: (files) => { contents.addFiles(files) } + Connections { + target: mainItem + function onReplyingToMessageChanged() { + if (mainItem.replyingToMessage) messageSender.focusTextArea() + } + function onChatChanged() {messageSender.focusTextArea()} + } } } } @@ -633,6 +654,7 @@ FocusScope { id: sharedFilesComponent MessageSharedFilesInfos { chatGui: mainItem.chat + showAsSquare: contentLoader.panelType === SelectedChatView.PanelType.Medias title: contentLoader.panelType === SelectedChatView.PanelType.Medias //: Shared medias ? qsTr("shared_medias_title") diff --git a/Linphone/view/Page/Form/Contact/ContactEdition.qml b/Linphone/view/Page/Form/Contact/ContactEdition.qml index 902058eed..a734c42ea 100644 --- a/Linphone/view/Page/Form/Contact/ContactEdition.qml +++ b/Linphone/view/Page/Form/Contact/ContactEdition.qml @@ -15,7 +15,8 @@ MainRightPanel { property FriendGui contact Connections { - target: contact.core + enabled: contact + target: contact? contact.core : null function onIsSavedChanged() { if (contact.core.isSaved) { mainItem.closeEdition(contact.core.defaultFullAddress) diff --git a/Linphone/view/Page/Form/Meeting/MeetingForm.qml b/Linphone/view/Page/Form/Meeting/MeetingForm.qml index 18cdad514..907dcd90f 100644 --- a/Linphone/view/Page/Form/Meeting/MeetingForm.qml +++ b/Linphone/view/Page/Form/Meeting/MeetingForm.qml @@ -76,7 +76,7 @@ FocusScope { maximumLength: width //: "Ajouter un titre" property string defaultText: qsTr("meeting_schedule_subject_hint") - Component.onCompleted: text = defaultText + Component.onCompleted: text = mainItem.conferenceInfoGui.core.subject === "" ? defaultText : mainItem.conferenceInfoGui.core.subject text: conferenceInfoGui.core.subject ? conferenceInfoGui.core.subject : "" color: DefaultStyle.main2_600 font { @@ -193,7 +193,7 @@ FocusScope { anchors.fill: parent color: DefaultStyle.grey_100 } - model: TimeZoneProxy{ + model: TimeZoneProxy { } visible: model.count > 0 onCurrentIndexChanged: { diff --git a/Linphone/view/Page/Form/Register/RegisterCheckingPage.qml b/Linphone/view/Page/Form/Register/RegisterCheckingPage.qml index 437a0a0f2..2f88adff8 100644 --- a/Linphone/view/Page/Form/Register/RegisterCheckingPage.qml +++ b/Linphone/view/Page/Form/Register/RegisterCheckingPage.qml @@ -14,6 +14,7 @@ LoginLayout { property string address property string sipIdentityAddress property string code + property alias errorMessage: codeItemLayout.errorMessage property bool ctrlIsPressed onCtrlIsPressedChanged: console.log("ctrl is pressed", ctrlIsPressed) titleContent: [ @@ -78,66 +79,71 @@ LoginLayout { text = qsTr("assistant_account_creation_confirmation_explanation").arg(completeString).arg(address) } } - RowLayout { - spacing: Utils.getSizeWithScreenRatio(45) - Repeater { - model: 4 - id: repeater - signal pasteRequested(string text) - DigitInput { - id: digitInput - required property int index - Layout.preferredWidth: width - Layout.preferredHeight: height - Connections { - target: repeater - function onPasteRequested(text) { - console.log("paste requested", text[digitInput.index]) - var test= text; - if (UtilsCpp.isInteger(text)) - { - digitInput.text = text[digitInput.index] + FormItemLayout { + id: codeItemLayout + errorTextTopMargin: Utils.getSizeWithScreenRatio(5) + contentItem: RowLayout { + spacing: Utils.getSizeWithScreenRatio(45) + Repeater { + model: 4 + id: repeater + signal pasteRequested(string text) + DigitInput { + id: digitInput + required property int index + Layout.preferredWidth: width + Layout.preferredHeight: height + isError: codeItemLayout.errorMessage !== "" + Connections { + target: repeater + function onPasteRequested(text) { + console.log("paste requested", text[digitInput.index]) + var test= text; + if (UtilsCpp.isInteger(text)) + { + digitInput.text = text[digitInput.index] + } } } - } - onTextChanged: { - console.log("text edited", text) - if (text.length > 0 ) { - mainItem.code = mainItem.code.slice(0, index) + text + mainItem.code.slice(index) - if (index < 3) - nextItemInFocusChain(true).forceActiveFocus() - else { - mainItem.sendCode(mainItem.code) - mainItem.code = "" - } - } else { - if (index > 0) - nextItemInFocusChain(false).forceActiveFocus() - } - } - Keys.onPressed: (event) => { - if (event.key == Qt.Key_Backspace) { - if (text.length === 0) { - nextItemInFocusChain(false).forceActiveFocus() - event.accepted = true + onTextChanged: { + console.log("text edited", text) + if (text.length > 0 ) { + mainItem.code = mainItem.code.slice(0, index) + text + mainItem.code.slice(index) + if (index < 3) + nextItemInFocusChain(true).forceActiveFocus() + else { + mainItem.sendCode(mainItem.code) + mainItem.code = "" + } } else { - event.accepted = false + if (index > 0) + nextItemInFocusChain(false).forceActiveFocus() } - } else if (event.key == Qt.Key_Control) { - mainItem.ctrlIsPressed = true - event.accepted = false - } else if (mainItem.ctrlIsPressed && event.key == Qt.Key_V) { - var clipboard = UtilsCpp.getClipboardText() - console.log("paste", clipboard) - repeater.pasteRequested(clipboard) - } else { - event.accepted = false } - } - Keys.onReleased: (event) => { - if (event.key == Qt.Key_Control) { - mainItem.ctrlIsPressed = false - event.accepted = true + Keys.onPressed: (event) => { + if (event.key == Qt.Key_Backspace) { + if (text.length === 0) { + nextItemInFocusChain(false).forceActiveFocus() + event.accepted = true + } else { + event.accepted = false + } + } else if (event.key == Qt.Key_Control) { + mainItem.ctrlIsPressed = true + event.accepted = false + } else if (mainItem.ctrlIsPressed && event.key == Qt.Key_V) { + var clipboard = UtilsCpp.getClipboardText() + console.log("paste", clipboard) + repeater.pasteRequested(clipboard) + } else { + event.accepted = false + } + } + Keys.onReleased: (event) => { + if (event.key == Qt.Key_Control) { + mainItem.ctrlIsPressed = false + event.accepted = true + } } } } diff --git a/Linphone/view/Page/Form/Register/RegisterPage.qml b/Linphone/view/Page/Form/Register/RegisterPage.qml index 1dc83c687..2a1fd3749 100644 --- a/Linphone/view/Page/Form/Register/RegisterPage.qml +++ b/Linphone/view/Page/Form/Register/RegisterPage.qml @@ -128,15 +128,11 @@ LoginLayout { ColumnLayout { id: contentLayout - anchors.left: parent.left - anchors.right: parent.right spacing: Utils.getSizeWithScreenRatio(8) ColumnLayout { id: formLayout spacing: Utils.getSizeWithScreenRatio(24) RowLayout { - Layout.preferredHeight: usernameItem.height - spacing: Utils.getSizeWithScreenRatio(16) FormItemLayout { id: usernameItem label: qsTr("username") @@ -152,6 +148,7 @@ LoginLayout { } RowLayout { spacing: Utils.getSizeWithScreenRatio(10) + Layout.leftMargin: Utils.getSizeWithScreenRatio(16) ComboBox { Layout.preferredWidth: Utils.getSizeWithScreenRatio(210) Layout.preferredHeight: Utils.getSizeWithScreenRatio(49) @@ -171,7 +168,8 @@ LoginLayout { currentIndex: bar.currentIndex PhoneNumberInput { id: phoneNumberInput - Layout.preferredWidth: Utils.getSizeWithScreenRatio(346) + Layout.fillWidth: false + Layout.preferredWidth: Utils.getSizeWithScreenRatio(390) property string completePhoneNumber: countryCode + phoneNumber //: "Numéro de téléphone" label: qsTr("phone_number") @@ -221,6 +219,7 @@ LoginLayout { } FormItemLayout { Layout.preferredWidth: Utils.getSizeWithScreenRatio(346) + Layout.leftMargin: Utils.getSizeWithScreenRatio(16) //: "Confirmation mot de passe" label: qsTr("assistant_account_register_password_confirmation") mandatory: true @@ -235,13 +234,14 @@ LoginLayout { } } } - TemporaryText { - id: otherErrorText - Layout.fillWidth: true - Layout.topMargin: Utils.getSizeWithScreenRatio(5) - } } } + TemporaryText { + id: otherErrorText + Layout.fillWidth: true + Layout.preferredHeight: implicitHeight + // Layout.topMargin: Utils.getSizeWithScreenRatio(5) + } // ColumnLayout { // spacing: Utils.getSizeWithScreenRatio(18) // RowLayout { @@ -276,6 +276,7 @@ LoginLayout { Accessible.name: acceptCguAndPrivacyPolicyItem.associatedText } Text { + id: privacyLinkText text: acceptCguAndPrivacyPolicyItem.associatedText onLinkActivated: (link) => Qt.openUrlExternally(link) font { @@ -284,9 +285,17 @@ LoginLayout { } MouseArea { anchors.fill: parent - acceptedButtons: Qt.NoButton + acceptedButtons: Qt.LeftButton cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor - onClicked: termsCheckBox.toggle() + onClicked: (mouse) => { + mouse.accepted = false + if (parent.hoveredLink) { + privacyLinkText.linkActivated(privacyLinkText.linkAt(mouse.x, mouse.y)) + } + else { + termsCheckBox.toggle() + } + } } } } diff --git a/Linphone/view/Page/Form/Settings/AccountSettingsPage.qml b/Linphone/view/Page/Form/Settings/AccountSettingsPage.qml index 742d7ee9f..b9e3a2b38 100644 --- a/Linphone/view/Page/Form/Settings/AccountSettingsPage.qml +++ b/Linphone/view/Page/Form/Settings/AccountSettingsPage.qml @@ -20,7 +20,7 @@ AbstractSettingsMenu { {title: qsTr("settings_account_title"), layout: "AccountSettingsParametersLayout", model: account} ] Connections { - target: account.core + target: account ? account.core : null function onRemoved() { accountRemoved() } } onGoBackRequested: if (!account.core.isSaved) { diff --git a/Linphone/view/Page/Layout/Chat/ConversationInfos.qml b/Linphone/view/Page/Layout/Chat/ConversationInfos.qml index 35bb8319d..8b60b284d 100644 --- a/Linphone/view/Page/Layout/Chat/ConversationInfos.qml +++ b/Linphone/view/Page/Layout/Chat/ConversationInfos.qml @@ -13,11 +13,10 @@ import "qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js" as Utils ColumnLayout { id: mainItem property ChatGui chatGui - property var chatCore: chatGui.core - property var contactObj: chatGui ? UtilsCpp.findFriendByAddress(mainItem.chatCore.peerAddress) : null + property var contactObj: chatGui ? UtilsCpp.findFriendByAddress(mainItem.chatGui.core.peerAddress) : null property FriendGui contact: contactObj ? contactObj.value : null property bool isAppFriend: contact && contact.core.isAppFriend - property bool isGroup: chatCore && chatCore.isGroupChat + property bool isGroup: chatGui && chatGui.core.isGroupChat spacing: 0 signal ephemeralSettingsRequested() signal showSharedFilesRequested(bool showMedias) @@ -29,7 +28,7 @@ ColumnLayout { Avatar { Layout.alignment: Qt.AlignHCenter contact: mainItem.contact - displayNameVal: mainItem.chatCore.avatarUri + displayNameVal: mainItem.chatGui.core.avatarUri secured: mainItem.chatGui && mainItem.chatGui.core.isSecured Layout.preferredWidth: Utils.getSizeWithScreenRatio(100) Layout.preferredHeight: Utils.getSizeWithScreenRatio(100) @@ -51,7 +50,7 @@ ColumnLayout { popup.contentItem: RowLayout { Text { id: chatroomaddress - text: chatCore?.chatRoomAddress || "" + text: chatGui?.core?.chatRoomAddress || "" } SmallButton { icon.source: AppIcons.copy @@ -70,10 +69,10 @@ ColumnLayout { RowLayout { id: titleMainItem property bool isEditingSubject: false - property bool canEditSubject: mainItem.chatCore.meAdmin && mainItem.chatCore.isGroupChat + property bool canEditSubject: mainItem.chatGui.core.meAdmin && mainItem.chatGui.core.isGroupChat function saveSubject() { - mainItem.chatCore.lSetSubject(title.text) + mainItem.chatGui.core.lSetSubject(title.text) } Item { @@ -96,7 +95,7 @@ ColumnLayout { anchors.margins: 6 font: Typography.p1 color: DefaultStyle.main2_700 - text: mainItem.chatCore.title || "" + text: mainItem.chatGui.core.title || "" enabled: titleMainItem.isEditingSubject wrapMode: TextEdit.Wrap horizontalAlignment: Text.AlignHCenter @@ -142,7 +141,7 @@ ColumnLayout { Text { font: Typography.p1 color: DefaultStyle.main2_700 - text: mainItem.chatCore.title || "" + text: mainItem.chatGui.core.title || "" } } @@ -156,7 +155,7 @@ ColumnLayout { Text { font: Typography.p3 color: DefaultStyle.main2_700 - text: mainItem.chatCore.peerAddress + text: SettingsCpp.hideSipAddresses ? UtilsCpp.getUsername(mainItem.chatGui.core.peerAddress) : mainItem.chatGui.core.peerAddress Layout.alignment: Qt.AlignHCenter Layout.topMargin: Utils.getSizeWithScreenRatio(5) } @@ -171,7 +170,7 @@ ColumnLayout { } RowLayout { - visible: !mainItem.chatCore.isReadOnly + visible: !mainItem.chatGui.core.isReadOnly spacing: Utils.getSizeWithScreenRatio(10) Layout.alignment: Qt.AlignHCenter Layout.topMargin: Utils.getSizeWithScreenRatio(30) @@ -200,11 +199,11 @@ ColumnLayout { Layout.maximumWidth: Utils.getSizeWithScreenRatio(130) button.icon.width: Utils.getSizeWithScreenRatio(24) button.icon.height: Utils.getSizeWithScreenRatio(24) - button.icon.source: mainItem.chatCore.muted ? AppIcons.bell : AppIcons.bellSlash + button.icon.source: mainItem.chatGui.core.muted ? AppIcons.bell : AppIcons.bellSlash //: "Sourdine" - label: mainItem.chatCore.muted ? qsTr("one_one_infos_unmute") : qsTr("one_one_infos_mute") + label: mainItem.chatGui.core.muted ? qsTr("one_one_infos_unmute") : qsTr("one_one_infos_mute") button.onClicked: { - mainItem.chatCore.muted = !mainItem.chatCore.muted + mainItem.chatGui.core.muted = !mainItem.chatGui.core.muted } } LabelButton { @@ -232,12 +231,12 @@ ColumnLayout { : qsTr("one_one_infos_create_contact") button.onClicked: { if (mainItem.isGroup) - UtilsCpp.getMainWindow().scheduleMeeting(mainItem.chatCore.title, mainItem.chatCore.participantsAddresses) + UtilsCpp.getMainWindow().scheduleMeeting(mainItem.chatGui.core.title, mainItem.chatGui.core.participantsAddresses) else { if (mainItem.isAppFriend) mainWindow.displayContactPage(mainItem.contact.core.defaultAddress) else - mainWindow.displayCreateContactPage("",mainItem.chatCore.peerAddress) + mainWindow.displayCreateContactPage("",mainItem.chatGui.core.peerAddress) } } } @@ -269,13 +268,13 @@ ColumnLayout { active: mainItem.isGroup sourceComponent: GroupChatInfoParticipants { Layout.fillWidth: true - title: qsTr("group_infos_participants").arg(mainItem.chatCore.participants.length) - participants: mainItem.chatCore.participants - chatCore: mainItem.chatCore + title: qsTr("group_infos_participants").arg(mainItem.chatGui.core.participants.length) + participants: mainItem.chatGui.core.participants + chatGui: mainItem.chatGui onManageParticipantsRequested: mainItem.manageParticipantsRequested() } Connections { - target: mainItem.chatCore + target: mainItem.chatGui ? mainItem.chatGui.core : null onParticipantsChanged : { // hacky reload to update intric height participantLoader.active = false participantLoader.active = true @@ -321,8 +320,8 @@ ColumnLayout { ? [ { icon: AppIcons.clockCountDown, - visible: !mainItem.chatCore.isReadOnly, - text: mainItem.chatCore.ephemeralEnabled ? qsTr("group_infos_ephemerals")+UtilsCpp.getEphemeralFormatedTime(mainItem.chatCore.ephemeralLifetime) : qsTr("group_infos_enable_ephemerals"), + visible: !mainItem.chatGui.core.isReadOnly, + text: mainItem.chatGui.core.ephemeralEnabled ? qsTr("group_infos_ephemerals")+UtilsCpp.getEphemeralFormatedTime(mainItem.chatGui.core.ephemeralLifetime) : qsTr("group_infos_enable_ephemerals"), color: DefaultStyle.main2_600, showRightArrow: false, action: function() { @@ -331,7 +330,7 @@ ColumnLayout { }, { icon: AppIcons.signOut, - visible: !mainItem.chatCore.isReadOnly, + visible: !mainItem.chatGui.core.isReadOnly, //: Leave chat room text: qsTr("group_infos_leave_room"), color: DefaultStyle.main2_600, @@ -344,7 +343,7 @@ ColumnLayout { "", function(confirmed) { if (confirmed) { - mainItem.chatCore.lLeave() + mainItem.chatGui.core.lLeave() } }) } @@ -364,7 +363,7 @@ ColumnLayout { "", function(confirmed) { if (confirmed) { - mainItem.chatCore.lDeleteHistory() + mainItem.chatGui.core.lDeleteHistory() } }) } @@ -373,8 +372,8 @@ ColumnLayout { : [ { icon: AppIcons.clockCountDown, - visible: !mainItem.chatCore.isReadOnly, - text: mainItem.chatCore.ephemeralEnabled ? qsTr("one_one_infos_ephemerals")+UtilsCpp.getEphemeralFormatedTime(mainItem.chatCore.ephemeralLifetime) : qsTr("one_one_infos_enable_ephemerals"), + visible: !mainItem.chatGui.core.isReadOnly, + text: mainItem.chatGui.core.ephemeralEnabled ? qsTr("one_one_infos_ephemerals")+UtilsCpp.getEphemeralFormatedTime(mainItem.chatGui.core.ephemeralLifetime) : qsTr("one_one_infos_enable_ephemerals"), color: DefaultStyle.main2_600, showRightArrow: false, action: function() { @@ -395,7 +394,7 @@ ColumnLayout { "", function(confirmed) { if (confirmed) { - mainItem.chatCore.lDeleteHistory() + mainItem.chatGui.core.lDeleteHistory() } }) } diff --git a/Linphone/view/Page/Layout/Chat/GroupChatInfoParticipants.qml b/Linphone/view/Page/Layout/Chat/GroupChatInfoParticipants.qml index ad50a401e..575573528 100644 --- a/Linphone/view/Page/Layout/Chat/GroupChatInfoParticipants.qml +++ b/Linphone/view/Page/Layout/Chat/GroupChatInfoParticipants.qml @@ -15,10 +15,10 @@ ColumnLayout { id: mainItem property var title: String property var participants - property var chatCore + property ChatGui chatGui signal manageParticipantsRequested() - property bool isGroupEditable: chatCore && chatCore.meAdmin && !chatCore.isReadOnly + property bool isGroupEditable: chatGui && chatGui.core.meAdmin && !chatGui.core.isReadOnly RowLayout { Text { @@ -65,12 +65,11 @@ ColumnLayout { Layout.rightMargin: Utils.getSizeWithScreenRatio(10) spacing: Utils.getSizeWithScreenRatio(10) property var participantGui: modelData - property var participantCore: participantGui.core - property var contactObj: UtilsCpp.findFriendByAddress(participantCore.sipAddress) + property var contactObj: UtilsCpp.findFriendByAddress(participantGui.core.sipAddress) property var contact: contactObj?.value || null Avatar { contact: contactObj?.value || null - displayNameVal: participantCore.displayName + displayNameVal: participantGui.core.displayName Layout.preferredWidth: Utils.getSizeWithScreenRatio(45) Layout.preferredHeight: Utils.getSizeWithScreenRatio(45) } @@ -83,13 +82,13 @@ ColumnLayout { Layout.alignment: Qt.AlignVCenter Text { - text: participantCore.displayName + text: participantGui.core.displayName font: Typography.p1 color: DefaultStyle.main2_700 } Text { - visible: participantCore.isAdmin + visible: participantGui.core.isAdmin text: qsTr("group_infos_participant_is_admin") font: Typography.p3 color: DefaultStyle.main2_500_main @@ -131,25 +130,24 @@ ColumnLayout { onClicked: { detailOptions.close() if (contact && contact.core.isAppFriend) - UtilsCpp.getMainWindow().displayContactPage(participantCore.sipAddress) + UtilsCpp.getMainWindow().displayContactPage(participantGui.core.sipAddress) else - UtilsCpp.getMainWindow().displayCreateContactPage("",participantCore.sipAddress) + UtilsCpp.getMainWindow().displayCreateContactPage("",participantGui.core.sipAddress) } } IconLabelButton { visible: mainItem.isGroupEditable Layout.fillWidth: true - text: participantCore.isAdmin ? qsTr("group_infos_remove_admin_rights") : qsTr("group_infos_give_admin_rights") + text: participantGui.core.isAdmin ? qsTr("group_infos_remove_admin_rights") : qsTr("group_infos_give_admin_rights") icon.source: AppIcons.profile icon.width: Utils.getSizeWithScreenRatio(32) icon.height: Utils.getSizeWithScreenRatio(32) onClicked: { detailOptions.close() - mainItem.chatCore.lToggleParticipantAdminStatusAtIndex(index) + mainItem.chatGui.core.lToggleParticipantAdminStatusAtIndex(index) } } IconLabelButton { - visible: !contact || (contact.core && !contact.core.isAppFriend) Layout.fillWidth: true text: qsTr("group_infos_copy_sip_address") icon.source: AppIcons.copy @@ -157,7 +155,7 @@ ColumnLayout { icon.height: Utils.getSizeWithScreenRatio(32) onClicked: { detailOptions.close() - UtilsCpp.copyToClipboard(participantCore.sipAddress) + UtilsCpp.copyToClipboard(participantGui.core.sipAddress) } } Rectangle { @@ -183,7 +181,7 @@ ColumnLayout { "", function(confirmed) { if (confirmed) { - mainItem.chatCore.lRemoveParticipantAtIndex(index) + mainItem.chatGui.core.lRemoveParticipantAtIndex(index) } }) } diff --git a/Linphone/view/Page/Layout/Chat/ManageParticipants.qml b/Linphone/view/Page/Layout/Chat/ManageParticipants.qml index eec1eb062..9962e331d 100644 --- a/Linphone/view/Page/Layout/Chat/ManageParticipants.qml +++ b/Linphone/view/Page/Layout/Chat/ManageParticipants.qml @@ -13,7 +13,6 @@ import "qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js" as Utils Rectangle { id: mainItem property ChatGui chatGui - property var chatCore: chatGui.core Layout.fillHeight: true Layout.fillWidth: true Layout.topMargin: Utils.getSizeWithScreenRatio(9) @@ -37,7 +36,7 @@ Rectangle { style: ButtonStyle.noBackground icon.source: AppIcons.leftArrow onClicked: { - mainItem.chatCore.lSetParticipantsAddresses(manageParticipantsLayout.selectedParticipants) + mainItem.chatGui.core.lSetParticipantsAddresses(manageParticipantsLayout.selectedParticipants) mainItem.done() } } @@ -56,11 +55,11 @@ Rectangle { Layout.topMargin: Utils.getSizeWithScreenRatio(9) Layout.bottomMargin: Utils.getSizeWithScreenRatio(17) Layout.alignment: Qt.AlignVCenter - selectedParticipants: mainItem.chatCore.participantsAddresses + selectedParticipants: mainItem.chatGui.core.participantsAddresses focus: true onVisibleChanged: { if (visible) - selectedParticipants = mainItem.chatCore.participantsAddresses + selectedParticipants = mainItem.chatGui.core.participantsAddresses } } Item { diff --git a/Linphone/view/Page/Layout/Chat/MessageSharedFilesInfos.qml b/Linphone/view/Page/Layout/Chat/MessageSharedFilesInfos.qml index 73d872202..8d4eee4bf 100644 --- a/Linphone/view/Page/Layout/Chat/MessageSharedFilesInfos.qml +++ b/Linphone/view/Page/Layout/Chat/MessageSharedFilesInfos.qml @@ -10,6 +10,7 @@ MessageInfosLayout { spacing: Utils.getSizeWithScreenRatio(25) property ChatGui chatGui property int filter + property bool showAsSquare: false tabbar.visible: false content: [ @@ -43,7 +44,7 @@ MessageInfosLayout { } delegate: FileView { contentGui: modelData - showAsSquare: false + showAsSquare: mainItem.showAsSquare width: gridView.cellWidth - Utils.getSizeWithScreenRatio(2) height: gridView.cellHeight - Utils.getSizeWithScreenRatio(2) } diff --git a/Linphone/view/Page/Layout/Main/MainLayout.qml b/Linphone/view/Page/Layout/Main/MainLayout.qml index 1d000de46..a80e77a7d 100644 --- a/Linphone/view/Page/Layout/Main/MainLayout.qml +++ b/Linphone/view/Page/Layout/Main/MainLayout.qml @@ -87,7 +87,6 @@ Item { AccountProxy { id: accountProxy - sourceModel: AppCpp.accounts onDefaultAccountChanged: if (tabbar.currentIndex === 0 && defaultAccount) defaultAccount.core?.lResetMissedCalls() } @@ -130,6 +129,7 @@ Item { Layout.preferredWidth: Utils.getSizeWithScreenRatio(82) defaultAccount: accountProxy.defaultAccount currentIndex: 0 + onCountChanged: if (currentIndex >= count) currentIndex = 0 Binding on currentIndex { when: mainItem.contextualMenuOpenedComponent != undefined value: -1 @@ -171,7 +171,7 @@ Item { } ] onCurrentIndexChanged: { - if (currentIndex === -1) + if (currentIndex === -1 || currentIndex >= tabbar.visibleCount) return; if (currentIndex === 0 && accountProxy.defaultAccount) accountProxy.defaultAccount.core?.lResetMissedCalls(); @@ -199,7 +199,8 @@ Item { } initButtons(); currentIndex = SettingsCpp.getLastActiveTabIndex(); - if (currentIndex === -1) + tabbar.updateVisibleCount() + if (currentIndex === -1 || currentIndex >= tabbar.visibleCount) currentIndex = 0; } } @@ -339,7 +340,7 @@ Item { model: accountProxy delegate: Item { Connections { - target: modelData.core + target: modelData ? modelData.core : null function onShowMwiChanged() { voicemail.updateCumulatedMwi(); } @@ -582,72 +583,89 @@ Item { } } } - CallPage { - id: callPage - Connections { - target: mainItem - function onOpenNewCallRequest() { - callPage.goToNewCall(); + Loader { + active: mainStackLayout.currentIndex === 0 + sourceComponent: CallPage { + id: callPage + Connections { + target: mainItem + function onOpenNewCallRequest() { + callPage.goToNewCall(); + } + function onCallCreated() { + callPage.goToCallHistory(); + } + function onOpenCallHistory() { + callPage.goToCallHistory(); + } + function onOpenNumPadRequest() { + callPage.openNumPadRequest(); + } } - function onCallCreated() { - callPage.goToCallHistory(); + onCreateContactRequested: (name, address) => { + mainItem.createContact(name, address); } - function onOpenCallHistory() { - callPage.goToCallHistory(); + Component.onCompleted: { + magicSearchBar.numericPadPopup = callPage.numericPadPopup; } - function onOpenNumPadRequest() { - callPage.openNumPadRequest(); - } - } - onCreateContactRequested: (name, address) => { - mainItem.createContact(name, address); - } - Component.onCompleted: { - magicSearchBar.numericPadPopup = callPage.numericPadPopup; - } - onGoToCallForwardSettings: { - var page = settingsPageComponent.createObject(parent, { - defaultIndex: 1 - }); - openContextualMenuComponent(page); - } - } - ContactPage { - id: contactPage - Connections { - target: mainItem - function onCreateContactRequested(name, address) { - contactPage.createContact(name, address); - } - function onDisplayContactRequested(contactAddress) { - contactPage.initialFriendToDisplay = contactAddress; + onGoToCallForwardSettings: { + var page = settingsPageComponent.createObject(parent, { + defaultIndex: 1 + }); + openContextualMenuComponent(page); } } } - ChatPage { - id: chatPage - Connections { - target: mainItem - function onDisplayChatRequested(contactAddress) { - console.log("display chat requested, open with address", contactAddress); - chatPage.remoteAddress = ""; - chatPage.remoteAddress = contactAddress; - } - function onOpenChatRequested(chat) { - console.log("open chat requested, open", chat.core.title); - chatPage.openChatRequested(chat); + Loader { + active: mainStackLayout.currentIndex === 1 + sourceComponent: ContactPage { + id: contactPage + Connections { + target: mainItem + function onCreateContactRequested(name, address) { + contactPage.createContact(name, address); + } + function onDisplayContactRequested(contactAddress) { + contactPage.initialFriendToDisplay = contactAddress; + } } } } - MeetingPage { - id: meetingPage - Connections { - target: mainItem - function onScheduleMeetingRequested(subject, addresses) { - meetingPage.createPreFilledMeeting(subject, addresses); + Loader { + active: mainStackLayout.currentIndex === 2 + sourceComponent: ChatPage { + id: chatPage + Connections { + target: mainItem + function onDisplayChatRequested(contactAddress) { + console.log("display chat requested, open with address", contactAddress); + chatPage.remoteAddress = ""; + chatPage.remoteAddress = contactAddress; + } + function onOpenChatRequested(chat) { + console.log("open chat requested, open", chat.core.title); + chatPage.openChatRequested(chat); + } } } } + + Loader { + active: mainStackLayout.currentIndex === 3 + sourceComponent: Component { + id: meetingComp + MeetingPage { + id: meetingPage + Connections { + target: mainItem + function onScheduleMeetingRequested(subject, addresses) { + meetingPage.createPreFilledMeeting(subject, addresses); + } + } + } + } + } + } } Component { diff --git a/Linphone/view/Page/Layout/Settings/AccountSettingsParametersLayout.qml b/Linphone/view/Page/Layout/Settings/AccountSettingsParametersLayout.qml index 7e43b08f8..21f236af9 100644 --- a/Linphone/view/Page/Layout/Settings/AccountSettingsParametersLayout.qml +++ b/Linphone/view/Page/Layout/Settings/AccountSettingsParametersLayout.qml @@ -31,7 +31,8 @@ AbstractSettingsLayout { } onUndo: account.core.undo() Connections { - target: account.core + enabled: account + target: account ? account.core : null function onIsSavedChanged() { console.log("saved changed", account.core.isSaved) if (account.core.isSaved) { @@ -60,9 +61,11 @@ AbstractSettingsLayout { id: mwiServerAddressField propertyName: "mwiServerAddress" propertyOwnerGui: account - //: "URI du serveur de messagerie vocale" + //: "MWI server address" title: qsTr("account_settings_mwi_uri_title") Layout.fillWidth: true + //: Address of the MWI server that sends SIP notifications to display new voicemail indicators + tooltip: qsTr("mwi_server_address_tooltip") isValid: function (text) { return text.length == 0 || !text.endsWith(".") } // work around sdk crash when adress ends with . @@ -70,7 +73,7 @@ AbstractSettingsLayout { Connections { enabled: account - target: account.core + target: account ? account.core : null function onMwiServerAddressChanged() { if (mwiServerAddressField.text != mwiServerAddressField.propertyOwnerGui.core[mwiServerAddressField.propertyName]) mwiServerAddressField.text = mwiServerAddressField.propertyOwnerGui.core[mwiServerAddressField.propertyName] @@ -81,14 +84,16 @@ AbstractSettingsLayout { id: voicemailAddressField propertyName: "voicemailAddress" propertyOwnerGui: account - //: "URI de messagerie vocale" + //: "Voicemail address" title: qsTr("account_settings_voicemail_uri_title") + //: SIP address dialed when clicking the voicemail button + tooltip: qsTr("voicemail_address_tooltip") Layout.fillWidth: true toValidate: true Connections { enabled: account - target: account.core + target: account ? account.core : null function onVoicemailAddressChanged() { if (voicemailAddressField.text != voicemailAddressField.propertyOwnerGui.core[voicemailAddressField.propertyName]) voicemailAddressField.text = voicemailAddressField.propertyOwnerGui.core[voicemailAddressField.propertyName] @@ -105,21 +110,25 @@ AbstractSettingsLayout { ColumnLayout { Layout.fillWidth: true spacing: Utils.getSizeWithScreenRatio(20) - Text { - //: "Transport" - text: qsTr("account_settings_transport_title") - color: DefaultStyle.main2_600 - font: Typography.p2l - } DecoratedTextField { + id: registrarUriField Layout.fillWidth: true //:"Registrar URI" title: qsTr("account_settings_registrar_uri_title") propertyName: "registrarUri" propertyOwnerGui: account toValidate: true + Connections { + enabled: account + target: account ? account.core : null + function onRegistrarUriChanged() { + if (registrarUriField.text != registrarUriField.propertyOwnerGui.core[registrarUriField.propertyName]) + registrarUriField.text = registrarUriField.propertyOwnerGui.core[registrarUriField.propertyName] + } + } } DecoratedTextField { + id: outboundProxyUriField Layout.fillWidth: true //:"Outbound SIP Proxy URI" title: qsTr("account_settings_sip_proxy_url_title") @@ -128,34 +137,79 @@ AbstractSettingsLayout { //: "If this field is filled, the outbound proxy will be enabled automatically. Leave it empty to disable it." tooltip: qsTr("login_proxy_server_url_tooltip") toValidate: true + Connections { + enabled: account + target: account ? account.core : null + function onOutboundProxyUriChanged() { + if (outboundProxyUriField.text != outboundProxyUriField.propertyOwnerGui.core[outboundProxyUriField.propertyName]) + outboundProxyUriField.text = outboundProxyUriField.propertyOwnerGui.core[outboundProxyUriField.propertyName] + } + } } DecoratedTextField { + id: stunServerField Layout.fillWidth: true propertyName: "stunServer" propertyOwnerGui: account //: "Adresse du serveur STUN" title: qsTr("account_settings_stun_server_url_title") toValidate: true + Connections { + enabled: account + target: account ? account.core : null + function onStunServerChanged() { + if (stunServerField.text != stunServerField.propertyOwnerGui.core[stunServerField.propertyName]) + stunServerField.text = stunServerField.propertyOwnerGui.core[stunServerField.propertyName] + } + } } SwitchSetting { + id: iceSwitch //: "Activer ICE" titleText: qsTr("account_settings_enable_ice_title") propertyName: "iceEnabled" propertyOwnerGui: account + Connections { + enabled: account + target: account ? account.core : null + function onIceEnabledChanged() { + if (iceSwitch.checked != iceSwitch.propertyOwnerGui.core[iceSwitch.propertyName]) + iceSwitch.checked = iceSwitch.propertyOwnerGui.core[iceSwitch.propertyName] + } + } } SwitchSetting { + id: avpfSwitch //: "AVPF" titleText: qsTr("account_settings_avpf_title") propertyName: "avpfEnabled" propertyOwnerGui: account + Connections { + enabled: account + target: account ? account.core : null + function onAvpfEnabledChanged() { + if (avpfSwitch.checked != avpfSwitch.propertyOwnerGui.core[avpfSwitch.propertyName]) + avpfSwitch.checked = avpfSwitch.propertyOwnerGui.core[avpfSwitch.propertyName] + } + } } SwitchSetting { + id: bundleModeSwitch //: "Mode bundle" titleText: qsTr("account_settings_bundle_mode_title") propertyName: "bundleModeEnabled" propertyOwnerGui: account + Connections { + enabled: account + target: account ? account.core : null + function onBundleModeEnabledChanged() { + if (bundleModeSwitch.checked != bundleModeSwitch.propertyOwnerGui.core[bundleModeSwitch.propertyName]) + bundleModeSwitch.checked = bundleModeSwitch.propertyOwnerGui.core[bundleModeSwitch.propertyName] + } + } } DecoratedTextField { + id: expireField Layout.fillWidth: true propertyName: "expire" propertyOwnerGui: account @@ -166,6 +220,13 @@ AbstractSettingsLayout { return !isNaN(Number(text)) } toValidate: true + Connections { + target: account ? account.core : null + function onExpireChanged() { + if (expireField.text != expireField.propertyOwnerGui.core[expireField.propertyName]) + expireField.text = expireField.propertyOwnerGui.core[expireField.propertyName] + } + } } DecoratedTextField { id: conferenceFactoryUriField @@ -174,14 +235,14 @@ AbstractSettingsLayout { title: qsTr("account_settings_conference_factory_uri_title") propertyName: "conferenceFactoryAddress" propertyOwnerGui: account + toValidate: true Connections { - target: account.core + target: account ? account.core : null function onConferenceFactoryAddressChanged() { if (conferenceFactoryUriField.text != conferenceFactoryUriField.propertyOwnerGui.core[conferenceFactoryUriField.propertyName]) conferenceFactoryUriField.text = conferenceFactoryUriField.propertyOwnerGui.core[conferenceFactoryUriField.propertyName] } } - toValidate: true } DecoratedTextField { id: audioVideoConfUriField @@ -192,7 +253,7 @@ AbstractSettingsLayout { propertyOwnerGui: account toValidate: true Connections { - target: account.core + target: account ? account.core : null function onAudioVideoConferenceFactoryAddressChanged() { if (audioVideoConfUriField.text != audioVideoConfUriField.propertyOwnerGui.core[audioVideoConfUriField.propertyName]) audioVideoConfUriField.text = audioVideoConfUriField.propertyOwnerGui.core[audioVideoConfUriField.propertyName] @@ -200,12 +261,20 @@ AbstractSettingsLayout { } } DecoratedTextField { + id: limeServerUrlField Layout.fillWidth: true //: "URL du serveur d’échange de clés de chiffrement" title: qsTr("account_settings_lime_server_url_title") propertyName: "limeServerUrl" propertyOwnerGui: account toValidate: true + Connections { + target: account ? account.core : null + function onLimeServerUrlChanged() { + if (limeServerUrlField.text != limeServerUrlField.propertyOwnerGui.core[limeServerUrlField.propertyName]) + limeServerUrlField.text = limeServerUrlField.propertyOwnerGui.core[limeServerUrlField.propertyName] + } + } } DecoratedTextField { Layout.fillWidth: true diff --git a/Linphone/view/Page/Layout/Settings/AdvancedSettingsLayout.qml b/Linphone/view/Page/Layout/Settings/AdvancedSettingsLayout.qml index 303b82dde..a02a7b25d 100644 --- a/Linphone/view/Page/Layout/Settings/AdvancedSettingsLayout.qml +++ b/Linphone/view/Page/Layout/Settings/AdvancedSettingsLayout.qml @@ -191,6 +191,7 @@ AbstractSettingsLayout { ColumnLayout { spacing: Utils.getSizeWithScreenRatio(20) ListView { + id: videoCodecList Layout.preferredHeight: contentHeight Layout.fillWidth: true spacing: Utils.getSizeWithScreenRatio(20) @@ -199,7 +200,7 @@ AbstractSettingsLayout { filterType: PayloadTypeProxy.Video | PayloadTypeProxy.NotDownloadable } delegate: SwitchSetting { - width: parent.width + width: videoCodecList.width height: Utils.getSizeWithScreenRatio(32) titleText: Utils.capitalizeFirstLetter(modelData.core.mimeType) subTitleText: modelData.core.encoderDescription @@ -219,6 +220,7 @@ AbstractSettingsLayout { } } ListView { + id: payloadList Layout.preferredHeight: contentHeight Layout.fillWidth: true spacing: Utils.getSizeWithScreenRatio(20) @@ -227,22 +229,22 @@ AbstractSettingsLayout { filterType: PayloadTypeProxy.Video | PayloadTypeProxy.Downloadable } delegate: SwitchSetting { - width: parent.width + width: payloadList.width height: Utils.getSizeWithScreenRatio(32) titleText: Utils.capitalizeFirstLetter(modelData.core.mimeType) subTitleText: modelData.core.encoderDescription - onCheckedChanged: Utils.openCodecOnlineInstallerDialog( + onToggled: Utils.openCodecOnlineInstallerDialog( UtilsCpp.getMainWindow(), modelData.core, function cancelCallBack() { - setChecked(false) + checked = false }, function successCallBack() { videoPayloadTypeProxy.reload() downloadableVideoPayloadTypeProxy.reload() }, function errorCallBack() { - setChecked(false) + checked = false }) } } diff --git a/Linphone/view/Page/Layout/Settings/CarddavSettingsLayout.qml b/Linphone/view/Page/Layout/Settings/CarddavSettingsLayout.qml index ae7bf8396..12324fb9c 100644 --- a/Linphone/view/Page/Layout/Settings/CarddavSettingsLayout.qml +++ b/Linphone/view/Page/Layout/Settings/CarddavSettingsLayout.qml @@ -34,7 +34,8 @@ AbstractSettingsLayout { } } Connections { - target: carddavGui.core + enabled: carddavGui + target: carddavGui ? carddavGui.core : null function onSaved(success, message) { if (success) UtilsCpp.showInformationPopup(qsTr("information_popup_synchronization_success_title"), diff --git a/Linphone/view/Page/Layout/Settings/ContactsSettingsProviderLayout.qml b/Linphone/view/Page/Layout/Settings/ContactsSettingsProviderLayout.qml index 51d531475..b5b051a9f 100644 --- a/Linphone/view/Page/Layout/Settings/ContactsSettingsProviderLayout.qml +++ b/Linphone/view/Page/Layout/Settings/ContactsSettingsProviderLayout.qml @@ -79,7 +79,7 @@ RowLayout { } Binding { id: binding - target: modelData.core + target: modelData ? modelData.core : null property: "enabled" value: switchButton.checked when: false @@ -94,7 +94,8 @@ RowLayout { } } Connections { - target: modelData.core + enabled: modelData + target: modelData ? modelData.core : null function onSavedChanged() { if (modelData.core.saved) UtilsCpp.showInformationPopup(qsTr("information_popup_success_title"), //: "Les changements ont été sauvegardés" diff --git a/Linphone/view/Page/Main/Account/AccountListView.qml b/Linphone/view/Page/Main/Account/AccountListView.qml index 98d80954b..7affd9a22 100644 --- a/Linphone/view/Page/Main/Account/AccountListView.qml +++ b/Linphone/view/Page/Main/Account/AccountListView.qml @@ -67,7 +67,6 @@ ColumnLayout{ Repeater{ model: AccountProxy { id: accountProxy - sourceModel: AppCpp.accounts } delegate: contactDelegate } diff --git a/Linphone/view/Page/Main/Call/CallPage.qml b/Linphone/view/Page/Main/Call/CallPage.qml index 33b5a63ed..58786d3a1 100644 --- a/Linphone/view/Page/Main/Call/CallPage.qml +++ b/Linphone/view/Page/Main/Call/CallPage.qml @@ -28,7 +28,6 @@ AbstractMainPage { property ConferenceInfoGui confInfoGui property AccountProxy accounts: AccountProxy { id: accountProxy - sourceModel: AppCpp.accounts } property AccountGui account: accountProxy.defaultAccount property var state: account && account.core?.registrationState || 0 diff --git a/Linphone/view/Page/Main/Call/WaitingRoom.qml b/Linphone/view/Page/Main/Call/WaitingRoom.qml index 97911d01e..c6f0f5b1c 100644 --- a/Linphone/view/Page/Main/Call/WaitingRoom.qml +++ b/Linphone/view/Page/Main/Call/WaitingRoom.qml @@ -35,7 +35,6 @@ RowLayout { mutedStatus: microButton.checked AccountProxy { id: accounts - sourceModel: AppCpp.accounts } account: accounts.defaultAccount } diff --git a/Linphone/view/Page/Main/Chat/ChatPage.qml b/Linphone/view/Page/Main/Chat/ChatPage.qml index 19b0bd7ad..17b8f3f50 100644 --- a/Linphone/view/Page/Main/Chat/ChatPage.qml +++ b/Linphone/view/Page/Main/Chat/ChatPage.qml @@ -18,7 +18,6 @@ AbstractMainPage { property AccountProxy accounts: AccountProxy { id: accountProxy - sourceModel: AppCpp.accounts } property AccountGui account: accountProxy.defaultAccount property var state: account && account.core?.registrationState || 0 @@ -28,7 +27,7 @@ AbstractMainPage { property var selectedChatGui: null property string remoteAddress onRemoteAddressChanged: console.log("ChatPage : remote address changed :", remoteAddress) - property var remoteChatObj: UtilsCpp.getChatForAddress(remoteAddress) + property var remoteChatObj: remoteAddress.length > 0 ? UtilsCpp.getChatForAddress(remoteAddress) : null property var remoteChat: remoteChatObj ? remoteChatObj.value : null signal openChatRequested(ChatGui chat) @@ -46,8 +45,6 @@ AbstractMainPage { UtilsCpp.showInformationPopup(qsTr("info_popup_error_title"), //: Chat room creation failed ! qsTr("info_popup_chatroom_creation_failed"), false) - } else if (remoteChat.core.state === LinphoneEnums.ChatRoomState.Created) { - openChatRequested(remoteChat) } } } @@ -62,7 +59,7 @@ AbstractMainPage { if (selectedChatGui) { if (!listStackView.currentItem || listStackView.currentItem.objectName !== "chatListItem") { listStackView.popToIndex(0) - if (listStackView.depth === 0 || listStackView.currentItem.objectName !== "chatListItem") listStackView.push(chatListItem) + if (listStackView.depth === 0 || listStackView.currentItem && listStackView.currentItem.objectName !== "chatListItem") listStackView.push(chatListItem) } } AppCpp.currentChat = visible ? selectedChatGui : null @@ -78,6 +75,7 @@ AbstractMainPage { rightPanelStackView.visible: false//listStackView.currentItem && listStackView.currentItem.objectName === "chatListItem" && selectedChatGui !== null onNoItemButtonPressed: goToNewChat() + signal newChatItemOpen() showDefaultItem: listStackView.currentItem && listStackView.currentItem.objectName == "chatListItem" @@ -88,6 +86,7 @@ AbstractMainPage { if (listStackView.currentItem && listStackView.currentItem.objectName != "newChatItem") listStackView.push(newChatItem) + newChatItemOpen() } Dialog { @@ -212,6 +211,7 @@ AbstractMainPage { id: chatListView Layout.fillWidth: true Layout.fillHeight: true + chatProxy.model: AppCpp.chats Layout.topMargin: Utils.getSizeWithScreenRatio(39) searchBar: searchBar Control.ScrollBar.vertical: scrollbar @@ -225,6 +225,10 @@ AbstractMainPage { function onOpenChatRequested(chat) { chatListView.chatToSelect = chat } + function onNewChatItemOpen() { + // reset index to clear right panel when opening new conversation + chatListView.currentIndex = -1 + } } } } diff --git a/Linphone/view/Page/Main/Contact/ContactPage.qml b/Linphone/view/Page/Main/Contact/ContactPage.qml index 47008aadc..778f9519e 100644 --- a/Linphone/view/Page/Main/Contact/ContactPage.qml +++ b/Linphone/view/Page/Main/Contact/ContactPage.qml @@ -31,9 +31,9 @@ AbstractMainPage { } onVisibleChanged: if (!visible) { - rightPanelStackView.clear() - contactList.resetSelections() - } + rightPanelStackView.clear() + contactList.resetSelections() + } function goToContactDetails() { if (selectedContact) { var firstItem = rightPanelStackView.get(0) @@ -55,15 +55,20 @@ AbstractMainPage { } } onSelectedContactChanged: { - goToContactDetails() + console.log("selected contact changed, go to contact details") + // if we are editing a contact, force staying on edition page + if (!rightPanelStackView.currentItem + || rightPanelStackView.currentItem.objectName != "contactEdition") { + goToContactDetails() + } } onNoItemButtonPressed: createContact("", "") function createContact(name, address) { var friendGui = Qt.createQmlObject('import Linphone -FriendGui{ -}', mainItem) + FriendGui{ + }', mainItem) friendGui.core.givenName = UtilsCpp.getGivenNameFromFullName(name) friendGui.core.familyName = UtilsCpp.getFamilyNameFromFullName(name) friendGui.core.appendAddress(address) @@ -840,8 +845,7 @@ FriendGui{ //: "Supprimer ce contact" text: qsTr("contact_details_delete") onClicked: { - mainItem.deleteContact( - mainItem.selectedContact) + mainItem.deleteContact(mainItem.selectedContact) } style: ButtonStyle.noBackgroundRed } diff --git a/Linphone/view/Page/Main/Meeting/MeetingPage.qml b/Linphone/view/Page/Main/Meeting/MeetingPage.qml index 1357dbc7d..e0aeec9d3 100644 --- a/Linphone/view/Page/Main/Meeting/MeetingPage.qml +++ b/Linphone/view/Page/Main/Meeting/MeetingPage.qml @@ -61,9 +61,10 @@ AbstractMainPage { } onSelectedConferenceChanged: { - // While a conference is being edited, we need to stay on the edit page - if (rightPanelStackView.currentItem && (rightPanelStackView.currentItem.objectName === "editConf")) return + // While a conference is being created or edited, we need to stay on the edition page rightPanelStackView.clear() + if ((rightPanelStackView.currentItem && rightPanelStackView.currentItem.objectName === "editConf") + || (leftPanelStackView.currentItem && leftPanelStackView.currentItem.objectName === "createConf")) return if (selectedConference && selectedConference.core && selectedConference.core.haveModel) { rightPanelStackView.push(meetingDetail, Control.StackView.Immediate) } @@ -226,6 +227,8 @@ AbstractMainPage { Layout.topMargin: Utils.getSizeWithScreenRatio(38 - 24) Layout.fillWidth: true Layout.fillHeight: true + focusPolicy: Qt.ClickFocus + focus: true searchBarText: searchBar.text @@ -496,7 +499,8 @@ AbstractMainPage { } } Connections { - target: conferenceEdit.conferenceInfoGui.core + enabled: conferenceEdit.conferenceInfoGui + target: conferenceEdit.conferenceInfoGui ? conferenceEdit.conferenceInfoGui.core : null ignoreUnknownSignals: true function onSaveFailed() { UtilsCpp.getMainWindow().closeLoadingPopup() diff --git a/Linphone/view/Page/Window/AbstractWindow.qml b/Linphone/view/Page/Window/AbstractWindow.qml index 2b21920fe..8ef87fdf2 100644 --- a/Linphone/view/Page/Window/AbstractWindow.qml +++ b/Linphone/view/Page/Window/AbstractWindow.qml @@ -19,6 +19,9 @@ ApplicationWindow { onActiveChanged: { if (active) UtilsCpp.setLastActiveWindow(this) } + onVisibleChanged: { + AppCpp.handleAppActivity() + } Component.onDestruction: if (UtilsCpp.getLastActiveWindow() === this) UtilsCpp.setLastActiveWindow(null) property bool isFullscreen: visibility == Window.FullScreen diff --git a/Linphone/view/Page/Window/Call/CallsWindow.qml b/Linphone/view/Page/Window/Call/CallsWindow.qml index e17661707..e59d8bb1c 100644 --- a/Linphone/view/Page/Window/Call/CallsWindow.qml +++ b/Linphone/view/Page/Window/Call/CallsWindow.qml @@ -55,6 +55,9 @@ AbstractWindow { || call.core.isMismatch)) { zrtpValidation.open() } + } else if (callState === LinphoneEnums.CallState.StreamsRunning) { + if (!mainWindow.chat && (!mainWindow.conference || mainWindow.conference.core.isChatEnabled)) + mainWindow.chatObj = UtilsCpp.getCurrentCallChat(mainWindow.call) } else if (callState === LinphoneEnums.CallState.Error || callState === LinphoneEnums.CallState.End) { zrtpValidation.close() @@ -90,14 +93,28 @@ AbstractWindow { } } onClosing: close => { - DesktopToolsCpp.screenSaverStatus = true - if (callsModel.haveCall) { - close.accepted = false - terminateAllCallsDialog.open() - } - if (middleItemStackView.currentItem.objectName === "waitingRoom") - middleItemStackView.replace(inCallItem) - } + DesktopToolsCpp.screenSaverStatus = true + if (callsModel.haveCall) { + close.accepted = false + terminateAllCallsDialog.open() + } + if (middleItemStackView.currentItem.objectName === "waitingRoom") + middleItemStackView.replace(inCallItem) + } + Connections { + enabled: activeFocusItem !== null + target: activeFocusItem.Keys + function onPressed(event) { + if (rightPanel.contentLoader.item && rightPanel.contentLoader.item.objectName === "dialerPanel"){ + mainWindow.keyPressedOnDialer(event) + } + if ((event.key === Qt.Key_Escape || event.key === Qt.Key_Return) && mainWindow.visibility == Window.FullScreen) + mainWindow.showNormal() + } + } + + signal keyPressedOnDialer(KeyEvent event) + function changeLayout(layoutIndex) { if (layoutIndex == 0) { @@ -169,7 +186,7 @@ AbstractWindow { Connections { enabled: !!mainWindow.call - target: mainWindow.call && mainWindow.call.core + target: mainWindow.call ? mainWindow.call.core : null function onSecurityUpdated() { if (mainWindow.call.core.encryption === LinphoneEnums.MediaEncryption.Zrtp) { if (call.core.tokenVerified) { @@ -316,16 +333,13 @@ AbstractWindow { Rectangle { anchors.fill: parent color: DefaultStyle.grey_900 - Keys.onEscapePressed: { - if (mainWindow.visibility == Window.FullScreen) - mainWindow.showNormal() - } + focus: true ColumnLayout { anchors.fill: parent - spacing: Utils.getSizeWithScreenRatio(10) anchors.bottomMargin: Utils.getSizeWithScreenRatio(10) anchors.topMargin: Utils.getSizeWithScreenRatio(10) + spacing: Utils.getSizeWithScreenRatio(10) Item { id: headerItem Layout.margins: Utils.getSizeWithScreenRatio(10) @@ -339,26 +353,51 @@ AbstractWindow { spacing: Utils.getSizeWithScreenRatio(10) RowLayout { spacing: Utils.getSizeWithScreenRatio(10) - EffectImage { - id: callStatusIcon + Loader { + id: callStatusIconLoader Layout.preferredWidth: Utils.getSizeWithScreenRatio(30) Layout.preferredHeight: Utils.getSizeWithScreenRatio(30) - // TODO : change with broadcast or meeting icon when available - imageSource: !mainWindow.call ? AppIcons.meeting : (mainWindow.callState === LinphoneEnums.CallState.End || mainWindow.callState === LinphoneEnums.CallState.Released) ? AppIcons.endCall : (mainWindow.callState === LinphoneEnums.CallState.Paused || mainWindow.callState === LinphoneEnums.CallState.PausedByRemote) ? AppIcons.pause : mainWindow.conference ? AppIcons.usersThree : mainWindow.call.core.dir === LinphoneEnums.CallDir.Outgoing ? AppIcons.arrowUpRight : AppIcons.arrowDownLeft - colorizationColor: !mainWindow.call - || mainWindow.call.core.paused - || mainWindow.callState - === LinphoneEnums.CallState.Paused - || mainWindow.callState - === LinphoneEnums.CallState.PausedByRemote - || mainWindow.callState - === LinphoneEnums.CallState.End - || mainWindow.callState - === LinphoneEnums.CallState.Released - || mainWindow.conference ? DefaultStyle.danger_500_main : mainWindow.call.core.dir === LinphoneEnums.CallDir.Outgoing ? DefaultStyle.info_500_main : DefaultStyle.success_500_main - onColorizationColorChanged: { - callStatusIcon.active = !callStatusIcon.active - callStatusIcon.active = !callStatusIcon.active + sourceComponent: EffectImage { + id: callStatusIcon + anchors.fill: parent + // TODO : change with broadcast or meeting icon when available + // onImageSourceChanged: console.log("image source changed", imageSource) + imageSource: !mainWindow.call + ? AppIcons.meeting + : (mainWindow.callState === LinphoneEnums.CallState.End || mainWindow.callState === LinphoneEnums.CallState.Released) + ? AppIcons.endCall + : (mainWindow.call.core.paused || mainWindow.callState === LinphoneEnums.CallState.Paused || mainWindow.callState === LinphoneEnums.CallState.PausedByRemote) + ? AppIcons.pause + : mainWindow.conference + ? AppIcons.usersThree + : mainWindow.call.core.dir === LinphoneEnums.CallDir.Outgoing + ? AppIcons.arrowUpRight + : AppIcons.arrowDownLeft + colorizationColor: !mainWindow.call + || mainWindow.call.core.paused + || mainWindow.callState + === LinphoneEnums.CallState.Paused + || mainWindow.callState + === LinphoneEnums.CallState.PausedByRemote + || mainWindow.callState + === LinphoneEnums.CallState.End + || mainWindow.callState + === LinphoneEnums.CallState.Released + || mainWindow.conference ? DefaultStyle.danger_500_main : mainWindow.call.core.dir === LinphoneEnums.CallDir.Outgoing ? DefaultStyle.info_500_main : DefaultStyle.success_500_main + + Binding { + target: callStatusIcon + when: middleItemStackView.currentItem.objectName === "waitingRoom" + property: "imageSource" + value: AppIcons.usersThree + } + } + Connections { + target: mainWindow + function onCallStateChanged() { + callStatusIconLoader.active = !callStatusIconLoader.active + callStatusIconLoader.active = !callStatusIconLoader.active + } } } ColumnLayout { @@ -548,7 +587,7 @@ AbstractWindow { Layout.preferredHeight: Utils.getSizeWithScreenRatio(40) Layout.rightMargin: Utils.getSizeWithScreenRatio(30) icon.source: quality >= 4 ? AppIcons.cellSignalFull : quality >= 3 ? AppIcons.cellSignalMedium : quality >= 2 ? AppIcons.cellSignalLow : AppIcons.cellSignalNone - colorizationColor: DefaultStyle.grey_0 + colorizationColor: quality >= 2 ? DefaultStyle.grey_0 : DefaultStyle.danger_500_main style: ButtonStyle.noBackgroundLightBorder Accessible.name: qsTr("open_statistic_panel_accessible_name") onClicked: { @@ -841,13 +880,12 @@ AbstractWindow { searchBarBorderColor: DefaultStyle.grey_200 numPadPopup: numericPad onContactClicked: contact => { - mainWindow.startCallWithContact( - contact, false, rightPanel) - } + mainWindow.startCallWithContact(contact, false, rightPanel) + } Connections { target: mainWindow function onCallChanged() { - if (newCallForm.Control.StackView.status === Control.StackView.Active) + if (rightPanel.contentLoader.item.objectName === "newCallPanel") rightPanel.visible = false } } @@ -909,6 +947,69 @@ AbstractWindow { UtilsCpp.createCall(dialerTextInput.text) } Component.onCompleted: parent.height = height + Connections { + target: mainWindow + function onKeyPressedOnDialer(event) { + if (event.modifiers & Qt.KeypadModifier) { + if (event.key === Qt.Key_0) { + numPad.keypadKeyPressedAtIndex(10) + event.accepted = true + } + if (event.key === Qt.Key_1) { + numPad.keypadKeyPressedAtIndex(0) + event.accepted = true + } + if (event.key === Qt.Key_2) { + numPad.keypadKeyPressedAtIndex(1) + event.accepted = true + } + if (event.key === Qt.Key_3) { + numPad.keypadKeyPressedAtIndex(2) + event.accepted = true + } + if (event.key === Qt.Key_4) { + numPad.keypadKeyPressedAtIndex(3) + event.accepted = true + } + if (event.key === Qt.Key_5) { + numPad.keypadKeyPressedAtIndex(4) + event.accepted = true + } + if (event.key === Qt.Key_6) { + numPad.keypadKeyPressedAtIndex(5) + event.accepted = true + } + if (event.key === Qt.Key_7) { + numPad.keypadKeyPressedAtIndex(6) + event.accepted = true + } + if (event.key === Qt.Key_8) { + numPad.keypadKeyPressedAtIndex(7) + event.accepted = true + } + if (event.key === Qt.Key_9) { + numPad.keypadKeyPressedAtIndex(8) + event.accepted = true + } + if (event.key === Qt.Key_Asterisk) { + numPad.keypadKeyPressedAtIndex(9) + event.accepted = true + } + if (event.key === Qt.Key_Plus) { + numPad.buttonPressed("+") + event.accepted = true + } + if (event.key === Qt.Key_Enter) { + numPad.launchCall() + event.accepted = true + } + } + if (event.key === Qt.Key_Backspace) { + numPad.wipe() + event.accepted = true + } + } + } } } } @@ -920,9 +1021,9 @@ AbstractWindow { objectName: "changeLayoutPanel" width: parent.width Keys.onEscapePressed: event => { - rightPanel.visible = false - event.accepted = true - } + rightPanel.visible = false + event.accepted = true + } call: mainWindow.call onChangeLayoutRequested: index => { mainWindow.changeLayout(index) @@ -934,9 +1035,9 @@ AbstractWindow { ColumnLayout { objectName: "callListPanel" Keys.onEscapePressed: event => { - rightPanel.visible = false - event.accepted = true - } + rightPanel.visible = false + event.accepted = true + } spacing: 0 Component { id: mergeCallPopupButton @@ -1190,12 +1291,6 @@ AbstractWindow { rightPanel.visible = false } } - Binding { - target: callStatusIcon - when: middleItemStackView.currentItem.objectName === "waitingRoom" - property: "imageSource" - value: AppIcons.usersThree - } Binding { target: callStatusText when: middleItemStackView.currentItem.objectName === "waitingRoom" @@ -1349,8 +1444,7 @@ AbstractWindow { pressedColor: enabled ? DefaultStyle.success_500_main : DefaultStyle.grey_600 hoveredColor: enabled ? DefaultStyle.main2_400 : DefaultStyle.grey_600 onClicked: { - mainWindow.call.core.lSetPaused( - !mainWindow.call.core.paused) + mainWindow.call.core.lSetPaused(!mainWindow.call.core.paused) } KeyNavigation.backtab: moreOptionsButton } @@ -1521,7 +1615,6 @@ AbstractWindow { onToggled: { if (checked) { rightPanel.visible = true - mainWindow.chatObj = UtilsCpp.getCurrentCallChat(mainWindow.call) rightPanel.replace(chatPanel) } else { rightPanel.visible = false diff --git a/Linphone/view/Page/Window/Main/MainWindow.qml b/Linphone/view/Page/Window/Main/MainWindow.qml index 30a0206e7..964b68add 100644 --- a/Linphone/view/Page/Window/Main/MainWindow.qml +++ b/Linphone/view/Page/Window/Main/MainWindow.qml @@ -91,6 +91,10 @@ AbstractWindow { mainWindowStackView.replace(loginPage) } + function openSSOPage() { + mainWindowStackView.replace(ssoPage) + } + function scheduleMeeting(subject, addresses) { openMainPage() mainWindowStackView.currentItem.scheduleMeeting(subject, addresses) @@ -145,7 +149,6 @@ AbstractWindow { id: accountProxyLoader active: AppCpp.coreStarted sourceComponent: AccountProxy { - sourceModel: AppCpp.accounts onInitializedChanged: if (isInitialized) { mainWindow.accountProxy = this mainWindow.initStackViewItem() @@ -181,6 +184,45 @@ AbstractWindow { } } } + Component { + id: ssoPage + Rectangle { + color: DefaultStyle.grey_0 + Image { + id: logoImage + anchors.centerIn: parent + source: AppIcons.splashscreenLogo + sourceSize.width: Utils.getSizeWithScreenRatio(395) + sourceSize.height: Utils.getSizeWithScreenRatio(395) + width: Utils.getSizeWithScreenRatio(395) + height: Utils.getSizeWithScreenRatio(395) + } + ColumnLayout { + anchors.top: logoImage.bottom + anchors.topMargin: Utils.getSizeWithScreenRatio(24) + anchors.horizontalCenter: parent.horizontalCenter + Text { + Layout.alignment: Qt.AlignHCenter + //: "Trying to connect to single sign on on web page ..." + text: qsTr("oidc_connection_waiting_message") + font: Typography.h2 + horizontalAlignment: Text.AlignHCenter + } + Text { + Layout.alignment: Qt.AlignHCenter + text: UtilsCpp.formatDuration(AppCpp.remainingTimeBeforeOidcTimeout) + font: Typography.h3m + horizontalAlignment: Text.AlignHCenter + } + Button { + Layout.alignment: Qt.AlignHCenter + //: Cancel + text: qsTr("cancel") + onClicked: AppCpp.lForceOidcTimeout() + } + } + } + } Component { id: loginPage LoginPage { @@ -230,6 +272,7 @@ AbstractWindow { Component { id: checkingPage RegisterCheckingPage { + id: registerCheckingPage onReturnToRegister: mainWindowStackView.pop() onSendCode: (code) => { RegisterPageCpp.linkNewAccountUsingCode(code, registerWithEmail, sipIdentityAddress) @@ -238,15 +281,16 @@ AbstractWindow { target: RegisterPageCpp function onLinkingNewAccountWithCodeSucceed() { goToLogin() - //: "Compte créé" - mainWindow.showInformationPopup(qsTr("assistant_register_success_title"), - //: "Le compte a été créé. Vous pouvez maintenant vous connecter" - qsTr("assistant_register_success_message"), true) + //: "Compte créé" + mainWindow.showInformationPopup(qsTr("assistant_register_success_title"), + //: "Le compte a été créé. Vous pouvez maintenant vous connecter" + qsTr("assistant_register_success_message"), true) } function onLinkingNewAccountWithCodeFailed(errorMessage) { - //: "Erreur dans le code de validation" - if (errorMessage.length === 0) errorMessage = qsTr("assistant_register_error_code") - mainWindow.showInformationPopup(qsTr("information_popup_error_title"), errorMessage, false) + registerCheckingPage.errorMessage = "" + //: "Erreur dans le code de validation" + if (errorMessage.length === 0) errorMessage = qsTr("assistant_register_error_code") + registerCheckingPage.errorMessage = errorMessage } } } diff --git a/README.md b/README.md index 9e6de4739..5ae1e7225 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,12 @@ For Desktop : you will need QT6 (_6.10.0 or newer_). `C++17` support is required - Using the [official QT installer](https://www.qt.io/download-thank-you) - Using an alternative installer like [aqtinstall](https://github.com/miurahr/aqtinstall) -The following QT optional modules are required: qtmultimedia qtnetworkauth qtshadertools . +The following QT optional modules are required: qtnetworkauth qtshadertools . + +If you wan't to build with Crash handler, you need to install the following tools: +- clang +- curl dev (for C development headers) (ex: `sudo apt install libcurl4-openssl-dev`) +- httplib2 v0.22.0 (use `pip install httplib2==0.22.0` or `pip3 install httplib2==0.22.0`) ### Set your environment @@ -151,6 +156,22 @@ Usually, if it is about VPX or Decaf, this could come from your Perl installatio * On Mac, the application can crash at the start from QOpenGLContext. A workaround is to deactivate the mipmap mode on images by adding into your configuration file (linphonerc): `mipmap_enabled=0` in `[ui]` section. +* On Fedora 43, emoji could appear as blank spaces because the Noto font in Fedora 43 uses COLRv1 format. To prevent this, you can follow the steps described in this github issue : https://github.com/zed-industries/zed/issues/42255 + +``` +# remove the System COLRv1 version of the font +sudo rm /usr/share/fonts/google-noto-color-emoji-fonts/Noto-COLRv1.ttf + +# install a version of the font that works in Zed +sudo wget https://github.com/googlefonts/noto-emoji/raw/main/fonts/NotoColorEmoji.ttf -O /usr/share/fonts/google-noto-color-emoji-fonts/NotoColorEmoji.ttf + +# reload font cache +fc-cache --force + +fc-match "Noto Color Emoji" +# should now show: NotoColorEmoji.ttf: "Noto Color Emoji" "Regular" +``` + ## Specific instructions for the Mac Os X platform @@ -245,7 +266,7 @@ For Fedora (42), the required packages are: - qt6-qtbase-devel - qt6-qttools-devel - qt6-qtsvg-devel - - qt6-qtmultimedia-devel + - qt6-qtnetworkauth-devel - qt6-qtquick3d-devel - qt6-qtlanguageserver-devel @@ -279,6 +300,7 @@ Also, more configurations are available in the docker-files folder of linphone-s | ENABLE_UNIT_TESTS | Enable unit test of SDK. | NO | | ENABLE_OPENLDAP | Build openLDAP external library. | YES | | ENABLE_UPDATE_CHECK | Enable update check. | YES | +| ENABLE_CRASH_HANDLER | Enable crash handler. | YES | | LINPHONE_SDK_MAKE_RELEASE_FILE_URL | Make a RELEASE file that work along check_version and use this URL | "" | diff --git a/cmake/Modules/FindCrashpad.cmake b/cmake/Modules/FindCrashpad.cmake new file mode 100644 index 000000000..96356ac95 --- /dev/null +++ b/cmake/Modules/FindCrashpad.cmake @@ -0,0 +1,154 @@ +############################################################################ +# Copyright (c) 2025 Belledonne Communications SARL. +# +# This file is part of Linphone. +# +# 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 . +# +############################################################################ +# This module will set the following variables in your project: +# +# Crashpad_FOUND - The crashpad library has been found +# Crashpad::Crashpad - Imported target to build against +# CRASHPAD_BIN_DIR - Directory where the crashpad_handler executable is found +# CRASHPAD_EXECUTABLE_NAME - Crash handler executable +# CRASHPAD_INCLUDE_DIR - Headers folder of Crashpad. + +if(TARGET Crashpad) + get_target_property(CRASHPAD_BIN_DIR Crashpad CRASHPAD_BIN_DIR) + get_target_property(CRASHPAD_BUILD_DIR Crashpad CRASHPAD_BUILD_DIR) + get_target_property(CRASHPAD_EXECUTABLE_NAME Crashpad CRASHPAD_EXECUTABLE_NAME) + get_target_property(CRASHPAD_GEN_DIR Crashpad CRASHPAD_GEN_DIR) + get_target_property(CRASHPAD_INCLUDE_DIR Crashpad CRASHPAD_INCLUDE_DIR) + get_target_property(CRASHPAD_LIB_DIR Crashpad CRASHPAD_LIB_DIR) + get_target_property(CRASHPAD_SOURCE_DIR Crashpad CRASHPAD_SOURCE_DIR) +endif() + +if(NOT CRASHPAD_BUILD_DIR) + set(CRASHPAD_BUILD_DIR ${CMAKE_BINARY_DIR}/external/google/crashpad/out) + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CRASHPAD_BUILD_DIR ${CRASHPAD_BUILD_DIR}/debug) + else() + set(CRASHPAD_BUILD_DIR ${CRASHPAD_BUILD_DIR}/release) + endif() +endif() +if(NOT CRASHPAD_SOURCE_DIR) + set(CRASHPAD_SOURCE_DIR ${CMAKE_SOURCE_DIR}/external/google/crashpad) +endif() + +if(NOT CRASHPAD_EXECUTABLE_NAME OR NOT CRASHPAD_BIN_DIR) + find_program(CRASHPAD_EXECUTABLE + NAMES crashpad_handler.exe crashpad_handler + HINTS + "${CRASHPAD_BUILD_DIR}" + "${CMAKE_PREFIX_PATH}" + ) + get_filename_component(CRASHPAD_BIN_DIR ${CRASHPAD_EXECUTABLE} DIRECTORY) + get_filename_component(CRASHPAD_EXECUTABLE_NAME ${CRASHPAD_EXECUTABLE} NAME) +endif() + + + +if(NOT CRASHPAD_INCLUDE_DIR) + find_path(CRASHPAD_INCLUDE_DIR + NAMES client/crashpad_client.h + HINTS + "${CRASHPAD_SOURCE_DIR}" + "${CMAKE_PREFIX_PATH}" + ) +endif() + +if(NOT CRASHPAD_LIB_DIR) + find_path(CRASHPAD_LIB_DIR + NAMES util/libutil.a util/util.lib + HINTS + "${CRASHPAD_BIN_DIR}/obj" + "${CMAKE_PREFIX_PATH}" + ) +endif() + +if(NOT CRASHPAD_GEN_DIR) + find_path(CRASHPAD_GEN_DIR + NAMES build/chromeos_buildflags.h + PATH_SUFFIXES gen + HINTS + "${CRASHPAD_BIN_DIR}" + "${CMAKE_PREFIX_PATH}" + ) +endif() + +if(APPLE) + find_path(CRASHPAD_OBJ_DIR + NAMES mig_output.child_portServer.o + PATH_SUFFIXES gen/util/mach + HINTS + "${CRASHPAD_OBJECT_DIR}" + "${CRASHPAD_LIB_DIR}" + "${CMAKE_PREFIX_PATH}" + ) + set(CRASHPAD_APPLE_VARS CRASHPAD_OBJ_DIR CRASHPAD_GEN_DIR) + find_library(FWbsm bsm) + find_library(FWAppKit AppKit) + find_library(FWIOKit IOKit) + find_library(FWSecurity Security) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Crashpad DEFAULT_MSG + CRASHPAD_BIN_DIR CRASHPAD_INCLUDE_DIR CRASHPAD_LIB_DIR CRASHPAD_EXECUTABLE_NAME ${CRASHPAD_APPLE_VARS} +) + +#[[ + +if(Crashpad_FOUND) + add_library(Crashpad::Crashpad UNKNOWN IMPORTED) + target_include_directories(Crashpad::Crashpad INTERFACE + "${CRASHPAD_INCLUDE_DIR}" + "${CRASHPAD_INCLUDE_DIR}/third_party/mini_chromium/mini_chromium" + "${CRASHPAD_GEN_DIR}") + if(WIN32) + target_link_libraries(Crashpad::Crashpad INTERFACE + "${CRASHPAD_LIB_DIR}/third_party/mini_chromium/mini_chromium/base/base.lib" + "${CRASHPAD_LIB_DIR}/util/util.lib" + "${CRASHPAD_LIB_DIR}/client/client.lib" + "${CRASHPAD_LIB_DIR}/client/common.lib" + advapi32) + set_target_properties(Crashpad::Crashpad PROPERTIES + IMPORTED_LOCATION "${CRASHPAD_LIB_DIR}/client/client.lib") + elseif(APPLE) + target_link_libraries(Crashpad::Crashpad INTERFACE + "${CRASHPAD_LIB_DIR}/third_party/mini_chromium/mini_chromium/base/libbase.a" + "${CRASHPAD_LIB_DIR}/util/libutil.a" + "${CRASHPAD_LIB_DIR}/client/libclient.a" + "${CRASHPAD_OBJ_DIR}/mig_output.child_portServer.o" + "${CRASHPAD_OBJ_DIR}/mig_output.child_portUser.o" + "${CRASHPAD_OBJ_DIR}/mig_output.excServer.o" + "${CRASHPAD_OBJ_DIR}/mig_output.excUser.o" + "${CRASHPAD_OBJ_DIR}/mig_output.mach_excServer.o" + "${CRASHPAD_OBJ_DIR}/mig_output.mach_excUser.o" + "${CRASHPAD_OBJ_DIR}/mig_output.notifyServer.o" + "${CRASHPAD_OBJ_DIR}/mig_output.notifyUser.o" + ${FWbsm} ${FWAppKit} ${FWIOKit} ${FWSecurity}) + set_target_properties(Crashpad::Crashpad PROPERTIES + IMPORTED_LOCATION "${CRASHPAD_LIB_DIR}/client/libclient.a") + elseif(UNIX) + target_link_libraries(Crashpad::Crashpad INTERFACE + "${CRASHPAD_LIB_DIR}/third_party/mini_chromium/mini_chromium/base/libbase.a" + "${CRASHPAD_LIB_DIR}/util/libutil.a" + "${CRASHPAD_LIB_DIR}/client/libclient.a") + set_target_properties(Crashpad::Crashpad PROPERTIES + IMPORTED_LOCATION "${CRASHPAD_LIB_DIR}/client/libclient.a") + endif() +endif() +]]# \ No newline at end of file diff --git a/cmake/install/cleanCPack.cmake.in b/cmake/install/cleanCPack.cmake.in index e9db59aa7..9689595a4 100644 --- a/cmake/install/cleanCPack.cmake.in +++ b/cmake/install/cleanCPack.cmake.in @@ -25,10 +25,20 @@ # It is neccessary to use it because CPack doesn't take account of some install() (those that do the move) cmake_policy(SET CMP0009 NEW)#Not following symlinks for file() +IF(POLICY CMP0012) + CMAKE_POLICY(SET CMP0012 NEW)#Allow if(ON) +ENDIF() set(DO_SIGNING @LINPHONE_BUILDER_SIGNING_IDENTITY@) set(DEPLOYQT_PROGRAM @DEPLOYQT_PROGRAM@) +function(remove_file expression) + file(GLOB_RECURSE UNWANTED_FILES ${expression}) + foreach(F ${UNWANTED_FILES}) + file(REMOVE ${F}) + endforeach() +endfunction() + if(APPLE) find_program(DSYMUTIL_PROGRAM dsymutil) @@ -61,8 +71,38 @@ if(APPLE) execute_process(COMMAND codesign --force --deep --sign "-" "${CPACK_TEMPORARY_INSTALL_DIRECTORY}/@APPLICATION_NAME@.app" )#If not code signed, app can crash because of APPLE on "Code Signature Invalid" (spotted for ARM64) endif() elseif(WIN32) - message(STATUS "Execute : @DEPLOYQT_PROGRAM@ ${CPACK_TEMPORARY_INSTALL_DIRECTORY}/@CMAKE_INSTALL_BINDIR@/@EXECUTABLE_NAME@.exe --qmldir=@LINPHONE_QML_DIR@ --verbose=0 --no-compiler-runtime") - execute_process(COMMAND @DEPLOYQT_PROGRAM@ "${CPACK_TEMPORARY_INSTALL_DIRECTORY}/@CMAKE_INSTALL_BINDIR@/@EXECUTABLE_NAME@.exe" "--qmldir=@LINPHONE_QML_DIR@" "--verbose=0" "--no-compiler-runtime") + message(STATUS "Execute : @DEPLOYQT_PROGRAM@ ${CPACK_TEMPORARY_INSTALL_DIRECTORY}/@CMAKE_INSTALL_BINDIR@/@EXECUTABLE_NAME@.exe --qmldir=@LINPHONE_QML_DIR@ --verbose=0 --no-compiler-runtime --pdb") + execute_process(COMMAND @DEPLOYQT_PROGRAM@ "${CPACK_TEMPORARY_INSTALL_DIRECTORY}/@CMAKE_INSTALL_BINDIR@/@EXECUTABLE_NAME@.exe" "--qmldir=@LINPHONE_QML_DIR@" "--verbose=0" "--no-compiler-runtime" "--pdb") # --plugindir "${CMAKE_CURRENT_BINARY_DIR}/winqt/plugins" # --dir "${CMAKE_CURRENT_BINARY_DIR}/winqt/" + if(@ENABLE_BUGSPLAT_SYMBOLS_UPLOAD@) + message(STATUS "Uploading Bugsplat symbols from ${CPACK_TEMPORARY_INSTALL_DIRECTORY}") + execute_process(COMMAND "@CRASHPAD_SYMBOLS_UPLOADER@" -m -b @BUGSPLAT_DATABASE@ -a "@LINPHONEAPP_APPLICATION_NAME@" -v "@LINPHONEAPP_VERSION@" -f "**/*.{pdb,exe,dll}" -d "${CPACK_TEMPORARY_INSTALL_DIRECTORY}/@CMAKE_INSTALL_BINDIR@" --clientId "@BUGSPLAT_CLIENT_ID@" --clientSecret "@BUGSPLAT_CLIENT_SECRET@" + WORKING_DIRECTORY ${CPACK_TEMPORARY_INSTALL_DIRECTORY} + RESULT_VARIABLE CPACK_COMMAND_RESULT + COMMAND_ECHO NONE + ) + if(CPACK_COMMAND_RESULT) + message(FATAL_ERROR "Failed to upload symbols! ${CPACK_COMMAND_RESULT}") + endif() + execute_process(COMMAND "@CRASHPAD_SYMBOLS_UPLOADER@" -m -b @BUGSPLAT_DATABASE@ -a "@LINPHONEAPP_APPLICATION_NAME@" -v "@LINPHONEAPP_VERSION@" -f "**/*.{pdb,exe,dll}" -d "${CPACK_TEMPORARY_INSTALL_DIRECTORY}/@CMAKE_INSTALL_LIBDIR@/mediastreamer/plugins" --clientId "@BUGSPLAT_CLIENT_ID@" --clientSecret "@BUGSPLAT_CLIENT_SECRET@" + WORKING_DIRECTORY ${CPACK_TEMPORARY_INSTALL_DIRECTORY} + RESULT_VARIABLE CPACK_COMMAND_RESULT + COMMAND_ECHO NONE + ) + if(CPACK_COMMAND_RESULT) + message(FATAL_ERROR "Failed to upload symbols! ${CPACK_COMMAND_RESULT}") + endif() + endif() + ##################################################### + # Clean all unwanted files to package. + ##################################################### + remove_file("${CPACK_TEMPORARY_INSTALL_DIRECTORY}/*/Qt*.pdb") + + # TODO: remove all pdb when CrashReporter is available. Symbols for crashing are in server so it is not needed for deployment. + # if debugging is needed, we could rebuild it and use pdb from local build instead of using official binaries. + # Warning: Do not remove files from here like below. NSIS have a check integrity that is done from install() commands. + #remove_file("${CPACK_TEMPORARY_INSTALL_DIRECTORY}/*/*.pdb") + #remove_file("${CPACK_TEMPORARY_INSTALL_DIRECTORY}/*/*.cmake") + #remove_file("${CPACK_TEMPORARY_INSTALL_DIRECTORY}/*/*.lib") endif() diff --git a/cmake/install/create_appimage.sh b/cmake/install/create_appimage.sh index fe7837d2c..d08321731 100755 --- a/cmake/install/create_appimage.sh +++ b/cmake/install/create_appimage.sh @@ -126,6 +126,8 @@ else echo "-- Code Signing of AppImage" # APPIMAGETOOL_SIGN_PASSPHRASE has to the parent environment (not here). Do not use export. ./${WORK_DIR}/AppBin/appimagetool-x86_64.AppImage --appimage-extract-and-run ${WORK_DIR}/AppDir --sign --sign-key $4 + + fi echo "Move Appimages into ${BIN_SOURCE_DIR}/Packages/$2.AppImage" diff --git a/cmake/install/install.cmake b/cmake/install/install.cmake index 3f1bcbe22..7ce70906d 100644 --- a/cmake/install/install.cmake +++ b/cmake/install/install.cmake @@ -23,6 +23,8 @@ cmake_minimum_required(VERSION 3.5) set(TARGET_NAME Linphone) +list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/Modules") + if (GIT_EXECUTABLE AND NOT(LINPHONEAPP_VERSION)) execute_process( @@ -33,10 +35,16 @@ if (GIT_EXECUTABLE AND NOT(LINPHONEAPP_VERSION)) ) endif() +if(NOT LINPHONEAPP_VERSION) + bc_compute_full_version(LINPHONEAPP_VERSION) +endif() + if (NOT(LINPHONEAPP_VERSION)) set(LINPHONEAPP_VERSION "6.1.0") endif () +include(${CMAKE_SOURCE_DIR}/Linphone/application_info.cmake) + set(LINPHONE_MAJOR_VERSION) set(LINPHONE_MINOR_VERSION) set(LINPHONE_MICRO_VERSION) @@ -131,32 +139,36 @@ elseif(WIN32) endif () endif() -#Windeployqt hack for CPack. WindeployQt cannot be used only with a simple 'install(CODE "execute_process' or CPack will not have all required files. -#Call it from target folder -function(deployqt_hack target) - if(WIN32) - find_program(DEPLOYQT_PROGRAM windeployqt HINTS "${_qt_bin_dir}") - if (NOT DEPLOYQT_PROGRAM) - message(FATAL_ERROR "Could not find the windeployqt program. Make sure it is in the PATH.") - endif () - add_custom_command(TARGET ${target} POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/winqt/" - COMMAND "${CMAKE_COMMAND}" -E - env PATH="${_qt_bin_dir}" "${DEPLOYQT_PROGRAM}" - --qmldir "${LINPHONE_QML_DIR}" - --plugindir "${CMAKE_CURRENT_BINARY_DIR}/winqt/plugins" - --verbose 0 - --no-compiler-runtime - --dir "${CMAKE_CURRENT_BINARY_DIR}/winqt/" - "$" - COMMENT "Deploying Qt..." - WORKING_DIRECTORY "${CMAKE_INSTALL_PREFIX}/.." - ) - install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/winqt/" DESTINATION bin) - set(CMAKE_INSTALL_UCRT_LIBRARIES TRUE) - include(InstallRequiredSystemLibraries) +################################################################ +# CRASHPAD +################################################################ +if(HAVE_CRASH_HANDLER) + get_target_property(CRASHPAD_BIN_DIR Crashpad CRASHPAD_BIN_DIR) + get_target_property(CRASHPAD_EXECUTABLE_NAME Crashpad CRASHPAD_EXECUTABLE_NAME) + install(FILES "${CRASHPAD_BIN_DIR}/${CRASHPAD_EXECUTABLE_NAME}" DESTINATION ${CMAKE_INSTALL_BINDIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + if(ENABLE_BUGSPLAT_SYMBOLS_UPLOAD) + if(NOT LINPHONEAPP_APPLICATION_NAME) + message(FATAL_ERROR "Missing application details for Bugsplat. Please fill LINPHONEAPP_APPLICATION_NAME (${LINPHONEAPP_APPLICATION_NAME} - ${LINPHONEAPP_VERSION})") + elseif(NOT BUGSPLAT_DATABASE) + message(FATAL_ERROR "Missing database application details for Bugsplat. Please fill BUGSPLAT_DATABASE in application_info.cmake") + elseif(NOT BUGSPLAT_CLIENT_ID OR NOT BUGSPLAT_CLIENT_SECRET) + message(FATAL_ERROR "Missing identifications for Bugsplat. Please fill BUGSPLAT_CLIENT_ID and BUGSPLAT_CLIENT_SECRET to activate symbol uploading") + else() + message(STATUS "Preparing symbol uploader Bugsplat for ${LINPHONEAPP_APPLICATION_NAME} - ${LINPHONEAPP_VERSION}.") + if(WIN32) + set(CRASHPAD_SYMBOLS_UPLOADER "${CMAKE_BINARY_DIR}/symbol-upload-windows.exe") + set(CRASHPAD_SYMBOLS_UPLOADER_URL "https://github.com/BugSplat-Git/symbol-upload/releases/download/v10.2.4/symbol-upload-windows.exe") + else() + set(CRASHPAD_SYMBOLS_UPLOADER "${CMAKE_BINARY_DIR}/symbol-upload-linux") + set(CRASHPAD_SYMBOLS_UPLOADER_URL "https://github.com/BugSplat-Git/symbol-upload/releases/download/v10.2.4/symbol-upload-linux") + endif() + if(NOT EXISTS "${CRASHPAD_SYMBOLS_UPLOADER}") + file(DOWNLOAD ${CRASHPAD_SYMBOLS_UPLOADER_URL} ${CRASHPAD_SYMBOLS_UPLOADER}) + file(CHMOD "${CRASHPAD_SYMBOLS_UPLOADER}" PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + endif() + endif() endif() -endfunction() +endif() # ============================================================================== # CPack. @@ -211,13 +223,19 @@ if(${ENABLE_APP_PACKAGING}) set(DO_GENERATOR YES) set(PACKAGE_EXT "exe") set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${PACKAGE_VERSION}-${BIN_ARCH}") + set(CPACK_SOURCE_IGNORE_FILES "*Qt*.pdb;*.lib;*.cmake") + set(CPACK_IGNORE_FILES ".*Qt.*\\.pdb$" ".*\\.lib$" ".*\\.cmake$") #Not documented + find_program(NSIS_PROGRAM makensis) if(NOT NSIS_PROGRAM) - - message(STATUS "Installing windows tools for nsis") - execute_process( - COMMAND "${MSYS2_PROGRAM}" "-${MINGW_TYPE}" "-here" "-full-path" "-defterm" "-shell" "sh" "-l" "-c" "pacman -Sy ${MINGW_PACKAGE_PREFIX}nsis --noconfirm --needed" - ) + if(ENABLE_WINDOWS_TOOLS_CHECK) + message(STATUS "Installing windows tools for nsis") + execute_process( + COMMAND "${MSYS2_PROGRAM}" "-${MINGW_TYPE}" "-here" "-full-path" "-defterm" "-shell" "sh" "-l" "-c" "pacman -Sy ${MINGW_PACKAGE_PREFIX}nsis --noconfirm --needed" + ) + else() + message(FATAL_ERROR "`makensis` is needed for packaging. Please use pacman -Sy ${MINGW_PACKAGE_PREFIX}nsis to install it.") + endif() endif() set(PACKAGE_EXT "exe") # Use magic `NSIS.template.in` template from the current source directory to force uninstallation @@ -304,4 +322,3 @@ if(${ENABLE_APP_PACKAGING}) endif() include(CPack) endif() - diff --git a/cmake/install/packaging.cmake.in b/cmake/install/packaging.cmake.in index 3e231ff88..08b3f962a 100644 --- a/cmake/install/packaging.cmake.in +++ b/cmake/install/packaging.cmake.in @@ -57,7 +57,7 @@ if (NOT "${CMAKE_INSTALL_PREFIX}" MATCHES .*/_CPack_Packages/.*) if(DO_GENERATOR) execute_process( COMMAND ${CMAKE_CPACK_COMMAND} -G @CPACK_GENERATOR@ -C @CMAKE_BUILD_TYPE@ RESULT_VARIABLE CPACK_COMMAND_RESULT) if(CPACK_COMMAND_RESULT) - message(FATAL_ERROR "Failed to create @CPACK_GENERATOR@ package!") + message(FATAL_ERROR "Failed to create @CPACK_GENERATOR@ package! ${CPACK_COMMAND_RESULT}") endif() endif() if(DO_APPIMAGE) @@ -69,6 +69,23 @@ if (NOT "${CMAKE_INSTALL_PREFIX}" MATCHES .*/_CPack_Packages/.*) execute_process( COMMAND "@CMAKE_SOURCE_DIR@/cmake/install/create_appimage.sh" @EXECUTABLE_NAME@ "@CPACK_PACKAGE_FILE_NAME@${ARCH}" @QT_PATH@ @LINPHONE_BUILDER_SIGNING_IDENTITY@ RESULT_VARIABLE CPACK_COMMAND_RESULT WORKING_DIRECTORY "@CMAKE_INSTALL_PREFIX@/.." ) if(CPACK_COMMAND_RESULT) message(FATAL_ERROR "Failed to create AppImage package with this command : '@CMAKE_CURRENT_SOURCE_DIR@/../../tools/create_appimage.sh @EXECUTABLE_NAME@ @QT_PATH@ @LINPHONEAPP_VERSION@' at @CMAKE_INSTALL_PREFIX@/..\nMaybe the .appimage already exists and is running. Please remove the file before packaging if it is the case.") + elseif(@ENABLE_BUGSPLAT_SYMBOLS_UPLOAD@) + execute_process(COMMAND "@CRASHPAD_SYMBOLS_UPLOADER@" -m -b @BUGSPLAT_DATABASE@ -a "@LINPHONEAPP_APPLICATION_NAME@" -v "@LINPHONEAPP_VERSION@" -f "@EXECUTABLE_NAME@" --clientId "@BUGSPLAT_CLIENT_ID@" --clientSecret "@BUGSPLAT_CLIENT_SECRET@" + WORKING_DIRECTORY @CMAKE_INSTALL_PREFIX@/../WORK/Packages/AppImageDir/AppDir/usr/bin/ + RESULT_VARIABLE CPACK_COMMAND_RESULT + COMMAND_ECHO NONE + ) + if(CPACK_COMMAND_RESULT) + message(FATAL_ERROR "Failed to upload symbols: '@CMAKE_CURRENT_SOURCE_DIR@/../../tools/create_appimage.sh @EXECUTABLE_NAME@ @QT_PATH@ @LINPHONEAPP_VERSION@' at @CMAKE_INSTALL_PREFIX@/..\nMaybe the .appimage already exists and is running. Please remove the file before packaging if it is the case.") + endif() + execute_process(COMMAND "@CRASHPAD_SYMBOLS_UPLOADER@" -m -b @BUGSPLAT_DATABASE@ -a "@LINPHONEAPP_APPLICATION_NAME@" -v "@LINPHONEAPP_VERSION@" -f "**/*.so{,.*}" --clientId "@BUGSPLAT_CLIENT_ID@" --clientSecret "@BUGSPLAT_CLIENT_SECRET@" + WORKING_DIRECTORY @CMAKE_INSTALL_PREFIX@/../WORK/Packages/AppImageDir/AppDir/usr/ + RESULT_VARIABLE CPACK_COMMAND_RESULT + COMMAND_ECHO NONE + ) + if(CPACK_COMMAND_RESULT) + message(FATAL_ERROR "Failed to upload symbols: '@CMAKE_CURRENT_SOURCE_DIR@/../../tools/create_appimage.sh @EXECUTABLE_NAME@ @QT_PATH@ @LINPHONEAPP_VERSION@' at @CMAKE_INSTALL_PREFIX@/..\nMaybe the .appimage already exists and is running. Please remove the file before packaging if it is the case.") + endif() endif() endif() if (@PERFORM_SIGNING@) diff --git a/docker-files/bc-dev-ubuntu-22-04-lts b/docker-files/bc-dev-ubuntu-22-04-lts index 88b2d6ec8..9bda5bd93 100644 --- a/docker-files/bc-dev-ubuntu-22-04-lts +++ b/docker-files/bc-dev-ubuntu-22-04-lts @@ -59,6 +59,10 @@ RUN sudo apt-get install -y gnupg2 # Install configuration tools RUN sudo apt-get install -y wget +# Install crashpad build tools +RUN sudo apt-get install -y libcurl4-openssl-dev +RUN sudo pip3 install httplib2==0.22.0 + # Configure user bc RUN useradd -ms /bin/bash bc && \ echo 'bc:cotcot' | chpasswd && \ @@ -84,9 +88,9 @@ RUN sudo pip3 install --upgrade aqtinstall RUN sudo python3 -m aqt install-qt linux desktop 5.15.2 -O /opt/Qt/opensource RUN sudo python3 -m aqt install-qt linux desktop 5.15.2 -O /opt/Qt/opensource --noarchives -m qtnetworkauth qtquick3d RUN sudo python3 -m aqt install-qt linux desktop 6.9.1 -O /opt/Qt/opensource -RUN sudo python3 -m aqt install-qt linux desktop 6.9.1 -O /opt/Qt/opensource --noarchives -m qtnetworkauth qtquick3d qtmultimedia qt5compat qtshadertools +RUN sudo python3 -m aqt install-qt linux desktop 6.9.1 -O /opt/Qt/opensource --noarchives -m qtnetworkauth qtquick3d qt5compat qtshadertools RUN sudo python3 -m aqt install-qt linux desktop 6.10.0 -O /opt/Qt/opensource -RUN sudo python3 -m aqt install-qt linux desktop 6.10.0 -O /opt/Qt/opensource --noarchives -m qtnetworkauth qtquick3d qtmultimedia qt5compat qtshadertools +RUN sudo python3 -m aqt install-qt linux desktop 6.10.0 -O /opt/Qt/opensource --noarchives -m qtnetworkauth qtquick3d qt5compat qtshadertools RUN sudo chown -R bc:bc /opt/Qt/ @@ -134,7 +138,7 @@ RUN /opt/Qt/proprietary/MaintenanceTool \ --confirm-command \ --root "/opt/Qt/proprietary" \ --auto-answer OperationDoesNotExistError=Ignore,OverwriteTargetDirectory=Yes,stopProcessesForUpdates=Ignore,installationErrorWithCancel=Ignore,InstallationErrorWithIgnore=Ignore,AssociateCommonFiletypes=Yes,telemetry-question=No \ - install qt.qt6.681.linux_gcc_64 qt.qt6.681.addons.qtnetworkauth qt.qt6.681.addons.qtquick3d qt.qt6.681.addons.qtmultimedia qt.qt6.681.addons.qt5compat qt.qt6.681.addons.qtshadertools + install qt.qt6.681.linux_gcc_64 qt.qt6.681.addons.qtnetworkauth qt.qt6.681.addons.qtquick3d qt.qt6.681.addons.qt5compat qt.qt6.681.addons.qtshadertools # # Install Qt 6.8.3 proprietary RUN /opt/Qt/proprietary/MaintenanceTool \ @@ -143,7 +147,7 @@ RUN /opt/Qt/proprietary/MaintenanceTool \ --confirm-command \ --root "/opt/Qt/proprietary" \ --auto-answer OperationDoesNotExistError=Ignore,OverwriteTargetDirectory=Yes,stopProcessesForUpdates=Ignore,installationErrorWithCancel=Ignore,InstallationErrorWithIgnore=Ignore,AssociateCommonFiletypes=Yes,telemetry-question=No \ - install qt.qt6.683.linux_gcc_64 qt.qt6.683.addons.qtnetworkauth qt.qt6.683.addons.qtquick3d qt.qt6.683.addons.qtmultimedia qt.qt6.683.addons.qt5compat qt.qt6.683.addons.qtshadertools + install qt.qt6.683.linux_gcc_64 qt.qt6.683.addons.qtnetworkauth qt.qt6.683.addons.qtquick3d qt.qt6.683.addons.qt5compat qt.qt6.683.addons.qtshadertools # # Install Qt 6.9.1 proprietary RUN /opt/Qt/proprietary/MaintenanceTool \ @@ -152,7 +156,7 @@ RUN /opt/Qt/proprietary/MaintenanceTool \ --confirm-command \ --root "/opt/Qt/proprietary" \ --auto-answer OperationDoesNotExistError=Ignore,OverwriteTargetDirectory=Yes,stopProcessesForUpdates=Ignore,installationErrorWithCancel=Ignore,InstallationErrorWithIgnore=Ignore,AssociateCommonFiletypes=Yes,telemetry-question=No \ - install qt.qt6.691.linux_gcc_64 qt.qt6.691.addons.qtnetworkauth qt.qt6.691.addons.qtquick3d qt.qt6.691.addons.qtmultimedia qt.qt6.691.addons.qt5compat qt.qt6.691.addons.qtshadertools + install qt.qt6.691.linux_gcc_64 qt.qt6.691.addons.qtnetworkauth qt.qt6.691.addons.qtquick3d qt.qt6.691.addons.qt5compat qt.qt6.691.addons.qtshadertools # # Install Qt 6.10.0 proprietary RUN /opt/Qt/proprietary/MaintenanceTool \ @@ -161,7 +165,7 @@ RUN /opt/Qt/proprietary/MaintenanceTool \ --confirm-command \ --root "/opt/Qt/proprietary" \ --auto-answer OperationDoesNotExistError=Ignore,OverwriteTargetDirectory=Yes,stopProcessesForUpdates=Ignore,installationErrorWithCancel=Ignore,InstallationErrorWithIgnore=Ignore,AssociateCommonFiletypes=Yes,telemetry-question=No \ - install qt.qt6.6100.linux_gcc_64 qt.qt6.6100.addons.qtnetworkauth qt.qt6.6100.addons.qtquick3d qt.qt6.6100.addons.qtmultimedia qt.qt6.6100.addons.qt5compat qt.qt6.6100.addons.qtshadertools + install qt.qt6.6100.linux_gcc_64 qt.qt6.6100.addons.qtnetworkauth qt.qt6.6100.addons.qtquick3d qt.qt6.6100.addons.qt5compat qt.qt6.6100.addons.qtshadertools RUN qtchooser -install 5.15.19-proprietary /opt/Qt/proprietary/5.15.19/gcc_64/bin/qmake RUN qtchooser -install 5.15.14-proprietary /opt/Qt/proprietary/5.15.14/gcc_64/bin/qmake @@ -173,4 +177,4 @@ RUN qtchooser -install 6.10.0-proprietary /opt/Qt/proprietary/6.10.0/gcc_64/bin/ RUN rm -f /home/bc/.local/share/Qt/qtaccount.ini RUN rm -f /home/bc/.local/share/Qt/qtlicenses.ini -cmd bash \ No newline at end of file +cmd bash diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 0ef245c8a..c52c8bcf3 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -3,4 +3,5 @@ add_subdirectory(linphone-sdk/) if(ENABLE_QT_KEYCHAIN) find_package(Qt6 REQUIRED COMPONENTS Test) add_subdirectory(qtkeychain/) -endif() \ No newline at end of file +endif() +add_subdirectory(google) \ No newline at end of file diff --git a/external/google/.gclient b/external/google/.gclient new file mode 100644 index 000000000..67bbc285f --- /dev/null +++ b/external/google/.gclient @@ -0,0 +1 @@ +solutions = [{"name": "crashpad", "url": "https://gitlab.linphone.org/BC/public/external/crashpad", "managed": False}] diff --git a/external/google/CMakeLists.txt b/external/google/CMakeLists.txt new file mode 100644 index 000000000..b40886faf --- /dev/null +++ b/external/google/CMakeLists.txt @@ -0,0 +1,116 @@ +set(DEPOT_TOOLS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/chromium-depot-tools") +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CRASHPAD_BIN_DIR "out/debug") +else() + set(CRASHPAD_BIN_DIR "out/release") +endif() +set(CRASHPAD_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/crashpad/${CRASHPAD_BIN_DIR}) + +if(WIN32) + set(GCLIENT_SCRIPT "${DEPOT_TOOLS_DIR}/gclient.bat") + set(GN_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/bc_gn.bat") + set(GN_COMMAND ${GN_SCRIPT} ${CRASHPAD_BUILD_DIR} ${CMAKE_BUILD_TYPE}) +else() + set(GCLIENT_SCRIPT "${DEPOT_TOOLS_DIR}/gclient") + set(GN_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/bc_gn.sh") + set(GN_COMMAND ${GN_SCRIPT} ${CRASHPAD_BUILD_DIR} ${CMAKE_BUILD_TYPE}) +endif() +message(STATUS "gn used ${GN_SCRIPT}") + +if (ENABLE_CRASH_HANDLER AND WIN32) + ########################################## + #Hack utils/Build.gn by adding cflags = ["-Wno-nontrivial-memcall"] + #because newer version of clang cannot build with this warning and it is not fixed by crashpad + message(STATUS "Syncing with gclient") + execute_process( + COMMAND "${GCLIENT_SCRIPT}" sync --nohooks --shallow + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE gclient_sync_result + OUTPUT_VARIABLE gclient_sync_output + ERROR_VARIABLE gclient_sync_error + COMMAND_ECHO STDOUT + ) + if(gclient_sync_result) + message(FATAL_ERROR "Failed syncing with gclient: ${gclient_sync_result} ${gclient_sync_output} ${gclient_sync_error}") + endif() + + message(STATUS "Generating build files for crashpad") + #We use GN_COMMAND because CMAKE mess up with quotes, spaces and arguments on Windows + execute_process( + COMMAND ${GN_COMMAND} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/crashpad" + RESULT_VARIABLE crashpad_gn_result + OUTPUT_VARIABLE crashpad_gn_output + ERROR_VARIABLE crashpad_gn_error + COMMAND_ECHO STDOUT + ) + if(crashpad_gn_result) + message(FATAL_ERROR "Failed generate crashpad build files: ${crashpad_gn_result} ${crashpad_gn_output} ${crashpad_gn_error}") + endif() + message(STATUS "Crashpad build files generated") + set(TARGET_NAME Crashpad) + +#NOTE: Order is important + if(WIN32) + set(CRASHPAD_OUTPUTS + "${CRASHPAD_BUILD_DIR}/obj/client/common.lib" + "${CRASHPAD_BUILD_DIR}/obj/client/client.lib" + "${CRASHPAD_BUILD_DIR}/obj/util/util.lib" + "${CRASHPAD_BUILD_DIR}/obj/third_party/mini_chromium/mini_chromium/base/base.lib" + ) + elseif(APPLE) + set(CRASHPAD_OUTPUTS + "${CRASHPAD_BUILD_DIR}/obj/client/libcommon.a" + "${CRASHPAD_BUILD_DIR}/obj/client/libclient.a" + "${CRASHPAD_BUILD_DIR}/obj/util/libutil.a" + "${CRASHPAD_BUILD_DIR}/obj/util/libmig_output.a" + "${CRASHPAD_BUILD_DIR}/obj/third_party/mini_chromium/mini_chromium/base/libbase.a" + ) + else() + set(CRASHPAD_OUTPUTS + "${CRASHPAD_BUILD_DIR}/obj/client/libcommon.a" + "${CRASHPAD_BUILD_DIR}/obj/client/libclient.a" + "${CRASHPAD_BUILD_DIR}/obj/util/libutil.a" + "${CRASHPAD_BUILD_DIR}/obj/third_party/mini_chromium/mini_chromium/base/libbase.a" + ) + endif() + + add_custom_command( + OUTPUT ${CRASHPAD_OUTPUTS} + COMMAND ninja -C . + WORKING_DIRECTORY "${CRASHPAD_BUILD_DIR}" + COMMENT "Crashpad build files generated" + ) + + add_custom_target(Crashpad_build DEPENDS ${CRASHPAD_OUTPUTS}) + + add_library(${TARGET_NAME} STATIC IMPORTED GLOBAL) + add_dependencies(${TARGET_NAME} Crashpad_build) + + target_include_directories(${TARGET_NAME} INTERFACE + "${CMAKE_CURRENT_SOURCE_DIR}/crashpad" + "${CMAKE_CURRENT_SOURCE_DIR}/crashpad/third_party/mini_chromium/mini_chromium" + "${CRASHPAD_BUILD_DIR}/gen") + target_link_libraries(${TARGET_NAME} INTERFACE ${CRASHPAD_OUTPUTS}) + set_target_properties(${TARGET_NAME} PROPERTIES CRASHPAD_EXECUTABLE_NAME "crashpad_handler${CMAKE_EXECUTABLE_SUFFIX}") + if(WIN32) + target_link_libraries(${TARGET_NAME} INTERFACE advapi32) + set_target_properties(${TARGET_NAME} PROPERTIES + IMPORTED_LOCATION "${CRASHPAD_BUILD_DIR}/obj/client/client.lib") + else() + set_target_properties(${TARGET_NAME} PROPERTIES + IMPORTED_LOCATION "${CRASHPAD_BUILD_DIR}/obj/client/libclient.a" + ) + endif() + set_target_properties(${TARGET_NAME} PROPERTIES + CRASHPAD_BIN_DIR "${CRASHPAD_BUILD_DIR}" + CRASHPAD_BUILD_DIR "${CRASHPAD_BUILD_DIR}" + CRASHPAD_GEN_DIR "${CRASHPAD_BUILD_DIR}/gen" + CRASHPAD_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/crashpad" + CRASHPAD_LIB_DIR "${CRASHPAD_BUILD_DIR}/obj" + CRASHPAD_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/crashpad" + ) + + set(HAVE_CRASH_HANDLER 1) + +endif() \ No newline at end of file diff --git a/external/google/bc_gn.bat b/external/google/bc_gn.bat new file mode 100644 index 000000000..16bfbe4fb --- /dev/null +++ b/external/google/bc_gn.bat @@ -0,0 +1,6 @@ + +if /i "%~2"=="Debug" ( + .\..\chromium-depot-tools\gn.bat gen %1 --args="extra_cflags=\"/MDd -Wno-nontrivial-memcall\"" +) else ( + .\..\chromium-depot-tools\gn.bat gen %1 --args="extra_cflags=\"/MD -Wno-nontrivial-memcall\"" +) \ No newline at end of file diff --git a/external/google/bc_gn.sh b/external/google/bc_gn.sh new file mode 100755 index 000000000..d1ec96228 --- /dev/null +++ b/external/google/bc_gn.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +./../chromium-depot-tools/gn gen $1 --args="extra_cflags=\"-Wno-nontrivial-memcall\"" diff --git a/external/google/chromium-depot-tools b/external/google/chromium-depot-tools new file mode 160000 index 000000000..db99cc40f --- /dev/null +++ b/external/google/chromium-depot-tools @@ -0,0 +1 @@ +Subproject commit db99cc40f562179c59f12b10e8af2d8fe2770ad2 diff --git a/external/google/crashpad b/external/google/crashpad new file mode 160000 index 000000000..4effb3358 --- /dev/null +++ b/external/google/crashpad @@ -0,0 +1 @@ +Subproject commit 4effb3358160e5c9d699d08c81b9410edd7dedfd diff --git a/external/google/gn b/external/google/gn new file mode 160000 index 000000000..6e0b557db --- /dev/null +++ b/external/google/gn @@ -0,0 +1 @@ +Subproject commit 6e0b557db44b3c164094e57687d20ba036a80667 diff --git a/external/linphone-sdk b/external/linphone-sdk index dc5d0d260..cbf282611 160000 --- a/external/linphone-sdk +++ b/external/linphone-sdk @@ -1 +1 @@ -Subproject commit dc5d0d260cc5db90dc4d7dcb49780a12aaada33b +Subproject commit cbf282611d1d39dac1405a5a9388de0e8ee5b9e5