UI: big pairing QR code (#22384)

pull/22391/head
Adeeb Shihadeh 2021-09-30 14:00:52 -07:00 committed by GitHub
parent b289ee6e53
commit 45409cb4fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 121 additions and 72 deletions

View File

@ -427,6 +427,7 @@ selfdrive/assets/assets.qrc
selfdrive/assets/*.png
selfdrive/assets/*.svg
selfdrive/assets/fonts/*.ttf
selfdrive/assets/icons/*
selfdrive/assets/images/*
selfdrive/assets/offroad/*
selfdrive/assets/sounds/*

View File

@ -5,6 +5,7 @@
<file>img_circled_slash.svg</file>
<file>img_eye_open.svg</file>
<file>img_eye_closed.svg</file>
<file>icons/close.svg</file>
<file>offroad/icon_lock_closed.svg</file>
<file>offroad/icon_checkmark.svg</file>
<file>offroad/icon_warning.png</file>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="svg4" width="63.238" height="63.238" fill="none" version="1.1" viewBox="0 0 63.238 63.238" xmlns="http://www.w3.org/2000/svg">
<path id="path2" d="m 55.7181,63.2381 7.52,-7.52 L 39.1199,31.6 63.219,7.5009 55.7371,0.019 31.6381,24.1181 7.5199,0 0,7.52 24.1181,31.6381 0.019,55.7372 7.5009,63.219 31.6,39.12 Z" fill="#000"/>
</svg>

After

Width:  |  Height:  |  Size: 379 B

View File

@ -0,0 +1,9 @@
#!/bin/bash
# sudo apt install scour
for svg in $(find icons/ -type f | grep svg$); do
# scour doesn't support overwriting input file
scour $svg --remove-metadata $svg.tmp
mv $svg.tmp $svg
done

View File

@ -32,7 +32,6 @@ QDialogBase::QDialogBase(QWidget *parent) : QDialog(parent) {
QPushButton:pressed {
background-color: #444444;
}
)");
}

View File

@ -8,21 +8,18 @@
#include <QStackedWidget>
#include <QTimer>
#include <QVBoxLayout>
#include <QrCode.hpp>
#include "selfdrive/ui/qt/request_repeater.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/qt_window.h"
using qrcodegen::QrCode;
PairingQRWidget::PairingQRWidget(QWidget* parent) : QWidget(parent) {
qrCode = new QLabel;
qrCode->setScaledContents(true);
QVBoxLayout* main_layout = new QVBoxLayout(this);
main_layout->addWidget(qrCode, 0, Qt::AlignCenter);
QTimer* timer = new QTimer(this);
timer->start(30 * 1000);
timer->start(5 * 60 * 1000);
connect(timer, &QTimer::timeout, this, &PairingQRWidget::refresh);
}
@ -31,35 +28,86 @@ void PairingQRWidget::showEvent(QShowEvent *event) {
}
void PairingQRWidget::refresh() {
QString pairToken = CommaApi::create_jwt({{"pair", true}});
QString qrString = "https://connect.comma.ai/?pair=" + pairToken;
this->updateQrCode(qrString);
if (isVisible()) {
QString pairToken = CommaApi::create_jwt({{"pair", true}});
QString qrString = "https://connect.comma.ai/?pair=" + pairToken;
this->updateQrCode(qrString);
}
}
void PairingQRWidget::updateQrCode(const QString &text) {
QrCode qr = QrCode::encodeText(text.toUtf8().data(), QrCode::Ecc::LOW);
qint32 sz = qr.getSize();
// make the image larger so we can have a white border
QImage im(sz + 2, sz + 2, QImage::Format_RGB32);
QImage im(sz, sz, QImage::Format_RGB32);
QRgb black = qRgb(0, 0, 0);
QRgb white = qRgb(255, 255, 255);
for (int y = 0; y < sz + 2; y++) {
for (int x = 0; x < sz + 2; x++) {
im.setPixel(x, y, white);
}
}
for (int y = 0; y < sz; y++) {
for (int x = 0; x < sz; x++) {
im.setPixel(x + 1, y + 1, qr.getModule(x, y) ? black : white);
im.setPixel(x, y, qr.getModule(x, y) ? black : white);
}
}
// Integer division to prevent anti-aliasing
int approx500 = (500 / (sz + 2)) * (sz + 2);
qrCode->setPixmap(QPixmap::fromImage(im.scaled(approx500, approx500, Qt::KeepAspectRatio, Qt::FastTransformation), Qt::MonoOnly));
qrCode->setFixedSize(approx500, approx500);
int final_sz = ((width() / sz) - 1) * sz;
img = QPixmap::fromImage(im.scaled(final_sz, final_sz, Qt::KeepAspectRatio), Qt::MonoOnly);
}
void PairingQRWidget::paintEvent(QPaintEvent *e) {
QPainter p(this);
p.fillRect(rect(), Qt::white);
QSize s = (size() - img.size()) / 2;
p.drawPixmap(s.width(), s.height(), img);
}
PairingPopup::PairingPopup(QWidget *parent) : QDialogBase(parent) {
QHBoxLayout *hlayout = new QHBoxLayout(this);
hlayout->setContentsMargins(0, 0, 0, 0);
hlayout->setSpacing(0);
setStyleSheet("PairingPopup { background-color: #E0E0E0; }");
// text
QVBoxLayout *vlayout = new QVBoxLayout();
vlayout->setContentsMargins(85, 70, 50, 70);
vlayout->setSpacing(50);
hlayout->addLayout(vlayout, 1);
{
QPushButton *close = new QPushButton(QIcon(":/icons/close.svg"), "", this);
close->setIconSize(QSize(80, 80));
close->setStyleSheet("border: none;");
vlayout->addWidget(close, 0, Qt::AlignLeft);
QObject::connect(close, &QPushButton::clicked, this, &QDialog::reject);
vlayout->addSpacing(30);
QLabel *title = new QLabel("Pair your device to your comma account", this);
title->setStyleSheet("font-size: 75px; color: black;");
title->setWordWrap(true);
vlayout->addWidget(title);
QLabel *instructions = new QLabel(R"(
<ol type='1' style='margin-left: 15px;'>
<li style='margin-bottom: 50px;'>Go to https://connect.comma.ai on your phone</li>
<li style='margin-bottom: 50px;'>Click "add new device" and scan the QR code on the right</li>
<li style='margin-bottom: 50px;'>Bookmark connect.comma.ai to your home screen to use it like an app</li>
</ol>
)", this);
instructions->setStyleSheet("font-size: 47px; font-weight: bold; color: black;");
instructions->setWordWrap(true);
vlayout->addWidget(instructions);
vlayout->addStretch();
}
// QR code
PairingQRWidget *qr = new PairingQRWidget(this);
hlayout->addWidget(qr, 1);
}
PrimeUserWidget::PrimeUserWidget(QWidget* parent) : QWidget(parent) {
mainLayout = new QVBoxLayout(this);
mainLayout->setMargin(0);
@ -101,7 +149,7 @@ PrimeUserWidget::PrimeUserWidget(QWidget* parent) : QWidget(parent) {
commaPoints->setStyleSheet("font-size: 41px; font-family: Inter SemiBold;");
pointsLayout->addWidget(commaPoints, 0, Qt::AlignTop);
points = new QLabel("210");
points = new QLabel("0");
points->setStyleSheet("font-size: 91px; font-weight: bold;");
pointsLayout->addWidget(points, 0, Qt::AlignTop);
@ -192,9 +240,9 @@ SetupWidget::SetupWidget(QWidget* parent) : QFrame(parent) {
finishRegistationLayout->addStretch();
QPushButton* finishButton = new QPushButton("Pair device");
finishButton->setFixedHeight(220);
finishButton->setStyleSheet(R"(
QPushButton* pair = new QPushButton("Pair device");
pair->setFixedHeight(220);
pair->setStyleSheet(R"(
QPushButton {
font-size: 55px;
font-weight: 400;
@ -205,34 +253,18 @@ SetupWidget::SetupWidget(QWidget* parent) : QFrame(parent) {
background-color: #3049F4;
}
)");
finishRegistationLayout->addWidget(finishButton);
QObject::connect(finishButton, &QPushButton::clicked, this, &SetupWidget::showQrCode);
finishRegistationLayout->addWidget(pair);
popup = new PairingPopup(this);
QObject::connect(pair, &QPushButton::clicked, popup, &PairingPopup::exec);
mainLayout->addWidget(finishRegistration);
// Pairing QR code layout
QWidget* q = new QWidget;
q->setObjectName("primeWidget");
QVBoxLayout* qrLayout = new QVBoxLayout(q);
qrLayout->setContentsMargins(90, 90, 90, 90);
QLabel* qrLabel = new QLabel("Scan the QR code to pair.");
qrLabel->setAlignment(Qt::AlignHCenter);
qrLabel->setStyleSheet("font-size: 47px; font-weight: light;");
qrLayout->addWidget(qrLabel);
qrLayout->addSpacing(50);
qrLayout->addWidget(new PairingQRWidget);
qrLayout->addStretch();
// setup widget
// build stacked layout
QVBoxLayout *outer_layout = new QVBoxLayout(this);
outer_layout->setContentsMargins(0, 0, 0, 0);
outer_layout->addWidget(mainLayout);
mainLayout->addWidget(q);
primeAd = new PrimeAdWidget;
mainLayout->addWidget(primeAd);
@ -259,27 +291,15 @@ SetupWidget::SetupWidget(QWidget* parent) : QFrame(parent) {
QString url = CommaApi::BASE_URL + "/v1.1/devices/" + *dongleId + "/";
RequestRepeater* repeater = new RequestRepeater(this, url, "ApiCache_Device", 5);
QObject::connect(repeater, &RequestRepeater::failedResponse, this, &SetupWidget::show);
QObject::connect(repeater, &RequestRepeater::receivedResponse, this, &SetupWidget::replyFinished);
QObject::connect(repeater, &RequestRepeater::failedResponse, this, &SetupWidget::parseError);
}
hide(); // Only show when first request comes back
}
void SetupWidget::parseError(const QString &response) {
show();
if (mainLayout->currentIndex() == 1) {
showQr = false;
mainLayout->setCurrentIndex(0);
}
}
void SetupWidget::showQrCode() {
showQr = true;
mainLayout->setCurrentIndex(1);
}
void SetupWidget::replyFinished(const QString &response) {
show();
QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
if (doc.isNull()) {
qDebug() << "JSON Parse failed on getting pairing and prime status";
@ -288,12 +308,13 @@ void SetupWidget::replyFinished(const QString &response) {
QJsonObject json = doc.object();
if (!json["is_paired"].toBool()) {
mainLayout->setCurrentIndex(showQr);
} else if (!json["prime"].toBool()) {
showQr = false;
mainLayout->setCurrentWidget(primeAd);
mainLayout->setCurrentIndex(0);
} else {
showQr = false;
mainLayout->setCurrentWidget(primeUser);
popup->reject();
if (!json["prime"].toBool()) {
mainLayout->setCurrentWidget(primeAd);
} else {
mainLayout->setCurrentWidget(primeUser);
}
}
}

View File

@ -5,14 +5,18 @@
#include <QVBoxLayout>
#include <QWidget>
#include "selfdrive/ui/qt/widgets/input.h"
// pairing QR code
class PairingQRWidget : public QWidget {
Q_OBJECT
public:
explicit PairingQRWidget(QWidget* parent = 0);
void paintEvent(QPaintEvent*) override;
private:
QLabel* qrCode;
QPixmap img;
void updateQrCode(const QString &text);
void showEvent(QShowEvent *event) override;
@ -20,6 +24,15 @@ private slots:
void refresh();
};
// pairing popup widget
class PairingPopup : public QDialogBase {
Q_OBJECT
public:
explicit PairingPopup(QWidget* parent);
};
// widget for paired users with prime
class PrimeUserWidget : public QWidget {
Q_OBJECT
public:
@ -33,12 +46,15 @@ private slots:
void replyFinished(const QString &response);
};
// widget for paired users without prime
class PrimeAdWidget : public QFrame {
Q_OBJECT
public:
explicit PrimeAdWidget(QWidget* parent = 0);
};
// container widget
class SetupWidget : public QFrame {
Q_OBJECT
@ -46,13 +62,11 @@ public:
explicit SetupWidget(QWidget* parent = 0);
private:
QStackedWidget* mainLayout;
PairingPopup *popup;
QStackedWidget *mainLayout;
PrimeAdWidget *primeAd;
PrimeUserWidget *primeUser;
bool showQr = false;
private slots:
void parseError(const QString &response);
void replyFinished(const QString &response);
void showQrCode();
};