|  |  |  | #include "selfdrive/ui/qt/setup/setup.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <cstdio>
 | 
					
						
							|  |  |  | #include <cstdlib>
 | 
					
						
							|  |  |  | #include <sstream>
 | 
					
						
							|  |  |  | #include <string>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <QApplication>
 | 
					
						
							|  |  |  | #include <QLabel>
 | 
					
						
							|  |  |  | #include <QVBoxLayout>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <curl/curl.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "common/util.h"
 | 
					
						
							|  |  |  | #include "system/hardware/hw.h"
 | 
					
						
							|  |  |  | #include "selfdrive/ui/qt/api.h"
 | 
					
						
							|  |  |  | #include "selfdrive/ui/qt/qt_window.h"
 | 
					
						
							|  |  |  | #include "selfdrive/ui/qt/network/networking.h"
 | 
					
						
							|  |  |  | #include "selfdrive/ui/qt/util.h"
 | 
					
						
							|  |  |  | #include "selfdrive/ui/qt/widgets/input.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const std::string USER_AGENT = "AGNOSSetup-";
 | 
					
						
							|  |  |  | const QString TEST_URL = "https://openpilot.comma.ai";
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool is_elf(char *fname) {
 | 
					
						
							|  |  |  |   FILE *fp = fopen(fname, "rb");
 | 
					
						
							|  |  |  |   if (fp == NULL) {
 | 
					
						
							|  |  |  |     return false;
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  |   char buf[4];
 | 
					
						
							|  |  |  |   size_t n = fread(buf, 1, 4, fp);
 | 
					
						
							|  |  |  |   fclose(fp);
 | 
					
						
							|  |  |  |   return n == 4 && buf[0] == 0x7f && buf[1] == 'E' && buf[2] == 'L' && buf[3] == 'F';
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Setup::download(QString url) {
 | 
					
						
							|  |  |  |   // autocomplete incomplete urls
 | 
					
						
							|  |  |  |   if (QRegularExpression("^([^/.]+)/([^/]+)$").match(url).hasMatch()) {
 | 
					
						
							|  |  |  |     url.prepend("https://installer.comma.ai/");
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   CURL *curl = curl_easy_init();
 | 
					
						
							|  |  |  |   if (!curl) {
 | 
					
						
							|  |  |  |     emit finished(url, tr("Something went wrong. Reboot the device."));
 | 
					
						
							|  |  |  |     return;
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   auto version = util::read_file("/VERSION");
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   struct curl_slist *list = NULL;
 | 
					
						
							|  |  |  |   list = curl_slist_append(list, ("X-openpilot-serial: " + Hardware::get_serial()).c_str());
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   char tmpfile[] = "/tmp/installer_XXXXXX";
 | 
					
						
							|  |  |  |   FILE *fp = fdopen(mkstemp(tmpfile), "w");
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   curl_easy_setopt(curl, CURLOPT_URL, url.toStdString().c_str());
 | 
					
						
							|  |  |  |   curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
 | 
					
						
							|  |  |  |   curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
 | 
					
						
							|  |  |  |   curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
 | 
					
						
							|  |  |  |   curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
 | 
					
						
							|  |  |  |   curl_easy_setopt(curl, CURLOPT_USERAGENT, (USER_AGENT + version).c_str());
 | 
					
						
							|  |  |  |   curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
 | 
					
						
							|  |  |  |   curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   int ret = curl_easy_perform(curl);
 | 
					
						
							|  |  |  |   long res_status = 0;
 | 
					
						
							|  |  |  |   curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &res_status);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (ret != CURLE_OK || res_status != 200) {
 | 
					
						
							|  |  |  |     emit finished(url, tr("Ensure the entered URL is valid, and the device’s internet connection is good."));
 | 
					
						
							|  |  |  |   } else if (!is_elf(tmpfile)) {
 | 
					
						
							|  |  |  |     emit finished(url, tr("No custom software found at this URL."));
 | 
					
						
							|  |  |  |   } else {
 | 
					
						
							|  |  |  |     rename(tmpfile, "/tmp/installer");
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     FILE *fp_url = fopen("/tmp/installer_url", "w");
 | 
					
						
							|  |  |  |     fprintf(fp_url, "%s", url.toStdString().c_str());
 | 
					
						
							|  |  |  |     fclose(fp_url);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     emit finished(url);
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   curl_slist_free_all(list);
 | 
					
						
							|  |  |  |   curl_easy_cleanup(curl);
 | 
					
						
							|  |  |  |   fclose(fp);
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | QWidget * Setup::low_voltage() {
 | 
					
						
							|  |  |  |   QWidget *widget = new QWidget();
 | 
					
						
							|  |  |  |   QVBoxLayout *main_layout = new QVBoxLayout(widget);
 | 
					
						
							|  |  |  |   main_layout->setContentsMargins(55, 0, 55, 55);
 | 
					
						
							|  |  |  |   main_layout->setSpacing(0);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // inner text layout: warning icon, title, and body
 | 
					
						
							|  |  |  |   QVBoxLayout *inner_layout = new QVBoxLayout();
 | 
					
						
							|  |  |  |   inner_layout->setContentsMargins(110, 144, 365, 0);
 | 
					
						
							|  |  |  |   main_layout->addLayout(inner_layout);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   QLabel *triangle = new QLabel();
 | 
					
						
							|  |  |  |   triangle->setPixmap(QPixmap(ASSET_PATH + "offroad/icon_warning.png"));
 | 
					
						
							|  |  |  |   inner_layout->addWidget(triangle, 0, Qt::AlignTop | Qt::AlignLeft);
 | 
					
						
							|  |  |  |   inner_layout->addSpacing(80);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   QLabel *title = new QLabel(tr("WARNING: Low Voltage"));
 | 
					
						
							|  |  |  |   title->setStyleSheet("font-size: 90px; font-weight: 500; color: #FF594F;");
 | 
					
						
							|  |  |  |   inner_layout->addWidget(title, 0, Qt::AlignTop | Qt::AlignLeft);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   inner_layout->addSpacing(25);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   QLabel *body = new QLabel(tr("Power your device in a car with a harness or proceed at your own risk."));
 | 
					
						
							|  |  |  |   body->setWordWrap(true);
 | 
					
						
							|  |  |  |   body->setAlignment(Qt::AlignTop | Qt::AlignLeft);
 | 
					
						
							|  |  |  |   body->setStyleSheet("font-size: 80px; font-weight: 300;");
 | 
					
						
							|  |  |  |   inner_layout->addWidget(body);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   inner_layout->addStretch();
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // power off + continue buttons
 | 
					
						
							|  |  |  |   QHBoxLayout *blayout = new QHBoxLayout();
 | 
					
						
							|  |  |  |   blayout->setSpacing(50);
 | 
					
						
							|  |  |  |   main_layout->addLayout(blayout, 0);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   QPushButton *poweroff = new QPushButton(tr("Power off"));
 | 
					
						
							|  |  |  |   poweroff->setObjectName("navBtn");
 | 
					
						
							|  |  |  |   blayout->addWidget(poweroff);
 | 
					
						
							|  |  |  |   QObject::connect(poweroff, &QPushButton::clicked, this, [=]() {
 | 
					
						
							|  |  |  |     Hardware::poweroff();
 | 
					
						
							|  |  |  |   });
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   QPushButton *cont = new QPushButton(tr("Continue"));
 | 
					
						
							|  |  |  |   cont->setObjectName("navBtn");
 | 
					
						
							|  |  |  |   blayout->addWidget(cont);
 | 
					
						
							|  |  |  |   QObject::connect(cont, &QPushButton::clicked, this, &Setup::nextPage);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return widget;
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | QWidget * Setup::getting_started() {
 | 
					
						
							|  |  |  |   QWidget *widget = new QWidget();
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   QHBoxLayout *main_layout = new QHBoxLayout(widget);
 | 
					
						
							|  |  |  |   main_layout->setMargin(0);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   QVBoxLayout *vlayout = new QVBoxLayout();
 | 
					
						
							|  |  |  |   vlayout->setContentsMargins(165, 280, 100, 0);
 | 
					
						
							|  |  |  |   main_layout->addLayout(vlayout);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   QLabel *title = new QLabel(tr("Getting Started"));
 | 
					
						
							|  |  |  |   title->setStyleSheet("font-size: 90px; font-weight: 500;");
 | 
					
						
							|  |  |  |   vlayout->addWidget(title, 0, Qt::AlignTop | Qt::AlignLeft);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   vlayout->addSpacing(90);
 | 
					
						
							|  |  |  |   QLabel *desc = new QLabel(tr("Before we get on the road, let’s finish installation and cover some details."));
 | 
					
						
							|  |  |  |   desc->setWordWrap(true);
 | 
					
						
							|  |  |  |   desc->setStyleSheet("font-size: 80px; font-weight: 300;");
 | 
					
						
							|  |  |  |   vlayout->addWidget(desc, 0, Qt::AlignTop | Qt::AlignLeft);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   vlayout->addStretch();
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   QPushButton *btn = new QPushButton();
 | 
					
						
							|  |  |  |   btn->setIcon(QIcon(":/img_continue_triangle.svg"));
 | 
					
						
							|  |  |  |   btn->setIconSize(QSize(54, 106));
 | 
					
						
							|  |  |  |   btn->setFixedSize(310, 1080);
 | 
					
						
							|  |  |  |   btn->setProperty("primary", true);
 | 
					
						
							|  |  |  |   btn->setStyleSheet("border: none;");
 | 
					
						
							|  |  |  |   main_layout->addWidget(btn, 0, Qt::AlignRight);
 | 
					
						
							|  |  |  |   QObject::connect(btn, &QPushButton::clicked, this, &Setup::nextPage);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return widget;
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | QWidget * Setup::network_setup() {
 | 
					
						
							|  |  |  |   QWidget *widget = new QWidget();
 | 
					
						
							|  |  |  |   QVBoxLayout *main_layout = new QVBoxLayout(widget);
 | 
					
						
							|  |  |  |   main_layout->setContentsMargins(55, 50, 55, 50);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // title
 | 
					
						
							|  |  |  |   QLabel *title = new QLabel(tr("Connect to Wi-Fi"));
 | 
					
						
							|  |  |  |   title->setStyleSheet("font-size: 90px; font-weight: 500;");
 | 
					
						
							|  |  |  |   main_layout->addWidget(title, 0, Qt::AlignLeft | Qt::AlignTop);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   main_layout->addSpacing(25);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // wifi widget
 | 
					
						
							|  |  |  |   Networking *networking = new Networking(this, false);
 | 
					
						
							|  |  |  |   networking->setStyleSheet("Networking {background-color: #292929; border-radius: 13px;}");
 | 
					
						
							|  |  |  |   main_layout->addWidget(networking, 1);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   main_layout->addSpacing(35);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // back + continue buttons
 | 
					
						
							|  |  |  |   QHBoxLayout *blayout = new QHBoxLayout;
 | 
					
						
							|  |  |  |   main_layout->addLayout(blayout);
 | 
					
						
							|  |  |  |   blayout->setSpacing(50);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   QPushButton *back = new QPushButton(tr("Back"));
 | 
					
						
							|  |  |  |   back->setObjectName("navBtn");
 | 
					
						
							|  |  |  |   QObject::connect(back, &QPushButton::clicked, this, &Setup::prevPage);
 | 
					
						
							|  |  |  |   blayout->addWidget(back);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   QPushButton *cont = new QPushButton();
 | 
					
						
							|  |  |  |   cont->setObjectName("navBtn");
 | 
					
						
							|  |  |  |   cont->setProperty("primary", true);
 | 
					
						
							|  |  |  |   QObject::connect(cont, &QPushButton::clicked, [=]() {
 | 
					
						
							|  |  |  |     auto w = currentWidget();
 | 
					
						
							|  |  |  |     QTimer::singleShot(0, [=]() {
 | 
					
						
							|  |  |  |       setCurrentWidget(downloading_widget);
 | 
					
						
							|  |  |  |     });
 | 
					
						
							|  |  |  |     QString url = InputDialog::getText(tr("Enter URL"), this, tr("for Custom Software"));
 | 
					
						
							|  |  |  |     if (!url.isEmpty()) {
 | 
					
						
							|  |  |  |       QTimer::singleShot(1000, this, [=]() {
 | 
					
						
							|  |  |  |         download(url);
 | 
					
						
							|  |  |  |       });
 | 
					
						
							|  |  |  |     } else {
 | 
					
						
							|  |  |  |       setCurrentWidget(w);
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  |   });
 | 
					
						
							|  |  |  |   blayout->addWidget(cont);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // setup timer for testing internet connection
 | 
					
						
							|  |  |  |   HttpRequest *request = new HttpRequest(this, false, 2500);
 | 
					
						
							|  |  |  |   QObject::connect(request, &HttpRequest::requestDone, [=](const QString &, bool success) {
 | 
					
						
							|  |  |  |     cont->setEnabled(success);
 | 
					
						
							|  |  |  |     if (success) {
 | 
					
						
							|  |  |  |       const bool wifi = networking->wifi->currentNetworkType() == NetworkType::WIFI;
 | 
					
						
							|  |  |  |       cont->setText(wifi ? tr("Continue") : tr("Continue without Wi-Fi"));
 | 
					
						
							|  |  |  |     } else {
 | 
					
						
							|  |  |  |       cont->setText(tr("Waiting for internet"));
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  |     repaint();
 | 
					
						
							|  |  |  |   });
 | 
					
						
							|  |  |  |   request->sendRequest(TEST_URL);
 | 
					
						
							|  |  |  |   QTimer *timer = new QTimer(this);
 | 
					
						
							|  |  |  |   QObject::connect(timer, &QTimer::timeout, [=]() {
 | 
					
						
							|  |  |  |     if (!request->active() && cont->isVisible()) {
 | 
					
						
							|  |  |  |       request->sendRequest(TEST_URL);
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  |   });
 | 
					
						
							|  |  |  |   timer->start(1000);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return widget;
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | QWidget * Setup::downloading() {
 | 
					
						
							|  |  |  |   QWidget *widget = new QWidget();
 | 
					
						
							|  |  |  |   QVBoxLayout *main_layout = new QVBoxLayout(widget);
 | 
					
						
							|  |  |  |   QLabel *txt = new QLabel(tr("Downloading..."));
 | 
					
						
							|  |  |  |   txt->setStyleSheet("font-size: 90px; font-weight: 500;");
 | 
					
						
							|  |  |  |   main_layout->addWidget(txt, 0, Qt::AlignCenter);
 | 
					
						
							|  |  |  |   return widget;
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | QWidget * Setup::download_failed(QLabel *url, QLabel *body) {
 | 
					
						
							|  |  |  |   QWidget *widget = new QWidget();
 | 
					
						
							|  |  |  |   QVBoxLayout *main_layout = new QVBoxLayout(widget);
 | 
					
						
							|  |  |  |   main_layout->setContentsMargins(55, 185, 55, 55);
 | 
					
						
							|  |  |  |   main_layout->setSpacing(0);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   QLabel *title = new QLabel(tr("Download Failed"));
 | 
					
						
							|  |  |  |   title->setStyleSheet("font-size: 90px; font-weight: 500;");
 | 
					
						
							|  |  |  |   main_layout->addWidget(title, 0, Qt::AlignTop | Qt::AlignLeft);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   main_layout->addSpacing(67);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   url->setWordWrap(true);
 | 
					
						
							|  |  |  |   url->setAlignment(Qt::AlignTop | Qt::AlignLeft);
 | 
					
						
							|  |  |  |   url->setStyleSheet("font-family: \"JetBrains Mono\"; font-size: 64px; font-weight: 400; margin-right: 100px;");
 | 
					
						
							|  |  |  |   main_layout->addWidget(url);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   main_layout->addSpacing(48);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   body->setWordWrap(true);
 | 
					
						
							|  |  |  |   body->setAlignment(Qt::AlignTop | Qt::AlignLeft);
 | 
					
						
							|  |  |  |   body->setStyleSheet("font-size: 80px; font-weight: 300; margin-right: 100px;");
 | 
					
						
							|  |  |  |   main_layout->addWidget(body);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   main_layout->addStretch();
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // reboot + start over buttons
 | 
					
						
							|  |  |  |   QHBoxLayout *blayout = new QHBoxLayout();
 | 
					
						
							|  |  |  |   blayout->setSpacing(50);
 | 
					
						
							|  |  |  |   main_layout->addLayout(blayout, 0);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   QPushButton *reboot = new QPushButton(tr("Reboot device"));
 | 
					
						
							|  |  |  |   reboot->setObjectName("navBtn");
 | 
					
						
							|  |  |  |   blayout->addWidget(reboot);
 | 
					
						
							|  |  |  |   QObject::connect(reboot, &QPushButton::clicked, this, [=]() {
 | 
					
						
							|  |  |  |     Hardware::reboot();
 | 
					
						
							|  |  |  |   });
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   QPushButton *restart = new QPushButton(tr("Start over"));
 | 
					
						
							|  |  |  |   restart->setObjectName("navBtn");
 | 
					
						
							|  |  |  |   restart->setProperty("primary", true);
 | 
					
						
							|  |  |  |   blayout->addWidget(restart);
 | 
					
						
							|  |  |  |   QObject::connect(restart, &QPushButton::clicked, this, [=]() {
 | 
					
						
							|  |  |  |     setCurrentIndex(1);
 | 
					
						
							|  |  |  |   });
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   widget->setStyleSheet(R"(
 | 
					
						
							|  |  |  |     QLabel {
 | 
					
						
							|  |  |  |       margin-left: 117;
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  |   )");
 | 
					
						
							|  |  |  |   return widget;
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Setup::prevPage() {
 | 
					
						
							|  |  |  |   setCurrentIndex(currentIndex() - 1);
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Setup::nextPage() {
 | 
					
						
							|  |  |  |   setCurrentIndex(currentIndex() + 1);
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Setup::Setup(QWidget *parent) : QStackedWidget(parent) {
 | 
					
						
							|  |  |  |   if (std::getenv("MULTILANG")) {
 | 
					
						
							|  |  |  |     selectLanguage();
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   std::stringstream buffer;
 | 
					
						
							|  |  |  |   buffer << std::ifstream("/sys/class/hwmon/hwmon1/in1_input").rdbuf();
 | 
					
						
							|  |  |  |   float voltage = (float)std::atoi(buffer.str().c_str()) / 1000.;
 | 
					
						
							|  |  |  |   if (voltage < 7) {
 | 
					
						
							|  |  |  |     addWidget(low_voltage());
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   addWidget(getting_started());
 | 
					
						
							|  |  |  |   addWidget(network_setup());
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   downloading_widget = downloading();
 | 
					
						
							|  |  |  |   addWidget(downloading_widget);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   QLabel *url_label = new QLabel();
 | 
					
						
							|  |  |  |   QLabel *body_label = new QLabel();
 | 
					
						
							|  |  |  |   failed_widget = download_failed(url_label, body_label);
 | 
					
						
							|  |  |  |   addWidget(failed_widget);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   QObject::connect(this, &Setup::finished, [=](const QString &url, const QString &error) {
 | 
					
						
							|  |  |  |     qDebug() << "finished" << url << error;
 | 
					
						
							|  |  |  |     if (error.isEmpty()) {
 | 
					
						
							|  |  |  |       // hide setup on success
 | 
					
						
							|  |  |  |       QTimer::singleShot(3000, this, &QWidget::hide);
 | 
					
						
							|  |  |  |     } else {
 | 
					
						
							|  |  |  |       url_label->setText(url);
 | 
					
						
							|  |  |  |       body_label->setText(error);
 | 
					
						
							|  |  |  |       setCurrentWidget(failed_widget);
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  |   });
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // TODO: revisit pressed bg color
 | 
					
						
							|  |  |  |   setStyleSheet(R"(
 | 
					
						
							|  |  |  |     * {
 | 
					
						
							|  |  |  |       color: white;
 | 
					
						
							|  |  |  |       font-family: Inter;
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  |     Setup {
 | 
					
						
							|  |  |  |       background-color: black;
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  |     QPushButton#navBtn {
 | 
					
						
							|  |  |  |       height: 160;
 | 
					
						
							|  |  |  |       font-size: 55px;
 | 
					
						
							|  |  |  |       font-weight: 400;
 | 
					
						
							|  |  |  |       border-radius: 10px;
 | 
					
						
							|  |  |  |       background-color: #333333;
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  |     QPushButton#navBtn:disabled, QPushButton[primary='true']:disabled {
 | 
					
						
							|  |  |  |       color: #808080;
 | 
					
						
							|  |  |  |       background-color: #333333;
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  |     QPushButton#navBtn:pressed {
 | 
					
						
							|  |  |  |       background-color: #444444;
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  |     QPushButton[primary='true'], #navBtn[primary='true'] {
 | 
					
						
							|  |  |  |       background-color: #465BEA;
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  |     QPushButton[primary='true']:pressed, #navBtn:pressed[primary='true'] {
 | 
					
						
							|  |  |  |       background-color: #3049F4;
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  |   )");
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Setup::selectLanguage() {
 | 
					
						
							|  |  |  |   QMap<QString, QString> langs = getSupportedLanguages();
 | 
					
						
							|  |  |  |   QString selection = MultiOptionDialog::getSelection(tr("Select a language"), langs.keys(), "", this);
 | 
					
						
							|  |  |  |   if (!selection.isEmpty()) {
 | 
					
						
							|  |  |  |     QString selectedLang = langs[selection];
 | 
					
						
							|  |  |  |     Params().put("LanguageSetting", selectedLang.toStdString());
 | 
					
						
							|  |  |  |     if (translator.load(":/" + selectedLang)) {
 | 
					
						
							|  |  |  |       qApp->installTranslator(&translator);
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int main(int argc, char *argv[]) {
 | 
					
						
							|  |  |  |   QApplication a(argc, argv);
 | 
					
						
							|  |  |  |   Setup setup;
 | 
					
						
							|  |  |  |   setMainWindow(&setup);
 | 
					
						
							|  |  |  |   return a.exec();
 | 
					
						
							|  |  |  | }
 |