diff --git a/selfdrive/assets/navigation/home.png b/selfdrive/assets/navigation/home.png new file mode 100644 index 0000000000..8a4f65c7d7 Binary files /dev/null and b/selfdrive/assets/navigation/home.png differ diff --git a/selfdrive/assets/navigation/home.svg b/selfdrive/assets/navigation/home.svg new file mode 100644 index 0000000000..f5d89514c3 --- /dev/null +++ b/selfdrive/assets/navigation/home.svg @@ -0,0 +1,65 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/selfdrive/assets/navigation/home_inactive.png b/selfdrive/assets/navigation/home_inactive.png new file mode 100644 index 0000000000..a58fd3864f Binary files /dev/null and b/selfdrive/assets/navigation/home_inactive.png differ diff --git a/selfdrive/assets/navigation/work.png b/selfdrive/assets/navigation/work.png new file mode 100644 index 0000000000..611f9b038d Binary files /dev/null and b/selfdrive/assets/navigation/work.png differ diff --git a/selfdrive/assets/navigation/work.svg b/selfdrive/assets/navigation/work.svg new file mode 100644 index 0000000000..2da7bb7d39 --- /dev/null +++ b/selfdrive/assets/navigation/work.svg @@ -0,0 +1,66 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/selfdrive/assets/navigation/work_inactive.png b/selfdrive/assets/navigation/work_inactive.png new file mode 100644 index 0000000000..679e6a54b2 Binary files /dev/null and b/selfdrive/assets/navigation/work_inactive.png differ diff --git a/selfdrive/common/params.cc b/selfdrive/common/params.cc index 582291c183..a440352743 100644 --- a/selfdrive/common/params.cc +++ b/selfdrive/common/params.cc @@ -151,6 +151,7 @@ std::unordered_map keys = { {"ApiCache_DriveStats", PERSISTENT}, {"ApiCache_Device", PERSISTENT}, {"ApiCache_Owner", PERSISTENT}, + {"ApiCache_NavDestinations", PERSISTENT}, {"AthenadPid", PERSISTENT}, {"CalibrationParams", PERSISTENT}, {"CarBatteryCapacity", PERSISTENT}, diff --git a/selfdrive/ui/qt/home.cc b/selfdrive/ui/qt/home.cc index 4fef26de24..f2ac7ff6d2 100644 --- a/selfdrive/ui/qt/home.cc +++ b/selfdrive/ui/qt/home.cc @@ -44,6 +44,10 @@ HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) { slayout->addWidget(driver_view); } +void HomeWindow::showSidebar(bool show) { + sidebar->setVisible(show); +} + void HomeWindow::offroadTransition(bool offroad) { if (offroad) { slayout->setCurrentWidget(home); diff --git a/selfdrive/ui/qt/home.h b/selfdrive/ui/qt/home.h index db3e2f8f6e..ab60af0a21 100644 --- a/selfdrive/ui/qt/home.h +++ b/selfdrive/ui/qt/home.h @@ -54,6 +54,7 @@ signals: public slots: void offroadTransition(bool offroad); void showDriverView(bool show); + void showSidebar(bool show); protected: void mousePressEvent(QMouseEvent* e) override; diff --git a/selfdrive/ui/qt/maps/map.cc b/selfdrive/ui/qt/maps/map.cc index d74e753d90..7bfb97b581 100644 --- a/selfdrive/ui/qt/maps/map.cc +++ b/selfdrive/ui/qt/maps/map.cc @@ -276,6 +276,7 @@ void MapWindow::recomputeRoute() { if (*new_destination != nav_destination) { setVisible(true); // Show map on destination set/change + // TODO: close sidebar should_recompute = true; } diff --git a/selfdrive/ui/qt/maps/map_settings.cc b/selfdrive/ui/qt/maps/map_settings.cc index f6a3bdaeb2..bf17091bc9 100644 --- a/selfdrive/ui/qt/maps/map_settings.cc +++ b/selfdrive/ui/qt/maps/map_settings.cc @@ -1,19 +1,124 @@ #include "map_settings.h" +#include + +#include "selfdrive/ui/qt/request_repeater.h" #include "selfdrive/ui/qt/widgets/controls.h" +#include "selfdrive/common/util.h" +static QString shorten(const QString &str, int max_len) { + return str.size() > max_len ? str.left(max_len).trimmed() + "…" : str; +} MapPanel::MapPanel(QWidget* parent) : QWidget(parent) { QVBoxLayout *main_layout = new QVBoxLayout(this); Params params = Params(); - QString dongle = QString::fromStdString(params.get("DongleId", false)); - // TODO: Add buttons for home/work shortcuts + // Home + QHBoxLayout *home_layout = new QHBoxLayout; + home_button = new QPushButton; + home_button->setIconSize(QSize(200, 200)); + home_layout->addWidget(home_button); + + home_address = new QLabel; + home_address->setWordWrap(true); + home_layout->addSpacing(30); + home_layout->addWidget(home_address); + home_layout->addStretch(); + + // Work + QHBoxLayout *work_layout = new QHBoxLayout; + work_button = new QPushButton; + work_button->setIconSize(QSize(200, 200)); + work_layout->addWidget(work_button); + + work_address = new QLabel; + work_address->setWordWrap(true); + work_layout->addSpacing(30); + work_layout->addWidget(work_address); + work_layout->addStretch(); + + // Home & Work layout + QHBoxLayout *home_work_layout = new QHBoxLayout; + home_work_layout->addLayout(home_layout, 1); + home_work_layout->addSpacing(50); + home_work_layout->addLayout(work_layout, 1); + main_layout->addLayout(home_work_layout); + main_layout->addSpacing(50); + main_layout->addWidget(horizontal_line()); + + // Settings main_layout->addWidget(new ParamControl("NavSettingTime24h", "Show ETA in 24h format", "Use 24h format instead of am/pm", "", this)); main_layout->addStretch(); + + clear(); + + std::string dongle_id = Params().get("DongleId"); + if (util::is_valid_dongle_id(dongle_id)) { + std::string url = "https://api.commadotai.com/v1/navigation/" + dongle_id + "/locations"; + RequestRepeater* repeater = new RequestRepeater(this, QString::fromStdString(url), "ApiCache_NavDestinations", 30); + QObject::connect(repeater, &RequestRepeater::receivedResponse, this, &MapPanel::parseResponse); + } +} + +void MapPanel::clear() { + home_button->setIcon(QPixmap("../assets/navigation/home_inactive.png")); + home_address->setStyleSheet(R"(font-size: 50px; color: grey;)"); + home_address->setText("No home\nlocation set"); + home_button->disconnect(); + + work_button->setIcon(QPixmap("../assets/navigation/work_inactive.png")); + work_address->setStyleSheet(R"(font-size: 50px; color: grey;)"); + work_address->setText("No work\nlocation set"); + work_button->disconnect(); +} + + +void MapPanel::parseResponse(const QString &response) { + QJsonDocument doc = QJsonDocument::fromJson(response.trimmed().toUtf8()); + if (doc.isNull()) { + qDebug() << "JSON Parse failed on navigation locations"; + return; + } + + clear(); + + for (auto location : doc.array()) { + auto obj = location.toObject(); + + auto type = obj["save_type"].toString(); + auto label = obj["label"].toString(); + auto name = obj["place_name"].toString(); + auto details = shorten(obj["place_details"].toString(), 30); + + if (type == "favorite") { + if (label == "home") { + home_address->setText(name); + home_address->setStyleSheet(R"(font-size: 50px; color: white;)"); + home_button->setIcon(QPixmap("../assets/navigation/home.png")); + QObject::connect(home_button, &QPushButton::clicked, [=]() { + navigateTo(obj); + emit closeSettings(); + }); + } else if (label == "work") { + work_address->setText(name); + work_address->setStyleSheet(R"(font-size: 50px; color: white;)"); + work_button->setIcon(QPixmap("../assets/navigation/work.png")); + QObject::connect(work_button, &QPushButton::clicked, [=]() { + navigateTo(obj); + emit closeSettings(); + }); + } + } + } +} + +void MapPanel::navigateTo(const QJsonObject &place) { + QJsonDocument doc(place); + Params().put("NavDestination", doc.toJson().toStdString()); } diff --git a/selfdrive/ui/qt/maps/map_settings.h b/selfdrive/ui/qt/maps/map_settings.h index b2deec696f..26e4f95f95 100644 --- a/selfdrive/ui/qt/maps/map_settings.h +++ b/selfdrive/ui/qt/maps/map_settings.h @@ -1,8 +1,24 @@ #pragma once #include +#include +#include +#include +#include +#include class MapPanel : public QWidget { Q_OBJECT public: explicit MapPanel(QWidget* parent = nullptr); -}; \ No newline at end of file + + void navigateTo(const QJsonObject &place); + void parseResponse(const QString &response); + void clear(); + +private: + QPushButton *home_button, *work_button; + QLabel *home_address, *work_address; + +signals: + void closeSettings(); +}; diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index 84f3533858..4ec5b6ebe0 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -345,7 +345,9 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) { #ifdef ENABLE_MAPS if (!Params().get("MapboxToken").empty()) { - panels.push_back({"Navigation", new MapPanel(this)}); + auto map_panel = new MapPanel(this); + panels.push_back({"Navigation", map_panel}); + QObject::connect(map_panel, &MapPanel::closeSettings, this, &SettingsWindow::closeSettings); } #endif const int padding = panels.size() > 3 ? 25 : 35; diff --git a/selfdrive/ui/qt/window.cc b/selfdrive/ui/qt/window.cc index 2a821abde4..04c50684b0 100644 --- a/selfdrive/ui/qt/window.cc +++ b/selfdrive/ui/qt/window.cc @@ -67,6 +67,10 @@ void MainWindow::openSettings() { void MainWindow::closeSettings() { main_layout->setCurrentWidget(homeWindow); + + if (QUIState::ui_state.scene.started) { + emit homeWindow->showSidebar(false); + } } bool MainWindow::eventFilter(QObject *obj, QEvent *event) {