mirror of
https://github.com/iDescriptor/iDescriptor.git
synced 2026-06-21 19:35:49 +08:00
342 lines
13 KiB
C++
342 lines
13 KiB
C++
#include "deviceinfowidget.h"
|
|
#include "batterywidget.h"
|
|
#include "diskusagewidget.h"
|
|
#include "fileexplorerwidget.h"
|
|
#include "iDescriptor.h"
|
|
#include <QDebug>
|
|
#include <QGraphicsPixmapItem>
|
|
#include <QGraphicsView>
|
|
#include <QGridLayout>
|
|
#include <QHBoxLayout>
|
|
#include <QLabel>
|
|
#include <QList>
|
|
#include <QMessageBox>
|
|
#include <QPainter>
|
|
#include <QPair>
|
|
#include <QPixmap>
|
|
#include <QPushButton>
|
|
#include <QResizeEvent>
|
|
#include <QTabWidget>
|
|
#include <QTimer>
|
|
#include <QVBoxLayout>
|
|
|
|
// A custom QGraphicsView that keeps the content fitted with aspect ratio on
|
|
// resize
|
|
class ResponsiveGraphicsView : public QGraphicsView
|
|
{
|
|
public:
|
|
ResponsiveGraphicsView(QGraphicsScene *scene, QWidget *parent = nullptr)
|
|
: QGraphicsView(scene, parent)
|
|
{
|
|
}
|
|
|
|
protected:
|
|
void resizeEvent(QResizeEvent *event) override
|
|
{
|
|
if (scene() && !scene()->items().isEmpty()) {
|
|
fitInView(scene()->itemsBoundingRect(), Qt::KeepAspectRatio);
|
|
}
|
|
QGraphicsView::resizeEvent(event);
|
|
}
|
|
};
|
|
|
|
DeviceInfoWidget::DeviceInfoWidget(iDescriptorDevice *device, QWidget *parent)
|
|
: QWidget(parent), m_device(device)
|
|
{
|
|
// Main layout with horizontal orientation
|
|
QHBoxLayout *mainLayout = new QHBoxLayout(this);
|
|
mainLayout->setContentsMargins(2, 2, 2, 2);
|
|
mainLayout->setSpacing(2);
|
|
|
|
QGraphicsScene *scene = new QGraphicsScene(this);
|
|
QGraphicsPixmapItem *pixmapItem =
|
|
new QGraphicsPixmapItem(QPixmap(":/resources/iphone.png"));
|
|
scene->addItem(pixmapItem);
|
|
|
|
QGraphicsView *graphicsView = new ResponsiveGraphicsView(scene, this);
|
|
graphicsView->setRenderHint(QPainter::Antialiasing);
|
|
graphicsView->setMinimumWidth(200);
|
|
graphicsView->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Expanding);
|
|
graphicsView->setStyleSheet("background: transparent; border: none;");
|
|
|
|
mainLayout->addWidget(graphicsView, 1); // Stretch factor 1
|
|
|
|
// Right side: Info Table
|
|
QWidget *infoContainer = new QWidget();
|
|
// infoContainer->setObjectName("infoContainer");
|
|
// infoContainer->setStyleSheet("QWidget#infoContainer { "
|
|
// " border: 1px solid #ccc; "
|
|
// " border-radius: 6px; "
|
|
// "}");
|
|
infoContainer->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
|
|
|
|
QVBoxLayout *infoLayout = new QVBoxLayout(infoContainer);
|
|
// infoLayout->setContentsMargins(15, 15, 15, 15);
|
|
// infoLayout->setSpacing(10);
|
|
|
|
// Header
|
|
QWidget *headerWidget = new QWidget();
|
|
headerWidget->setObjectName("headerWidget");
|
|
headerWidget->setStyleSheet("QWidget#headerWidget { "
|
|
" border: 1px solid #ccc; "
|
|
" border-radius: 6px; "
|
|
"}");
|
|
|
|
QHBoxLayout *headerLayout = new QHBoxLayout(headerWidget);
|
|
headerLayout->setContentsMargins(10, 10, 10, 10);
|
|
headerLayout->setSpacing(15);
|
|
|
|
QLabel *devProductType =
|
|
new QLabel(QString::fromStdString(device->deviceInfo.productType));
|
|
devProductType->setStyleSheet("font-size: 1rem; font-weight: bold;");
|
|
|
|
QLabel *diskCapacityLabel = new QLabel(
|
|
QString::number(device->deviceInfo.diskInfo.totalDiskCapacity /
|
|
(1000 * 1000 * 1000)) +
|
|
" GB");
|
|
m_chargingStatusLabel =
|
|
new QLabel(device->deviceInfo.batteryInfo.isCharging ? "Charging"
|
|
: "Not Charging");
|
|
m_chargingStatusLabel->setStyleSheet("font-size: 1rem;");
|
|
|
|
m_chargingWattsLabel =
|
|
new QLabel(QString::number(device->deviceInfo.batteryInfo.watts) + "W");
|
|
|
|
m_cableTypeLabel =
|
|
new QLabel(device->deviceInfo.batteryInfo.usbConnectionType ==
|
|
BatteryInfo::ConnectionType::USB
|
|
? "USB"
|
|
: "USB-C");
|
|
|
|
m_batteryWidget =
|
|
new BatteryWidget(device->deviceInfo.batteryInfo.currentBatteryLevel,
|
|
device->deviceInfo.batteryInfo.isCharging, this);
|
|
|
|
headerLayout->addWidget(devProductType);
|
|
headerLayout->addWidget(diskCapacityLabel);
|
|
headerLayout->addWidget(m_chargingStatusLabel);
|
|
headerLayout->addWidget(m_batteryWidget);
|
|
headerLayout->addWidget(m_chargingWattsLabel);
|
|
headerLayout->addWidget(m_cableTypeLabel);
|
|
|
|
infoLayout->addWidget(headerWidget);
|
|
// add spacer
|
|
infoLayout->addSpacerItem(
|
|
new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding));
|
|
// Add maximum stretch between header and grid
|
|
infoLayout->addStretch();
|
|
|
|
// Grid for device details
|
|
QWidget *gridWidget = new QWidget();
|
|
gridWidget->setObjectName("infoGrid");
|
|
gridWidget->setStyleSheet("QWidget#infoGrid { "
|
|
" border: 1px solid #ccc; "
|
|
" border-radius: 6px; "
|
|
"}");
|
|
QGridLayout *gridLayout = new QGridLayout();
|
|
gridLayout->setSpacing(8);
|
|
gridLayout->setColumnStretch(1, 1); // Allow value column to stretch
|
|
gridLayout->setColumnStretch(
|
|
3, 1); // Allow value column for right side to stretch
|
|
gridLayout->setContentsMargins(17, 17, 17, 17);
|
|
gridWidget->setLayout(gridLayout);
|
|
QList<QPair<QString, QWidget *>> infoItems;
|
|
|
|
auto createValueLabel = [](const QString &text) {
|
|
return new QLabel(text);
|
|
};
|
|
|
|
infoItems.append({"iOS Version:", createValueLabel(QString::fromStdString(
|
|
device->deviceInfo.productVersion))});
|
|
infoItems.append({"Device Name:", createValueLabel(QString::fromStdString(
|
|
device->deviceInfo.deviceName))});
|
|
|
|
// Activation state label with color and tooltip
|
|
QLabel *activationLabel = new QLabel;
|
|
QString stateText;
|
|
QString tooltipText;
|
|
QColor color;
|
|
|
|
switch (device->deviceInfo.activationState) {
|
|
case DeviceInfo::ActivationState::Activated:
|
|
stateText = "Activated";
|
|
color = QColor(0, 180, 0); // Green
|
|
tooltipText = "Device is activated and ready for use.";
|
|
break;
|
|
case DeviceInfo::ActivationState::FactoryActivated:
|
|
stateText = "Factory Activated";
|
|
color = QColor(255, 140, 0); // Orange
|
|
tooltipText = "Activation is most likely bypassed.";
|
|
break;
|
|
default:
|
|
stateText = "Unactivated";
|
|
color = QColor(220, 0, 0); // Red
|
|
tooltipText = "Device is not activated and requires setup.";
|
|
break;
|
|
}
|
|
|
|
activationLabel->setText(stateText);
|
|
activationLabel->setStyleSheet("color: " + color.name() + ";");
|
|
activationLabel->setToolTip(tooltipText);
|
|
infoItems.append({"Activation State:", activationLabel});
|
|
|
|
infoItems.append({"Device Class:", createValueLabel(QString::fromStdString(
|
|
device->deviceInfo.deviceClass))});
|
|
infoItems.append({"Device Color:", createValueLabel(QString::fromStdString(
|
|
device->deviceInfo.deviceColor))});
|
|
infoItems.append(
|
|
{"Jailbroken:", createValueLabel(QString::fromStdString(
|
|
device->deviceInfo.jailbroken ? "Yes" : "No"))});
|
|
infoItems.append({"Model Number:", createValueLabel(QString::fromStdString(
|
|
device->deviceInfo.modelNumber))});
|
|
infoItems.append(
|
|
{"CPU Architecture:", createValueLabel(QString::fromStdString(
|
|
device->deviceInfo.cpuArchitecture))});
|
|
infoItems.append({"Build Version:", createValueLabel(QString::fromStdString(
|
|
device->deviceInfo.buildVersion))});
|
|
infoItems.append(
|
|
{"Hardware Model:", createValueLabel(QString::fromStdString(
|
|
device->deviceInfo.hardwareModel))});
|
|
infoItems.append(
|
|
{"Hardware Platform:", createValueLabel(QString::fromStdString(
|
|
device->deviceInfo.hardwarePlatform))});
|
|
infoItems.append(
|
|
{"Ethernet Address:", createValueLabel(QString::fromStdString(
|
|
device->deviceInfo.ethernetAddress))});
|
|
infoItems.append(
|
|
{"Bluetooth Address:", createValueLabel(QString::fromStdString(
|
|
device->deviceInfo.bluetoothAddress))});
|
|
infoItems.append(
|
|
{"Firmware Version:", createValueLabel(QString::fromStdString(
|
|
device->deviceInfo.firmwareVersion))});
|
|
|
|
// Battery Info
|
|
QWidget *batteryWidget = new QWidget();
|
|
QHBoxLayout *batteryLayout = new QHBoxLayout(batteryWidget);
|
|
batteryLayout->setContentsMargins(0, 0, 0, 0);
|
|
batteryLayout->setSpacing(5);
|
|
batteryLayout->addWidget(new QLabel(device->deviceInfo.batteryInfo.health));
|
|
QPushButton *moreButton = new QPushButton("More");
|
|
connect(moreButton, &QPushButton::clicked, this,
|
|
&DeviceInfoWidget::onBatteryMoreClicked);
|
|
batteryLayout->addWidget(moreButton);
|
|
batteryLayout->addStretch();
|
|
infoItems.append({"Battery Health:", batteryWidget});
|
|
|
|
infoItems.append(
|
|
{"Production Device:",
|
|
createValueLabel(QString::fromStdString(
|
|
device->deviceInfo.productionDevice ? "Yes" : "No"))});
|
|
|
|
// Distribute items into the grid
|
|
int numRows = (infoItems.size() + 1) / 2;
|
|
for (int i = 0; i < numRows; ++i) {
|
|
// Left column item
|
|
QLabel *keyLabelLeft = new QLabel(infoItems[i].first);
|
|
keyLabelLeft->setStyleSheet("font-weight: bold;");
|
|
gridLayout->addWidget(keyLabelLeft, i, 0);
|
|
gridLayout->addWidget(infoItems[i].second, i, 1);
|
|
|
|
// Right column item
|
|
int rightIndex = i + numRows;
|
|
if (rightIndex < infoItems.size()) {
|
|
QLabel *keyLabelRight = new QLabel(infoItems[rightIndex].first);
|
|
keyLabelRight->setStyleSheet("font-weight: bold;");
|
|
gridLayout->addWidget(keyLabelRight, i, 2);
|
|
gridLayout->addWidget(infoItems[rightIndex].second, i, 3);
|
|
}
|
|
}
|
|
|
|
infoLayout->addWidget(gridWidget);
|
|
// infoLayout->addStretch(); // Pushes footer to the bottom
|
|
|
|
// Footer
|
|
QLabel *footerLabel =
|
|
new QLabel("UDID: " + QString::fromStdString(device->udid));
|
|
footerLabel->setStyleSheet(
|
|
"font-size: 10px; color: #666; padding-top: 5px; "
|
|
"border-top: 1px solid #eee;");
|
|
footerLabel->setWordWrap(true);
|
|
infoLayout->addWidget(footerLabel);
|
|
|
|
// Create a vertical layout for the right side to stack info and disk usage
|
|
QVBoxLayout *rightSideLayout = new QVBoxLayout();
|
|
rightSideLayout->setSpacing(10);
|
|
rightSideLayout->addWidget(infoContainer);
|
|
rightSideLayout->addWidget(new DiskUsageWidget(device, this));
|
|
rightSideLayout->setAlignment(Qt::AlignCenter);
|
|
mainLayout->addLayout(rightSideLayout, 2); // Stretch factor 2
|
|
|
|
m_updateTimer = new QTimer(this);
|
|
connect(m_updateTimer, &QTimer::timeout, this,
|
|
&DeviceInfoWidget::updateBatteryInfo);
|
|
m_updateTimer->start(30000); // Update every 30 seconds
|
|
}
|
|
|
|
void DeviceInfoWidget::onBatteryMoreClicked()
|
|
{
|
|
QMessageBox msgBox;
|
|
msgBox.setWindowTitle("Battery Details");
|
|
QString details =
|
|
"Battery Cycle Count: " +
|
|
QString::number(m_device->deviceInfo.batteryInfo.cycleCount) + "\n" +
|
|
"Battery Serial Number: " +
|
|
QString::fromStdString(m_device->deviceInfo.batteryInfo.serialNumber);
|
|
msgBox.setText(details);
|
|
msgBox.exec();
|
|
}
|
|
|
|
QPixmap DeviceInfoWidget::getDeviceIcon(const std::string &productType)
|
|
{
|
|
// Create a simple colored icon based on device type
|
|
QPixmap icon(16, 16);
|
|
icon.fill(Qt::transparent);
|
|
|
|
QPainter painter(&icon);
|
|
painter.setRenderHint(QPainter::Antialiasing);
|
|
|
|
if (productType.find("iPhone") != std::string::npos) {
|
|
painter.setBrush(QColor(0, 122, 255)); // iOS blue
|
|
painter.drawEllipse(2, 2, 12, 12);
|
|
} else if (productType.find("iPad") != std::string::npos) {
|
|
painter.setBrush(QColor(255, 149, 0)); // Orange
|
|
painter.drawRect(2, 2, 12, 12);
|
|
} else {
|
|
painter.setBrush(QColor(128, 128, 128)); // Gray for unknown
|
|
painter.drawEllipse(2, 2, 12, 12);
|
|
}
|
|
|
|
return icon;
|
|
}
|
|
|
|
void DeviceInfoWidget::updateBatteryInfo()
|
|
{
|
|
qDebug() << "Updating battery info...";
|
|
plist_t diagnostics = nullptr;
|
|
get_battery_info(m_device->deviceInfo.rawProductType, m_device->device,
|
|
m_device->deviceInfo.is_iPhone, diagnostics);
|
|
|
|
if (!diagnostics) {
|
|
qDebug() << "Failed to get diagnostics plist.";
|
|
return;
|
|
}
|
|
/*DATA*/
|
|
DeviceInfo &d = m_device->deviceInfo;
|
|
qDebug() << "old device" << d.oldDevice;
|
|
PlistNavigator ioreg = PlistNavigator(diagnostics)["IORegistry"];
|
|
if (d.oldDevice)
|
|
parseOldDeviceBattery(ioreg, d);
|
|
else
|
|
parseDeviceBattery(ioreg, d);
|
|
/*UI*/
|
|
m_chargingStatusLabel->setText(d.batteryInfo.isCharging ? "Charging"
|
|
: "Not Charging");
|
|
m_chargingWattsLabel->setText(QString::number(d.batteryInfo.watts) + "W");
|
|
m_cableTypeLabel->setText(d.batteryInfo.usbConnectionType ==
|
|
BatteryInfo::ConnectionType::USB
|
|
? "USB"
|
|
: "USB-C");
|
|
|
|
m_batteryWidget->updateContext(d.batteryInfo.isCharging,
|
|
d.batteryInfo.currentBatteryLevel);
|
|
} |