#include "selfdrive/ui/qt/network/networking.h" #include #include #include #include #include "selfdrive/ui/ui.h" #include "selfdrive/ui/qt/qt_window.h" #include "selfdrive/ui/qt/util.h" #include "selfdrive/ui/qt/widgets/controls.h" #include "selfdrive/ui/qt/widgets/scrollview.h" static const int ICON_WIDTH = 49; // Networking functions Networking::Networking(QWidget* parent, bool show_advanced) : QFrame(parent) { main_layout = new QStackedLayout(this); wifi = new WifiManager(this); connect(wifi, &WifiManager::refreshSignal, this, &Networking::refresh); connect(wifi, &WifiManager::wrongPassword, this, &Networking::wrongPassword); wifiScreen = new QWidget(this); QVBoxLayout* vlayout = new QVBoxLayout(wifiScreen); vlayout->setContentsMargins(20, 20, 20, 20); if (show_advanced) { QPushButton* advancedSettings = new QPushButton(tr("Advanced")); advancedSettings->setObjectName("advanced_btn"); advancedSettings->setStyleSheet("margin-right: 30px;"); advancedSettings->setFixedSize(400, 100); connect(advancedSettings, &QPushButton::clicked, [=]() { main_layout->setCurrentWidget(an); }); vlayout->addSpacing(10); vlayout->addWidget(advancedSettings, 0, Qt::AlignRight); vlayout->addSpacing(10); } wifiWidget = new WifiUI(this, wifi); wifiWidget->setObjectName("wifiWidget"); connect(wifiWidget, &WifiUI::connectToNetwork, this, &Networking::connectToNetwork); ScrollView *wifiScroller = new ScrollView(wifiWidget, this); wifiScroller->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); vlayout->addWidget(wifiScroller, 1); main_layout->addWidget(wifiScreen); an = new AdvancedNetworking(this, wifi); connect(an, &AdvancedNetworking::backPress, [=]() { main_layout->setCurrentWidget(wifiScreen); }); main_layout->addWidget(an); QPalette pal = palette(); pal.setColor(QPalette::Window, QColor(0x29, 0x29, 0x29)); setAutoFillBackground(true); setPalette(pal); setStyleSheet(R"( #wifiWidget > QPushButton, #back_btn, #advanced_btn { font-size: 50px; margin: 0px; padding: 15px; border-width: 0; border-radius: 30px; color: #dddddd; background-color: #393939; } #back_btn:pressed, #advanced_btn:pressed { background-color: #4a4a4a; } )"); main_layout->setCurrentWidget(wifiScreen); } void Networking::refresh() { wifiWidget->refresh(); an->refresh(); } void Networking::connectToNetwork(const Network n) { if (wifi->isKnownConnection(n.ssid)) { wifi->activateWifiConnection(n.ssid); } else if (n.security_type == SecurityType::OPEN) { wifi->connect(n); } else if (n.security_type == SecurityType::WPA) { QString pass = InputDialog::getText(tr("Enter password"), this, tr("for \"%1\"").arg(QString::fromUtf8(n.ssid)), true, 8); if (!pass.isEmpty()) { wifi->connect(n, pass); } } } void Networking::wrongPassword(const QString &ssid) { if (wifi->seenNetworks.contains(ssid)) { const Network &n = wifi->seenNetworks.value(ssid); QString pass = InputDialog::getText(tr("Wrong password"), this, tr("for \"%1\"").arg(QString::fromUtf8(n.ssid)), true, 8); if (!pass.isEmpty()) { wifi->connect(n, pass); } } } void Networking::showEvent(QShowEvent *event) { wifi->start(); } void Networking::hideEvent(QHideEvent *event) { main_layout->setCurrentWidget(wifiScreen); wifi->stop(); } // AdvancedNetworking functions AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWidget(parent), wifi(wifi) { QVBoxLayout* main_layout = new QVBoxLayout(this); main_layout->setMargin(40); main_layout->setSpacing(20); // Back button QPushButton* back = new QPushButton(tr("Back")); back->setObjectName("back_btn"); back->setFixedSize(400, 100); connect(back, &QPushButton::clicked, [=]() { emit backPress(); }); main_layout->addWidget(back, 0, Qt::AlignLeft); ListWidget *list = new ListWidget(this); // Enable tethering layout tetheringToggle = new ToggleControl(tr("Enable Tethering"), "", "", wifi->isTetheringEnabled()); list->addItem(tetheringToggle); QObject::connect(tetheringToggle, &ToggleControl::toggleFlipped, this, &AdvancedNetworking::toggleTethering); // Change tethering password ButtonControl *editPasswordButton = new ButtonControl(tr("Tethering Password"), tr("EDIT")); connect(editPasswordButton, &ButtonControl::clicked, [=]() { QString pass = InputDialog::getText(tr("Enter new tethering password"), this, "", true, 8, wifi->getTetheringPassword()); if (!pass.isEmpty()) { wifi->changeTetheringPassword(pass); } }); list->addItem(editPasswordButton); // IP address ipLabel = new LabelControl(tr("IP Address"), wifi->ipv4_address); list->addItem(ipLabel); // SSH keys list->addItem(new SshToggle()); list->addItem(new SshControl()); // Roaming toggle const bool roamingEnabled = params.getBool("GsmRoaming"); roamingToggle = new ToggleControl(tr("Enable Roaming"), "", "", roamingEnabled); QObject::connect(roamingToggle, &ToggleControl::toggleFlipped, [=](bool state) { params.putBool("GsmRoaming", state); wifi->updateGsmSettings(state, QString::fromStdString(params.get("GsmApn")), params.getBool("GsmMetered")); }); list->addItem(roamingToggle); // APN settings editApnButton = new ButtonControl(tr("APN Setting"), tr("EDIT")); connect(editApnButton, &ButtonControl::clicked, [=]() { const QString cur_apn = QString::fromStdString(params.get("GsmApn")); QString apn = InputDialog::getText(tr("Enter APN"), this, tr("leave blank for automatic configuration"), false, -1, cur_apn).trimmed(); if (apn.isEmpty()) { params.remove("GsmApn"); } else { params.put("GsmApn", apn.toStdString()); } wifi->updateGsmSettings(params.getBool("GsmRoaming"), apn, params.getBool("GsmMetered")); }); list->addItem(editApnButton); // Metered toggle const bool metered = params.getBool("GsmMetered"); meteredToggle = new ToggleControl(tr("Cellular Metered"), tr("Prevent large data uploads when on a metered connection"), "", metered); QObject::connect(meteredToggle, &SshToggle::toggleFlipped, [=](bool state) { params.putBool("GsmMetered", state); wifi->updateGsmSettings(params.getBool("GsmRoaming"), QString::fromStdString(params.get("GsmApn")), state); }); list->addItem(meteredToggle); // Set initial config wifi->updateGsmSettings(roamingEnabled, QString::fromStdString(params.get("GsmApn")), metered); connect(uiState(), &UIState::primeTypeChanged, this, [=](PrimeType prime_type) { bool gsmVisible = prime_type == PrimeType::NONE || prime_type == PrimeType::LITE; roamingToggle->setVisible(gsmVisible); editApnButton->setVisible(gsmVisible); meteredToggle->setVisible(gsmVisible); }); main_layout->addWidget(new ScrollView(list, this)); main_layout->addStretch(1); } void AdvancedNetworking::refresh() { ipLabel->setText(wifi->ipv4_address); tetheringToggle->setEnabled(true); update(); } void AdvancedNetworking::toggleTethering(bool enabled) { wifi->setTetheringEnabled(enabled); tetheringToggle->setEnabled(false); } // WifiUI functions WifiUI::WifiUI(QWidget *parent, WifiManager* wifi) : QWidget(parent), wifi(wifi) { QVBoxLayout *main_layout = new QVBoxLayout(this); main_layout->setContentsMargins(0, 0, 0, 0); main_layout->setSpacing(0); // load imgs for (const auto &s : {"low", "medium", "high", "full"}) { QPixmap pix(ASSET_PATH + "/offroad/icon_wifi_strength_" + s + ".svg"); strengths.push_back(pix.scaledToHeight(68, Qt::SmoothTransformation)); } lock = QPixmap(ASSET_PATH + "offroad/icon_lock_closed.svg").scaledToWidth(ICON_WIDTH, Qt::SmoothTransformation); checkmark = QPixmap(ASSET_PATH + "offroad/icon_checkmark.svg").scaledToWidth(ICON_WIDTH, Qt::SmoothTransformation); circled_slash = QPixmap(ASSET_PATH + "img_circled_slash.svg").scaledToWidth(ICON_WIDTH, Qt::SmoothTransformation); scanningLabel = new QLabel(tr("Scanning for networks...")); scanningLabel->setStyleSheet("font-size: 65px;"); main_layout->addWidget(scanningLabel, 0, Qt::AlignCenter); wifi_list_widget = new ListWidget(this); wifi_list_widget->setVisible(false); main_layout->addWidget(wifi_list_widget); setStyleSheet(R"( QScrollBar::handle:vertical { min-height: 0px; border-radius: 4px; background-color: #8A8A8A; } #forgetBtn { font-size: 32px; font-weight: 600; color: #292929; background-color: #BDBDBD; border-width: 1px solid #828282; border-radius: 5px; padding: 40px; padding-bottom: 16px; padding-top: 16px; } #forgetBtn:pressed { background-color: #828282; } #connecting { font-size: 32px; font-weight: 600; color: white; border-radius: 0; padding: 27px; padding-left: 43px; padding-right: 43px; background-color: black; } #ssidLabel { text-align: left; border: none; padding-top: 50px; padding-bottom: 50px; } #ssidLabel:disabled { color: #696969; } )"); } void WifiUI::refresh() { bool is_empty = wifi->seenNetworks.isEmpty(); scanningLabel->setVisible(is_empty); wifi_list_widget->setVisible(!is_empty); if (is_empty) return; setUpdatesEnabled(false); const bool is_tethering_enabled = wifi->isTetheringEnabled(); QList sortedNetworks = wifi->seenNetworks.values(); std::sort(sortedNetworks.begin(), sortedNetworks.end(), compare_by_strength); int n = 0; for (Network &network : sortedNetworks) { QPixmap status_icon; if (network.connected == ConnectedType::CONNECTED) { status_icon = checkmark; } else if (network.security_type == SecurityType::UNSUPPORTED) { status_icon = circled_slash; } else if (network.security_type == SecurityType::WPA) { status_icon = lock; } bool show_forget_btn = wifi->isKnownConnection(network.ssid) && !is_tethering_enabled; QPixmap strength = strengths[strengthLevel(network.strength)]; auto item = getItem(n++); item->setItem(network, status_icon, show_forget_btn, strength); item->setVisible(true); } for (; n < wifi_items.size(); ++n) wifi_items[n]->setVisible(false); if (n == wifi_items.size()) { Network hidden_network; hidden_network.ssid = "Hidden Network"; hidden_network.security_type = SecurityType::WPA; QPixmap status_icon = lock; QPixmap strength_icon = strengths[strengthLevel(0)]; auto hiddenNetworkItem = getItem(n++); hiddenNetworkItem->setItem(hidden_network, status_icon, false, strength_icon); hiddenNetworkItem->setVisible(true); QObject::connect(hiddenNetworkItem, &WifiItem::connectToNetwork, this, &WifiUI::handleHiddenNetwork); } setUpdatesEnabled(true); } void WifiUI::handleHiddenNetwork() { QString ssid = InputDialog::getText(tr("Enter SSID"), this, "", false); if (!ssid.isEmpty()) { QString pass = InputDialog::getText(tr("Enter password"), this, tr("for \"%1\"").arg(ssid), true, 8); if (!pass.isEmpty()) { Network hidden_network; hidden_network.ssid = ssid.toUtf8(); hidden_network.security_type = SecurityType::WPA; // or adjust based on your needs wifi->connect(hidden_network, pass); } } } WifiItem *WifiUI::getItem(int n) { auto item = n < wifi_items.size() ? wifi_items[n] : wifi_items.emplace_back(new WifiItem(tr("CONNECTING..."), tr("FORGET"))); if (!item->parentWidget()) { QObject::connect(item, &WifiItem::connectToNetwork, this, &WifiUI::connectToNetwork); QObject::connect(item, &WifiItem::forgotNetwork, [this](const Network n) { if (ConfirmationDialog::confirm(tr("Forget Wi-Fi Network \"%1\"?").arg(QString::fromUtf8(n.ssid)), tr("Forget"), this)) wifi->forgetConnection(n.ssid); }); wifi_list_widget->addItem(item); } return item; } // WifiItem WifiItem::WifiItem(const QString &connecting_text, const QString &forget_text, QWidget *parent) : QWidget(parent) { QHBoxLayout *hlayout = new QHBoxLayout(this); hlayout->setContentsMargins(44, 0, 73, 0); hlayout->setSpacing(50); hlayout->addWidget(ssidLabel = new ElidedLabel()); ssidLabel->setObjectName("ssidLabel"); ssidLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); hlayout->addWidget(connecting = new QPushButton(connecting_text), 0, Qt::AlignRight); connecting->setObjectName("connecting"); hlayout->addWidget(forgetBtn = new QPushButton(forget_text), 0, Qt::AlignRight); forgetBtn->setObjectName("forgetBtn"); hlayout->addWidget(iconLabel = new QLabel(), 0, Qt::AlignRight); hlayout->addWidget(strengthLabel = new QLabel(), 0, Qt::AlignRight); iconLabel->setFixedWidth(ICON_WIDTH); QObject::connect(forgetBtn, &QPushButton::clicked, [this]() { emit forgotNetwork(network); }); QObject::connect(ssidLabel, &ElidedLabel::clicked, [this]() { if (network.connected == ConnectedType::DISCONNECTED) emit connectToNetwork(network); }); } void WifiItem::setItem(const Network &n, const QPixmap &status_icon, bool show_forget_btn, const QPixmap &strength_icon) { network = n; ssidLabel->setText(n.ssid); ssidLabel->setEnabled(n.security_type != SecurityType::UNSUPPORTED); ssidLabel->setFont(InterFont(55, network.connected == ConnectedType::DISCONNECTED ? QFont::Normal : QFont::Bold)); connecting->setVisible(n.connected == ConnectedType::CONNECTING); forgetBtn->setVisible(show_forget_btn); iconLabel->setPixmap(status_icon); strengthLabel->setPixmap(strength_icon); }