diff --git a/selfdrive/frogpilot/navigation/navigation_training/both_keys_set.png b/selfdrive/frogpilot/navigation/navigation_training/both_keys_set.png new file mode 100644 index 0000000..41cf320 Binary files /dev/null and b/selfdrive/frogpilot/navigation/navigation_training/both_keys_set.png differ diff --git a/selfdrive/frogpilot/navigation/navigation_training/no_keys_set.png b/selfdrive/frogpilot/navigation/navigation_training/no_keys_set.png new file mode 100644 index 0000000..7fc89a8 Binary files /dev/null and b/selfdrive/frogpilot/navigation/navigation_training/no_keys_set.png differ diff --git a/selfdrive/frogpilot/navigation/navigation_training/public_key_set.png b/selfdrive/frogpilot/navigation/navigation_training/public_key_set.png new file mode 100644 index 0000000..ac3e6d1 Binary files /dev/null and b/selfdrive/frogpilot/navigation/navigation_training/public_key_set.png differ diff --git a/selfdrive/frogpilot/navigation/navigation_training/setup_completed.png b/selfdrive/frogpilot/navigation/navigation_training/setup_completed.png new file mode 100644 index 0000000..afd64fb Binary files /dev/null and b/selfdrive/frogpilot/navigation/navigation_training/setup_completed.png differ diff --git a/selfdrive/frogpilot/navigation/ui/navigation_settings.cc b/selfdrive/frogpilot/navigation/ui/navigation_settings.cc new file mode 100644 index 0000000..8139cd5 --- /dev/null +++ b/selfdrive/frogpilot/navigation/ui/navigation_settings.cc @@ -0,0 +1,156 @@ +#include + +#include "selfdrive/frogpilot/navigation/ui/navigation_settings.h" + +FrogPilotNavigationPanel::FrogPilotNavigationPanel(QWidget *parent) : QFrame(parent), scene(uiState()->scene) { + mainLayout = new QStackedLayout(this); + + navigationWidget = new QWidget(); + QVBoxLayout *navigationLayout = new QVBoxLayout(navigationWidget); + navigationLayout->setMargin(40); + + FrogPilotListWidget *list = new FrogPilotListWidget(navigationWidget); + + Primeless *primelessPanel = new Primeless(this); + mainLayout->addWidget(primelessPanel); + + ButtonControl *manageNOOButton = new ButtonControl(tr("Manage Primeless Navigation Settings"), tr("MANAGE"), tr("Manage primeless navigate on openpilot settings.")); + QObject::connect(manageNOOButton, &ButtonControl::clicked, [=]() { mainLayout->setCurrentWidget(primelessPanel); }); + QObject::connect(primelessPanel, &Primeless::backPress, [=]() { mainLayout->setCurrentWidget(navigationWidget); }); + list->addItem(manageNOOButton); + manageNOOButton->setVisible(!uiState()->hasPrime()); +} + +Primeless::Primeless(QWidget *parent) : QWidget(parent) { + QStackedLayout *primelessLayout = new QStackedLayout(this); + + QWidget *mainWidget = new QWidget(); + mainLayout = new QVBoxLayout(mainWidget); + mainLayout->setMargin(40); + + backButton = new QPushButton(tr("Back"), this); + backButton->setObjectName("backButton"); + backButton->setFixedSize(400, 100); + QObject::connect(backButton, &QPushButton::clicked, this, [this]() { emit backPress(); }); + mainLayout->addWidget(backButton, 0, Qt::AlignLeft); + + list = new FrogPilotListWidget(mainWidget); + + wifi = new WifiManager(this); + ipLabel = new LabelControl(tr("Manage Your Settings At"), QString("%1:8082").arg(wifi->getIp4Address())); + list->addItem(ipLabel); + + std::vector searchOptions{tr("MapBox"), tr("Amap"), tr("Google")}; + ButtonParamControl *searchInput = new ButtonParamControl("SearchInput", tr("Destination Search Provider"), + tr("Select a search provider for destination queries in Navigate on Openpilot. Options include MapBox (recommended), Amap, and Google Maps."), + "", searchOptions); + list->addItem(searchInput); + + createMapboxKeyControl(publicMapboxKeyControl, tr("Public Mapbox Key"), "MapboxPublicKey", "pk."); + createMapboxKeyControl(secretMapboxKeyControl, tr("Secret Mapbox Key"), "MapboxSecretKey", "sk."); + + mapboxPublicKeySet = !params.get("MapboxPublicKey").empty(); + mapboxSecretKeySet = !params.get("MapboxSecretKey").empty(); + setupCompleted = mapboxPublicKeySet && mapboxSecretKeySet; + + QHBoxLayout *setupLayout = new QHBoxLayout(); + setupLayout->setMargin(0); + + imageLabel = new QLabel(this); + pixmap.load(currentStep); + imageLabel->setPixmap(pixmap.scaledToWidth(1500, Qt::SmoothTransformation)); + setupLayout->addWidget(imageLabel, 0, Qt::AlignCenter); + imageLabel->hide(); + + ButtonControl *setupButton = new ButtonControl(tr("Setup Instructions"), tr("VIEW"), tr("View the instructions to set up MapBox for Primeless Navigation."), this); + QObject::connect(setupButton, &ButtonControl::clicked, this, [this]() { + updateStep(); + backButton->hide(); + list->setVisible(false); + imageLabel->show(); + }); + list->addItem(setupButton); + + QObject::connect(uiState(), &UIState::uiUpdate, this, &Primeless::updateState); + + mainLayout->addLayout(setupLayout); + mainLayout->addWidget(new ScrollView(list, mainWidget)); + mainWidget->setLayout(mainLayout); + primelessLayout->addWidget(mainWidget); + + setLayout(primelessLayout); + + setStyleSheet(R"( + QPushButton { + font-size: 50px; + margin: 0px; + padding: 15px; + border-width: 0; + border-radius: 30px; + color: #dddddd; + background-color: #393939; + } + QPushButton:pressed { + background-color: #4a4a4a; + } + )"); +} + +void Primeless::hideEvent(QHideEvent *event) { + QWidget::hideEvent(event); + backButton->show(); + list->setVisible(true); + imageLabel->hide(); +} + +void Primeless::mousePressEvent(QMouseEvent *event) { + backButton->show(); + list->setVisible(true); + imageLabel->hide(); +} + +void Primeless::updateState() { + if (!isVisible()) return; + + QString ipAddress = wifi->getIp4Address(); + ipLabel->setText(ipAddress.isEmpty() ? tr("Device Offline") : QString("%1:8082").arg(ipAddress)); + + mapboxPublicKeySet = !params.get("MapboxPublicKey").empty(); + mapboxSecretKeySet = !params.get("MapboxSecretKey").empty(); + setupCompleted = mapboxPublicKeySet && mapboxSecretKeySet && setupCompleted; + + publicMapboxKeyControl->setText(mapboxPublicKeySet ? tr("REMOVE") : tr("ADD")); + secretMapboxKeyControl->setText(mapboxSecretKeySet ? tr("REMOVE") : tr("ADD")); + + if (imageLabel->isVisible()) { + updateStep(); + } +} + +void Primeless::createMapboxKeyControl(ButtonControl *&control, const QString &label, const std::string ¶mKey, const QString &prefix) { + control = new ButtonControl(label, "", tr("Manage your %1."), this); + QObject::connect(control, &ButtonControl::clicked, this, [this, control, label, paramKey, prefix] { + if (control->text() == tr("ADD")) { + QString key = InputDialog::getText(tr("Enter your %1").arg(label), this); + if (!key.startsWith(prefix)) { + key = prefix + key; + } + if (key.length() >= 80) { + params.put(paramKey, key.toStdString()); + } + } else { + params.remove(paramKey); + } + }); + list->addItem(control); + control->setText(params.get(paramKey).empty() ? tr("ADD") : tr("REMOVE")); +} + +void Primeless::updateStep() { + currentStep = setupCompleted ? "../frogpilot/navigation/navigation_training/setup_completed.png" : + (mapboxPublicKeySet && mapboxSecretKeySet) ? "../frogpilot/navigation/navigation_training/both_keys_set.png" : + mapboxPublicKeySet ? "../frogpilot/navigation/navigation_training/public_key_set.png" : "../frogpilot/navigation/navigation_training/no_keys_set.png"; + + pixmap.load(currentStep); + imageLabel->setPixmap(pixmap.scaledToWidth(1500, Qt::SmoothTransformation)); +} diff --git a/selfdrive/frogpilot/navigation/ui/navigation_settings.h b/selfdrive/frogpilot/navigation/ui/navigation_settings.h new file mode 100644 index 0000000..2bf9ede --- /dev/null +++ b/selfdrive/frogpilot/navigation/ui/navigation_settings.h @@ -0,0 +1,60 @@ +#pragma once + +#include "selfdrive/ui/qt/network/wifi_manager.h" +#include "selfdrive/ui/qt/offroad/settings.h" +#include "selfdrive/ui/qt/widgets/scrollview.h" +#include "selfdrive/ui/ui.h" + +class Primeless : public QWidget { + Q_OBJECT + +public: + explicit Primeless(QWidget *parent = nullptr); + +protected: + void mousePressEvent(QMouseEvent *event) override; + void hideEvent(QHideEvent *event) override; + +private: + void createMapboxKeyControl(ButtonControl *&control, const QString &label, const std::string ¶mKey, const QString &prefix); + void updateState(); + void updateStep(); + + QVBoxLayout *mainLayout; + FrogPilotListWidget *list; + + QPushButton *backButton; + QLabel *imageLabel; + + ButtonControl *publicMapboxKeyControl; + ButtonControl *secretMapboxKeyControl; + LabelControl *ipLabel; + + WifiManager *wifi; + + bool mapboxPublicKeySet; + bool mapboxSecretKeySet; + bool setupCompleted; + QPixmap pixmap; + QString currentStep = "../assets/images/setup_completed.png"; + + Params params; + +signals: + void backPress(); +}; + +class FrogPilotNavigationPanel : public QFrame { + Q_OBJECT + +public: + explicit FrogPilotNavigationPanel(QWidget *parent = 0); + +private: + QStackedLayout *mainLayout; + QWidget *navigationWidget; + + Params params; + Params paramsMemory{"/dev/shm/params"}; + UIScene &scene; +}; diff --git a/selfdrive/navd/navd.py b/selfdrive/navd/navd.py index 8f906f1..dfa99b3 100755 --- a/selfdrive/navd/navd.py +++ b/selfdrive/navd/navd.py @@ -57,6 +57,9 @@ class RouteEngine: if "MAPBOX_TOKEN" in os.environ: self.mapbox_token = os.environ["MAPBOX_TOKEN"] self.mapbox_host = "https://api.mapbox.com" + elif self.params.get_int("PrimeType") == 0: + self.mapbox_token = self.params.get("MapboxPublicKey", encoding='utf8') + self.mapbox_host = "https://api.mapbox.com" else: self.api = Api(self.params.get("DongleId", encoding='utf8')) self.mapbox_host = "https://maps.comma.ai" diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index bff18d6..8539eb8 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -24,7 +24,7 @@ widgets_src = ["ui.cc", "qt/widgets/input.cc", "qt/widgets/wifi.cc", "qt/widgets/offroad_alerts.cc", "qt/widgets/prime.cc", "qt/widgets/keyboard.cc", "qt/widgets/scrollview.cc", "qt/widgets/cameraview.cc", "#third_party/qrcode/QrCode.cc", "qt/request_repeater.cc", "qt/qt_window.cc", "qt/network/networking.cc", "qt/network/wifi_manager.cc", - "../frogpilot/ui/qt/widgets/frogpilot_controls.cc", + "../frogpilot/ui/qt/widgets/frogpilot_controls.cc", "../frogpilot/navigation/ui/navigation_settings.cc", "../frogpilot/ui/qt/offroad/control_settings.cc", "../frogpilot/ui/qt/offroad/vehicle_settings.cc", "../frogpilot/ui/qt/offroad/visual_settings.cc"] diff --git a/selfdrive/ui/qt/maps/map_helpers.h b/selfdrive/ui/qt/maps/map_helpers.h index aefc0c4..d1ac768 100644 --- a/selfdrive/ui/qt/maps/map_helpers.h +++ b/selfdrive/ui/qt/maps/map_helpers.h @@ -14,7 +14,7 @@ #include "common/transformations/orientation.hpp" #include "cereal/messaging/messaging.h" -const QString MAPBOX_TOKEN = util::getenv("MAPBOX_TOKEN").c_str(); +const QString MAPBOX_TOKEN = QString::fromStdString(Params().get("MapboxPublicKey")); const QString MAPS_HOST = util::getenv("MAPS_HOST", MAPBOX_TOKEN.isEmpty() ? "https://maps.comma.ai" : "https://api.mapbox.com").c_str(); const QString MAPS_CACHE_PATH = "/data/mbgl-cache-navd.db"; diff --git a/selfdrive/ui/qt/maps/map_settings.cc b/selfdrive/ui/qt/maps/map_settings.cc index 4d655be..d6fc724 100644 --- a/selfdrive/ui/qt/maps/map_settings.cc +++ b/selfdrive/ui/qt/maps/map_settings.cc @@ -62,7 +62,7 @@ MapSettings::MapSettings(bool closeable, QWidget *parent) : QFrame(parent) { title->setStyleSheet("color: #FFFFFF; font-size: 54px; font-weight: 600;"); heading->addWidget(title); - auto *subtitle = new QLabel(tr("Manage at connect.comma.ai"), this); + subtitle = new QLabel(tr("Manage at connect.comma.ai"), this); subtitle->setStyleSheet("color: #A0A0A0; font-size: 40px; font-weight: 300;"); heading->addWidget(subtitle); } @@ -93,6 +93,8 @@ MapSettings::MapSettings(bool closeable, QWidget *parent) : QFrame(parent) { setStyleSheet("MapSettings { background-color: #333333; }"); QObject::connect(NavManager::instance(), &NavManager::updated, this, &MapSettings::refresh); + + wifi = new WifiManager(this); } void MapSettings::showEvent(QShowEvent *event) { @@ -138,6 +140,12 @@ void MapSettings::refresh() { for (; n < widgets.size(); ++n) widgets[n]->setVisible(false); setUpdatesEnabled(true); + + // Use IP for NOO without Prime + if (!uiState()->hasPrime()) { + QString ipAddress = QString("%1:8082").arg(wifi->getIp4Address()); + subtitle->setText(tr("Manage at %1").arg(ipAddress)); + } } void MapSettings::navigateTo(const QJsonObject &place) { diff --git a/selfdrive/ui/qt/maps/map_settings.h b/selfdrive/ui/qt/maps/map_settings.h index 0e151df..1a96336 100644 --- a/selfdrive/ui/qt/maps/map_settings.h +++ b/selfdrive/ui/qt/maps/map_settings.h @@ -12,6 +12,7 @@ #include #include "common/params.h" +#include "selfdrive/ui/qt/network/wifi_manager.h" #include "selfdrive/ui/qt/util.h" #include "selfdrive/ui/qt/widgets/controls.h" @@ -64,6 +65,11 @@ private: DestinationWidget *work_widget; std::vector widgets; + // FrogPilot variables + QLabel *subtitle; + + WifiManager *wifi; + signals: void closeSettings(); }; diff --git a/selfdrive/ui/qt/network/wifi_manager.h b/selfdrive/ui/qt/network/wifi_manager.h index 933f25c..39305e5 100644 --- a/selfdrive/ui/qt/network/wifi_manager.h +++ b/selfdrive/ui/qt/network/wifi_manager.h @@ -58,6 +58,7 @@ public: void setTetheringEnabled(bool enabled); bool isTetheringEnabled(); void changeTetheringPassword(const QString &newPassword); + QString getIp4Address(); QString getTetheringPassword(); private: @@ -72,7 +73,6 @@ private: QString getAdapter(const uint = NM_DEVICE_TYPE_WIFI); uint getAdapterType(const QDBusObjectPath &path); - QString getIp4Address(); void deactivateConnectionBySsid(const QString &ssid); void deactivateConnection(const QDBusObjectPath &path); QVector getActiveConnections(); diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index df64e73..dd0987d 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -26,6 +26,7 @@ #include "selfdrive/ui/qt/util.h" #include "selfdrive/ui/qt/qt_window.h" +#include "selfdrive/frogpilot/navigation/ui/navigation_settings.h" #include "selfdrive/frogpilot/ui/qt/offroad/control_settings.h" #include "selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h" #include "selfdrive/frogpilot/ui/qt/offroad/visual_settings.h" @@ -636,6 +637,7 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) { {tr("Toggles"), toggles}, {tr("Software"), new SoftwarePanel(this)}, {tr("Controls"), frogpilotControls}, + {tr("Navigation"), new FrogPilotNavigationPanel(this)}, {tr("Vehicles"), new FrogPilotVehiclesPanel(this)}, {tr("Visuals"), frogpilotVisuals}, };