mirror of
https://github.com/iDescriptor/iDescriptor.git
synced 2026-06-21 19:35:49 +08:00
refactor DeviceInfoWidget
- Adds battery widget and real-time battery monitoring - Creates custom battery widget component for visual battery status display - Integrates comprehensive battery information including charging state, current level, connection type (USB/USB-C), and power consumption - Implements automatic battery status updates every 30 seconds to keep information current during device connection - Refactors battery data retrieval into separate service function for better code organization and reusability - Enhances device info header with charging status, power consumption, and connection type indicators alongside visual battery widget
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
// https://github.com/p-dobrzynski-dev/QtCustomWidgets/blob/master/batterywidget.cpp
|
||||
#include "batterywidget.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFontMetrics>
|
||||
#include <QPainter>
|
||||
#include <QPainterPath>
|
||||
#include <QResizeEvent>
|
||||
|
||||
BatteryWidget::BatteryWidget(float value, bool isCharging, QWidget *parent)
|
||||
: QWidget(parent), m_value(value), m_isCharging(isCharging)
|
||||
{
|
||||
setMinimumSize(50, 40);
|
||||
setMaximumSize(60, 40);
|
||||
}
|
||||
|
||||
void BatteryWidget::resizeEvent(QResizeEvent *)
|
||||
{
|
||||
widgetFrame = this->rect();
|
||||
|
||||
widgetFrame.setSize(QSize(widgetFrame.width(), widgetFrame.height() / 2));
|
||||
widgetFrame.moveTop(widgetFrame.center().y());
|
||||
|
||||
float scaleValue = 0.95;
|
||||
QSizeF mainBatteryFrameSize =
|
||||
QSizeF(widgetFrame.width() * scaleValue, widgetFrame.height());
|
||||
mainBatteryFrame.setSize(mainBatteryFrameSize);
|
||||
|
||||
mainBatteryFrame.moveTopLeft(widgetFrame.topLeft());
|
||||
|
||||
QSizeF tipBatteryFrameSize =
|
||||
QSizeF(widgetFrame.width() / 3, widgetFrame.height() / 2);
|
||||
tipBatteryFrame.setSize(tipBatteryFrameSize);
|
||||
|
||||
QPointF tipBatteryFramePoint =
|
||||
QPointF(widgetFrame.topRight().x() - tipBatteryFrameSize.width(),
|
||||
widgetFrame.topRight().y() + tipBatteryFrameSize.height() / 2);
|
||||
tipBatteryFrame.moveTopLeft(tipBatteryFramePoint);
|
||||
|
||||
float batteryLevelOffset = mainBatteryFrame.height() / 20;
|
||||
QSizeF batteryLevelFrameSize =
|
||||
QSizeF(mainBatteryFrame.width() - 2 * batteryLevelOffset,
|
||||
mainBatteryFrame.height() - 2 * batteryLevelOffset);
|
||||
batteryLevelFrame.setSize(batteryLevelFrameSize);
|
||||
|
||||
QPointF batteryFramePoint =
|
||||
QPointF(mainBatteryFrame.topLeft().x() + batteryLevelOffset,
|
||||
mainBatteryFrame.topLeft().y() + batteryLevelOffset);
|
||||
batteryLevelFrame.moveTopLeft(batteryFramePoint);
|
||||
}
|
||||
|
||||
void BatteryWidget::setValue(float newValue)
|
||||
{
|
||||
m_value = newValue;
|
||||
this->update();
|
||||
}
|
||||
|
||||
float BatteryWidget::getValue() const { return m_value; }
|
||||
|
||||
void BatteryWidget::setChargingState(bool state)
|
||||
{
|
||||
m_isCharging = state;
|
||||
this->update();
|
||||
this->repaint();
|
||||
}
|
||||
|
||||
void BatteryWidget::updateContext(bool isCharging, float newValue)
|
||||
{
|
||||
m_isCharging = isCharging;
|
||||
m_value = newValue;
|
||||
this->update();
|
||||
this->repaint();
|
||||
}
|
||||
|
||||
bool BatteryWidget::getChargingState() { return m_isCharging; }
|
||||
|
||||
void BatteryWidget::paintEvent(QPaintEvent *)
|
||||
{
|
||||
|
||||
QPainter painter(this);
|
||||
painter.setRenderHint(QPainter::Antialiasing, true);
|
||||
QPen pen = QPen(Qt::red);
|
||||
QBrush brush = QBrush(Qt::white);
|
||||
|
||||
painter.setPen(pen);
|
||||
// Drawing battery frame
|
||||
|
||||
float widgetCorner = widgetFrame.height() / 15;
|
||||
|
||||
QPainterPath FramePath;
|
||||
FramePath.setFillRule(Qt::WindingFill);
|
||||
FramePath.addRoundedRect(mainBatteryFrame, widgetCorner, widgetCorner);
|
||||
FramePath.addRoundedRect(tipBatteryFrame, widgetCorner, widgetCorner);
|
||||
FramePath = FramePath.simplified();
|
||||
|
||||
pen.setColor(Qt::gray);
|
||||
pen.setWidth(widgetFrame.width() / 75);
|
||||
painter.setPen(pen);
|
||||
painter.drawPath(FramePath);
|
||||
|
||||
painter.setBrush(QBrush(QColor("#44bd32")));
|
||||
painter.setPen(Qt::NoPen);
|
||||
QRectF batteryLevelRect = QRectF();
|
||||
QSize batterySizeRect =
|
||||
QSize(batteryLevelFrame.width() * m_value / m_maxValue,
|
||||
batteryLevelFrame.height());
|
||||
batteryLevelRect.setSize(batterySizeRect);
|
||||
batteryLevelRect.moveTo(batteryLevelFrame.topLeft());
|
||||
painter.drawRoundedRect(batteryLevelRect, widgetCorner, widgetCorner);
|
||||
|
||||
pen.setColor(Qt::white);
|
||||
painter.setPen(pen);
|
||||
QFont textFont = QFont();
|
||||
textFont.setPixelSize(widgetFrame.height() / 2);
|
||||
painter.setFont(textFont);
|
||||
QFontMetrics fm(textFont);
|
||||
QString percentageLevelString = QString("%1%").arg(m_value);
|
||||
float textWidth = fm.horizontalAdvance(percentageLevelString);
|
||||
float textHeight = fm.height();
|
||||
|
||||
QPointF textPosition = QPointF(widgetFrame.center().x() - textWidth / 2,
|
||||
widgetFrame.center().y() + textHeight / 3);
|
||||
painter.drawText(textPosition, percentageLevelString);
|
||||
|
||||
float chargerSize = widgetFrame.height() / 2;
|
||||
|
||||
// if (isCharging) {
|
||||
// QPixmap pixmap(":/img/charge.png");
|
||||
// painter.drawPixmap(widgetFrame.center().x() - chargerSize * 1.5,
|
||||
// widgetFrame.top() + chargerSize / 2, chargerSize,
|
||||
// chargerSize, pixmap);
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef BATTERYWIDGET_H
|
||||
#define BATTERYWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class BatteryWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
BatteryWidget(float value, bool isCharging, QWidget *parent);
|
||||
bool getChargingState();
|
||||
void setChargingState(bool state);
|
||||
void updateContext(bool isCharging, float newValue);
|
||||
|
||||
// New methods for value management
|
||||
void setValue(float newValue);
|
||||
float getValue() const;
|
||||
|
||||
private:
|
||||
QRectF widgetFrame;
|
||||
QRectF mainBatteryFrame;
|
||||
QRectF tipBatteryFrame;
|
||||
QRectF batteryLevelFrame;
|
||||
|
||||
bool m_isCharging = false;
|
||||
|
||||
float m_value = 0.0f;
|
||||
float m_minValue = 0.0f;
|
||||
float m_maxValue = 100.0f;
|
||||
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
};
|
||||
|
||||
#endif // BATTERYWIDGET_H
|
||||
@@ -0,0 +1,43 @@
|
||||
#include "../../iDescriptor.h"
|
||||
#include "plist/plist.h"
|
||||
#include <QDebug>
|
||||
#include <libimobiledevice/diagnostics_relay.h>
|
||||
#include <string>
|
||||
|
||||
void get_battery_info(std::string productType, idevice_t idevice,
|
||||
bool is_iphone, plist_t &diagnostics)
|
||||
{
|
||||
diagnostics_relay_client_t diagnostics_client = nullptr;
|
||||
try {
|
||||
|
||||
if (diagnostics_relay_client_start_service(idevice, &diagnostics_client,
|
||||
nullptr) !=
|
||||
DIAGNOSTICS_RELAY_E_SUCCESS) {
|
||||
qDebug() << "Failed to start diagnostics relay service.";
|
||||
return;
|
||||
}
|
||||
|
||||
bool newerThaniPhone8 =
|
||||
is_product_type_newer(productType, std::string("iPhone8,1"));
|
||||
|
||||
const char *batteryQuery =
|
||||
is_iphone
|
||||
? newerThaniPhone8 ? "AppleSmartBattery" : "AppleARMPMUCharger"
|
||||
: "AppleARMPMUCharger";
|
||||
|
||||
if (diagnostics_relay_query_ioregistry_entry(
|
||||
diagnostics_client, nullptr, batteryQuery, &diagnostics) !=
|
||||
DIAGNOSTICS_RELAY_E_SUCCESS &&
|
||||
!diagnostics) {
|
||||
|
||||
qDebug()
|
||||
<< "Failed to query diagnostics relay for AppleARMPMUCharger.";
|
||||
if (diagnostics_client)
|
||||
diagnostics_relay_client_free(diagnostics_client);
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
if (diagnostics_client)
|
||||
diagnostics_relay_client_free(diagnostics_client);
|
||||
qDebug() << "Exception in get_battery_info: " << e.what();
|
||||
}
|
||||
}
|
||||
@@ -36,8 +36,8 @@ std::string safeGetXML(const char *key, pugi::xml_node dict)
|
||||
|
||||
// TODO: return tyype
|
||||
DeviceInfo fullDeviceInfo(const pugi::xml_document &doc,
|
||||
afc_client_t &afcClient, plist_t &diagnostics,
|
||||
DeviceInfo &d)
|
||||
afc_client_t &afcClient,
|
||||
IDescriptorInitDeviceResult &result)
|
||||
{
|
||||
pugi::xml_node dict = doc.child("plist").child("dict");
|
||||
auto safeGet = [&](const char *key) -> std::string {
|
||||
@@ -67,7 +67,7 @@ DeviceInfo fullDeviceInfo(const pugi::xml_document &doc,
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
DeviceInfo &d = result.deviceInfo;
|
||||
d.deviceName = safeGet("DeviceName");
|
||||
d.deviceClass = safeGet("DeviceClass");
|
||||
d.deviceColor = safeGet("DeviceColor");
|
||||
@@ -115,38 +115,71 @@ DeviceInfo fullDeviceInfo(const pugi::xml_document &doc,
|
||||
DeviceInfo::ActivationState::Unactivated; // Default value
|
||||
}
|
||||
// TODO:RegionInfo: LL/A
|
||||
d.productType = parse_product_type(safeGet("ProductType"));
|
||||
std::string rawProductType = safeGet("ProductType");
|
||||
d.productType = parse_product_type(rawProductType);
|
||||
d.rawProductType = rawProductType;
|
||||
d.jailbroken = detect_jailbroken(afcClient);
|
||||
d.is_iPhone = safeGet("DeviceClass") == "iPhone";
|
||||
|
||||
uint64_t cycleCount;
|
||||
plist_get_uint_val(
|
||||
PlistNavigator(diagnostics)["IORegistry"]["BatteryData"]["CycleCount"],
|
||||
&cycleCount);
|
||||
/*BatteryInfo*/
|
||||
plist_t diagnostics = nullptr;
|
||||
get_battery_info(rawProductType, result.device, d.is_iPhone, diagnostics);
|
||||
if (!diagnostics) {
|
||||
qDebug() << "Failed to get diagnostics plist.";
|
||||
return d;
|
||||
}
|
||||
|
||||
char *batterySerialNumber = nullptr;
|
||||
plist_get_string_val(
|
||||
plist_print(diagnostics);
|
||||
uint64_t cycleCount =
|
||||
PlistNavigator(diagnostics)["IORegistry"]["BatteryData"]["CycleCount"]
|
||||
.getUInt();
|
||||
|
||||
std::string batterySerialNumber =
|
||||
PlistNavigator(
|
||||
diagnostics)["IORegistry"]["BatteryData"]["BatterySerialNumber"],
|
||||
&batterySerialNumber);
|
||||
|
||||
uint64_t designCapacity = 0;
|
||||
plist_get_uint_val(
|
||||
diagnostics)["IORegistry"]["BatteryData"]["BatterySerialNumber"]
|
||||
.getString();
|
||||
uint64_t designCapacity =
|
||||
PlistNavigator(
|
||||
diagnostics)["IORegistry"]["BatteryData"]["DesignCapacity"],
|
||||
&designCapacity);
|
||||
diagnostics)["IORegistry"]["BatteryData"]["DesignCapacity"]
|
||||
.getUInt();
|
||||
|
||||
uint64_t absoluteCapacity = 0;
|
||||
plist_get_uint_val(
|
||||
PlistNavigator(diagnostics)["IORegistry"]["AbsoluteCapacity"],
|
||||
&absoluteCapacity);
|
||||
uint64_t appleRawCurrentCapacity =
|
||||
PlistNavigator(diagnostics)["IORegistry"]["AppleRawCurrentCapacity"]
|
||||
.getUInt();
|
||||
|
||||
d.batteryInfo.health =
|
||||
QString::number((absoluteCapacity * 100) / designCapacity) + "%";
|
||||
QString::number((appleRawCurrentCapacity * 100) / designCapacity) + "%";
|
||||
d.batteryInfo.cycleCount = cycleCount;
|
||||
d.batteryInfo.serialNumber = batterySerialNumber
|
||||
d.batteryInfo.serialNumber = !batterySerialNumber.empty()
|
||||
? batterySerialNumber
|
||||
: "Error retrieving serial number";
|
||||
|
||||
d.batteryInfo.isCharging =
|
||||
PlistNavigator(diagnostics)["IORegistry"]["IsCharging"].getBool();
|
||||
|
||||
d.batteryInfo.fullyCharged =
|
||||
PlistNavigator(diagnostics)["IORegistry"]["FullyCharged"].getBool();
|
||||
|
||||
d.batteryInfo.currentBatteryLevel =
|
||||
PlistNavigator(diagnostics)["IORegistry"]["CurrentCapacity"].getUInt();
|
||||
|
||||
d.batteryInfo.usbConnectionType =
|
||||
PlistNavigator(
|
||||
diagnostics)["IORegistry"]["AdapterDetails"]["Description"]
|
||||
.getString() == "usb type-c"
|
||||
? BatteryInfo::ConnectionType::USB_TYPEC
|
||||
: BatteryInfo::ConnectionType::USB;
|
||||
|
||||
d.batteryInfo.adapterVoltage =
|
||||
PlistNavigator(diagnostics)["IORegistry"]["AppleRawAdapterDetails"][0]
|
||||
["AdapterVoltage"]
|
||||
.getUInt();
|
||||
|
||||
d.batteryInfo.watts =
|
||||
PlistNavigator(
|
||||
diagnostics)["IORegistry"]["AppleRawAdapterDetails"][0]["Watts"]
|
||||
.getUInt();
|
||||
|
||||
plist_free(diagnostics);
|
||||
diagnostics = nullptr;
|
||||
|
||||
@@ -167,7 +200,6 @@ IDescriptorInitDeviceResult init_idescriptor_device(const char *udid)
|
||||
// LOCKDOWN_E_PAIRING_DIALOG_RESPONSE_PENDING = -19,
|
||||
lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
|
||||
lockdownd_service_descriptor_t lockdownService = nullptr;
|
||||
diagnostics_relay_client_t diagnostics_client = nullptr;
|
||||
afc_client_t afcClient = nullptr;
|
||||
try {
|
||||
idevice_error_t ret = idevice_new_with_options(&result.device, udid,
|
||||
@@ -211,13 +243,6 @@ IDescriptorInitDeviceResult init_idescriptor_device(const char *udid)
|
||||
return result;
|
||||
}
|
||||
|
||||
if (diagnostics_relay_client_start_service(
|
||||
result.device, &diagnostics_client, nullptr) !=
|
||||
DIAGNOSTICS_RELAY_E_SUCCESS) {
|
||||
qDebug() << "Failed to start diagnostics relay service.";
|
||||
return result;
|
||||
}
|
||||
|
||||
pugi::xml_document infoXml;
|
||||
get_device_info_xml(udid, 0, 0, infoXml, client, result.device);
|
||||
|
||||
@@ -232,62 +257,20 @@ IDescriptorInitDeviceResult init_idescriptor_device(const char *udid)
|
||||
return result;
|
||||
}
|
||||
|
||||
plist_t diagnostics = nullptr;
|
||||
std::string productType =
|
||||
safeGetXML("ProductType", infoXml.child("plist").child("dict"));
|
||||
|
||||
bool is_iphone =
|
||||
safeGetXML("DeviceClass", infoXml.child("plist").child("dict")) ==
|
||||
"iPhone";
|
||||
if (is_iphone) {
|
||||
|
||||
qDebug() << "iPhone is newer than iPhone 8 ?"
|
||||
<< is_product_type_newer(productType,
|
||||
std::string("iPhone10,1"));
|
||||
}
|
||||
|
||||
const char *batteryQuery =
|
||||
is_iphone
|
||||
? is_product_type_newer(productType, std::string("iPhone8,1"))
|
||||
? "AppleSmartBattery"
|
||||
: "AppleARMPMUCharger"
|
||||
: "AppleARMPMUCharger";
|
||||
// TODO: iPhone 8 and above should query AppleSmartBattery
|
||||
// TODO: try catch here
|
||||
|
||||
if (diagnostics_relay_query_ioregistry_entry(
|
||||
diagnostics_client, nullptr, batteryQuery, &diagnostics) !=
|
||||
DIAGNOSTICS_RELAY_E_SUCCESS &&
|
||||
!diagnostics) {
|
||||
|
||||
qDebug()
|
||||
<< "Failed to query diagnostics relay for AppleARMPMUCharger.";
|
||||
// Clean up resources before returning
|
||||
// if (afcClient)
|
||||
// afc_client_free(afcClient);
|
||||
if (lockdownService)
|
||||
lockdownd_service_descriptor_free(lockdownService);
|
||||
if (client)
|
||||
lockdownd_client_free(client);
|
||||
if (diagnostics_client)
|
||||
diagnostics_relay_client_free(diagnostics_client);
|
||||
return result;
|
||||
}
|
||||
|
||||
// if (result.device) idevice_free(result.device);
|
||||
|
||||
fullDeviceInfo(infoXml, afcClient, diagnostics, result.deviceInfo);
|
||||
fullDeviceInfo(infoXml, afcClient, result);
|
||||
result.afcClient = afcClient;
|
||||
result.success = true;
|
||||
// TODO: cleanup needed ?
|
||||
// if (afcClient)
|
||||
// afc_client_free(afcClient);
|
||||
// if (lockdownService)
|
||||
// lockdownd_service_descriptor_free(lockdownService);
|
||||
// if (client)
|
||||
// lockdownd_client_free(client);
|
||||
// if (diagnostics_client)
|
||||
// diagnostics_relay_client_free(diagnostics_client);
|
||||
|
||||
if (lockdownService)
|
||||
lockdownd_service_descriptor_free(lockdownService);
|
||||
if (client)
|
||||
lockdownd_client_free(client);
|
||||
|
||||
return result;
|
||||
|
||||
} catch (const std::exception &e) {
|
||||
|
||||
+101
-5
@@ -1,4 +1,5 @@
|
||||
#include "deviceinfowidget.h"
|
||||
#include "batterywidget.h"
|
||||
#include "diskusagewidget.h"
|
||||
#include "fileexplorerwidget.h"
|
||||
#include "iDescriptor.h"
|
||||
@@ -16,6 +17,7 @@
|
||||
#include <QPushButton>
|
||||
#include <QResizeEvent>
|
||||
#include <QTabWidget>
|
||||
#include <QTimer>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
// A custom QGraphicsView that keeps the content fitted with aspect ratio on
|
||||
@@ -39,7 +41,7 @@ protected:
|
||||
};
|
||||
|
||||
DeviceInfoWidget::DeviceInfoWidget(iDescriptorDevice *device, QWidget *parent)
|
||||
: QWidget(parent), device(device)
|
||||
: QWidget(parent), m_device(device)
|
||||
{
|
||||
// Main layout with horizontal orientation
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout(this);
|
||||
@@ -74,15 +76,49 @@ DeviceInfoWidget::DeviceInfoWidget(iDescriptorDevice *device, QWidget *parent)
|
||||
|
||||
// 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(0, 0, 0, 0);
|
||||
// headerLayout->setSpacing(10);
|
||||
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(
|
||||
@@ -102,6 +138,7 @@ DeviceInfoWidget::DeviceInfoWidget(iDescriptorDevice *device, QWidget *parent)
|
||||
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;
|
||||
|
||||
@@ -229,6 +266,11 @@ DeviceInfoWidget::DeviceInfoWidget(iDescriptorDevice *device, QWidget *parent)
|
||||
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()
|
||||
@@ -237,9 +279,9 @@ void DeviceInfoWidget::onBatteryMoreClicked()
|
||||
msgBox.setWindowTitle("Battery Details");
|
||||
QString details =
|
||||
"Battery Cycle Count: " +
|
||||
QString::number(device->deviceInfo.batteryInfo.cycleCount) + "\n" +
|
||||
QString::number(m_device->deviceInfo.batteryInfo.cycleCount) + "\n" +
|
||||
"Battery Serial Number: " +
|
||||
QString::fromStdString(device->deviceInfo.batteryInfo.serialNumber);
|
||||
QString::fromStdString(m_device->deviceInfo.batteryInfo.serialNumber);
|
||||
msgBox.setText(details);
|
||||
msgBox.exec();
|
||||
}
|
||||
@@ -265,4 +307,58 @@ QPixmap DeviceInfoWidget::getDeviceIcon(const std::string &productType)
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
d.batteryInfo.isCharging =
|
||||
PlistNavigator(diagnostics)["IORegistry"]["IsCharging"].getBool();
|
||||
|
||||
d.batteryInfo.fullyCharged =
|
||||
PlistNavigator(diagnostics)["IORegistry"]["FullyCharged"].getBool();
|
||||
|
||||
d.batteryInfo.currentBatteryLevel =
|
||||
PlistNavigator(diagnostics)["IORegistry"]["CurrentCapacity"].getUInt();
|
||||
|
||||
d.batteryInfo.usbConnectionType =
|
||||
PlistNavigator(
|
||||
diagnostics)["IORegistry"]["AdapterDetails"]["Description"]
|
||||
.getString() == "usb type-c"
|
||||
? BatteryInfo::ConnectionType::USB_TYPEC
|
||||
: BatteryInfo::ConnectionType::USB;
|
||||
|
||||
d.batteryInfo.adapterVoltage =
|
||||
PlistNavigator(diagnostics)["IORegistry"]["AppleRawAdapterDetails"][0]
|
||||
["AdapterVoltage"]
|
||||
.getUInt();
|
||||
|
||||
d.batteryInfo.watts =
|
||||
PlistNavigator(
|
||||
diagnostics)["IORegistry"]["AppleRawAdapterDetails"][0]["Watts"]
|
||||
.getUInt();
|
||||
|
||||
/*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);
|
||||
}
|
||||
+10
-1
@@ -1,6 +1,9 @@
|
||||
#ifndef DEVICEINFOWIDGET_H
|
||||
#define DEVICEINFOWIDGET_H
|
||||
#include "batterywidget.h"
|
||||
#include "iDescriptor.h"
|
||||
#include <QLabel>
|
||||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
|
||||
class DeviceInfoWidget : public QWidget
|
||||
@@ -15,7 +18,13 @@ private slots:
|
||||
|
||||
private:
|
||||
QPixmap getDeviceIcon(const std::string &productType);
|
||||
iDescriptorDevice *device;
|
||||
iDescriptorDevice *m_device;
|
||||
QTimer *m_updateTimer;
|
||||
void updateBatteryInfo();
|
||||
QLabel *m_chargingStatusLabel;
|
||||
QLabel *m_chargingWattsLabel;
|
||||
QLabel *m_cableTypeLabel;
|
||||
BatteryWidget *m_batteryWidget;
|
||||
};
|
||||
|
||||
#endif // DEVICEINFOWIDGET_H
|
||||
|
||||
+62
-1
@@ -29,6 +29,15 @@ struct BatteryInfo {
|
||||
// uint64_t maxCapacity;
|
||||
// uint64_t fullChargeCapacity;
|
||||
std::string serialNumber;
|
||||
bool isCharging;
|
||||
bool fullyCharged;
|
||||
uint64_t currentBatteryLevel;
|
||||
enum class ConnectionType {
|
||||
USB,
|
||||
USB_TYPEC,
|
||||
} usbConnectionType;
|
||||
uint64_t adapterVoltage; // in mV
|
||||
uint64_t watts;
|
||||
};
|
||||
|
||||
//! IOS 12
|
||||
@@ -59,6 +68,7 @@ struct DeviceInfo {
|
||||
} activationState;
|
||||
std::string activationStateAcknowledged;
|
||||
std::string productType;
|
||||
std::string rawProductType;
|
||||
bool jailbroken;
|
||||
std::string basebandActivationTicketVersion;
|
||||
std::string basebandCertId;
|
||||
@@ -120,6 +130,7 @@ struct DeviceInfo {
|
||||
bool productionDevice;
|
||||
BatteryInfo batteryInfo;
|
||||
DiskInfo diskInfo;
|
||||
bool is_iPhone;
|
||||
};
|
||||
|
||||
struct iDescriptorDevice {
|
||||
@@ -132,6 +143,7 @@ struct iDescriptorDevice {
|
||||
clients are not long lived, so do not assume this will be valid
|
||||
*/
|
||||
afc_client_t afcClient;
|
||||
bool is_iPhone;
|
||||
};
|
||||
|
||||
struct IDescriptorInitDeviceResult {
|
||||
@@ -248,6 +260,7 @@ private:
|
||||
public:
|
||||
PlistNavigator(plist_t node) : current_node(node) {}
|
||||
|
||||
// Existing dictionary key access
|
||||
PlistNavigator operator[](const char *key)
|
||||
{
|
||||
if (!current_node || plist_get_node_type(current_node) != PLIST_DICT) {
|
||||
@@ -257,8 +270,53 @@ public:
|
||||
return PlistNavigator(next);
|
||||
}
|
||||
|
||||
// Add array index access
|
||||
PlistNavigator operator[](int index)
|
||||
{
|
||||
if (!current_node || plist_get_node_type(current_node) != PLIST_ARRAY) {
|
||||
return PlistNavigator(nullptr);
|
||||
}
|
||||
if (index < 0 ||
|
||||
index >= static_cast<int>(plist_array_get_size(current_node))) {
|
||||
return PlistNavigator(nullptr);
|
||||
}
|
||||
plist_t next = plist_array_get_item(current_node, index);
|
||||
return PlistNavigator(next);
|
||||
}
|
||||
|
||||
operator plist_t() const { return current_node; }
|
||||
bool valid() const { return current_node != nullptr; }
|
||||
|
||||
// Your existing helper methods
|
||||
bool getBool() const
|
||||
{
|
||||
if (!current_node)
|
||||
return false;
|
||||
uint8_t value = false;
|
||||
plist_get_bool_val(current_node, &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
uint64_t getUInt() const
|
||||
{
|
||||
if (!current_node)
|
||||
return 0;
|
||||
uint64_t value = 0;
|
||||
plist_get_uint_val(current_node, &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
std::string getString() const
|
||||
{
|
||||
if (!current_node)
|
||||
return "";
|
||||
char *value = nullptr;
|
||||
plist_get_string_val(current_node, &value);
|
||||
std::string result = value ? value : "";
|
||||
if (value)
|
||||
free(value);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
afc_error_t safe_afc_read_directory(afc_client_t afcClient, idevice_t device,
|
||||
@@ -376,4 +434,7 @@ bool query_mobile_gestalt(iDescriptorDevice *id_device, const QStringList &keys,
|
||||
uint32_t &xml_size, char *&xml_data);
|
||||
;
|
||||
|
||||
std::string safeGetXML(const char *key, pugi::xml_node dict);
|
||||
std::string safeGetXML(const char *key, pugi::xml_node dict);
|
||||
|
||||
void get_battery_info(std::string productType, idevice_t idevice,
|
||||
bool is_iphone, plist_t &diagnostics);
|
||||
Reference in New Issue
Block a user