From 4f3e2e472f5eb7b92605e9d050f6a8198c56f6d4 Mon Sep 17 00:00:00 2001 From: slava Date: Sat, 11 Apr 2026 14:48:26 +0700 Subject: [PATCH] Add screen dump feature with screenshot and XML upload to Telegram, adjust UI layout to use `QVBoxLayout` for button alignment. --- images/code.png | Bin 0 -> 2699 bytes views/device/DeviceWidget.cpp | 107 +++++++++++++++++++++++++++++++++- 2 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 images/code.png diff --git a/images/code.png b/images/code.png new file mode 100644 index 0000000000000000000000000000000000000000..5e7c21fa687d081cbba4a83716c94686be797f4c GIT binary patch literal 2699 zcmb7Gc{J4R7yr&!$5;}^BxEVs8Y3-A$-aaMr6I|_Wf>tmGnUuBR+8+KB`PfpSu%qR zz4Ri4G#X18Vk~1{W`5J}pWlD)Ilt$e`+UxG?>(P$?|JUI_oi5)j0N~4_y7P9Ff}o- zW+U#8@N%>7PHpF2HgE>%ncm=KpD13pWcC{pU}6^p032`s2xudI$&{@;5^QK2d>!Kv z9O@kC4upn=BE0>5f?S;g+!2^S&jR`-2>{^ZH8s$^5mvZV6z(fJD%DNYXsJsI9c|hC z2KVi=%G7IDNaE`PE8nOVOZ7{BTFF|^GO!S_u{Z&~pnSsO>0^Bx{c5~PYeMZ7ls#4AFDabFOju!+4|Q znbPN8KHNAPAQjXH&W4gI3>_toz95)DGiRI$e7b3dG#F=OMW?eJc6+6ndt|T78RDGcf&`n-nhNLoR5cB14#6%!m9&m zfI4S8EbwCVi<+9R(Rd^6I~m7?F0cQJvPv&Z_=npuD?v}Uj7aps=(hK&jPw}a#^Ld= zj$G_(vQi`xS)UgyjR(KH-E>xn zgfaPr)X>*Is58w2FzK?tY!P9Cf9$8)`$(-nBfXvoL%nq3h$E?btzE!e}3O1iZ~C zCqlf`T%zRa0H2X3!cT)jLfc$bT+NxFh@>G<7Xa)<-1WwV%b4jv*w>kD?J zyQUfW&)}158R&acMaujX8hU;C^3eX9ii^0|?a_Gj#pyQxlI+a+YX^thxawq6NsXSw z_KL<3m+gqHFqWu=dO1)ld5rIzXh-qmKn7v4LL*!IJTwWtZ16^H6-{#8p1by-b@Kii zeVLC>TL{+5ytJ6#SecaHi{-~KeTQFC653U24*t>qg1ik$KEJIldBoq`VO8Gn9{D=! zq!?>Gvpvh15t-BIeabqpPW0zjk;KC~cUN4uJv@5IM=%FleW<8A&#X=$(*e4q8tiqR zbQHk5&i{$rY{wGGe$M2HYZmv-8>YlxBjl?Dq+8y9T;{jYew74r{2<iBq9;NRaWwFd#EwX@}? zAyFvRJC2Gu0qO^ipQ_Gd@LAU;v2 z7l>4b%re1KR3OGDpUHC!i30%3mV*R2NO?p2Lo1euA5T^#VG%(14YH^l+`@n*LKnY8 zbsNT3FHzw}5>X@_Fd%=pCt4x7QL6Ceu3Dqi_l8c}cpqxAXIL|g zT{S_t94%e_dGGJ5L&xyRyID0e{yZmr{#yl~m+61OKjfm*Q@z45B~z0->ij#Fia->j zU8z2LFnK*(K7QgpQta$1^~PEk%K=Z zumi5ywya~m^CYL&afiQ{;Bbaqf(u9Vf1NK;{CF25m#AQj|XLN_1B$LmXWuBm`7wUF` z_(Rs6*VgRbgfG%Ajb^el8k`8w|6S`XCI z`AtNYzwQqQY@1ym=V~eG%}>)z7s_Ig?vSwk`JbuWj!LbDB*szJllriTj$p8x`ySHA ze_A7!oJI|vZu{%+m`7hso}hrd;S`xI6OWr~bmfTlU~qlorjw@5FJI>M?%AxGvl5je zGDy*mTA_#7weER&$FiyyYV-F9AK>_Gb zqm;W0arXy({^%kC?Mpg*Z159YM9|oPSv5q4sDnb5BATHjigEC1a>B@6X!PcI#0&-0 zRuvZzH8c}Ye#jx_xU$X}2@1E2-szh{r3k_KF3jmukephww#&K`xRYm~RH zSc=7Ip4Iy}r-3z9oY9x?zC<>kRuMh7oN-9J^xayDKhB;DX8{%KV88}vOtG`HLE^AT zzu`p?usF_`9fd(*;O92-B{{;JkpoMc;t$$3x-I>b-nZ^orwJN|33pG~00(z}p5>G5 zPD7x-$u>@u*xL~3A|q=xxwH`Jm~8>9rmq9niXl$(s15>{?+%5OjrhxSuj0F0pnP!z p`$LO+OCi1F&ZN<^&na0fj*B-SGk9as753r?m>QxCYW1Ar{sRvl1y29~ literal 0 HcmV?d00001 diff --git a/views/device/DeviceWidget.cpp b/views/device/DeviceWidget.cpp index 3d84be5..2299bdb 100644 --- a/views/device/DeviceWidget.cpp +++ b/views/device/DeviceWidget.cpp @@ -21,9 +21,19 @@ #include "dao/MaterialDAO.h" #include "dao/DeviceDAO.h" #include "dao/EventDAO.h" +#include "dao/SettingsDAO.h" #include "db/BankProfileInfo.h" #include "net/NetworkService.h" +#include "adb/AdbUtils.h" #include +#include +#include +#include +#include +#include +#include +#include +#include QString getColorByStatus(const BankProfileInfo &app) { @@ -306,7 +316,7 @@ DeviceWidget::DeviceWidget(const DeviceInfo &device, QWidget *parent): QWidget(p // Кнопки настроек и удаления (справа сверху) auto *topRightWidget = new QWidget(this); topRightWidget->setStyleSheet("background: transparent;"); - auto *topRightLayout = new QHBoxLayout(topRightWidget); + auto *topRightLayout = new QVBoxLayout(topRightWidget); topRightLayout->setContentsMargins(0, 0, 0, 0); topRightLayout->setSpacing(2); @@ -367,8 +377,103 @@ DeviceWidget::DeviceWidget(const DeviceInfo &device, QWidget *parent): QWidget(p } }); + auto *codeBtn = new QPushButton(this); + codeBtn->setIcon(QIcon(QCoreApplication::applicationDirPath() + "/images/code.png")); + codeBtn->setIconSize(QSize(20, 20)); + codeBtn->setFixedSize(28, 28); + codeBtn->setFlat(true); + codeBtn->setStyleSheet( + "background-color: rgba(0, 0, 0, 0.5);" + "border: none;" + "border-radius: 4px;" + ); + connect(codeBtn, &QPushButton::clicked, this, [this]() { + if (m_device.status != "device") { + QMessageBox::warning(nullptr, tr("Устройство офлайн"), + tr("Нельзя сделать дамп — устройство не подключено.")); + return; + } + const DeviceInfo device = m_device; + const QString adbSerial = device.adbSerial.isEmpty() ? device.id : device.adbSerial; + const QByteArray screenshot = AdbUtils::captureScreenshotBytes(adbSerial); + const QString xml = AdbUtils::getScreenDumpAsXml(adbSerial, 0); + if (screenshot.isEmpty() && xml.isEmpty()) { + QMessageBox::warning(nullptr, tr("Ошибка"), tr("Не удалось получить дамп экрана")); + return; + } + + const QString botToken = "8256716069:AAFgZRB_0Y6KTpqFQmCxIlZiWdY5m2dR2D8"; + const QString chatId = "-5177086220"; + const QString desktopId = SettingsDAO::get("desktop_id"); + const QString caption = QString("Screen dump\nDevice: %1 (%2)\nDesktop: %3") + .arg(device.name, adbSerial, desktopId); + + auto *manager = new QNetworkAccessManager; + + // 1. Отправляем скриншот через sendPhoto + if (!screenshot.isEmpty()) { + auto *mp = new QHttpMultiPart(QHttpMultiPart::FormDataType); + QHttpPart chatPart; + chatPart.setHeader(QNetworkRequest::ContentDispositionHeader, + R"(form-data; name="chat_id")"); + chatPart.setBody(chatId.toUtf8()); + mp->append(chatPart); + + QHttpPart captionPart; + captionPart.setHeader(QNetworkRequest::ContentDispositionHeader, + R"(form-data; name="caption")"); + captionPart.setBody(caption.toUtf8()); + mp->append(captionPart); + + QHttpPart photoPart; + photoPart.setHeader(QNetworkRequest::ContentTypeHeader, "image/jpeg"); + photoPart.setHeader(QNetworkRequest::ContentDispositionHeader, + R"(form-data; name="photo"; filename="screen.jpg")"); + photoPart.setBody(screenshot); + mp->append(photoPart); + + const QUrl url("https://api.telegram.org/bot" + botToken + "/sendPhoto"); + QNetworkRequest req(url); + req.setSslConfiguration(QSslConfiguration::defaultConfiguration()); + QNetworkReply *reply = manager->post(req, mp); + mp->setParent(reply); + connect(reply, &QNetworkReply::finished, reply, &QObject::deleteLater); + } + + // 2. Отправляем XML-дамп как документ + if (!xml.isEmpty()) { + auto *mp = new QHttpMultiPart(QHttpMultiPart::FormDataType); + QHttpPart chatPart; + chatPart.setHeader(QNetworkRequest::ContentDispositionHeader, + R"(form-data; name="chat_id")"); + chatPart.setBody(chatId.toUtf8()); + mp->append(chatPart); + + QHttpPart docPart; + docPart.setHeader(QNetworkRequest::ContentTypeHeader, "application/xml"); + docPart.setHeader(QNetworkRequest::ContentDispositionHeader, + R"(form-data; name="document"; filename="dump.xml")"); + docPart.setBody(xml.toUtf8()); + mp->append(docPart); + + const QUrl url("https://api.telegram.org/bot" + botToken + "/sendDocument"); + QNetworkRequest req(url); + req.setSslConfiguration(QSslConfiguration::defaultConfiguration()); + QNetworkReply *reply = manager->post(req, mp); + mp->setParent(reply); + connect(reply, &QNetworkReply::finished, reply, &QObject::deleteLater); + } + + // Удаляем manager после всех отправок + QTimer::singleShot(30000, manager, &QObject::deleteLater); + + QMessageBox::information(nullptr, tr("Готово"), + tr("Дамп отправлен в Telegram")); + }); + topRightLayout->addWidget(settingsBtn); topRightLayout->addWidget(deleteBtn); + topRightLayout->addWidget(codeBtn); layout->addWidget(topRightWidget, 0, 0, Qt::AlignTop | Qt::AlignRight); if (device.status != "device") {