diff --git a/applet/contents/ui/Toolbar.qml b/applet/contents/ui/Toolbar.qml --- a/applet/contents/ui/Toolbar.qml +++ b/applet/contents/ui/Toolbar.qml @@ -21,6 +21,7 @@ import QtQuick 2.2 import QtQuick.Layouts 1.2 import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.components 3.0 as PlasmaComponents3 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.networkmanagement 0.2 as PlasmaNM import org.kde.kquickcontrolsaddons 2.0 @@ -60,6 +61,10 @@ } } + PlasmaNM.Configuration { + id: configuration + } + Row { Layout.fillWidth: true @@ -124,19 +129,79 @@ Row { Layout.column: 1 - PlasmaComponents.ToolButton { + + PlasmaComponents3.ToolButton { + id: hotspotButton + + icon { + height: 16 + width: 16 + name: "network-wireless-on" + } + checkable: true + text: i18n("Hotspot") + visible: handler.hotspotSupported + + onClicked: { + if (configuration.hotspotConnectionPath) { + checked = false + handler.stopHotspot() + } else { + checked = true + handler.createHotspot() + } + } + + PlasmaComponents3.ToolTip { + id: tooltip + } + + Connections { + target: handler + onHotspotCreated: { + hotspotButton.checked = true + tooltip.text = i18n("Disable Hotspot") + } + + onHotspotDisabled: { + hotspotButton.checked = false + tooltip.text = i18n("Create Hotspot") + } + } + + Component.onCompleted: { + checked = configuration.hotspotConnectionPath + tooltip.text = configuration.hotspotConnectionPath ? i18n("Disable Hotspot") : i18n("Create Hotspot") + } + } + + PlasmaComponents3.ToolButton { id: searchToggleButton - iconSource: "search" - tooltip: i18ndc("plasma-nm", "button tooltip", "Search the connections") + icon { + height: 16 + width: 16 + name: "search" + } checkable: true + + PlasmaComponents3.ToolTip { + text: i18ndc("plasma-nm", "button tooltip", "Search the connections") + } } - PlasmaComponents.ToolButton { + PlasmaComponents3.ToolButton { id: openEditorButton - iconSource: "configure" - tooltip: i18n("Configure network connections...") + icon { + height: 16 + width: 16 + name: "configure" + } visible: mainWindow.kcmAuthorized + PlasmaComponents3.ToolTip { + text: i18n("Configure network connections...") + } + onClicked: { KCMShell.open(mainWindow.kcm) } diff --git a/kcm/kcm.cpp b/kcm/kcm.cpp --- a/kcm/kcm.cpp +++ b/kcm/kcm.cpp @@ -77,13 +77,45 @@ kdeclarative.setupEngine(m_ui->connectionView->engine()); kdeclarative.setupContext(); + // Check if we can use AP mode to identify security type + bool useApMode = false; + bool foundInactive = false; + + NetworkManager::WirelessDevice::Ptr wifiDev; + + for (const NetworkManager::Device::Ptr &device : NetworkManager::networkInterfaces()) { + if (device->type() == NetworkManager::Device::Wifi) { + wifiDev = device.objectCast(); + if (wifiDev) { + if (!wifiDev->isActive()) { + foundInactive = true; + } else { + // Prefer previous device if it was inactive + if (foundInactive) { + break; + } + } + + if (wifiDev->wirelessCapabilities().testFlag(NetworkManager::WirelessDevice::ApCap)) { + useApMode = true; + } + + // We prefer inactive wireless card with AP capabilities + if (foundInactive && useApMode) { + break; + } + } + } + } + m_ui->connectionView->setMinimumWidth(300); m_ui->connectionView->rootContext()->setContextProperty("alternateBaseColor", mainWidget->palette().color(QPalette::Active, QPalette::AlternateBase)); m_ui->connectionView->rootContext()->setContextProperty("backgroundColor", mainWidget->palette().color(QPalette::Active, QPalette::Window)); m_ui->connectionView->rootContext()->setContextProperty("baseColor", mainWidget->palette().color(QPalette::Active, QPalette::Base)); m_ui->connectionView->rootContext()->setContextProperty("highlightColor", mainWidget->palette().color(QPalette::Active, QPalette::Highlight)); m_ui->connectionView->rootContext()->setContextProperty("textColor", mainWidget->palette().color(QPalette::Active, QPalette::Text)); m_ui->connectionView->rootContext()->setContextProperty("connectionModified", false); + m_ui->connectionView->rootContext()->setContextProperty("useApMode", useApMode); m_ui->connectionView->setClearColor(Qt::transparent); m_ui->connectionView->setResizeMode(QQuickWidget::SizeRootObjectToView); m_ui->connectionView->setSource(QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kcm_networkmanagement/qml/main.qml")))); diff --git a/kcm/qml/ConfigurationDialog.qml b/kcm/qml/ConfigurationDialog.qml --- a/kcm/qml/ConfigurationDialog.qml +++ b/kcm/qml/ConfigurationDialog.qml @@ -49,19 +49,61 @@ anchors.right: parent.right anchors.topMargin: units.gridUnit + Kirigami.Heading { + id: generalLabel + level: 2 + text: i18n("General") + } + QQC2.CheckBox { id: unlockModem text: i18n("Ask for PIN on modem detection") - onClicked: okButton.enabled = true + onClicked: configurationChanged() Component.onCompleted: checked = configuration.unlockModemOnDetection } QQC2.CheckBox { id: manageVirtualConnections text: i18n("Show virtual connections") - onClicked: okButton.enabled = true + onClicked: configurationChanged() Component.onCompleted: checked = configuration.manageVirtualConnections } + + Kirigami.Heading { + id: hotspotLabel + level: 2 + text: i18n("Hotspot") + Component.onCompleted: visible = handler.hotspotSupported + } + + QQC2.TextField { + id: hotspotName + Kirigami.FormData.label: i18n("Hotspot name:") + onTextChanged: configurationChanged() + Component.onCompleted: { + text = configuration.hotspotName + visible = handler.hotspotSupported + } + } + + QQC2.TextField { + id: hotspotPassword + Kirigami.FormData.label: i18n("Hotspot password:") + validator: RegExpValidator { + regExp: if (useApMode) { + /^$|^(?:.{8,64}){1}$/ + } else { + /^$|^(?:.{5}|[0-9a-fA-F]{10}|.{13}|[0-9a-fA-F]{26}){1}$/ + } + } + + onAcceptableInputChanged: configurationChanged() + + Component.onCompleted: { + text = configuration.hotspotPassword + visible = handler.hotspotSupported + } + } } Row { @@ -94,16 +136,28 @@ } } + function configurationChanged() { + if (handler.hotspotSupported) { + okButton.enabled = hotspotPassword.acceptableInput && hotspotName.text + } else { + okButton.enabled = true + } + } + onVisibleChanged: { if (visible) { unlockModem.checked = configuration.unlockModemOnDetection manageVirtualConnections.checked = configuration.manageVirtualConnections + hotspotName.text = configuration.hotspotName + hotspotPassword.text = configuration.hotspotPassword } } onAccepted: { configuration.unlockModemOnDetection = unlockModem.checked configuration.manageVirtualConnections = manageVirtualConnections.checked + configuration.hotspotName = hotspotName.text + configuration.hotspotPassword = hotspotPassword.text } } diff --git a/kcm/qml/main.qml b/kcm/qml/main.qml --- a/kcm/qml/main.qml +++ b/kcm/qml/main.qml @@ -56,6 +56,10 @@ sourceModel: connectionModel } + PlasmaNM.Configuration { + id: configuration + } + QQC2.TextField { id: searchField diff --git a/kded/networkmanagement.notifyrc b/kded/networkmanagement.notifyrc --- a/kded/networkmanagement.notifyrc +++ b/kded/networkmanagement.notifyrc @@ -838,3 +838,9 @@ Urgency=Low IconName=dialog-password Action=Popup + +[Event/FailedToCreateHotspot] +Name=Failed to create hotspot +Urgency=Low +IconName=applications-internet +Action=Popup diff --git a/libs/configuration.h b/libs/configuration.h --- a/libs/configuration.h +++ b/libs/configuration.h @@ -30,6 +30,9 @@ Q_PROPERTY(bool unlockModemOnDetection READ unlockModemOnDetection WRITE setUnlockModemOnDetection) Q_PROPERTY(bool manageVirtualConnections READ manageVirtualConnections WRITE setManageVirtualConnections) Q_PROPERTY(bool airplaneModeEnabled READ airplaneModeEnabled WRITE setAirplaneModeEnabled) + Q_PROPERTY(QString hotspotName READ hotspotName WRITE setHotspotName) + Q_PROPERTY(QString hotspotPassword READ hotspotPassword WRITE setHotspotPassword) + Q_PROPERTY(QString hotspotConnectionPath READ hotspotConnectionPath WRITE setHotspotConnectionPath) //Readonly constant property, as this value should only be set by the platform Q_PROPERTY(bool showPasswordDialog READ showPasswordDialog CONSTANT) @@ -44,6 +47,17 @@ static bool airplaneModeEnabled(); static void setAirplaneModeEnabled(bool enabled); + static QString hotspotName(); + static void setHotspotName(const QString &name); + + static QString hotspotPassword(); + static void setHotspotPassword(const QString &password); + + static bool hotspotCreated(); + + static QString hotspotConnectionPath(); + static void setHotspotConnectionPath(const QString &path); + static bool showPasswordDialog(); }; diff --git a/libs/configuration.cpp b/libs/configuration.cpp --- a/libs/configuration.cpp +++ b/libs/configuration.cpp @@ -22,12 +22,15 @@ #include #include +#include -Q_GLOBAL_STATIC_WITH_ARGS(KSharedConfigPtr, config, (KSharedConfig::openConfig(QLatin1String("plasma-nm")))) +static bool propManageVirtualConnectionsInitialized = false; +static bool propManageVirtualConnections = false; bool Configuration::unlockModemOnDetection() { - KConfigGroup grp(*config, QLatin1String("General")); + KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("plasma-nm")); + KConfigGroup grp(config, QLatin1String("General")); if (grp.isValid()) { return grp.readEntry(QLatin1String("UnlockModemOnDetection"), true); @@ -38,30 +41,42 @@ void Configuration::setUnlockModemOnDetection(bool unlock) { - KConfigGroup grp(*config, QLatin1String("General")); + KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("plasma-nm")); + KConfigGroup grp(config, QLatin1String("General")); if (grp.isValid()) { grp.writeEntry(QLatin1String("UnlockModemOnDetection"), unlock); } } bool Configuration::manageVirtualConnections() { - KConfigGroup grp(*config, QLatin1String("General")); + // Avoid reading from the config file over and over + if (propManageVirtualConnectionsInitialized) { + return propManageVirtualConnections; + } + + KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("plasma-nm")); + KConfigGroup grp(config, QLatin1String("General")); if (grp.isValid()) { - return grp.readEntry(QLatin1String("ManageVirtualConnections"), false); + propManageVirtualConnections = grp.readEntry(QLatin1String("ManageVirtualConnections"), false); + propManageVirtualConnectionsInitialized = true; + + return propManageVirtualConnections; } return true; } void Configuration::setManageVirtualConnections(bool manage) { - KConfigGroup grp(*config, QLatin1String("General")); + KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("plasma-nm")); + KConfigGroup grp(config, QLatin1String("General")); if (grp.isValid()) { grp.writeEntry(QLatin1String("ManageVirtualConnections"), manage); + propManageVirtualConnections = manage; } } @@ -72,7 +87,8 @@ const bool isWifiDisabled = !NetworkManager::isWirelessEnabled() || !NetworkManager::isWirelessHardwareEnabled(); const bool isWwanDisabled = !NetworkManager::isWwanEnabled() || !NetworkManager::isWwanHardwareEnabled(); - KConfigGroup grp(*config, QLatin1String("General")); + KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("plasma-nm")); + KConfigGroup grp(config, QLatin1String("General")); if (grp.isValid()) { if (grp.readEntry(QLatin1String("AirplaneModeEnabled"), false)) { @@ -90,16 +106,87 @@ void Configuration::setAirplaneModeEnabled(bool enabled) { - KConfigGroup grp(*config, QLatin1String("General")); + KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("plasma-nm")); + KConfigGroup grp(config, QLatin1String("General")); if (grp.isValid()) { grp.writeEntry(QLatin1String("AirplaneModeEnabled"), enabled); } } +QString Configuration::hotspotName() +{ + KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("plasma-nm")); + KConfigGroup grp(config, QLatin1String("General")); + KUser currentUser; + + const QString defaultName = currentUser.loginName() + QLatin1String("-hotspot"); + + if (grp.isValid()) { + return grp.readEntry(QLatin1String("HotspotName"), defaultName); + } + + return defaultName; +} + +void Configuration::setHotspotName(const QString &name) +{ + KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("plasma-nm")); + KConfigGroup grp(config, QLatin1String("General")); + + if (grp.isValid()) { + grp.writeEntry(QLatin1String("HotspotName"), name); + } +} + +QString Configuration::hotspotPassword() +{ + KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("plasma-nm")); + KConfigGroup grp(config, QLatin1String("General")); + + if (grp.isValid()) { + return grp.readEntry(QLatin1String("HotspotPassword"), QString()); + } + + return QString(); +} + +void Configuration::setHotspotPassword(const QString &password) +{ + KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("plasma-nm")); + KConfigGroup grp(config, QLatin1String("General")); + + if (grp.isValid()) { + grp.writeEntry(QLatin1String("HotspotPassword"), password); + } +} + +QString Configuration::hotspotConnectionPath() +{ + KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("plasma-nm")); + KConfigGroup grp(config, QLatin1String("General")); + + if (grp.isValid()) { + return grp.readEntry(QLatin1String("HotspotConnectionPath"), QString()); + } + + return QString(); +} + +void Configuration::setHotspotConnectionPath(const QString &path) +{ + KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("plasma-nm")); + KConfigGroup grp(config, QLatin1String("General")); + + if (grp.isValid()) { + grp.writeEntry(QLatin1String("HotspotConnectionPath"), path); + } +} + bool Configuration::showPasswordDialog() { - KConfigGroup grp(*config, QLatin1String("General")); + KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("plasma-nm")); + KConfigGroup grp(config, QLatin1String("General")); if (grp.isValid()) { return grp.readEntry(QLatin1String("ShowPasswordDialog"), true); diff --git a/libs/declarative/enabledconnections.h b/libs/declarative/enabledconnections.h --- a/libs/declarative/enabledconnections.h +++ b/libs/declarative/enabledconnections.h @@ -47,6 +47,7 @@ * Indicates if the mobile broadband hardware is currently enabled, i.e. the state of the RF kill switch. */ Q_PROPERTY(bool wwanHwEnabled READ isWwanHwEnabled NOTIFY wwanHwEnabled) + Q_OBJECT public: explicit EnabledConnections(QObject* parent = nullptr); diff --git a/libs/handler.h b/libs/handler.h --- a/libs/handler.h +++ b/libs/handler.h @@ -45,12 +45,17 @@ DeactivateConnection, RemoveConnection, RequestScan, - UpdateConnection + UpdateConnection, + CreateHotspot, }; explicit Handler(QObject* parent = nullptr); ~Handler() override; + Q_PROPERTY(bool hotspotSupported READ hotspotSupported NOTIFY hotspotSupportedChanged); +public: + bool hotspotSupported() const { return m_hotspotSupported; }; + public Q_SLOTS: /** * Activates given connection @@ -116,17 +121,25 @@ void updateConnection(const NetworkManager::Connection::Ptr &connection, const NMVariantMapMap &map); void requestScan(const QString &interface = QString()); + void createHotspot(); + void stopHotspot(); + private Q_SLOTS: void initKdedModule(); void secretAgentError(const QString &connectionPath, const QString &message); void replyFinished(QDBusPendingCallWatcher *watcher); + void hotspotCreated(QDBusPendingCallWatcher *watcher); #if WITH_MODEMMANAGER_SUPPORT void unlockRequiredChanged(MMModemLock modemLock); #endif Q_SIGNALS: void connectionActivationFailed(const QString &connectionPath, const QString &message); + void hotspotCreated(); + void hotspotDisabled(); + void hotspotSupportedChanged(bool hotspotSupported); private: + bool m_hotspotSupported; bool m_tmpWirelessEnabled; bool m_tmpWwanEnabled; #if WITH_MODEMMANAGER_SUPPORT @@ -141,6 +154,7 @@ void enableBluetooth(bool enable); void scanRequestFailed(const QString &interface); bool checkRequestScanRateLimit(const NetworkManager::WirelessDevice::Ptr &wifiDevice); + bool checkHotspotSupported(); void scheduleRequestScan(const QString &interface, int timeout); }; diff --git a/libs/handler.cpp b/libs/handler.cpp --- a/libs/handler.cpp +++ b/libs/handler.cpp @@ -20,6 +20,7 @@ #include "handler.h" #include "connectioneditordialog.h" +#include "configuration.h" #include "uiutils.h" #include "debug.h" @@ -83,6 +84,15 @@ QStringLiteral(AGENT_IFACE), QStringLiteral("secretsError"), this, SLOT(secretAgentError(QString, QString))); + + m_hotspotSupported = checkHotspotSupported(); + + if (NetworkManager::checkVersion(1, 16, 0)) { + connect(NetworkManager::notifier(), &NetworkManager::Notifier::primaryConnectionTypeChanged, [this] () { + m_hotspotSupported = checkHotspotSupported(); + Q_EMIT hotspotSupportedChanged(m_hotspotSupported); + }); + } } Handler::~Handler() @@ -528,6 +538,110 @@ } } +void Handler::createHotspot() +{ + bool foundInactive = false; + bool useApMode = false; + NetworkManager::WirelessDevice::Ptr wifiDev; + + NetworkManager::ConnectionSettings::Ptr connectionSettings; + connectionSettings = NetworkManager::ConnectionSettings::Ptr(new NetworkManager::ConnectionSettings(NetworkManager::ConnectionSettings::Wireless)); + + NetworkManager::WirelessSetting::Ptr wifiSetting = connectionSettings->setting(NetworkManager::Setting::Wireless).dynamicCast(); + wifiSetting->setMode(NetworkManager::WirelessSetting::Adhoc); + wifiSetting->setSsid(Configuration::hotspotName().toUtf8()); + + for (const NetworkManager::Device::Ptr &device : NetworkManager::networkInterfaces()) { + if (device->type() == NetworkManager::Device::Wifi) { + wifiDev = device.objectCast(); + if (wifiDev) { + if (!wifiDev->isActive()) { + foundInactive = true; + } else { + // Prefer previous device if it was inactive + if (foundInactive) { + break; + } + } + + if (wifiDev->wirelessCapabilities().testFlag(NetworkManager::WirelessDevice::ApCap)) { + useApMode = true; + } + + // We prefer inactive wireless card with AP capabilities + if (foundInactive && useApMode) { + break; + } + } + } + } + + if (!wifiDev) { + qCWarning(PLASMA_NM) << "Failed to create hotspot: missing wireless device"; + return; + } + + wifiSetting->setInitialized(true); + wifiSetting->setMode(useApMode ? NetworkManager::WirelessSetting::Ap :NetworkManager::WirelessSetting::Adhoc); + + if (!Configuration::hotspotPassword().isEmpty()) { + NetworkManager::WirelessSecuritySetting::Ptr wifiSecurity = connectionSettings->setting(NetworkManager::Setting::WirelessSecurity).dynamicCast(); + wifiSecurity->setInitialized(true); + + if (useApMode) { + // Use WPA2 + wifiSecurity->setKeyMgmt(NetworkManager::WirelessSecuritySetting::WpaPsk); + wifiSecurity->setPsk(Configuration::hotspotPassword()); + wifiSecurity->setPskFlags(NetworkManager::Setting::AgentOwned); + } else { + // Use WEP + wifiSecurity->setKeyMgmt(NetworkManager::WirelessSecuritySetting::Wep); + wifiSecurity->setWepKeyType(NetworkManager::WirelessSecuritySetting::Passphrase); + wifiSecurity->setWepTxKeyindex(0); + wifiSecurity->setWepKey0(Configuration::hotspotPassword()); + wifiSecurity->setWepKeyFlags(NetworkManager::Setting::AgentOwned); + wifiSecurity->setAuthAlg(NetworkManager::WirelessSecuritySetting::Open); + } + } + + NetworkManager::Ipv4Setting::Ptr ipv4Setting = connectionSettings->setting(NetworkManager::Setting::Ipv4).dynamicCast(); + ipv4Setting->setMethod(NetworkManager::Ipv4Setting::Shared); + ipv4Setting->setInitialized(true); + + connectionSettings->setId(Configuration::hotspotName()); + connectionSettings->setAutoconnect(false); + connectionSettings->setUuid(NetworkManager::ConnectionSettings::createNewUuid()); + + const QVariantMap options = { {QLatin1String("persist"), QLatin1String("volatile")} }; + + QDBusPendingReply reply = NetworkManager::addAndActivateConnection2(connectionSettings->toMap(), wifiDev->uni(), QString(), options); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this); + watcher->setProperty("action", Handler::CreateHotspot); + watcher->setProperty("connection", Configuration::hotspotName()); + connect(watcher, &QDBusPendingCallWatcher::finished, this, &Handler::replyFinished); + connect(watcher, &QDBusPendingCallWatcher::finished, this, QOverload::of(&Handler::hotspotCreated)); +} + +void Handler::stopHotspot() +{ + const QString activeConnectionPath = Configuration::hotspotConnectionPath(); + + if (activeConnectionPath.isEmpty()) { + return; + } + + NetworkManager::ActiveConnection::Ptr hotspot = NetworkManager::findActiveConnection(activeConnectionPath); + + if (!hotspot) { + return; + } + + NetworkManager::deactivateConnection(activeConnectionPath); + Configuration::setHotspotConnectionPath(QString()); + + Q_EMIT hotspotDisabled(); +} + bool Handler::checkRequestScanRateLimit(const NetworkManager::WirelessDevice::Ptr &wifiDevice) { QDateTime now = QDateTime::currentDateTime(); @@ -547,6 +661,41 @@ return true; } +bool Handler::checkHotspotSupported() +{ + if (NetworkManager::checkVersion(1, 16, 0)) { + bool unusedWifiFound = false; + bool wifiFound = false; + + for (const NetworkManager::Device::Ptr &device : NetworkManager::networkInterfaces()) { + if (device->type() == NetworkManager::Device::Wifi) { + wifiFound = true; + + NetworkManager::WirelessDevice::Ptr wifiDev = device.objectCast(); + if (wifiDev && !wifiDev->isActive()) { + unusedWifiFound = true; + } + } + } + + + if (!wifiFound) { + return false; + } + + if (unusedWifiFound) { + return true; + } + + // Check if the primary connection which is used for internet connectivity is not using WiFi + if (NetworkManager::primaryConnectionType() != NetworkManager::ConnectionSettings::Wireless) { + return true; + } + } + + return false; +} + void Handler::scheduleRequestScan(const QString &interface, int timeout) { QTimer *timer; @@ -633,6 +782,10 @@ scanRequestFailed(interface); break; } + case Handler::CreateHotspot: + notification = new KNotification("FailedToCreateHotspot", KNotification::CloseOnTimeout, this); + notification->setTitle(i18n("Failed to create hotspot %1", watcher->property("connection").toString())); + break; default: break; } @@ -678,6 +831,36 @@ watcher->deleteLater(); } +void Handler::hotspotCreated(QDBusPendingCallWatcher *watcher) +{ + QDBusPendingReply reply = *watcher; + + if (!reply.isError() && reply.isValid()) { + const QString activeConnectionPath = reply.argumentAt(1).value().path(); + + if (activeConnectionPath.isEmpty()) { + return; + } + + Configuration::setHotspotConnectionPath(activeConnectionPath); + + NetworkManager::ActiveConnection::Ptr hotspot = NetworkManager::findActiveConnection(activeConnectionPath); + + if (!hotspot) { + return; + } + + connect(hotspot.data(), &NetworkManager::ActiveConnection::stateChanged, [=] (NetworkManager::ActiveConnection::State state) { + if (state > NetworkManager::ActiveConnection::Activated) { + Configuration::setHotspotConnectionPath(QString()); + Q_EMIT hotspotDisabled(); + } + }); + + Q_EMIT hotspotCreated(); + } +} + #if WITH_MODEMMANAGER_SUPPORT void Handler::unlockRequiredChanged(MMModemLock modemLock) { diff --git a/libs/models/networkmodel.cpp b/libs/models/networkmodel.cpp --- a/libs/models/networkmodel.cpp +++ b/libs/models/networkmodel.cpp @@ -20,6 +20,7 @@ #include "networkmodel.h" #include "networkmodelitem.h" +#include "configuration.h" #include "debug.h" #include "uiutils.h" @@ -447,6 +448,16 @@ { initializeSignals(network); + // Avoid duplicating entries in the model + if (!Configuration::hotspotConnectionPath().isEmpty()) { + NetworkManager::ActiveConnection::Ptr activeConnection = NetworkManager::findActiveConnection(Configuration::hotspotConnectionPath()); + + // If we are trying to add an AP which is the one created by our hotspot, then we can skip this and don't add it twice + if (activeConnection && activeConnection->specificObject() == network->referenceAccessPoint()->uni()) { + return; + } + } + // BUG: 386342 // When creating a new hidden wireless network and attempting to connect to it, NM then later reports that AccessPoint appeared, but // it doesn't know its SSID from some reason, this also makes Wireless device to advertise a new available connection, which we later