From 13d2fefcd1b29372e14e9255032863fdecef959c Mon Sep 17 00:00:00 2001 From: Christophe Deschamps Date: Thu, 5 Sep 2024 06:28:09 +0000 Subject: [PATCH] Handle set of new configuration parameters --- Linphone/core/App.cpp | 168 ++++++++++++++++++++++ Linphone/core/App.hpp | 3 + Linphone/core/setting/SettingsCore.cpp | 13 ++ Linphone/core/setting/SettingsCore.hpp | 7 + Linphone/model/setting/SettingsModel.cpp | 126 ++++++++++++---- Linphone/model/setting/SettingsModel.hpp | 5 + Linphone/tool/AbstractObject.hpp | 21 ++- Linphone/view/App/Main.qml | 23 ++- Linphone/view/Page/Login/SIPLoginPage.qml | 14 +- 9 files changed, 345 insertions(+), 35 deletions(-) diff --git a/Linphone/core/App.cpp b/Linphone/core/App.cpp index cfa0b2fee..114ec56f7 100644 --- a/Linphone/core/App.cpp +++ b/Linphone/core/App.cpp @@ -85,10 +85,168 @@ DEFINE_ABSTRACT_OBJECT(App) #ifdef Q_OS_LINUX +const QString AutoStartDirectory(QDir::homePath().append(QStringLiteral("/.config/autostart/"))); const QString ApplicationsDirectory(QDir::homePath().append(QStringLiteral("/.local/share/applications/"))); const QString IconsDirectory(QDir::homePath().append(QStringLiteral("/.local/share/icons/hicolor/scalable/apps/"))); +#elif defined(Q_OS_MACOS) +const QString OsascriptExecutable(QStringLiteral("osascript")); +#else +const QString + AutoStartSettingsFilePath(QStringLiteral("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run")); #endif +// ----------------------------------------------------------------------------- +// Autostart +// ----------------------------------------------------------------------------- + +#ifdef Q_OS_LINUX +bool App::autoStartEnabled() { + const QString confPath(AutoStartDirectory + EXECUTABLE_NAME ".desktop"); + QFile file(confPath); + if (!QDir(AutoStartDirectory).exists() || !file.exists()) return false; + if (!file.open(QFile::ReadOnly)) { + qWarning() << "Unable to open autostart file in read only: `" << confPath << "`."; + return false; + } + + // Check if installation is done via Flatpak, AppImage, or classic package + // in order to check if there is a correct exec path for autostart + + QString exec = getApplicationPath(); + + QTextStream in(&file); + QString autoStartConf = in.readAll(); + + int index = -1; + // check if the Exec part of the autostart ini file not corresponding to our executable (old desktop entry with + // wrong version in filename) + if (autoStartConf.indexOf(QString("Exec=" + exec + " ")) < + 0) { // On autostart, there is the option --iconified so there is one space. + // replace file + setAutoStart(true); + } + + return true; +} +#elif defined(Q_OS_MACOS) +static inline QString getMacOsBundlePath() { + QDir dir(QCoreApplication::applicationDirPath()); + if (dir.dirName() != QLatin1String("MacOS")) return QString(); + + dir.cdUp(); + dir.cdUp(); + + QString path(dir.path()); + if (path.length() > 0 && path.right(1) == "/") path.chop(1); + return path; +} + +static inline QString getMacOsBundleName() { + return QFileInfo(getMacOsBundlePath()).baseName(); +} + +bool App::autoStartEnabled() { + const QByteArray expectedWord(getMacOsBundleName().toUtf8()); + if (expectedWord.isEmpty()) { + qInfo() << QStringLiteral("Application is not installed. Autostart unavailable."); + return false; + } + + QProcess process; + process.start(OsascriptExecutable, + {"-e", "tell application \"System Events\" to get the name of every login item"}); + if (!process.waitForFinished()) { + qWarning() << QStringLiteral("Unable to execute properly: `%1` (%2).") + .arg(OsascriptExecutable) + .arg(process.errorString()); + return false; + } + + // TODO: Move in utils? + const QByteArray buf(process.readAll()); + for (const char *p = buf.data(), *word = p, *end = p + buf.length(); p <= end; ++p) { + switch (*p) { + case ' ': + case '\r': + case '\n': + case '\t': + case '\0': + if (word != p) { + if (!strncmp(word, expectedWord, size_t(p - word))) return true; + word = p + 1; + } + default: + break; + } + } + + return false; +} +#else +bool App::autoStartEnabled() { + return QSettings(AutoStartSettingsFilePath, QSettings::NativeFormat).value(EXECUTABLE_NAME).isValid(); +} +#endif // ifdef Q_OS_LINUX + +#ifdef Q_OS_LINUX + +void App::setAutoStart(bool enabled) { + if (enabled == mAutoStart) return; + + QDir dir(AutoStartDirectory); + if (!dir.exists() && !dir.mkpath(AutoStartDirectory)) { + qWarning() << QStringLiteral("Unable to build autostart dir path: `%1`.").arg(AutoStartDirectory); + return; + } + + const QString confPath(AutoStartDirectory + EXECUTABLE_NAME ".desktop"); + if (generateDesktopFile(confPath, !enabled, true)) { + mAutoStart = enabled; + } +} + +#elif defined(Q_OS_MACOS) + +void App::setAutoStart(bool enabled) { + if (enabled == mAutoStart) return; + + if (getMacOsBundlePath().isEmpty()) { + qWarning() << QStringLiteral("Application is not installed. Unable to change autostart state."); + return; + } + + if (enabled) + QProcess::execute(OsascriptExecutable, + {"-e", "tell application \"System Events\" to make login item at end with properties" + "{ path: \"" + + getMacOsBundlePath() + "\", hidden: false }"}); + else + QProcess::execute(OsascriptExecutable, {"-e", "tell application \"System Events\" to delete login item \"" + + getMacOsBundleName() + "\""}); + + mAutoStart = enabled; +} + +#else + +void App::setAutoStart(bool enabled) { + if (enabled == mAutoStart) return; + + QSettings settings(AutoStartSettingsFilePath, QSettings::NativeFormat); + if (enabled) settings.setValue(EXECUTABLE_NAME, QDir::toNativeSeparators(applicationFilePath())); + else settings.remove(EXECUTABLE_NAME); + + mAutoStart = enabled; +} + +#endif // ifdef Q_OS_LINUX + +// ----------------------------------------------------------------------------- +// End Autostart +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- + App::App(int &argc, char *argv[]) : SingleApplication(argc, argv, true, Mode::User | Mode::ExcludeAppPath | Mode::ExcludeAppVersion) { // Do not use APPLICATION_NAME here. @@ -121,6 +279,8 @@ App::App(int &argc, char *argv[]) .arg(applicationVersion()) .arg(Utils::getOsProduct()) .arg(qVersion()); + + mAutoStart = autoStartEnabled(); } App::~App() { @@ -317,6 +477,10 @@ void App::initCore() { } }, Qt::QueuedConnection); + QObject::connect(mSettings.get(), &Settings::autoStartChanged, [this]() { + mustBeInMainThread(log().arg(Q_FUNC_INFO)); + setAutoStart(mSettings->getAutoStart()); + }); mEngine->load(url); }); // coreModel.reset(); @@ -665,3 +829,7 @@ bool App::event(QEvent *event) { } #endif + +//----------------------------------------------------------- +// AutoStart +//----------------------------------------------------------- diff --git a/Linphone/core/App.hpp b/Linphone/core/App.hpp index b6bd4557f..44d925474 100644 --- a/Linphone/core/App.hpp +++ b/Linphone/core/App.hpp @@ -103,6 +103,7 @@ public: void initCore(); void initCppInterfaces(); void restart(); + bool autoStartEnabled(); void onLoggerInitialized(); void sendCommand(); @@ -134,6 +135,7 @@ signals: private: void createCommandParser(); + void setAutoStart(bool enabled); QCommandLineParser *mParser = nullptr; Thread *mLinphoneThread = nullptr; @@ -143,6 +145,7 @@ private: QSharedPointer mSettings; QSharedPointer> mCoreModelConnection; QSharedPointer> mCliModelConnection; + bool mAutoStart = false; DECLARE_ABSTRACT_OBJECT }; diff --git a/Linphone/core/setting/SettingsCore.cpp b/Linphone/core/setting/SettingsCore.cpp index 66fa5537f..abe6072fd 100644 --- a/Linphone/core/setting/SettingsCore.cpp +++ b/Linphone/core/setting/SettingsCore.cpp @@ -87,6 +87,10 @@ Settings::Settings(QObject *parent) : QObject(parent) { INIT_CORE_MEMBER(OnlyDisplaySipUriUsername, mSettingsModel) INIT_CORE_MEMBER(DarkModeAllowed, mSettingsModel) INIT_CORE_MEMBER(MaxAccount, mSettingsModel) + INIT_CORE_MEMBER(AssistantGoDirectlyToThirdPartySipAccountLogin, mSettingsModel) + INIT_CORE_MEMBER(AssistantThirdPartySipAccountDomain, mSettingsModel) + INIT_CORE_MEMBER(AssistantThirdPartySipAccountTransport, mSettingsModel) + INIT_CORE_MEMBER(AutoStart, mSettingsModel) } Settings::~Settings() { @@ -294,6 +298,15 @@ void Settings::setSelf(QSharedPointer me) { DarkModeAllowed) DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, Settings, SettingsModel, mSettingsModel, int, maxAccount, MaxAccount) + DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, Settings, SettingsModel, mSettingsModel, bool, + assistantGoDirectlyToThirdPartySipAccountLogin, + AssistantGoDirectlyToThirdPartySipAccountLogin) + DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, Settings, SettingsModel, mSettingsModel, QString, + assistantThirdPartySipAccountDomain, AssistantThirdPartySipAccountDomain) + DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, Settings, SettingsModel, mSettingsModel, QString, + assistantThirdPartySipAccountTransport, AssistantThirdPartySipAccountTransport) + DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, Settings, SettingsModel, mSettingsModel, bool, autoStart, + AutoStart) auto coreModelConnection = QSharedPointer>( new SafeConnection(me, CoreModel::getInstance()), &QObject::deleteLater); diff --git a/Linphone/core/setting/SettingsCore.hpp b/Linphone/core/setting/SettingsCore.hpp index 50530525f..3e6767bb2 100644 --- a/Linphone/core/setting/SettingsCore.hpp +++ b/Linphone/core/setting/SettingsCore.hpp @@ -151,6 +151,13 @@ public: DECLARE_CORE_GETSET(bool, onlyDisplaySipUriUsername, OnlyDisplaySipUriUsername) DECLARE_CORE_GETSET(bool, darkModeAllowed, DarkModeAllowed) DECLARE_CORE_GETSET(int, maxAccount, MaxAccount) + DECLARE_CORE_GETSET(bool, + assistantGoDirectlyToThirdPartySipAccountLogin, + AssistantGoDirectlyToThirdPartySipAccountLogin) + DECLARE_CORE_GETSET(QString, assistantThirdPartySipAccountDomain, AssistantThirdPartySipAccountDomain) + DECLARE_CORE_GETSET(QString, assistantThirdPartySipAccountTransport, AssistantThirdPartySipAccountTransport) + DECLARE_CORE_GETSET(bool, autoStart, AutoStart) + bool getAutoStart() { return mAutoStart; }; signals: diff --git a/Linphone/model/setting/SettingsModel.cpp b/Linphone/model/setting/SettingsModel.cpp index 850a22765..78841ed1b 100644 --- a/Linphone/model/setting/SettingsModel.cpp +++ b/Linphone/model/setting/SettingsModel.cpp @@ -40,6 +40,23 @@ SettingsModel::SettingsModel(QObject *parent) : QObject(parent) { if (mConfig->hasEntry(UiSection, "do_not_disturb") == 1) { enableDnd(dndEnabled()); } + QObject::connect( + CoreModel::getInstance().get(), &CoreModel::globalStateChanged, this, + [this](const std::shared_ptr &core, linphone::GlobalState gstate, const std::string &message) { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + if (gstate == linphone::GlobalState::On) { // reached when misc|config-uri is set in config and app starts + // and after config is fetched. + notifyConfigReady(); + } + }); + QObject::connect(CoreModel::getInstance().get(), &CoreModel::configuringStatus, this, + [this](const std::shared_ptr &core, linphone::ConfiguringState status, + const std::string &message) { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + if (status == linphone::ConfiguringState::Successful) { + notifyConfigReady(); + } + }); } SettingsModel::~SettingsModel() { @@ -460,38 +477,91 @@ bool SettingsModel::getShowChats() const { return mConfig->getBool(UiSection, "disable_chat_feature", false); }*/ +void SettingsModel::notifyConfigReady(){ + DEFINE_NOTIFY_CONFIG_READY(assistantGoDirectlyToThirdPartySipAccountLogin, + AssistantGoDirectlyToThirdPartySipAccountLogin) + DEFINE_NOTIFY_CONFIG_READY(assistantThirdPartySipAccountDomain, AssistantThirdPartySipAccountDomain) + DEFINE_NOTIFY_CONFIG_READY(assistantThirdPartySipAccountTransport, AssistantThirdPartySipAccountTransport) + DEFINE_NOTIFY_CONFIG_READY(autoStart, AutoStart) +} + DEFINE_GETSET_CONFIG(SettingsModel, bool, Bool, disableChatFeature, DisableChatFeature, "disable_chat_feature", false) DEFINE_GETSET_CONFIG( - SettingsModel, bool, Bool, disableMeetingsFeature, DisableMeetingsFeature, "disable_meetings_feature", false) -DEFINE_GETSET_CONFIG( - SettingsModel, bool, Bool, disableBroadcastFeature, DisableBroadcastFeature, "disable_broadcast_feature", false) + SettingsModel, bool, Bool, disableMeetingsFeature, DisableMeetingsFeature, "disable_meetings_feature", false) +DEFINE_GETSET_CONFIG(SettingsModel, + bool, + Bool, + disableBroadcastFeature, + DisableBroadcastFeature, + "disable_broadcast_feature", + false) DEFINE_GETSET_CONFIG(SettingsModel, bool, Bool, hideSettings, HideSettings, "hide_settings", false) -DEFINE_GETSET_CONFIG( - SettingsModel, bool, Bool, hideAccountSettings, HideAccountSettings, "hide_account_settings", false) -DEFINE_GETSET_CONFIG( - SettingsModel, bool, Bool, disableCallRecordings, DisableCallRecordings, "disable_call_recordings_feature", false) +DEFINE_GETSET_CONFIG(SettingsModel, bool, Bool, hideAccountSettings, HideAccountSettings, "hide_account_settings", false) DEFINE_GETSET_CONFIG(SettingsModel, - bool, - Bool, - assistantHideCreateAccount, - AssistantHideCreateAccount, - "assistant_hide_create_account", - false) -DEFINE_GETSET_CONFIG( - SettingsModel, bool, Bool, assistantDisableQrCode, AssistantDisableQrCode, "assistant_disable_qr_code", true) + bool, + Bool, + disableCallRecordings, + DisableCallRecordings, + "disable_call_recordings_feature", + false) DEFINE_GETSET_CONFIG(SettingsModel, - bool, - Bool, - assistantHideThirdPartyAccount, - AssistantHideThirdPartyAccount, - "assistant_hide_third_party_account", - false) + bool, + Bool, + assistantHideCreateAccount, + AssistantHideCreateAccount, + "assistant_hide_create_account", + false) DEFINE_GETSET_CONFIG(SettingsModel, - bool, - Bool, - onlyDisplaySipUriUsername, - OnlyDisplaySipUriUsername, - "only_display_sip_uri_username", - false) -DEFINE_GETSET_CONFIG(SettingsModel, bool, Bool, darkModeAllowed, DarkModeAllowed, "dark_mode_allowed", false) + bool, + Bool, + assistantDisableQrCode, + AssistantDisableQrCode, + "assistant_disable_qr_code", + true) +DEFINE_GETSET_CONFIG(SettingsModel, + bool, + Bool, + assistantHideThirdPartyAccount, + AssistantHideThirdPartyAccount, + "assistant_hide_third_party_account", + false) +DEFINE_GETSET_CONFIG(SettingsModel, + bool, + Bool, + onlyDisplaySipUriUsername, + OnlyDisplaySipUriUsername, + "only_display_sip_uri_username", + false) +DEFINE_GETSET_CONFIG(SettingsModel, + bool, + Bool, + darkModeAllowed, + DarkModeAllowed, + "dark_mode_allowed", + false) DEFINE_GETSET_CONFIG(SettingsModel, int, Int, maxAccount, MaxAccount, "max_account", 0) +DEFINE_GETSET_CONFIG(SettingsModel, + bool, + Bool, + assistantGoDirectlyToThirdPartySipAccountLogin, + AssistantGoDirectlyToThirdPartySipAccountLogin, + "assistant_go_directly_to_third_party_sip_account_login", + false) +DEFINE_GETSET_CONFIG_STRING(SettingsModel, + assistantThirdPartySipAccountDomain, + AssistantThirdPartySipAccountDomain, + "assistant_third_party_sip_account_domain", + "") +DEFINE_GETSET_CONFIG_STRING(SettingsModel, + assistantThirdPartySipAccountTransport, + AssistantThirdPartySipAccountTransport, + "assistant_third_party_sip_account_transport", + "TLS") +DEFINE_GETSET_CONFIG(SettingsModel, + bool, + Bool, + autoStart, + AutoStart, + "auto_start", + false) + diff --git a/Linphone/model/setting/SettingsModel.hpp b/Linphone/model/setting/SettingsModel.hpp index 0756766de..4701019a2 100644 --- a/Linphone/model/setting/SettingsModel.hpp +++ b/Linphone/model/setting/SettingsModel.hpp @@ -136,6 +136,10 @@ public: DECLARE_GETSET(bool, onlyDisplaySipUriUsername, OnlyDisplaySipUriUsername) DECLARE_GETSET(bool, darkModeAllowed, DarkModeAllowed) DECLARE_GETSET(int, maxAccount, MaxAccount) + DECLARE_GETSET(bool, assistantGoDirectlyToThirdPartySipAccountLogin, AssistantGoDirectlyToThirdPartySipAccountLogin) + DECLARE_GETSET(QString, assistantThirdPartySipAccountDomain, AssistantThirdPartySipAccountDomain) + DECLARE_GETSET(QString, assistantThirdPartySipAccountTransport, AssistantThirdPartySipAccountTransport) + DECLARE_GETSET(bool, autoStart, AutoStart) signals: @@ -171,6 +175,7 @@ signals: void dndChanged(bool value); private: + void notifyConfigReady(); MediastreamerUtils::SimpleCaptureGraph *mSimpleCaptureGraph = nullptr; int mCaptureGraphListenerCount = 0; DECLARE_ABSTRACT_OBJECT diff --git a/Linphone/tool/AbstractObject.hpp b/Linphone/tool/AbstractObject.hpp index 4c429b93c..882ec9efc 100644 --- a/Linphone/tool/AbstractObject.hpp +++ b/Linphone/tool/AbstractObject.hpp @@ -79,8 +79,10 @@ public: [this](type data) { safe->invokeToModel([this, data]() { model->set##X(data); }); }); \ safe->makeConnectToModel(&ModelClass::x##Changed, [this](type data) { \ safe->invokeToCore([this, data]() { \ - m##X = data; \ - emit x##Changed(); \ + if (m##X != data) { \ + m##X = data; \ + emit x##Changed(); \ + } \ }); \ }); @@ -96,6 +98,21 @@ public: } \ } +#define DEFINE_GETSET_CONFIG_STRING(Class, x, X, key, def) \ + QString Class::get##X() const { \ + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); \ + return Utils::coreStringToAppString(mConfig->getString(UiSection, key, def)); \ + } \ + void Class::set##X(QString data) { \ + if (get##X() != data) { \ + mConfig->setString(UiSection, key, Utils::appStringToCoreString(data)); \ + emit x##Changed(data); \ + } \ + } + +#define DEFINE_NOTIFY_CONFIG_READY(x, X) \ + emit x##Changed(get##X()); + class AbstractObject { public: virtual QString getClassName() const = 0; diff --git a/Linphone/view/App/Main.qml b/Linphone/view/App/Main.qml index b023438f1..bf4f6948b 100644 --- a/Linphone/view/App/Main.qml +++ b/Linphone/view/App/Main.qml @@ -44,8 +44,23 @@ AppWindow { function initStackViewItem() { if (accountProxy.haveAccount) mainWindowStackView.replace(mainPage, StackView.Immediate) else if (SettingsCpp.getFirstLaunch()) mainWindowStackView.replace(welcomePage, StackView.Immediate) + else if (SettingsCpp.assistantGoDirectlyToThirdPartySipAccountLogin) mainWindowStackView.replace(sipLoginPage, StackView.Immediate) else mainWindowStackView.replace(loginPage, StackView.Immediate) } + + function goToLogin() { + if (SettingsCpp.assistantGoDirectlyToThirdPartySipAccountLogin) + mainWindowStackView.replace(sipLoginPage) + else + mainWindowStackView.replace(loginPage) + } + + Connections { + target: SettingsCpp + function onAssistantGoDirectlyToThirdPartySipAccountLoginChanged() { + initStackViewItem() + } + } AccountProxy { id: accountProxy @@ -69,7 +84,7 @@ AppWindow { id: welcomePage WelcomePage { onStartButtonPressed: { - mainWindowStackView.replace(loginPage)// Replacing the first item will destroy the old. + goToLogin() // Replacing the first item will destroy the old. SettingsCpp.setFirstLaunch(false) } } @@ -100,7 +115,7 @@ AppWindow { Component { id: registerPage RegisterPage { - onReturnToLogin: mainWindowStackView.replace(loginPage) + onReturnToLogin: goToLogin() onBrowserValidationRequested: mainWindow.showLoadingPopup(qsTr("Veuillez valider le captcha sur la page web"), true) Connections { target: RegisterPageCpp @@ -125,7 +140,7 @@ AppWindow { Connections { target: RegisterPageCpp function onLinkingNewAccountWithCodeSucceed() { - mainWindowStackView.replace(loginPage) + goToLogin() mainWindow.showInformationPopup(qsTr("Compte créé"), qsTr("Le compte a été créé avec succès. Vous pouvez maintenant vous connecter"), true) } function onLinkingNewAccountWithCodeFailed(errorMessage) { @@ -150,7 +165,7 @@ AppWindow { Component { id: mainPage MainLayout { - onAddAccountRequest: mainWindowStackView.replace(loginPage) + onAddAccountRequest: goToLogin() onAccountRemoved: { initStackViewItem() } diff --git a/Linphone/view/Page/Login/SIPLoginPage.qml b/Linphone/view/Page/Login/SIPLoginPage.qml index b62aa0b46..4322794b1 100644 --- a/Linphone/view/Page/Login/SIPLoginPage.qml +++ b/Linphone/view/Page/Login/SIPLoginPage.qml @@ -4,6 +4,7 @@ import QtQuick.Controls as Control import Linphone import ConstantsCpp import SettingsCpp +import 'qrc:/Linphone/view/Tool/utils.js' as Utils LoginLayout { id: mainItem @@ -30,6 +31,7 @@ LoginLayout { console.debug("[SIPLoginPage] User: return") mainItem.goBack() } + visible: !SettingsCpp.assistantGoDirectlyToThirdPartySipAccountLogin } Image { fillMode: Image.PreserveAspectFit @@ -210,10 +212,17 @@ LoginLayout { contentItem: TextField { id: domainEdit isError: domain.errorTextVisible + initialText: SettingsCpp.assistantThirdPartySipAccountDomain Layout.preferredWidth: 360 * DefaultStyle.dp KeyNavigation.up: passwordEdit KeyNavigation.down: displayName } + Connections { + target: SettingsCpp + function onAssistantThirdPartySipAccountDomainChanged() { + domain.resetText() + } + } } FormItemLayout { label: qsTr("Nom d'affichage") @@ -238,6 +247,9 @@ LoginLayout { {text: "TLS", value: LinphoneEnums.TransportType.Tls}, {text: "DTLS", value: LinphoneEnums.TransportType.Dtls} ] + currentIndex: Utils.findIndex(model, function (entry) { + return entry === SettingsCpp.assistantThirdPartySipAccountTransport.toUpperCase() + }) } } } @@ -333,7 +345,7 @@ LoginLayout { centerContent: [ Control.StackView { id: rootStackView - initialItem: firstItem + initialItem: SettingsCpp.assistantGoDirectlyToThirdPartySipAccountLogin ? secondItem : firstItem anchors.top: parent.top anchors.left: parent.left anchors.bottom: parent.bottom