diff --git a/.gitlab-ci-files/job-linux-desktop-centos7.yml b/.gitlab-ci-files/job-linux-desktop-centos7.yml
index 53c348da2..0d8d30694 100644
--- a/.gitlab-ci-files/job-linux-desktop-centos7.yml
+++ b/.gitlab-ci-files/job-linux-desktop-centos7.yml
@@ -100,6 +100,7 @@ job-centos7-ninja-gcc-package:
artifacts:
paths:
- build/OUTPUT/Packages/*.AppImage
+ when: always
expire_in: 1 week
#################################################
diff --git a/.gitlab-ci-files/job-macosx-desktop.yml b/.gitlab-ci-files/job-macosx-desktop.yml
index 4d5260866..d82dd6cce 100644
--- a/.gitlab-ci-files/job-macosx-desktop.yml
+++ b/.gitlab-ci-files/job-macosx-desktop.yml
@@ -97,6 +97,7 @@ job-macosx-makefile-package:
- codesign --options runtime,library --verbose -s "$MACOS_SIGNING_IDENTITY" OUTPUT/Packages/Linphone*.dmg
- ./../tools/app_notarization.sh
artifacts:
+ when: always
paths:
- build/OUTPUT/*
when: always
diff --git a/.gitlab-ci-files/job-windows-desktop.yml b/.gitlab-ci-files/job-windows-desktop.yml
index 13c34448b..9436b35ec 100644
--- a/.gitlab-ci-files/job-windows-desktop.yml
+++ b/.gitlab-ci-files/job-windows-desktop.yml
@@ -82,6 +82,7 @@ job-windows-vs2017-package:
artifacts:
paths:
- results\*
+ when: always
expire_in: 1 weeks
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 480068966..d2c0be0b4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -38,11 +38,12 @@ endforeach()
if(ENABLE_BUILD_VERBOSE)
message("User Args : ${USER_ARGS}")
endif()
-
-if(NOT CMAKE_GENERATOR_PLATFORM AND WIN32)
+if(WIN32)
if(NOT ${CMAKE_GENERATOR} MATCHES "Ninja")
- set(CMAKE_GENERATOR_PLATFORM "Win32")
- message(STATUS "Setting Platform to ${CMAKE_GENERATOR_PLATFORM}")
+ if(NOT CMAKE_GENERATOR_PLATFORM)
+ set(CMAKE_GENERATOR_PLATFORM "Win32")
+ message(STATUS "Setting Platform to ${CMAKE_GENERATOR_PLATFORM}")
+ endif()
endif()
endif()
diff --git a/linphone-app/CMakeLists.txt b/linphone-app/CMakeLists.txt
index 6cf94a4d9..71a41fbf4 100644
--- a/linphone-app/CMakeLists.txt
+++ b/linphone-app/CMakeLists.txt
@@ -89,7 +89,7 @@ if( WIN32)
endif()
set(CMAKE_INCLUDE_CURRENT_DIR ON)#useful for config.h
-set(QT5_PACKAGES Core Gui Quick Widgets QuickControls2 Svg LinguistTools Concurrent Network)
+set(QT5_PACKAGES Core Gui Quick Widgets QuickControls2 Svg LinguistTools Concurrent Network Test)
if (UNIX AND NOT APPLE)
list(APPEND QT5_PACKAGES DBus)
endif ()
diff --git a/linphone-app/assets/languages/en.ts b/linphone-app/assets/languages/en.ts
index f30489997..97ff91265 100644
--- a/linphone-app/assets/languages/en.ts
+++ b/linphone-app/assets/languages/en.ts
@@ -92,6 +92,22 @@
about
About
+
+ commandLineOptionFetchConfig
+ specify the %1 configuration file to be fetch. It will be merged with the current configuration.
+
+
+ commandLineOptionFetchConfigArg
+ url, path or file
+
+
+ commandLineOptionCall
+ make a call
+
+
+ commandLineOptionCallArg
+ sip address
+
AssistantAbstractView
diff --git a/linphone-app/assets/linphone.desktop.cmake b/linphone-app/assets/linphone.desktop.cmake
index 23f3b6b9f..22e4f821d 100644
--- a/linphone-app/assets/linphone.desktop.cmake
+++ b/linphone-app/assets/linphone.desktop.cmake
@@ -7,5 +7,5 @@ Exec=@EXECUTABLE_NAME@ %u
Icon=@EXECUTABLE_NAME@
Terminal=false
Categories=Network;Telephony;
-MimeType=x-scheme-handler/sip-linphone;x-scheme-handler/sip;x-scheme-handler/sips-linphone;x-scheme-handler/sips;x-scheme-handler/tel;x-scheme-handler/callto;
+MimeType=x-scheme-handler/sip-linphone;x-scheme-handler/sip;x-scheme-handler/sips-linphone;x-scheme-handler/sips;x-scheme-handler/tel;x-scheme-handler/callto;x-scheme-handler/linphone-config;
X-PulseAudio-Properties=media.role=phone
diff --git a/linphone-app/cmake_builder/linphone_package/macos/Info.plist.in b/linphone-app/cmake_builder/linphone_package/macos/Info.plist.in
index bab2f6e76..d0a3d98c8 100644
--- a/linphone-app/cmake_builder/linphone_package/macos/Info.plist.in
+++ b/linphone-app/cmake_builder/linphone_package/macos/Info.plist.in
@@ -45,6 +45,7 @@
sips-linphone
tel
callto
+ linphone-config
diff --git a/linphone-app/cmake_builder/linphone_package/windows/install.nsi.in b/linphone-app/cmake_builder/linphone_package/windows/install.nsi.in
index 866e925fc..f1cdc0ce7 100644
--- a/linphone-app/cmake_builder/linphone_package/windows/install.nsi.in
+++ b/linphone-app/cmake_builder/linphone_package/windows/install.nsi.in
@@ -19,6 +19,9 @@ WriteRegStr HKCR "sip" "URL Protocol" ""
WriteRegStr HKCR "sip-linphone" "" "URL:sip-linphone Protocol"
WriteRegStr HKCR "sip-linphone" "URL Protocol" ""
+WriteRegStr HKCR "linphone-config" "" "URL:linphone-config Protocol"
+WriteRegStr HKCR "linphone-config" "URL Protocol" ""
+
WriteRegStr HKCR "sips" "" "URL:sips Protocol"
WriteRegStr HKCR "sips" "URL Protocol" ""
@@ -62,6 +65,13 @@ WriteRegStr HKCR "@APPLICATION_NAME@.sips-linphone\Shell\Open" "" ""
WriteRegStr HKCR "@APPLICATION_NAME@.sips-linphone\Shell\Open\Command" "" "$INSTDIR\bin\@EXECUTABLE_NAME@.exe $\"%1$\""
WriteRegStr HKLM "SOFTWARE\@APPLICATION_VENDOR@\@APPLICATION_NAME@\Capabilities\URLAssociations" "sips-linphone" "@APPLICATION_NAME@.sips-linphone"
+## LINPHONE-CONFIG
+WriteRegStr HKCR "@APPLICATION_NAME@.linphone-config" "" "@APPLICATION_NAME@ linphone-config Protocol"
+WriteRegStr HKCR "@APPLICATION_NAME@.linphone-config\Shell" "" ""
+WriteRegStr HKCR "@APPLICATION_NAME@.linphone-config\Shell\Open" "" ""
+WriteRegStr HKCR "@APPLICATION_NAME@.linphone-config\Shell\Open\Command" "" "$INSTDIR\bin\@EXECUTABLE_NAME@.exe $\"%1$\""
+WriteRegStr HKLM "SOFTWARE\@APPLICATION_VENDOR@\@APPLICATION_NAME@\Capabilities\URLAssociations" "linphone-config" "@APPLICATION_NAME@.linphone-config"
+
## TEL
WriteRegStr HKCR "@APPLICATION_NAME@.tel" "" "@APPLICATION_NAME@ tel Protocol"
WriteRegStr HKCR "@APPLICATION_NAME@.tel\Shell" "" ""
diff --git a/linphone-app/cmake_builder/linphone_package/windows/uninstall.nsi.in b/linphone-app/cmake_builder/linphone_package/windows/uninstall.nsi.in
index 1f288ba0e..e7b3d0f82 100644
--- a/linphone-app/cmake_builder/linphone_package/windows/uninstall.nsi.in
+++ b/linphone-app/cmake_builder/linphone_package/windows/uninstall.nsi.in
@@ -17,6 +17,7 @@ DeleteRegKey HKCR "@APPLICATION_NAME@.sip"
DeleteRegKey HKCR "@APPLICATION_NAME@.sip-linphone"
DeleteRegKey HKCR "@APPLICATION_NAME@.sips"
DeleteRegKey HKCR "@APPLICATION_NAME@.sips-linphone"
+DeleteRegKey HKCR "@APPLICATION_NAME@.linphone-config"
DeleteRegKey HKCR "@APPLICATION_NAME@.tel"
DeleteRegKey HKCR "@APPLICATION_NAME@.callto"
diff --git a/linphone-app/src/app/App.cpp b/linphone-app/src/app/App.cpp
index 353fe126f..6ad57872f 100644
--- a/linphone-app/src/app/App.cpp
+++ b/linphone-app/src/app/App.cpp
@@ -156,18 +156,50 @@ static inline bool installLocale (App &app, QTranslator &translator, const QLoca
return translator.load(locale, LanguagePath) && app.installTranslator(&translator);
}
-static inline shared_ptr getConfigIfExists (const QCommandLineParser &parser) {
- string configPath(Paths::getConfigFilePath(parser.value("config"), false));
- if (!Paths::filePathExists(configPath))
- configPath.clear();
+static inline string getConfigPathIfExists (const QCommandLineParser &parser) {
+ QString filePath = parser.value("config");
+ string configPath;
+ if(!QUrl(filePath).isRelative()){
+ configPath = Utils::appStringToCoreString(FileDownloader::synchronousDownload(filePath, Utils::coreStringToAppString(Paths::getConfigDirPath(false)), true));
+ }
+ if( configPath == "")
+ configPath = Paths::getConfigFilePath(filePath, false);
+ if( configPath == "" )
+ configPath = Paths::getConfigFilePath("", false);
+ return configPath;
+}
+static inline shared_ptr getConfigIfExists (const string &configPath) {
string factoryPath(Paths::getFactoryConfigFilePath());
if (!Paths::filePathExists(factoryPath))
factoryPath.clear();
return linphone::Config::newWithFactory(configPath, factoryPath);
}
-
+bool App::setFetchConfig (QCommandLineParser *parser) {
+ bool fetched = false;
+ QString filePath = parser->value("fetch-config");
+ if( !filePath.isEmpty()){
+ if(QUrl(filePath).isRelative()){// this is a file path
+ filePath = Utils::coreStringToAppString(Paths::getConfigFilePath(filePath, false));
+ if(!filePath.isEmpty())
+ filePath = "file://"+filePath;
+ }
+ if(!filePath.isEmpty()){
+ auto instance = CoreManager::getInstance();
+ if(instance){
+ auto core = instance->getCore();
+ if(core){
+ filePath.replace('\\','/');
+ core->setProvisioningUri(Utils::appStringToCoreString(filePath));
+ parser->process(cleanParserKeys(parser, QStringList("fetch-config")));// Remove this parameter from the parser
+ fetched = true;
+ }
+ }
+ }
+ }
+ return fetched;
+}
// -----------------------------------------------------------------------------
App::App (int &argc, char *argv[]) : SingleApplication(argc, argv, true, Mode::User | Mode::ExcludeAppPath | Mode::ExcludeAppVersion) {
@@ -180,7 +212,7 @@ App::App (int &argc, char *argv[]) : SingleApplication(argc, argv, true, Mode::U
mParser->process(*this);
// Initialize logger.
- shared_ptr config = getConfigIfExists(*mParser);
+ shared_ptr config = getConfigIfExists(getConfigPathIfExists(*mParser));
Logger::init(config);
if (mParser->isSet("verbose"))
Logger::getInstance()->setVerbose(true);
@@ -195,7 +227,6 @@ App::App (int &argc, char *argv[]) : SingleApplication(argc, argv, true, Mode::U
initLocale(config);
if (mParser->isSet("help")) {
- createParser();
mParser->showHelp();
}
@@ -221,6 +252,30 @@ App::~App () {
// -----------------------------------------------------------------------------
+QStringList App::cleanParserKeys(QCommandLineParser * parser, QStringList keys){
+ QStringList oldArguments = parser->optionNames();
+ QStringList parameters;
+ parameters << "dummy";
+ for(int i = 0 ; i < oldArguments.size() ; ++i){
+ if( !keys.contains(oldArguments[i])){
+ if( mParser->value(oldArguments[i]).isEmpty())
+ parameters << "--"+oldArguments[i];
+ else
+ parameters << "--"+oldArguments[i]+"="+parser->value(oldArguments[i]);
+ }
+ }
+ return parameters;
+}
+
+void App::processArguments(QHash args){
+ QList keys = args.keys();
+ QStringList parameters = cleanParserKeys(mParser, keys);
+ for(auto i = keys.begin() ; i != keys.end() ; ++i){
+ parameters << "--"+(*i)+"="+args.value(*i);
+ }
+ mParser->process(parameters);
+}
+
static QQuickWindow *createSubWindow (QQmlApplicationEngine *engine, const char *path) {
qInfo() << QStringLiteral("Creating subwindow: `%1`.").arg(path);
@@ -243,18 +298,22 @@ static QQuickWindow *createSubWindow (QQmlApplicationEngine *engine, const char
// -----------------------------------------------------------------------------
void App::initContentApp () {
- shared_ptr config = getConfigIfExists(*mParser);
+ std::string configPath;
+ shared_ptr config;
bool mustBeIconified = false;
+ bool needRestart = true;
// Destroy qml components and linphone core if necessary.
if (mEngine) {
+ needRestart = false;
+ setFetchConfig(mParser);
setOpened(false);
qInfo() << QStringLiteral("Restarting app...");
delete mEngine;
mNotifier = nullptr;
mSystemTrayIcon = nullptr;
-
+ //
CoreManager::uninit();
removeTranslator(mTranslator);
removeTranslator(mDefaultTranslator);
@@ -262,8 +321,12 @@ void App::initContentApp () {
delete mDefaultTranslator;
mTranslator = new DefaultTranslator(this);
mDefaultTranslator = new DefaultTranslator(this);
+ configPath = getConfigPathIfExists(*mParser);
+ config = getConfigIfExists(configPath);
initLocale(config);
} else {
+ configPath = getConfigPathIfExists(*mParser);
+ config = getConfigIfExists(configPath);
// Update and download codecs.
VideoCodecsModel::updateCodecs();
VideoCodecsModel::downloadUpdatableCodecs(this);
@@ -289,18 +352,8 @@ void App::initContentApp () {
mColors->useConfig(config);
// Init core.
- CoreManager::init(this, mParser->value("config"));
+ CoreManager::init(this, Utils::coreStringToAppString(configPath));
- // Execute command argument if needed.
- if (!mEngine) {
- const QString commandArgument = getCommandArgument();
- if (!commandArgument.isEmpty()) {
- Cli::CommandFormat format;
- Cli::executeCommand(commandArgument, &format);
- if (format == Cli::UriFormat)
- mustBeIconified = true;
- }
- }
// Init engine content.
mEngine = new QQmlApplicationEngine();
@@ -343,12 +396,22 @@ void App::initContentApp () {
qFatal("Unable to open main window.");
QObject::connect(
- CoreManager::getInstance()->getHandlers().get(),
- &CoreHandlers::coreStarted,
- [this, mustBeIconified]() {
- openAppAfterInit(mustBeIconified);
+ CoreManager::getInstance(),
+ &CoreManager::coreStarted, CoreManager::getInstance(),
+ [this, mustBeIconified]() mutable {
+ if(CoreManager::getInstance()->started())
+ openAppAfterInit(mustBeIconified);
}
);
+
+ // Execute command argument if needed.
+ const QString commandArgument = getCommandArgument();
+ if (!commandArgument.isEmpty()) {
+ Cli::CommandFormat format;
+ Cli::executeCommand(commandArgument, &format);
+ if (format == Cli::UriFormat || format == Cli::UrlFormat )
+ mustBeIconified = true;
+ }
}
// -----------------------------------------------------------------------------
@@ -434,6 +497,8 @@ void App::createParser () {
{ "cli-help", tr("commandLineOptionCliHelp").replace("%1", APPLICATION_NAME) },
{ { "v", "version" }, tr("commandLineOptionVersion") },
{ "config", tr("commandLineOptionConfig").replace("%1", EXECUTABLE_NAME), tr("commandLineOptionConfigArg") },
+ { "fetch-config", tr("commandLineOptionFetchConfig").replace("%1", EXECUTABLE_NAME), tr("commandLineOptionFetchConfigArg") },
+ { { "c", "call" }, tr("commandLineOptionCall").replace("%1", EXECUTABLE_NAME),tr("commandLineOptionCallArg") },
#ifndef Q_OS_MACOS
{ "iconified", tr("commandLineOptionIconified") },
#endif // ifndef Q_OS_MACOS
@@ -782,14 +847,14 @@ void App::setAutoStart (bool enabled) {
void App::openAppAfterInit (bool mustBeIconified) {
qInfo() << QStringLiteral("Open " APPLICATION_NAME " app.");
-
+ auto coreManager = CoreManager::getInstance();
// Create other windows.
mCallsWindow = createSubWindow(mEngine, QmlViewCallsWindow);
mSettingsWindow = createSubWindow(mEngine, QmlViewSettingsWindow);
- QObject::connect(mSettingsWindow, &QWindow::visibilityChanged, this, [](QWindow::Visibility visibility) {
+ QObject::connect(mSettingsWindow, &QWindow::visibilityChanged, this, [coreManager](QWindow::Visibility visibility) {
if (visibility == QWindow::Hidden) {
qInfo() << QStringLiteral("Update nat policy.");
- shared_ptr core = CoreManager::getInstance()->getCore();
+ shared_ptr core = coreManager->getCore();
core->setNatPolicy(core->getNatPolicy());
}
});
@@ -802,16 +867,10 @@ void App::openAppAfterInit (bool mustBeIconified) {
qWarning("System tray not found on this system.");
else
setTrayIcon();
-
- if (!mustBeIconified)
- smartShowWindow(mainWindow);
- #else
- Q_UNUSED(mustBeIconified);
- smartShowWindow(mainWindow);
#endif // ifndef __APPLE__
// Display Assistant if it does not exist proxy config.
- if (CoreManager::getInstance()->getCore()->getProxyConfigList().empty())
+ if (coreManager->getCore()->getProxyConfigList().empty())
QMetaObject::invokeMethod(mainWindow, "setView", Q_ARG(QVariant, AssistantViewName), Q_ARG(QVariant, QString("")));
#ifdef ENABLE_UPDATE_CHECK
@@ -824,7 +883,36 @@ void App::openAppAfterInit (bool mustBeIconified) {
checkForUpdate();
#endif // ifdef ENABLE_UPDATE_CHECK
- setOpened(true);
+ if(setFetchConfig(mParser))
+ restart();
+ else{
+// Launch call if wanted and clean parser
+ if( mParser->isSet("call")){
+ QString sipAddress = mParser->value("call");
+ mParser->parse(cleanParserKeys(mParser, QStringList("call")));// Clean call from parser
+ if(coreManager->started()){
+ coreManager->getCallsListModel()->launchAudioCall(sipAddress);
+ }else{
+ QObject * context = new QObject();
+ QObject::connect(CoreManager::getInstance(), &CoreManager::coreStarted,context,
+ [sipAddress,coreManager, context]() mutable {
+ if(context){
+ delete context;
+ context = nullptr;
+ coreManager->getCallsListModel()->launchAudioCall(sipAddress);
+ }
+ });
+ }
+ }
+#ifndef __APPLE__
+ if (!mustBeIconified)
+ smartShowWindow(mainWindow);
+#else
+ Q_UNUSED(mustBeIconified);
+ smartShowWindow(mainWindow);
+#endif
+ setOpened(true);
+ }
}
// -----------------------------------------------------------------------------
diff --git a/linphone-app/src/app/App.hpp b/linphone-app/src/app/App.hpp
index c4422056e..d465f595d 100644
--- a/linphone-app/src/app/App.hpp
+++ b/linphone-app/src/app/App.hpp
@@ -55,9 +55,13 @@ public:
~App ();
void initContentApp ();
+ QStringList cleanParserKeys(QCommandLineParser * parser, QStringList keys);// Get all options from parser and remove the selected keys. Return the result that can be passed to parser process.
+ void processArguments(QHash args);
QString getCommandArgument ();
+ bool setFetchConfig (QCommandLineParser *parser);
+
#ifdef Q_OS_MACOS
bool event (QEvent *event) override;
#endif // ifdef Q_OS_MACOS
diff --git a/linphone-app/src/app/AppController.cpp b/linphone-app/src/app/AppController.cpp
index e502d9fdf..aea9bddb9 100644
--- a/linphone-app/src/app/AppController.cpp
+++ b/linphone-app/src/app/AppController.cpp
@@ -67,7 +67,17 @@ AppController::AppController (int &argc, char *argv[]) {
#endif // ifdef Q_OS_MACOS
QString command = mApp->getCommandArgument();
- mApp->sendMessage(command.isEmpty() ? "show" : command.toLocal8Bit(), -1);
+ if( command.isEmpty()){
+ command = "show";
+ QStringList parametersList;
+ for(int i = 1 ; i < argc ; ++i){
+ QString a = argv[i];
+ if(a.startsWith("--"))// show is a command : remove <-->-style parameters
+ a.remove(0,2);
+ command += " "+a;
+ }
+ }
+ mApp->sendMessage(command.toLocal8Bit(), -1);
return;
}
diff --git a/linphone-app/src/app/cli/Cli.cpp b/linphone-app/src/app/cli/Cli.cpp
index a3542a8ce..79b44d3b4 100644
--- a/linphone-app/src/app/cli/Cli.cpp
+++ b/linphone-app/src/app/cli/Cli.cpp
@@ -41,13 +41,24 @@ using namespace std;
// API.
// =============================================================================
-static void cliShow (QHash &) {
+static void cliShow (QHash &args) {
App *app = App::getInstance();
+ if( args.size() > 0){
+ app->processArguments(args);
+ app->initContentApp();
+ }
app->smartShowWindow(app->getMainWindow());
}
static void cliCall (QHash &args) {
- CoreManager::getInstance()->getCallsListModel()->launchAudioCall(args["sip-address"]);
+ if(args.size() > 1){// Call with options
+ App *app = App::getInstance();
+ args["call"] = args["sip-address"];// Swap cli def to parser
+ args.remove("sip-address");
+ app->processArguments(args);
+ app->initContentApp();
+ }else
+ CoreManager::getInstance()->getCallsListModel()->launchAudioCall(args["sip-address"]);
}
static void cliJoinConference (QHash &args) {
@@ -247,24 +258,26 @@ Cli::Command::Command (
const QString &functionName,
const char *functionDescription,
Cli::Function function,
- const QHash &argsScheme
+ const QHash &argsScheme,
+ const bool &genericArguments
) :
mFunctionName(functionName),
mFunctionDescription(functionDescription),
mFunction(function),
- mArgsScheme(argsScheme) {}
+ mArgsScheme(argsScheme),
+ mGenericArguments(genericArguments) {}
void Cli::Command::execute (QHash &args) const {
- // Check arguments validity.
- for (const auto &argName : args.keys()) {
- if (!mArgsScheme.contains(argName)) {
- qWarning() << QStringLiteral("Command with invalid argument: `%1 (%2)`.")
- .arg(mFunctionName).arg(argName);
-
- return;
+ if(!mGenericArguments){// Check arguments validity.
+ for (const auto &argName : args.keys()) {
+ if (!mArgsScheme.contains(argName)) {
+ qWarning() << QStringLiteral("Command with invalid argument: `%1 (%2)`.")
+ .arg(mFunctionName).arg(argName);
+
+ return;
+ }
}
}
-
// Check missing arguments.
for (const auto &argName : mArgsScheme.keys()) {
if (!mArgsScheme[argName].isOptional && (!args.contains(argName) || args[argName].isEmpty())) {
@@ -281,16 +294,38 @@ void Cli::Command::execute (QHash &args) const {
(*mFunction)(args);
} else {
Function f = mFunction;
- Utils::connectOnce(app, &App::opened, app, [f, args] {
- qInfo() << QStringLiteral("Execute deferred command:") << args;
- QHash fuckConst = args;
- (*f)(fuckConst);
- });
+ QObject * context = new QObject();
+ QObject::connect(app, &App::opened,
+ [f, args, context]()mutable {
+ if(context){
+ delete context;
+ context = nullptr;
+ qInfo() << QStringLiteral("Execute deferred command:") << args;
+ QHash fuckConst = args;
+ (*f)(fuckConst);
+ }
+ }
+ );
}
}
void Cli::Command::executeUri (const shared_ptr &address) const {
QHash args;
+ QString qAddress = Utils::coreStringToAppString(address->asString());
+ QUrl url(qAddress);
+ QString query = url.query();
+
+ QStringList parameters = query.split('&');
+ for(int i = 0 ; i < parameters.size() ; ++i){
+ QStringList parameter = parameters[i].split('=');
+ if( parameter[0] != "method"){
+ if(parameter.size() > 1)
+ args[parameter[0]] = QByteArray::fromBase64(parameter[1].toUtf8() );
+ else
+ args[parameter[0]] = "";
+ }
+ }
+
// TODO: check if there is too much headers.
for (const auto &argName : mArgsScheme.keys()) {
const string header = address->getHeader(Utils::appStringToCoreString(argName));
@@ -301,6 +336,25 @@ void Cli::Command::executeUri (const shared_ptr &address) con
execute(args);
}
+// pUrl can be `anytoken?p1=x&p2=y` or `p1=x&p2=y`. It will only use p1 and p2
+void Cli::Command::executeUrl (const QString &pUrl) const {
+ QHash args;
+ QUrl url(pUrl);
+ QString query = (url.hasQuery()?url.query():pUrl);
+
+ QStringList parameters = query.split('&');
+ for(int i = 0 ; i < parameters.size() ; ++i){
+ QStringList parameter = parameters[i].split('=');
+ if( parameter[0] != "method"){
+ if(parameter.size() > 1)
+ args[parameter[0]] = QByteArray::fromBase64(parameter[1].toUtf8() );
+ else
+ args[parameter[0]] = "";
+ }
+ }
+ execute(args);
+}
+
QString Cli::Command::getFunctionSyntax () const {
QString functionSyntax;
functionSyntax += QStringLiteral("\"");
@@ -333,10 +387,10 @@ QRegExp Cli::mRegExpArgs("(?:(?:([\\w-]+)\\s*)=\\s*(?:\"([^\"\\\\]*(?:\\\\.[^\"\
QRegExp Cli::mRegExpFunctionName("^\\s*([a-z-]+)\\s*");
QMap Cli::mCommands = {
- createCommand("show", QT_TR_NOOP("showFunctionDescription"), cliShow),
+ createCommand("show", QT_TR_NOOP("showFunctionDescription"), cliShow, QHash(), true),
createCommand("call", QT_TR_NOOP("callFunctionDescription"), cliCall, {
{ "sip-address", {} }
- }),
+ }, true),
createCommand("initiate-conference", QT_TR_NOOP("initiateConferenceFunctionDescription"), cliInitiateConference, {
{ "sip-address", {} }, { "conference-id", {} }
}),
@@ -364,43 +418,52 @@ void Cli::executeCommand (const QString &command, CommandFormat *format) {
QHash args = parseArgs(command);
mCommands[functionName].execute(args);
}
-
if (format)
*format = CliFormat;
-
return;
+ }else{
+ string scheme = address->getScheme();
+ bool ok = false;
+ for (const string &validScheme : { "sip", "sip-linphone", "sips", "sips-linphone", "tel", "callto", "linphone-config" })
+ if (scheme == validScheme)
+ ok = true;
+ if( !ok){
+ qWarning() << QStringLiteral("Not a valid uri: `%1` Unsupported scheme: `%2`.").arg(command).arg(Utils::coreStringToAppString(scheme));
+ return;
+ }else{
+ if( scheme == "linphone-config" ){
+ QHash args = parseArgs(command);
+ if(args.contains("fetch-config"))
+ args["fetch-config"] = QByteArray::fromBase64(args["fetch-config"].toUtf8() );
+ else {
+ QUrl url(command);
+ url.setScheme("https");
+ args["fetch-config"] = url.toString();
+ }
+ if (format)
+ *format = CliFormat;
+ mCommands["show"].execute(args);
+ }else{
+ if (format)
+ *format = UriFormat;
+ // Execute uri command.
+ qInfo() << QStringLiteral("Detecting uri command: `%1`...").arg(command);
+ if (address->getUsername().empty()) {
+ qWarning() << QStringLiteral("Failed to execute command. No username given.");
+ return;
+ }
+ const QString functionName = Utils::coreStringToAppString(address->getHeader("method")).isEmpty()
+ ? QStringLiteral("call")
+ : Utils::coreStringToAppString(address->getHeader("method"));
+
+ if (!functionName.isEmpty() && !mCommands.contains(functionName)) {
+ qWarning() << QStringLiteral("This command doesn't exist: `%1`.").arg(functionName);
+ return;
+ }
+ mCommands[functionName].executeUri(address);
+ }
+ }
}
-
- if (format)
- *format = UriFormat;
-
- // Execute uri command.
- qInfo() << QStringLiteral("Detecting uri command: `%1`...").arg(command);
-
- if (address->getUsername().empty()) {
- qWarning() << QStringLiteral("Failed to execute command. No username given.");
- return;
- }
-
- string scheme = address->getScheme();
- for (const string &validScheme : { "sip", "sip-linphone", "sips", "sips-linphone", "tel", "callto" })
- if (scheme == validScheme)
- goto success;
- qWarning() << QStringLiteral("Not a valid uri: `%1` Unsupported scheme: `%2`.")
- .arg(command).arg(Utils::coreStringToAppString(scheme));
- return;
-
-success:
- const QString functionName = Utils::coreStringToAppString(address->getHeader("method")).isEmpty()
- ? QStringLiteral("call")
- : Utils::coreStringToAppString(address->getHeader("method"));
-
- if (!functionName.isEmpty() && !mCommands.contains(functionName)) {
- qWarning() << QStringLiteral("This command doesn't exist: `%1`.").arg(functionName);
- return;
- }
-
- mCommands[functionName].executeUri(address);
}
void Cli::showHelp () {
@@ -425,9 +488,10 @@ pair Cli::createCommand (
const QString &functionName,
const char *functionDescription,
Function function,
- const QHash &argsScheme
+ const QHash &argsScheme,
+ const bool &genericArguments
) {
- return { functionName, Cli::Command(functionName, functionDescription, function, argsScheme) };
+ return { functionName, Cli::Command(functionName, functionDescription, function, argsScheme, genericArguments) };
}
// -----------------------------------------------------------------------------
diff --git a/linphone-app/src/app/cli/Cli.hpp b/linphone-app/src/app/cli/Cli.hpp
index d84c0abed..b3f52a11d 100644
--- a/linphone-app/src/app/cli/Cli.hpp
+++ b/linphone-app/src/app/cli/Cli.hpp
@@ -59,11 +59,13 @@ class Cli : public QObject {
const QString &functionName,
const char *functionDescription,
Function function,
- const QHash &argsScheme
+ const QHash &argsScheme,
+ const bool &genericArguments=false
);
void execute (QHash &args) const;
void executeUri (const std::shared_ptr &address) const;
+ void executeUrl (const QString &url) const;
const char *getFunctionDescription () const {
return mFunctionDescription;
@@ -76,13 +78,15 @@ class Cli : public QObject {
const char *mFunctionDescription;
Function mFunction = nullptr;
QHash mArgsScheme;
+ bool mGenericArguments=false;// Used to avoid check on arguments
};
public:
enum CommandFormat {
UnknownFormat,
CliFormat,
- UriFormat
+ UriFormat, // Parameters are in base64
+ UrlFormat
};
static void executeCommand (const QString &command, CommandFormat *format = nullptr);
@@ -96,7 +100,8 @@ private:
const QString &functionName,
const char *functionDescription,
Function function,
- const QHash &argsScheme = QHash()
+ const QHash &argsScheme = QHash(),
+ const bool &genericArguments=false
);
static QString parseFunctionName (const QString &command);
diff --git a/linphone-app/src/app/paths/Paths.cpp b/linphone-app/src/app/paths/Paths.cpp
index b4c03d201..485bd828b 100644
--- a/linphone-app/src/app/paths/Paths.cpp
+++ b/linphone-app/src/app/paths/Paths.cpp
@@ -215,11 +215,25 @@ string Paths::getCodecsDirPath () {
return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + PathCodecs);
}
-string Paths::getConfigFilePath (const QString &configPath, bool writable) {
- const QString path = configPath.isEmpty()
- ? getAppConfigFilePath()
- : QFileInfo(configPath).absoluteFilePath();
+string Paths::getConfigDirPath (bool writable) {
+ return writable ? getWritableFilePath(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation)+QDir::separator()) : getReadableFilePath(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation)+QDir::separator());
+}
+string Paths::getConfigFilePath (const QString &configPath, bool writable) {
+ QString path;
+ if( !configPath.isEmpty()){
+ QFileInfo file(configPath);
+ if( !writable && (!file.exists() || !file.isFile())){// This file cannot be found. Check if it exists in standard folder
+ QString defaultConfigPath = Utils::coreStringToAppString(getConfigDirPath(false));
+ file = QFileInfo(defaultConfigPath+QDir::separator()+configPath);
+ if( !file.exists() || !file.isFile())
+ path = "";
+ else
+ path = file.absoluteFilePath();
+ }else
+ path = file.absoluteFilePath();
+ }else
+ path = getAppConfigFilePath();
return writable ? getWritableFilePath(path) : getReadableFilePath(path);
}
diff --git a/linphone-app/src/app/paths/Paths.hpp b/linphone-app/src/app/paths/Paths.hpp
index c08296efd..54c8e34a5 100644
--- a/linphone-app/src/app/paths/Paths.hpp
+++ b/linphone-app/src/app/paths/Paths.hpp
@@ -33,6 +33,7 @@ namespace Paths {
std::string getCallHistoryFilePath ();
std::string getCapturesDirPath ();
std::string getCodecsDirPath ();
+ std::string getConfigDirPath (bool writable = true);
std::string getConfigFilePath (const QString &configPath = QString(), bool writable = true);
std::string getDownloadDirPath ();
std::string getFactoryConfigFilePath ();
diff --git a/linphone-app/src/components/calls/CallsListModel.cpp b/linphone-app/src/components/calls/CallsListModel.cpp
index d4f9e2bdc..5f9b47f0b 100644
--- a/linphone-app/src/components/calls/CallsListModel.cpp
+++ b/linphone-app/src/components/calls/CallsListModel.cpp
@@ -113,7 +113,23 @@ void CallsListModel::launchAudioCall (const QString &sipAddress, const QHashsetProxyConfig(core->getDefaultProxyConfig());
CallModel::setRecordFile(params, Utils::coreStringToAppString(address->getUsername()));
- core->inviteAddressWithParams(address, params);
+ shared_ptr currentProxyConfig = core->getDefaultProxyConfig();
+ if(currentProxyConfig){
+ if(currentProxyConfig->getState() == linphone::RegistrationState::Ok)
+ core->inviteAddressWithParams(address, params);
+ else{
+ QObject * context = new QObject();
+ QObject::connect(CoreManager::getInstance()->getHandlers().get(), &CoreHandlers::registrationStateChanged,context,
+ [address,core,params,currentProxyConfig, context](const std::shared_ptr &proxyConfig, linphone::RegistrationState state) mutable {
+ if(context && proxyConfig==currentProxyConfig && state==linphone::RegistrationState::Ok){
+ delete context;
+ context = nullptr;
+ core->inviteAddressWithParams(address, params);
+ }
+ });
+ }
+ }else
+ core->inviteAddressWithParams(address, params);
}
void CallsListModel::launchVideoCall (const QString &sipAddress) const {
diff --git a/linphone-app/src/components/core/CoreHandlers.cpp b/linphone-app/src/components/core/CoreHandlers.cpp
index ad77ad1e5..5ecb295c7 100644
--- a/linphone-app/src/components/core/CoreHandlers.cpp
+++ b/linphone-app/src/components/core/CoreHandlers.cpp
@@ -56,6 +56,7 @@ CoreHandlers::CoreHandlers (CoreManager *coreManager) {
CoreHandlers::~CoreHandlers () {
delete mCoreStartedLock;
+ mCoreStartedLock = nullptr;
}
// -----------------------------------------------------------------------------
@@ -66,7 +67,6 @@ void CoreHandlers::handleCoreCreated () {
Q_ASSERT(mCoreCreated == false);
mCoreCreated = true;
notifyCoreStarted();
-
mCoreStartedLock->unlock();
}
diff --git a/linphone-app/src/components/core/CoreManager.cpp b/linphone-app/src/components/core/CoreManager.cpp
index 25307bd00..5ccda1a7b 100644
--- a/linphone-app/src/components/core/CoreManager.cpp
+++ b/linphone-app/src/components/core/CoreManager.cpp
@@ -24,7 +24,7 @@
#include
#include
#include
-
+#include
#include "config.h"
#include "app/paths/Paths.hpp"
@@ -79,6 +79,7 @@ CoreManager::CoreManager (QObject *parent, const QString &configPath) :
createLinphoneCore(configPath);
qInfo() << QStringLiteral("Core created. Enable iterate.");
mInstance->mCbsTimer->start();
+ std::shared_ptr h = mInstance->getHandlers();// Protect handler as we will enter its function where it can be deleted (like while restarting)
emit mInstance->coreCreated();
});
@@ -171,8 +172,17 @@ void CoreManager::init (QObject *parent, const QString &configPath) {
void CoreManager::uninit () {
if (mInstance) {
- delete mInstance;
- mInstance = nullptr;
+ connect(mInstance, &QObject::destroyed, []()mutable{
+ mInstance = nullptr;
+ qInfo() << "Linphone Core is destroyed";
+ });
+ mInstance->mCbsTimer->stop();
+ mInstance->deleteLater();// Ensure to take time to delete the instance
+ QTest::qWaitFor([&]() {return mInstance == nullptr;},10000);
+ if( mInstance){
+ qWarning() << "Linphone Core couldn't destroy in time. It may lead to have multiple session of Linphone Core";
+ mInstance = nullptr;
+ }
}
}
diff --git a/linphone-app/src/components/core/CoreManager.hpp b/linphone-app/src/components/core/CoreManager.hpp
index 0f5544a43..8f65d3c89 100644
--- a/linphone-app/src/components/core/CoreManager.hpp
+++ b/linphone-app/src/components/core/CoreManager.hpp
@@ -55,7 +55,6 @@ public:
}
std::shared_ptr getCore () {
- Q_CHECK_PTR(mCore);
return mCore;
}
@@ -110,6 +109,11 @@ public:
return mAccountSettingsModel;
}
+ static CoreManager *getInstance () {
+ Q_CHECK_PTR(mInstance);
+ return mInstance;
+ }
+
// ---------------------------------------------------------------------------
// Initialization.
// ---------------------------------------------------------------------------
@@ -117,11 +121,6 @@ public:
static void init (QObject *parent, const QString &configPath);
static void uninit ();
- static CoreManager *getInstance () {
- Q_CHECK_PTR(mInstance);
- return mInstance;
- }
-
// ---------------------------------------------------------------------------
// Must be used in a qml scene.
@@ -136,6 +135,8 @@ public:
int getMissedCallCount(const QString &peerAddress, const QString &localAddress) const;// Get missed call count from a chat (useful for showing bubbles on Timelines)
int getMissedCallCountFromLocal(const QString &localAddress) const;// Get missed call count from a chat (useful for showing bubbles on Timelines)
+ static bool isInstanciated(){return mInstance!=nullptr;}
+
signals:
void coreCreated ();
void coreStarted ();
diff --git a/linphone-app/src/components/core/event-count-notifier/EventCountNotifierSystemTrayIcon.cpp b/linphone-app/src/components/core/event-count-notifier/EventCountNotifierSystemTrayIcon.cpp
index 2499c741d..372b057a4 100644
--- a/linphone-app/src/components/core/event-count-notifier/EventCountNotifierSystemTrayIcon.cpp
+++ b/linphone-app/src/components/core/event-count-notifier/EventCountNotifierSystemTrayIcon.cpp
@@ -115,7 +115,7 @@ void EventCountNotifier::notifyEventCount (int n) {
void EventCountNotifier::update () {
QSystemTrayIcon *sysTrayIcon = App::getInstance()->getSystemTrayIcon();
- Q_CHECK_PTR(sysTrayIcon);
- sysTrayIcon->setIcon(QIcon(mDisplayCounter ? *mBufWithCounter : *mBuf));
+ if(sysTrayIcon)
+ sysTrayIcon->setIcon(QIcon(mDisplayCounter ? *mBufWithCounter : *mBuf));
mDisplayCounter = !mDisplayCounter;
}
diff --git a/linphone-app/src/components/file/FileDownloader.cpp b/linphone-app/src/components/file/FileDownloader.cpp
index 331596531..55fe1de9f 100644
--- a/linphone-app/src/components/file/FileDownloader.cpp
+++ b/linphone-app/src/components/file/FileDownloader.cpp
@@ -18,6 +18,7 @@
* along with this program. If not, see .
*/
+#include
#include "app/paths/Paths.hpp"
#include "components/core/CoreManager.hpp"
#include "components/settings/SettingsModel.hpp"
@@ -31,13 +32,15 @@ namespace {
constexpr char cDefaultFileName[] = "download";
}
-static QString getDownloadFilePath (const QString &folder, const QUrl &url) {
+static QString getDownloadFilePath (const QString &folder, const QUrl &url, const bool& overwrite) {
QFileInfo fileInfo(url.path());
QString fileName = fileInfo.fileName();
if (fileName.isEmpty())
fileName = cDefaultFileName;
fileName.prepend(folder);
+ if( overwrite && QFile::exists(fileName))
+ QFile::remove(fileName);
if (!QFile::exists(fileName))
return fileName;
@@ -89,12 +92,15 @@ void FileDownloader::download () {
#endif
if (mDownloadFolder.isEmpty()) {
- mDownloadFolder = CoreManager::getInstance()->getSettingsModel()->getDownloadFolder();
+ if(CoreManager::isInstanciated())
+ mDownloadFolder = CoreManager::getInstance()->getSettingsModel()->getDownloadFolder();
+ else
+ mDownloadFolder = QDir::cleanPath(Utils::coreStringToAppString(Paths::getDownloadDirPath ()) + QDir::separator());
emit downloadFolderChanged(mDownloadFolder);
}
Q_ASSERT(!mDestinationFile.isOpen());
- mDestinationFile.setFileName(getDownloadFilePath(QDir::cleanPath(mDownloadFolder) + QDir::separator(), mUrl));
+ mDestinationFile.setFileName(getDownloadFilePath(QDir::cleanPath(mDownloadFolder) + QDir::separator(), mUrl, mOverwriteFile));
if (!mDestinationFile.open(QIODevice::WriteOnly))
emitOutputError();
else {
@@ -210,6 +216,43 @@ void FileDownloader::setDownloadFolder (const QString &downloadFolder) {
}
}
+QString FileDownloader::getDestinationFileName () const{
+ return mDestinationFile.fileName();
+}
+
+void FileDownloader::setOverwriteFile(const bool &overwrite){
+ mOverwriteFile = overwrite;
+}
+
+QString FileDownloader::synchronousDownload(const QUrl &url, const QString &destinationFolder, const bool &overwriteFile){
+ QString filePath;
+ FileDownloader downloader;
+ if(url.isRelative())
+ qWarning() << "FileDownloader: The specified URL is not valid";
+ else{
+ bool isOver = false;
+ bool * pIsOver = &isOver;
+ downloader.setUrl(url);
+ downloader.setOverwriteFile(overwriteFile);
+ downloader.setDownloadFolder(destinationFolder);
+ connect(&downloader, &FileDownloader::downloadFinished, [pIsOver]()mutable{
+ *pIsOver=true;
+ });
+ connect(&downloader, &FileDownloader::downloadFailed, [pIsOver]()mutable{
+ *pIsOver=true;
+ });
+ downloader.download();
+ if(QTest::qWaitFor([&]() {return isOver;}, DefaultTimeout)){
+ filePath = downloader.getDestinationFileName();
+ if(!QFile::exists(filePath)) {
+ filePath = "";
+ qWarning() << "FileDownloader: Cannot download the specified file";
+ }
+ }
+ }
+ return filePath;
+}
+
qint64 FileDownloader::getReadBytes () const {
return mReadBytes;
}
diff --git a/linphone-app/src/components/file/FileDownloader.hpp b/linphone-app/src/components/file/FileDownloader.hpp
index 782768bd0..6a203783d 100644
--- a/linphone-app/src/components/file/FileDownloader.hpp
+++ b/linphone-app/src/components/file/FileDownloader.hpp
@@ -23,12 +23,13 @@
#include
#include
+#include
// =============================================================================
class QSslError;
-class FileDownloader : public QObject {
+class FileDownloader : public QObject{
Q_OBJECT;
// TODO: Add an error property to use in UI.
@@ -57,6 +58,11 @@ public:
QString getDownloadFolder () const;
void setDownloadFolder (const QString &downloadFolder);
+ QString getDestinationFileName () const;
+
+ void setOverwriteFile(const bool &overwrite);
+ static QString synchronousDownload(const QUrl &url, const QString &destinationFolder, const bool &overwriteFile);// Return the filpath. Empty if nof file could be downloaded
+
signals:
void urlChanged (const QUrl &url);
void downloadFolderChanged (const QString &downloadFolder);
@@ -95,6 +101,7 @@ private:
qint64 mReadBytes = 0;
qint64 mTotalBytes = 0;
bool mDownloading = false;
+ bool mOverwriteFile = false;
QPointer mNetworkReply;
QNetworkAccessManager mManager;
diff --git a/linphone-app/src/components/settings/SettingsModel.cpp b/linphone-app/src/components/settings/SettingsModel.cpp
index 1fc951a20..e9900d9e8 100644
--- a/linphone-app/src/components/settings/SettingsModel.cpp
+++ b/linphone-app/src/components/settings/SettingsModel.cpp
@@ -263,37 +263,51 @@ QStringList SettingsModel::getPlaybackDevices () const {
// -----------------------------------------------------------------------------
QString SettingsModel::getCaptureDevice () const {
- return Utils::coreStringToAppString(
- CoreManager::getInstance()->getCore()->getCaptureDevice()
- );
+ auto audioDevice = CoreManager::getInstance()->getCore()->getInputAudioDevice();
+ return Utils::coreStringToAppString(audioDevice? audioDevice->getId() : CoreManager::getInstance()->getCore()->getCaptureDevice());
}
void SettingsModel::setCaptureDevice (const QString &device) {
- CoreManager::getInstance()->getCore()->setCaptureDevice(
- Utils::appStringToCoreString(device)
- );
- emit captureDeviceChanged(device);
- if (mSimpleCaptureGraph && mSimpleCaptureGraph->isRunning()) {
- createCaptureGraph();
- }
+ std::string devId = Utils::appStringToCoreString(device);
+ auto list = CoreManager::getInstance()->getCore()->getExtendedAudioDevices();
+ auto audioDevice = find_if(list.cbegin(), list.cend(), [&] ( const std::shared_ptr & audioItem) {
+ return audioItem->getId() == devId;
+ });
+ if(audioDevice != list.cend()){
+ CoreManager::getInstance()->getCore()->setCaptureDevice(devId);
+ CoreManager::getInstance()->getCore()->setInputAudioDevice(*audioDevice);
+ emit captureDeviceChanged(device);
+ if (mSimpleCaptureGraph && mSimpleCaptureGraph->isRunning()) {
+ createCaptureGraph();
+ }
+ }else
+ qWarning() << "Cannot set Capture device. The ID cannot be matched with an existant device : " << device;
}
// -----------------------------------------------------------------------------
QString SettingsModel::getPlaybackDevice () const {
- return Utils::coreStringToAppString(
- CoreManager::getInstance()->getCore()->getPlaybackDevice()
- );
+ auto audioDevice = CoreManager::getInstance()->getCore()->getOutputAudioDevice();
+ return Utils::coreStringToAppString(audioDevice? audioDevice->getId() : CoreManager::getInstance()->getCore()->getPlaybackDevice());
}
void SettingsModel::setPlaybackDevice (const QString &device) {
- CoreManager::getInstance()->getCore()->setPlaybackDevice(
- Utils::appStringToCoreString(device)
- );
- emit playbackDeviceChanged(device);
- if (mSimpleCaptureGraph && mSimpleCaptureGraph->isRunning()) {
- createCaptureGraph();
- }
+ std::string devId = Utils::appStringToCoreString(device);
+
+ auto list = CoreManager::getInstance()->getCore()->getExtendedAudioDevices();
+ auto audioDevice = find_if(list.cbegin(), list.cend(), [&] ( const std::shared_ptr & audioItem) {
+ return audioItem->getId() == devId;
+ });
+ if(audioDevice != list.cend()){
+
+ CoreManager::getInstance()->getCore()->setPlaybackDevice(devId);
+ CoreManager::getInstance()->getCore()->setOutputAudioDevice(*audioDevice);
+ emit playbackDeviceChanged(device);
+ if (mSimpleCaptureGraph && mSimpleCaptureGraph->isRunning()) {
+ createCaptureGraph();
+ }
+ }else
+ qWarning() << "Cannot set Playback device. The ID cannot be matched with an existant device : " << device;
}
// -----------------------------------------------------------------------------
diff --git a/linphone-sdk b/linphone-sdk
index c8700f691..3609b3815 160000
--- a/linphone-sdk
+++ b/linphone-sdk
@@ -1 +1 @@
-Subproject commit c8700f691113dc7771d24e80feb339e1cf0970a0
+Subproject commit 3609b38153ce4b715e93389673538003425cbac2