diff --git a/resources.qrc b/resources.qrc
index dbfad0c..4e0bf7f 100644
--- a/resources.qrc
+++ b/resources.qrc
@@ -5,6 +5,7 @@
icons/MdiLightningBolt.png
icons/MingcuteSettings7Line.png
icons/ClarityHardDiskSolidAlerted.png
+ icons/icon.png
qml/MapView.qml
resources/dump.js
resources/iphone.png
diff --git a/src/afcexplorerwidget.cpp b/src/afcexplorerwidget.cpp
index 4fc6775..78b219b 100644
--- a/src/afcexplorerwidget.cpp
+++ b/src/afcexplorerwidget.cpp
@@ -33,7 +33,9 @@ AfcExplorerWidget::AfcExplorerWidget(afc_client_t afcClient,
// Main layout
QHBoxLayout *mainLayout = new QHBoxLayout(this);
+ mainLayout->setContentsMargins(0, 0, 0, 0);
setLayout(mainLayout);
+
mainLayout->addWidget(m_explorer);
// Initialize
@@ -170,7 +172,6 @@ void AfcExplorerWidget::updateBreadcrumb(const QString &path)
void AfcExplorerWidget::loadPath(const QString &path)
{
m_fileList->clear();
- // pathLabel->setText(path); // removed
updateBreadcrumb(path);
@@ -263,16 +264,6 @@ void AfcExplorerWidget::onExportClicked()
}
}
-void AfcExplorerWidget::onExportDeleteClicked()
-{
- // Placeholder for future implementation
- QList selectedItems = m_fileList->selectedItems();
- if (selectedItems.isEmpty())
- return;
- // TODO: Implement export & delete logic
- return;
-}
-
void AfcExplorerWidget::exportSelectedFile(QListWidgetItem *item,
const QString &directory)
{
@@ -438,17 +429,18 @@ void AfcExplorerWidget::setupFileExplorer()
{
m_explorer = new QWidget();
QVBoxLayout *explorerLayout = new QVBoxLayout(m_explorer);
+ explorerLayout->setContentsMargins(0, 0, 0, 0);
+ m_explorer->setStyleSheet("border : none;");
// Export/Import buttons layout
QHBoxLayout *exportLayout = new QHBoxLayout();
m_exportBtn = new QPushButton("Export");
- m_exportDeleteBtn = new QPushButton("Export & Delete");
m_importBtn = new QPushButton("Import");
m_addToFavoritesBtn = new QPushButton("Add to Favorites");
exportLayout->addWidget(m_exportBtn);
- exportLayout->addWidget(m_exportDeleteBtn);
exportLayout->addWidget(m_importBtn);
exportLayout->addWidget(m_addToFavoritesBtn);
+ exportLayout->setContentsMargins(0, 0, 0, 0);
exportLayout->addStretch();
explorerLayout->addLayout(exportLayout);
@@ -473,8 +465,6 @@ void AfcExplorerWidget::setupFileExplorer()
&AfcExplorerWidget::onItemDoubleClicked);
connect(m_exportBtn, &QPushButton::clicked, this,
&AfcExplorerWidget::onExportClicked);
- connect(m_exportDeleteBtn, &QPushButton::clicked, this,
- &AfcExplorerWidget::onExportDeleteClicked);
connect(m_importBtn, &QPushButton::clicked, this,
&AfcExplorerWidget::onImportClicked);
connect(m_addToFavoritesBtn, &QPushButton::clicked, this,
diff --git a/src/afcexplorerwidget.h b/src/afcexplorerwidget.h
index b06903e..375c458 100644
--- a/src/afcexplorerwidget.h
+++ b/src/afcexplorerwidget.h
@@ -33,7 +33,6 @@ private slots:
void onBreadcrumbClicked();
void onFileListContextMenu(const QPoint &pos);
void onExportClicked();
- void onExportDeleteClicked();
void onImportClicked();
void onSidebarItemClicked(QTreeWidgetItem *item, int column);
void onAddToFavoritesClicked();
@@ -43,7 +42,6 @@ private:
QWidget *m_explorer;
QPushButton *m_backBtn;
QPushButton *m_exportBtn;
- QPushButton *m_exportDeleteBtn;
QPushButton *m_importBtn;
QPushButton *m_addToFavoritesBtn;
QListWidget *m_fileList;
diff --git a/src/appcontext.cpp b/src/appcontext.cpp
index 95dc99a..25d02d8 100644
--- a/src/appcontext.cpp
+++ b/src/appcontext.cpp
@@ -74,15 +74,7 @@ void AppContext::addDevice(QString udid, idevice_connection_type conn_type,
qDebug() << "Failed to initialize device with UDID: " << udid;
// return onDeviceInitFailed(udid, initResult.error);
if (initResult.error == LOCKDOWN_E_PASSWORD_PROTECTED) {
- // warn("Device with UDID " + udid +
- // " is password protected. Please unlock the device
- // and " "try again.",
- // "Warning");
- // TODO: also handle pairing devices
- // the reason why we don't handle pairing devices here is
- // because it's less likely that it will be an error typeof
- // LOCKDOWN_E_PASSWORD_PROTECTED if the device is paired type
- if (addType == AddType::Regular) { // TODO:IMPLEMENT
+ if (addType == AddType::Regular) {
// FIXME: if a device never gets paired, it will stay in
// this
m_pendingDevices.append(udid);
@@ -128,33 +120,6 @@ void AppContext::addDevice(QString udid, idevice_connection_type conn_type,
// processing device information");
}
}
-// TODO:WIP
-void AppContext::instanceRemoveDevice(QString _udid)
-{
- const std::string uuid = _udid.toStdString();
- if (!m_devices.contains(uuid)) {
- qDebug() << "Device with UUID " + _udid +
- " not found. Please report this issue.",
- "Error";
- return;
- }
-
- qDebug() << "Removing device with UUID:" << QString::fromStdString(uuid);
-
- // cleanDevice(device);
- iDescriptorDevice *device = m_devices[uuid];
- m_devices.remove(uuid);
-
- emit deviceRemoved(uuid);
- // TODO: Cleanup now should be done wherever there are initialized
- // lockdownd_client_free(device->lockdownClient);
- if (device->afcClient)
- afc_client_free(device->afcClient);
- idevice_free(device->device);
- // lockdownd_service_descriptor_free(device->lockdownService);
- delete device;
- // return true;
-}
int AppContext::getConnectedDeviceCount() const
{
@@ -167,36 +132,28 @@ void AppContext::removeDevice(QString _udid)
const std::string uuid = _udid.toStdString();
if (!m_devices.contains(uuid)) {
qDebug() << "Device with UUID " + _udid +
- " not found. Please report this issue.",
- "Error";
+ " not found. Please report this issue.";
return;
}
qDebug() << "Removing device with UUID:" << QString::fromStdString(uuid);
- // cleanDevice(device);
iDescriptorDevice *device = m_devices[uuid];
m_devices.remove(uuid);
emit deviceRemoved(uuid);
- // TODO: Cleanup now should be done wherever there are initialized
- // lockdownd_client_free(device->lockdownClient);
if (device->afcClient)
afc_client_free(device->afcClient);
idevice_free(device->device);
- // lockdownd_service_descriptor_free(device->lockdownService);
delete device;
- // return true;
}
-void AppContext::removeRecoveryDevice(const QString &ecid)
+void AppContext::removeRecoveryDevice(QString ecid)
{
std::string std_ecid = ecid.toStdString();
if (!m_recoveryDevices.contains(std_ecid)) {
- // warning popup
- warn("Device with ECID " + ecid +
- " not found. Please report this issue.",
- "Error");
+ qDebug() << "Device with ECID " + ecid +
+ " not found. Please report this issue.";
return;
}
@@ -206,9 +163,8 @@ void AppContext::removeRecoveryDevice(const QString &ecid)
RecoveryDeviceInfo *deviceInfo = m_recoveryDevices[std_ecid];
m_recoveryDevices.remove(std_ecid);
- delete deviceInfo;
-
emit recoveryDeviceRemoved(ecid);
+ delete deviceInfo;
}
iDescriptorDevice *AppContext::getDevice(const std::string &uuid)
@@ -233,7 +189,7 @@ bool AppContext::noDevicesConnected() const
m_pendingDevices.isEmpty());
}
-std::string AppContext::addRecoveryDevice(RecoveryDeviceInfo *deviceInfo)
+void AppContext::addRecoveryDevice(RecoveryDeviceInfo *deviceInfo)
{
// Generate a unique ID for the recovery device
// std::string uuid =
@@ -243,11 +199,23 @@ std::string AppContext::addRecoveryDevice(RecoveryDeviceInfo *deviceInfo)
// uint64_t to std::string
m_recoveryDevices[std::to_string(deviceInfo->ecid)] = deviceInfo;
- // emit recoveryDeviceAdded(uuid);
- return std::to_string(deviceInfo->ecid);
+ emit recoveryDeviceAdded(deviceInfo);
}
AppContext::~AppContext()
{
- // Cleanup if needed
+ for (auto device : m_devices) {
+ emit deviceRemoved(device->udid);
+ if (device->afcClient)
+ afc_client_free(device->afcClient);
+ idevice_free(device->device);
+ delete device;
+ }
+
+ // TODO
+ for (auto recoveryDevice : m_recoveryDevices) {
+ // emit
+ // recoveryDeviceRemoved(QString::fromStdString(recoveryDevice->ecid));
+ // delete recoveryDevice;
+ }
}
\ No newline at end of file
diff --git a/src/appcontext.h b/src/appcontext.h
index dbff59d..d54116d 100644
--- a/src/appcontext.h
+++ b/src/appcontext.h
@@ -16,16 +16,10 @@ public:
explicit AppContext(QObject *parent = nullptr);
void handleDBusSignal(const QDBusMessage &msg);
bool noDevicesConnected() const;
- std::string addDevice(iDescriptorDevice *device);
-
- std::string addRecoveryDevice(RecoveryDeviceInfo *deviceInfo);
- // std::string addRecoveryDevice(const RecoveryDeviceInfo& deviceInfo);
- void removeRecoveryDevice(const QString &udid);
// Returns whether there are any devices connected (regular or recovery)
QList getAllRecoveryDevices();
~AppContext();
- void instanceRemoveDevice(QString _udid);
int getConnectedDeviceCount() const;
private:
@@ -46,6 +40,8 @@ public slots:
void removeDevice(QString udid);
void addDevice(QString udid, idevice_connection_type connType,
AddType addType);
+ void addRecoveryDevice(RecoveryDeviceInfo *deviceInfo);
+ void removeRecoveryDevice(QString ecid);
};
#endif // APPCONTEXT_H
diff --git a/src/appswidget.cpp b/src/appswidget.cpp
index 4660da7..2b6ec8f 100644
--- a/src/appswidget.cpp
+++ b/src/appswidget.cpp
@@ -54,10 +54,6 @@ void AppsWidget::setupUI()
QHBoxLayout *headerLayout = new QHBoxLayout(headerWidget);
headerLayout->setContentsMargins(20, 10, 20, 10);
- QLabel *titleLabel = new QLabel("App Store");
- titleLabel->setStyleSheet(
- "font-size: 24px; font-weight: bold; color: #333;");
-
// Create status label first
m_statusLabel = new QLabel("Not signed in");
m_statusLabel->setStyleSheet("margin-right: 20px;");
@@ -97,9 +93,6 @@ void AppsWidget::setupUI()
connect(searchAction, &QAction::triggered, this,
&AppsWidget::performSearch);
- headerLayout->addWidget(titleLabel);
-
- headerLayout->addStretch();
headerLayout->addWidget(m_searchEdit);
headerLayout->addStretch();
headerLayout->addWidget(m_statusLabel);
diff --git a/src/cableinfowidget.cpp b/src/cableinfowidget.cpp
index 7050040..45bd653 100644
--- a/src/cableinfowidget.cpp
+++ b/src/cableinfowidget.cpp
@@ -1,6 +1,8 @@
#include "cableinfowidget.h"
+#include "appcontext.h"
#include
#include
+#include
#include
#include
@@ -9,14 +11,18 @@ CableInfoWidget::CableInfoWidget(iDescriptorDevice *device, QWidget *parent)
{
setupUI();
initCableInfo();
-
- // Auto-refresh cable info after UI is set up
- // QTimer::singleShot(100, this, &CableInfoWidget::refreshCableInfo);
+ connect(AppContext::sharedInstance(), &AppContext::deviceRemoved, this,
+ [this](const std::string &udid) {
+ if (m_device->udid == udid) {
+ this->close();
+ this->deleteLater();
+ }
+ });
}
void CableInfoWidget::setupUI()
{
- setWindowTitle("Cable Information");
+ setWindowTitle("Cable Information - iDescriptor");
m_mainLayout = new QVBoxLayout(this);
m_mainLayout->setSpacing(20);
m_mainLayout->setContentsMargins(20, 20, 20, 20);
@@ -24,70 +30,50 @@ void CableInfoWidget::setupUI()
// Header section
QHBoxLayout *headerLayout = new QHBoxLayout();
- m_iconLabel = new QLabel();
- m_iconLabel->setFixedSize(48, 48);
- m_iconLabel->setScaledContents(true);
- m_iconLabel->setAlignment(Qt::AlignCenter);
-
m_statusLabel = new QLabel("Analyzing cable...");
m_statusLabel->setStyleSheet("QLabel { "
"font-size: 18px; "
- "font-weight: bold; "
- "color: #333; "
"}");
+ m_descriptionLabel =
+ new QLabel("Please wait while we analyze the connected cable.");
+ m_descriptionLabel->setStyleSheet("font-size: 9px;");
- headerLayout->addWidget(m_iconLabel);
- headerLayout->addWidget(m_statusLabel, 1);
+ headerLayout->addWidget(m_statusLabel);
m_mainLayout->addLayout(headerLayout);
- // Scroll area to make the info section scrollable
- QScrollArea *scrollArea = new QScrollArea();
- scrollArea->setWidgetResizable(true);
- scrollArea->setFrameShape(QFrame::NoFrame);
- scrollArea->setStyleSheet("QScrollArea { "
- "background-color: #f8f9fa; "
- "border: 1px solid #dee2e6; "
- "border-radius: 8px; "
- "}");
+ m_infoWidget = new QGroupBox("Cable Information");
- // Info widget that goes inside the scroll area
- m_infoWidget = new QWidget();
- m_infoWidget->setStyleSheet("QWidget { "
- "background: transparent; "
- "padding: 16px; "
- "color: #333; "
- "}");
m_infoLayout = new QGridLayout(m_infoWidget);
m_infoLayout->setSpacing(12);
m_infoLayout->setColumnStretch(1, 1);
- scrollArea->setWidget(m_infoWidget);
-
- m_mainLayout->addWidget(scrollArea);
+ m_mainLayout->addWidget(m_descriptionLabel);
+ m_mainLayout->addWidget(m_infoWidget);
m_mainLayout->addStretch();
}
void CableInfoWidget::initCableInfo()
{
if (!m_device || !m_device->device) {
- m_statusLabel->setText("❌ Device not available");
+ m_statusLabel->setText("Something went wrong (no device ?)");
m_statusLabel->setStyleSheet(
"QLabel { color: #dc3545; font-size: 18px; font-weight: bold; }");
return;
}
m_statusLabel->setText("Analyzing cable...");
- m_statusLabel->setStyleSheet(
- "QLabel { color: #6c757d; font-size: 18px; font-weight: bold; }");
-
// Get cable info
get_cable_info(m_device->device, m_response);
-
+ char *xml_string = nullptr;
+ uint32_t xml_length = 0;
+ plist_to_xml(m_response, &xml_string, &xml_length);
+ qDebug() << "Cable info plist:\n"
+ << QString::fromUtf8(xml_string, xml_length);
analyzeCableInfo();
updateUI();
}
-
+// FIXME: genuine check is not perfect, still need more research
void CableInfoWidget::analyzeCableInfo()
{
qDebug() << "Analyzing cable info...";
@@ -118,12 +104,6 @@ void CableInfoWidget::analyzeCableInfo()
m_cableInfo.interfaceModuleSerial = QString::fromStdString(
ioreg["IOAccessoryInterfaceModuleSerialNumber"].getString());
- // Determine if genuine based on manufacturer and presence of detailed info
- m_cableInfo.isGenuine =
- (m_cableInfo.manufacturer.contains("Apple", Qt::CaseInsensitive) &&
- !m_cableInfo.modelNumber.isEmpty() &&
- !m_cableInfo.accessoryName.isEmpty());
-
// Check if Type-C (based on accessory name or TriStar class)
m_cableInfo.triStarClass =
QString::fromStdString(ioreg["TriStarICClass"].getString());
@@ -131,6 +111,27 @@ void CableInfoWidget::analyzeCableInfo()
(m_cableInfo.accessoryName.contains("USB-C", Qt::CaseInsensitive) ||
m_cableInfo.triStarClass.contains("1612")); // CBTL1612 is Type-C
+ // Determine if genuine based on manufacturer and presence of detailed info
+ bool preGenuineCheck =
+ (m_cableInfo.manufacturer.contains("Apple", Qt::CaseInsensitive) &&
+ !m_cableInfo.modelNumber.isEmpty() &&
+ !m_cableInfo.accessoryName.isEmpty());
+
+ // Further checks for Type-C cables
+ // if report says it's Type-C, it must match the actual connection type
+ if (m_cableInfo.isTypeC) {
+ bool actuallyTypeC =
+ m_device->deviceInfo.batteryInfo.usbConnectionType ==
+ BatteryInfo::ConnectionType::USB_TYPEC;
+ if (!actuallyTypeC) {
+ // most likely a fake cable with faked info
+ m_cableInfo.isFakeInfo = true;
+ }
+ m_cableInfo.isGenuine = actuallyTypeC && preGenuineCheck;
+ } else {
+ m_cableInfo.isGenuine = preGenuineCheck;
+ }
+
// Power information
m_cableInfo.currentLimit = ioreg["IOAccessoryUSBCurrentLimit"].getUInt();
m_cableInfo.chargingVoltage =
@@ -181,46 +182,31 @@ void CableInfoWidget::updateUI()
delete item;
}
- // if (!m_cableInfo.isConnected) {
- // m_statusLabel->setText("❌ No cable detected");
- // m_statusLabel->setStyleSheet(
- // "QLabel { color: #dc3545; font-size: 18px; font-weight: bold;
- // }");
- // m_iconLabel->setText("🔌");
- // m_iconLabel->setStyleSheet("QLabel { font-size: 32px; }");
-
- // QLabel *noDataLabel = new QLabel("No cable information available");
- // noDataLabel->setStyleSheet(
- // "QLabel { color: #6c757d; font-size: 14px; text-align: center;
- // }");
- // m_infoLayout->addWidget(noDataLabel, 0, 0, 1, 2, Qt::AlignCenter);
- // return;
- // }
-
// Update status and icon based on cable type
QString statusText;
QString statusStyle;
- QString iconText;
- // todo: sometimes they fake the manufacturer even if it's not genuine
- // compare m_cableInfo.isTypeC to the actual values we get from ioreg
+
+ m_descriptionLabel->setText("Please note that this check may not be "
+ "absolute guarantee of authenticity.");
if (m_cableInfo.isGenuine) {
- statusText = QString("Genuine %1")
+ statusText = QString("✅ Genuine %1")
.arg(m_cableInfo.isTypeC ? "USB-C to Lightning Cable"
: "Lightning Cable");
statusStyle =
"QLabel { color: #28a745; font-size: 18px; font-weight: bold; }";
- iconText = m_cableInfo.isTypeC ? "Type-C" : "Lightning";
} else {
statusText = "⚠️ Third-party Cable";
statusStyle =
- "QLabel { color: #ffc107; font-size: 18px; font-weight: bold; }";
- iconText = "❓";
+ "QLabel { color: #dc3545; font-size: 18px; font-weight: bold; }";
+
+ if (m_cableInfo.isFakeInfo) {
+ m_descriptionLabel->setText("The cable reports false information. "
+ "It is most likely a fake cable.");
+ }
}
m_statusLabel->setText(statusText);
m_statusLabel->setStyleSheet(statusStyle);
- m_iconLabel->setText(iconText);
- m_iconLabel->setStyleSheet("QLabel { font-size: 32px; }");
int row = 0;
@@ -286,24 +272,11 @@ void CableInfoWidget::updateUI()
}
void CableInfoWidget::createInfoRow(QGridLayout *layout, int row,
- const QString &label, const QString &value,
- const QString &style)
+ const QString &label, const QString &value)
{
- qDebug() << "Creating info row:" << label << value;
QLabel *labelWidget = new QLabel(label);
- labelWidget->setStyleSheet("QLabel { "
- "font-weight: bold; "
- "color: #495057; "
- "font-size: 13px; "
- "}");
QLabel *valueWidget = new QLabel(value);
- QString valueStyle = style.isEmpty() ? "QLabel { "
- "color: #212529; "
- "font-size: 13px; "
- "}"
- : style;
- valueWidget->setStyleSheet(valueStyle);
valueWidget->setWordWrap(true);
layout->addWidget(labelWidget, row, 0, Qt::AlignTop);
diff --git a/src/cableinfowidget.h b/src/cableinfowidget.h
index e87f952..d47029f 100644
--- a/src/cableinfowidget.h
+++ b/src/cableinfowidget.h
@@ -3,6 +3,7 @@
#include "iDescriptor.h"
#include
+#include
#include
#include
#include
@@ -27,7 +28,7 @@ private:
void analyzeCableInfo();
void updateUI();
void createInfoRow(QGridLayout *layout, int row, const QString &label,
- const QString &value, const QString &style = "");
+ const QString &value);
// Cable information structure
struct CableInfo {
@@ -45,13 +46,14 @@ private:
QString triStarClass;
QStringList supportedTransports;
QStringList activeTransports;
+ bool isFakeInfo = false;
};
// UI components
QVBoxLayout *m_mainLayout;
QLabel *m_statusLabel;
- QLabel *m_iconLabel;
- QWidget *m_infoWidget;
+ QLabel *m_descriptionLabel;
+ QGroupBox *m_infoWidget;
QGridLayout *m_infoLayout;
// Data
diff --git a/src/core/services/get_mounted_image.cpp b/src/core/services/get_mounted_image.cpp
index f1acafb..0f8bd70 100644
--- a/src/core/services/get_mounted_image.cpp
+++ b/src/core/services/get_mounted_image.cpp
@@ -29,10 +29,9 @@
#include
#endif
-QPair _get_mounted_image(const char *udid)
+plist_t _get_mounted_image(const char *udid)
{
mobile_image_mounter_client_t mim = NULL;
- int res = -1;
lockdownd_client_t lckd = NULL;
lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
afc_client_t afc = NULL;
@@ -48,14 +47,12 @@ QPair _get_mounted_image(const char *udid)
IDEVICE_LOOKUP_USBMUX)) {
qDebug() << "ERROR: Could not create idevice!";
- res = -1;
goto leave;
}
if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(
device, &lckd, TOOL_NAME))) {
qDebug() << "ERROR: Could not connect to lockdownd service!";
- res = -1;
goto leave;
}
@@ -75,30 +72,14 @@ QPair _get_mounted_image(const char *udid)
if (!service || service->port == 0) {
qDebug() << "ERROR: Could not start mobile_image_mounter service!";
- res = -1;
goto leave;
}
- // if locked
- // {
- // "Error": "DeviceLocked"
- // }
+ // will sometimes return MOBILE_IMAGE_MOUNTER_E_SUCCESS even if the device
+ // is locked - mostly on older devices
err = mobile_image_mounter_lookup_image(mim, imagetype, &result);
- if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
- res = 0;
- } else {
- res = -1;
- printf("Error: lookup_image returned %d\n", err);
- }
leave:
- // if (f) {
- // fclose(f);
- // }
- // TODO:need to free result
- // if (result) {
- // plist_free(result);
- // }
if (mim) {
mobile_image_mounter_free(mim);
}
@@ -112,7 +93,7 @@ leave:
idevice_free(device);
}
- return {res == 0, result};
+ return result;
}
// int main(){return 0;}
\ No newline at end of file
diff --git a/src/core/services/mount_dev_image.cpp b/src/core/services/mount_dev_image.cpp
index 13a5c93..fb7b145 100644
--- a/src/core/services/mount_dev_image.cpp
+++ b/src/core/services/mount_dev_image.cpp
@@ -49,43 +49,28 @@
#include
#endif
-static int list_mode = 0;
-static int use_network = 0;
-static int xml_mode = 0;
-static const char *udid = NULL;
-static const char *imagetype = NULL;
-
-static const char PKG_PATH[] = "PublicStaging";
-static const char PATH_PREFIX[] = "/private/var/mobile/Media";
-
typedef enum {
DISK_IMAGE_UPLOAD_TYPE_AFC,
DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE
} disk_image_upload_type_t;
-enum cmd_mode {
- CMD_NONE = 0,
- CMD_MOUNT,
- CMD_UNMOUNT,
- CMD_LIST,
- CMD_DEVMODESTATUS
-};
+static const char *imagetype = NULL;
-// int cmd = CMD_NONE;
+static const char PKG_PATH[] = "PublicStaging";
+static const char PATH_PREFIX[] = "/private/var/mobile/Media";
#ifndef SOURCE_DIR
#define SOURCE_DIR "."
#endif
+
static ssize_t mim_upload_cb(void *buf, size_t size, void *userdata)
{
return fread(buf, 1, size, (FILE *)userdata);
}
-// TODO: cleanup
-// TODO: may not work on a broken ,faulty or fake usb cable
-// TypeC cables work better
-// TODO : sometimes ERROR: Device is locked, can't mount. Unlock device and try
-// again.
-bool mount_dev_image(const char *udid, const char *image_dir_path)
+// extend the mobile_image_mounter_error_t type and return sucess if there is
+// already a disk image
+mobile_image_mounter_error_t mount_dev_image(const char *udid,
+ const char *image_dir_path)
{
mobile_image_mounter_client_t mim = NULL;
int res = -1;
@@ -111,9 +96,7 @@ bool mount_dev_image(const char *udid, const char *image_dir_path)
size_t sig_length = 0;
if (IDEVICE_E_SUCCESS !=
- idevice_new_with_options(&device, udid,
- (use_network) ? IDEVICE_LOOKUP_NETWORK
- : IDEVICE_LOOKUP_USBMUX)) {
+ idevice_new_with_options(&device, udid, IDEVICE_LOOKUP_USBMUX)) {
qDebug() << "ERROR: Could not create idevice!";
res = -1;
goto leave;
@@ -550,7 +533,6 @@ bool mount_dev_image(const char *udid, const char *image_dir_path)
res = -1;
goto leave;
}
- qDebug() << "done.";
qDebug() << "Mounting...";
err = mobile_image_mounter_mount_image_with_options(
@@ -565,11 +547,18 @@ bool mount_dev_image(const char *udid, const char *image_dir_path)
if (!strcmp(status, "Complete")) {
qDebug() << "Done.";
res = 0;
+ } else {
+ err = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR;
}
free(status);
+ } else {
+ err = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR;
}
+ } else {
+ err = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR;
}
if (res != 0) { // If not complete, log the error
+ err = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR;
node = plist_dict_get_item(result, "Error");
if (node) {
char *error = NULL;
@@ -580,8 +569,17 @@ bool mount_dev_image(const char *udid, const char *image_dir_path)
}
node = plist_dict_get_item(result, "DetailedError");
if (node) {
- qDebug() << "DetailedError:"
- << plist_get_string_ptr(node, NULL);
+ const char *str = plist_get_string_ptr(node, NULL);
+ auto sd_str = std::string(str);
+ if (sd_str.find("already mounted at /Developer") !=
+ std::string::npos) {
+ // FIXME: need an error code for this
+ qDebug() << "Image is already mounted";
+ // res = 0;
+ // err = MOBILE_IMAGE_MOUNTER_E_SUCCESS;
+ } else {
+ qDebug() << "DetailedError:" << str;
+ }
}
}
}
@@ -628,7 +626,5 @@ leave:
free(mountname);
}
- return res == 0;
-}
-
-// int main(){return 0;}
\ No newline at end of file
+ return err;
+}
\ No newline at end of file
diff --git a/src/devdiskimageswidget.cpp b/src/devdiskimageswidget.cpp
index f014ae5..e6f461b 100644
--- a/src/devdiskimageswidget.cpp
+++ b/src/devdiskimageswidget.cpp
@@ -2,6 +2,7 @@
#include "appcontext.h"
#include "devdiskmanager.h"
#include "iDescriptor.h"
+#include "qprocessindicator.h"
#include "settingsmanager.h"
#include
#include
@@ -26,18 +27,14 @@
#include
#include
#include
+#include
#include
-// TODO:sometimes non authentic cables do not work with img mounting
-
DevDiskImagesWidget::DevDiskImagesWidget(iDescriptorDevice *device,
QWidget *parent)
: QWidget{parent}, m_currentDevice(device)
{
setupUi();
-
- // Connect to manager signals
- // TODO: can prevent race condition ?
connect(DevDiskManager::sharedInstance(), &DevDiskManager::imageListFetched,
this, &DevDiskImagesWidget::onImageListFetched);
@@ -49,6 +46,12 @@ DevDiskImagesWidget::DevDiskImagesWidget(iDescriptorDevice *device,
connect(m_deviceComboBox,
QOverload::of(&QComboBox::currentIndexChanged), this,
&DevDiskImagesWidget::onDeviceSelectionChanged);
+
+ connect(m_imageListWidget, &QListWidget::itemClicked, this,
+ [this](QListWidgetItem *item) {
+ m_mountButton->setEnabled(item != nullptr);
+ });
+ // connect
}
void DevDiskImagesWidget::setupUi()
@@ -62,6 +65,7 @@ void DevDiskImagesWidget::setupUi()
m_deviceComboBox = new QComboBox(this);
mountLayout->addWidget(m_deviceComboBox);
m_mountButton = new QPushButton("Mount", this);
+ m_mountButton->setEnabled(false);
m_check_mountedButton = new QPushButton("Check Mounted", this);
connect(m_mountButton, &QPushButton::clicked, this,
&DevDiskImagesWidget::onMountButtonClicked);
@@ -76,52 +80,45 @@ void DevDiskImagesWidget::setupUi()
// main path/info row (no shadow)
auto *pathWidget = new QWidget();
pathWidget->setLayout(pathLayout);
- pathLayout->addWidget(
- new QLabel("You can change the download path from settings :"));
+ QLabel *tipLabel =
+ new QLabel("You can change the download path from settings :");
+ tipLabel->setStyleSheet("font-size: 9px;");
+ pathLayout->addWidget(tipLabel);
QPushButton *openSettingsButton = new QPushButton("Open Settings");
+ openSettingsButton->setSizePolicy(QSizePolicy::Preferred,
+ QSizePolicy::Preferred);
pathLayout->addWidget(openSettingsButton);
+ pathLayout->addStretch();
connect(openSettingsButton, &QPushButton::clicked, this, [this]() {
SettingsManager::sharedInstance()->showSettingsDialog();
});
pathLayout->setContentsMargins(10, 10, 10, 10);
layout->addWidget(pathWidget);
- // thin centered bottom line + shadow (shadow only applied to this line)
- QWidget *lineContainer = new QWidget();
- QHBoxLayout *lineLayout = new QHBoxLayout(lineContainer);
- lineLayout->setContentsMargins(0, 0, 0, 0); // adjust centering / width
- lineLayout->setSpacing(0);
-
- QWidget *innerLine = new QWidget();
- innerLine->setFixedHeight(2); // thickness of the visible border
- innerLine->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
- innerLine->setStyleSheet("background-color: #363d32;");
- innerLine->setLayout(new QHBoxLayout());
- innerLine->layout()->setContentsMargins(0, 0, 0, 0);
- innerLine->layout()->setSpacing(0);
-
- // apply shadow only to the thin line so shadow appears only under bottom
- QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect(this);
- shadow->setBlurRadius(30);
- shadow->setColor(QColor(0, 0, 0, 30));
- shadow->setOffset(0, 6);
- innerLine->setGraphicsEffect(shadow);
-
- // If you want the line to be shorter than full width, give it a max width:
- // innerLine->setMaximumWidth( int(width * 0.8) ); // or manage in
- // resizeEvent
-
- lineLayout->addStretch();
- lineLayout->addWidget(innerLine);
- lineLayout->addStretch();
- layout->addWidget(lineContainer);
-
m_stackedWidget = new QStackedWidget(this);
layout->addWidget(m_stackedWidget);
+ // Create loading page with process indicator
+ auto *loadingPage = new QWidget();
+ auto *loadingLayout = new QVBoxLayout(loadingPage);
+ loadingLayout->addStretch();
+
+ auto *indicatorLayout = new QHBoxLayout();
+ indicatorLayout->addStretch();
+ m_processIndicator = new QProcessIndicator(loadingPage);
+ m_processIndicator->setFixedSize(40, 40);
+ m_processIndicator->setType(QProcessIndicator::line_rotate);
+ indicatorLayout->addWidget(m_processIndicator);
+ indicatorLayout->addStretch();
+ loadingLayout->addLayout(indicatorLayout);
+
m_statusLabel = new QLabel("Fetching image list...");
m_statusLabel->setAlignment(Qt::AlignCenter);
- m_stackedWidget->addWidget(m_statusLabel);
+ m_statusLabel->setStyleSheet("QLabel { color: #666; margin-top: 10px; }");
+ loadingLayout->addWidget(m_statusLabel);
+ loadingLayout->addStretch();
+
+ m_stackedWidget->addWidget(loadingPage);
m_imageListWidget = new QListWidget(this);
m_imageListWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
@@ -131,15 +128,19 @@ void DevDiskImagesWidget::setupUi()
m_stackedWidget->addWidget(m_imageListWidget);
- displayImages();
if (DevDiskManager::sharedInstance()->isImageListReady()) {
+ displayImages();
m_stackedWidget->setCurrentWidget(m_imageListWidget);
+ } else {
+ m_processIndicator->start();
+ m_stackedWidget->setCurrentIndex(0); // Show loading page
}
}
void DevDiskImagesWidget::fetchImages()
{
- m_stackedWidget->setCurrentWidget(m_statusLabel);
+ m_processIndicator->start();
+ m_stackedWidget->setCurrentIndex(0); // Show loading page
m_statusLabel->setText("Fetching image list...");
// DevDiskManager::sharedInstance()->fetchImageList();
}
@@ -147,14 +148,17 @@ void DevDiskImagesWidget::fetchImages()
void DevDiskImagesWidget::onImageListFetched(bool success,
const QString &errorMessage)
{
+ m_processIndicator->stop();
- qDebug() << "Image list fetched successfully";
if (!success) {
+ qDebug() << "Error fetching image list:" << errorMessage;
m_statusLabel->setText(
QString("Error fetching image list: %1").arg(errorMessage));
+ // Keep showing the loading page with error message
return;
}
+ qDebug() << "Image list fetched successfully";
displayImages();
m_stackedWidget->setCurrentWidget(m_imageListWidget);
}
@@ -165,7 +169,11 @@ void DevDiskImagesWidget::onDeviceSelectionChanged(int index)
index >= AppContext::sharedInstance()->getAllDevices().size())
return;
- m_currentDevice = AppContext::sharedInstance()->getAllDevices()[index];
+ auto device = AppContext::sharedInstance()->getAllDevices()[index];
+ if (device == nullptr)
+ return;
+
+ m_currentDevice = device;
displayImages();
}
@@ -191,7 +199,7 @@ void DevDiskImagesWidget::displayImages()
// Parse images using manager
GetImagesSortedFinalResult sortedResult =
DevDiskManager::sharedInstance()->parseImageList(
- deviceMajorVersion, deviceMinorVersion, m_mounted_sig,
+ deviceMajorVersion, deviceMinorVersion, m_mounted_sig.c_str(),
m_mounted_sig_len);
auto compatibleImages = sortedResult.compatibleImages;
@@ -465,6 +473,16 @@ void DevDiskImagesWidget::onFileDownloadFinished()
void DevDiskImagesWidget::updateDeviceList()
{
auto devices = AppContext::sharedInstance()->getAllDevices();
+
+ if (devices.isEmpty()) {
+ m_currentDevice = nullptr;
+ m_check_mountedButton->setEnabled(false);
+ m_deviceComboBox->setEnabled(false);
+ } else {
+ m_deviceComboBox->setEnabled(true);
+ m_check_mountedButton->setEnabled(true);
+ }
+
QString currentUdid = "";
if (m_deviceComboBox->count() > 0 &&
m_deviceComboBox->currentIndex() >= 0) {
@@ -477,9 +495,9 @@ void DevDiskImagesWidget::updateDeviceList()
for (int i = 0; i < devices.size(); ++i) {
auto *device = devices.at(i);
m_deviceComboBox->addItem(
- QString("%1 (%2)")
+ QString("%1 / (%2)")
.arg(QString::fromStdString(device->deviceInfo.deviceName))
- .arg(QString::fromStdString(device->udid)),
+ .arg(QString::fromStdString(device->deviceInfo.productType)),
QString::fromStdString(device->udid));
if (QString().fromStdString((device->udid)) == currentUdid) {
newIndex = i;
@@ -494,6 +512,7 @@ void DevDiskImagesWidget::updateDeviceList()
void DevDiskImagesWidget::onMountButtonClicked()
{
+ qDebug() << "Current index:" << m_deviceComboBox->currentIndex();
if (m_deviceComboBox->currentIndex() < 0) {
QMessageBox::warning(this, "No Device",
"Please select a device to mount the image on.");
@@ -514,12 +533,13 @@ void DevDiskImagesWidget::onMountButtonClicked()
QString version = button->property("version").toString();
- mountImage(version);
+ this->mountImage(version);
}
void DevDiskImagesWidget::mountImage(const QString &version)
{
QString udid = m_deviceComboBox->currentData().toString();
+ m_deviceComboBox->setEnabled(false);
if (udid.isEmpty()) {
QMessageBox::warning(this, "No Device", "Please select a device.");
return;
@@ -538,20 +558,60 @@ void DevDiskImagesWidget::mountImage(const QString &version)
m_mountButton->setEnabled(false);
m_mountButton->setText("Mounting...");
- bool success = DevDiskManager::sharedInstance()->mountImage(version, udid);
+ mobile_image_mounter_error_t err =
+ DevDiskManager::sharedInstance()->mountImage(version, udid);
- m_mountButton->setEnabled(true);
- m_mountButton->setText("Mount");
+ auto updateUI = [&]() {
+ m_mountButton->setEnabled(true);
+ m_mountButton->setText("Mount");
+ m_deviceComboBox->setEnabled(true);
+ };
- if (success) {
+ switch (err) {
+ case MOBILE_IMAGE_MOUNTER_E_INVALID_ARG:
+ QMessageBox::critical(this, "Mount Failed",
+ "Invalid argument provided for mounting.");
+ updateUI();
+ return;
+ case MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED:
+ QMessageBox::critical(this, "Mount Failed",
+ "The device is locked. Please unlock it and try "
+ "again.");
+ updateUI();
+ return;
+ case MOBILE_IMAGE_MOUNTER_E_SUCCESS:
QMessageBox::information(this, "Success",
QString("Image mounted successfully on %1.")
.arg(m_deviceComboBox->currentText()));
displayImages(); // Refresh to show mounted status
- } else {
- QMessageBox::critical(this, "Mount Failed",
- QString("Failed to mount image on %1.")
- .arg(m_deviceComboBox->currentText()));
+ updateUI();
+ break;
+ default:
+ GetMountedImageResult result =
+ DevDiskManager::sharedInstance()->getMountedImage(
+ udid.toStdString().c_str());
+ /*
+ * FIXME: there is no error enum like
+ * MOBILE_IMAGE_MOUNTER_E_ALREADY_MOUNTED so we work around here
+ */
+ qDebug() << "Mount result:" << result.success << result.message.c_str()
+ << QString::fromStdString(result.sig);
+ if (result.success && !result.sig.empty()) {
+ m_mounted_sig = result.sig;
+ m_mounted_sig_len = result.sig.size();
+ updateUI();
+ displayImages();
+ QMessageBox::information(this, "Already Mounted",
+ "There is already a developer disk image "
+ "mounted on the device.");
+ return;
+ }
+
+ QMessageBox::critical(
+ this, "Mount Failed",
+ QString("Failed to mount image on %1. Try with a genuine cable.")
+ .arg(m_deviceComboBox->currentText()));
+ updateUI();
}
}
@@ -590,10 +650,11 @@ void DevDiskImagesWidget::closeEvent(QCloseEvent *event)
void DevDiskImagesWidget::checkMountedImage()
{
+ // just in case
+ if (!m_currentDevice || !m_currentDevice->device) {
+ return;
+ }
if (m_deviceComboBox->currentIndex() < 0) {
- QMessageBox::warning(
- this, "No Device",
- "Please select a device to check the mounted image.");
return;
}
@@ -602,53 +663,22 @@ void DevDiskImagesWidget::checkMountedImage()
m_currentDevice->udid.c_str());
qDebug() << "checkMountedImage result:" << result.success
- << result.message.c_str() << QString::fromStdString(result.output);
+ << result.message.c_str() << QString::fromStdString(result.sig);
- if (result.success) {
-
- m_mounted_sig = strdup(result.output.c_str());
- m_mounted_sig_len = result.output.size();
+ if (result.success && !result.sig.empty()) {
+ m_mounted_sig = result.sig;
+ m_mounted_sig_len = result.sig.size();
displayImages(); // Refresh to show mounted status
+ QMessageBox::information(
+ this, "Check Mounted Image",
+ "There is already a developer disk image mounted on the device.");
return;
}
- QMessageBox::information(this, "Something went wrong",
- result.message.c_str());
- // get_mounted_image(m_currentDevice->udid.c_str());
+ QString errorMsg = QString::fromStdString(result.message);
+ if (errorMsg.isEmpty()) {
+ errorMsg = "Unknown error occurred while checking mounted image";
+ }
- // plist_t sig_array_node =
- // plist_dict_get_item(result.output, "ImageSignature");
-
- // if (result.success == false || sig_array_node == NULL) {
- // QMessageBox::information(
- // this, "Locked",
- // "The device is locked. Please unlock it and try again.");
- // return;
- // }
-
- // char *mounted_sig = nullptr;
- // uint64_t mounted_sig_len = 0;
-
- // if (sig_array_node && plist_get_node_type(sig_array_node) == PLIST_ARRAY
- // &&
- // plist_array_get_size(sig_array_node) > 0) {
- // plist_t sig_data_node = plist_array_get_item(sig_array_node, 0);
- // if (sig_data_node && plist_get_node_type(sig_data_node) ==
- // PLIST_DATA) {
- // plist_get_data_val(sig_data_node, &mounted_sig,
- // &mounted_sig_len);
- // }
- // }
-
- // auto compatibleImages =
- // DevDiskManager::sharedInstance()->getCompatibleImages();
- // for (const auto &info : compatibleImages) {
- // if (info.isMounted) {
- // displayImages(); // Refresh to show mounted status
- // return;
- // }
- // }
-
- // QMessageBox::information(this, "Not Mounted",
- // "The device has no mounted images.");
+ QMessageBox::warning(this, "Check Mounted Image Failed", errorMsg);
}
diff --git a/src/devdiskimageswidget.h b/src/devdiskimageswidget.h
index 2119a7b..3e2ecd4 100644
--- a/src/devdiskimageswidget.h
+++ b/src/devdiskimageswidget.h
@@ -2,6 +2,7 @@
#define DEVDISKIMAGESWIDGET_H
#include "iDescriptor.h"
+#include "qprocessindicator.h"
#include
#include
#include
@@ -13,6 +14,7 @@
#include
#include
#include
+#include
class DevDiskImagesWidget : public QWidget
{
@@ -52,7 +54,7 @@ private:
qint64 sigReceived = 0;
};
- char *m_mounted_sig = NULL;
+ std::string m_mounted_sig = "";
uint64_t m_mounted_sig_len = 0;
QStackedWidget *m_stackedWidget;
@@ -63,6 +65,7 @@ private:
QComboBox *m_deviceComboBox;
QPushButton *m_mountButton;
QPushButton *m_check_mountedButton;
+ QProcessIndicator *m_processIndicator;
iDescriptorDevice *m_currentDevice;
QStringList m_compatibleVersions;
diff --git a/src/devdiskmanager.cpp b/src/devdiskmanager.cpp
index 2521df7..b3479f8 100644
--- a/src/devdiskmanager.cpp
+++ b/src/devdiskmanager.cpp
@@ -12,6 +12,7 @@
#include
#include
#include
+#include
DevDiskManager *DevDiskManager::sharedInstance()
{
@@ -199,6 +200,7 @@ GetImagesSortedResult DevDiskManager::getImagesSorted(
int imageMinorVersion = (versionParts.size() >= 2)
? versionParts[1].toInt(&mi_ok)
: 0;
+ // todo: add "maybe compatible"
if (ma_ok && mi_ok) {
if (deviceMajorVersion >= 16) {
info.isCompatible = (imageMajorVersion == 16);
@@ -217,7 +219,14 @@ GetImagesSortedResult DevDiskManager::getImagesSorted(
}
// Check if mounted
- if (info.isCompatible && info.isDownloaded && mounted_sig) {
+ /*
+ in my testing some ios versions do accept older minor versions
+ as well for example an iPhone 5s with iOS 12.5 accepts 12.4 but
+ newer iPhones are more strict, so lets just check where it's
+ compatible or not
+ */
+ // if (info.isCompatible && info.isDownloaded && mounted_sig)
+ if (info.isDownloaded && mounted_sig) {
QString sigLocalPath =
QDir(
QDir(
@@ -375,21 +384,12 @@ bool DevDiskManager::downloadCompatibleImage(iDescriptorDevice *device)
// TODO: boolean to download
bool DevDiskManager::mountCompatibleImageInternal(iDescriptorDevice *device)
{
- GetMountedImageResult res = getMountedImage(device->udid.c_str());
- if (res.success) {
- qDebug() << "An image is already mounted on device:"
- << device->udid.c_str();
- return true;
- }
-
unsigned int device_version = idevice_get_device_version(device->device);
unsigned int deviceMajorVersion = (device_version >> 16) & 0xFF;
unsigned int deviceMinorVersion = (device_version >> 8) & 0xFF;
- // TODO: use actual device version
GetImagesSortedFinalResult images =
- parseImageList(deviceMajorVersion, deviceMinorVersion,
- res.output.c_str(), res.output.length());
+ parseImageList(deviceMajorVersion, deviceMinorVersion, "", 0);
// 1. Try to mount an already downloaded compatible image
for (const ImageInfo &info : images.compatibleImages) {
@@ -398,7 +398,8 @@ bool DevDiskManager::mountCompatibleImageInternal(iDescriptorDevice *device)
<< info.version;
qDebug() << "Attempting to mount image version" << info.version
<< "on device:" << device->udid.c_str();
- if (mountImage(info.version, device->udid.c_str())) {
+ if (MOBILE_IMAGE_MOUNTER_E_SUCCESS ==
+ mountImage(info.version, device->udid.c_str())) {
qDebug() << "Mounted existing image version" << info.version
<< "on device:" << device->udid.c_str();
return true;
@@ -440,6 +441,7 @@ bool DevDiskManager::mountCompatibleImageInternal(iDescriptorDevice *device)
Qt::SingleShotConnection);
// Start the download
+ // todo leak?
QPair replies =
downloadImage(versionToDownload);
auto *downloadItem = new DownloadItem();
@@ -467,7 +469,7 @@ bool DevDiskManager::mountCompatibleImageInternal(iDescriptorDevice *device)
return false;
}
-
+// DOES NOT CHECK IF THERE IS ALREADY AN IMAGE MOUNTED
bool DevDiskManager::mountCompatibleImage(iDescriptorDevice *device)
{
if (m_isImageListReady) {
@@ -488,18 +490,17 @@ bool DevDiskManager::mountCompatibleImage(iDescriptorDevice *device)
},
Qt::SingleShotConnection);
- // The operation is now asynchronous, the immediate return value
- // indicates that the process has started.
return true;
}
}
-bool DevDiskManager::mountImage(const QString &version, const QString &udid)
+mobile_image_mounter_error_t DevDiskManager::mountImage(const QString &version,
+ const QString &udid)
{
const QString downloadPath =
SettingsManager::sharedInstance()->devdiskimgpath();
if (!isImageDownloaded(version, downloadPath)) {
- return false;
+ return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
}
QString versionPath = QDir(downloadPath).filePath(version);
@@ -631,38 +632,41 @@ bool DevDiskManager::compareSignatures(const char *signature_file_path,
return matches;
}
+// mobile_image_mounter_error_t
+
GetMountedImageResult DevDiskManager::getMountedImage(const char *udid)
{
- QPair result = _get_mounted_image(udid);
+ /*
+ FIXME: _get_mounted_image can return MOBILE_IMAGE_MOUNTER_E_SUCCESS even
+ if the device is locked so we are going to go off of the result
+ dictionary
+ */
+ plist_t result = _get_mounted_image(udid);
+ const char *lockedErr = "DeviceLocked";
- if (result.first == false) {
- plist_t sig_err = plist_dict_get_item(result.second, "Error");
- // TODO: should print ?
- plist_print(result.second);
- if (sig_err) {
- char *error = NULL;
- plist_get_string_val(sig_err, &error);
- if (error == "DeviceLocked") {
- qDebug() << "Error:" << error;
- free(error);
- plist_free(result.second);
- return GetMountedImageResult{false, "", "Device is locked"};
- }
- } else {
+ PlistNavigator r = PlistNavigator(result);
+
+ std::string error = r["Error"].getString();
+
+ if (!error.empty()) {
+ plist_free(result);
+ if (error == lockedErr) {
+ return GetMountedImageResult{
+ false, "", "Device is locked, please unlock it and try again."};
+ } else
return GetMountedImageResult{false, "", "Unknown error"};
- }
}
- plist_t sig_array_node =
- plist_dict_get_item(result.second, "ImageSignature");
+ plist_t sig_array_node = r["ImageSignature"].getNode();
+
if (sig_array_node == NULL) {
- plist_free(result.second);
- return GetMountedImageResult{false, "", "No disk image mounted"};
+ plist_free(result);
+ return GetMountedImageResult{true, "", "No disk image mounted"};
}
char *mounted_sig = nullptr;
uint64_t mounted_sig_len = 0;
-
+ // get the signature
if (sig_array_node && plist_get_node_type(sig_array_node) == PLIST_ARRAY &&
plist_array_get_size(sig_array_node) > 0) {
plist_t sig_data_node = plist_array_get_item(sig_array_node, 0);
@@ -672,10 +676,10 @@ GetMountedImageResult DevDiskManager::getMountedImage(const char *udid)
}
std::string mounted_sig_str(mounted_sig ? mounted_sig : "");
free(mounted_sig);
- plist_free(result.second);
+ plist_free(result);
if (mounted_sig_str.empty()) {
return GetMountedImageResult{
- false, "", "No disk image mounted (No signature found)"};
+ true, "", "No disk image mounted (No signature found)"};
}
return GetMountedImageResult{true, mounted_sig_str, "Success"};
}
diff --git a/src/devdiskmanager.h b/src/devdiskmanager.h
index 7205c11..3a6f260 100644
--- a/src/devdiskmanager.h
+++ b/src/devdiskmanager.h
@@ -8,6 +8,7 @@
#include
#include
#include
+#include
class DevDiskManager : public QObject
{
@@ -33,7 +34,8 @@ public:
// Mount operations
- bool mountImage(const QString &version, const QString &udid);
+ mobile_image_mounter_error_t mountImage(const QString &version,
+ const QString &udid);
bool unmountImage();
// Signature comparison
diff --git a/src/deviceinfowidget.cpp b/src/deviceinfowidget.cpp
index 7010170..426f0e5 100644
--- a/src/deviceinfowidget.cpp
+++ b/src/deviceinfowidget.cpp
@@ -30,7 +30,7 @@ DeviceInfoWidget::DeviceInfoWidget(iDescriptorDevice *device, QWidget *parent)
{
// Main layout with horizontal orientation
QHBoxLayout *mainLayout = new QHBoxLayout(this);
- mainLayout->setContentsMargins(0, 0, 0, 0);
+ mainLayout->setContentsMargins(0, 0, 10, 0);
mainLayout->setSpacing(1);
m_graphicsScene = new QGraphicsScene(this); // no parent
QGraphicsPixmapItem *pixmapItem =
@@ -60,6 +60,7 @@ DeviceInfoWidget::DeviceInfoWidget(iDescriptorDevice *device, QWidget *parent)
// Header
QGroupBox *headerWidget = new QGroupBox();
QHBoxLayout *headerLayout = new QHBoxLayout(headerWidget);
+ headerLayout->setContentsMargins(5, 5, 5, 5);
headerLayout->setSpacing(15);
QLabel *devProductType =
@@ -75,7 +76,7 @@ DeviceInfoWidget::DeviceInfoWidget(iDescriptorDevice *device, QWidget *parent)
QSizePolicy::Preferred);
diskCapacityLabel->setAttribute(Qt::WA_StyledBackground, true);
diskCapacityLabel->setStyleSheet("background-color: rgba(0, 255, 30, 0.5);"
- "padding: 4px;"
+ "padding: 2px;"
"border-radius: 13px;");
m_chargingStatusLabel =
diff --git a/src/devicemanagerwidget.cpp b/src/devicemanagerwidget.cpp
index 8f4c381..7009691 100644
--- a/src/devicemanagerwidget.cpp
+++ b/src/devicemanagerwidget.cpp
@@ -2,6 +2,7 @@
#include "appcontext.h"
#include "devicemenuwidget.h"
#include "devicependingwidget.h"
+#include "recoverydeviceinfowidget.h"
#include
DeviceManagerWidget::DeviceManagerWidget(QWidget *parent)
@@ -40,6 +41,40 @@ DeviceManagerWidget::DeviceManagerWidget(QWidget *parent)
emit updateNoDevicesConnected();
});
+ connect(AppContext::sharedInstance(), &AppContext::recoveryDeviceAdded,
+ this, [this](QObject *recoveryDeviceInfoObj) {
+ if (!recoveryDeviceInfoObj)
+ return;
+ try {
+ RecoveryDeviceInfo *device =
+ qobject_cast(
+ recoveryDeviceInfoObj);
+ if (!device) {
+ qDebug() << "Invalid recovery device info object";
+ return;
+ }
+ // IDescriptorInitDeviceResultRecovery initResult=
+ // init_idescriptor_recovery_device(deviceInfo);
+
+ // IDescriptorInitDeviceResult initResult =
+ // init_idescriptor_device(udid.toStdString().c_str());
+
+ qDebug() << "Recovery device initialized: " << device->ecid;
+
+ // std::string added_ecid =
+ // AppContext::sharedInstance()->addRecoveryDevice(device);
+
+ // Create device info widget
+ RecoveryDeviceInfoWidget *recoveryDeviceInfoWidget =
+ new RecoveryDeviceInfoWidget(device);
+ m_stackedWidget->addWidget(recoveryDeviceInfoWidget);
+
+ } catch (...) {
+ qDebug() << "Error initializing recovery device";
+ }
+ emit updateNoDevicesConnected();
+ });
+
// connect(AppContext::sharedInstance(), &AppContext::recoveryDeviceRemoved,
// this, [this](const QString &ecid) {
// qDebug() << "Removing:" << ecid;
@@ -98,10 +133,31 @@ void DeviceManagerWidget::addDevice(iDescriptorDevice *device)
m_deviceWidgets[device->udid] = std::pair{
deviceWidget, m_sidebar->addToSidebar(tabTitle, device->udid)};
- // If this is the first device, make it current
- // if (m_currentDeviceIndex == -1) {
- // setCurrentDevice(deviceIndex);
+ // todo
+ // If this is the first device, make it current
+ // if (m_currentDeviceIndex == -1) {
+ // setCurrentDevice(deviceIndex);
+ // }
+}
+
+void DeviceManagerWidget::addRecoveryDevice(RecoveryDeviceInfo *device)
+{
+ // if (m_deviceWidgets.contains(device->ecid)) {
+ // qWarning() << "Recovery device already exists:"
+ // << QString::fromStdString(device->ecid);
+ // return;
// }
+ // qDebug() << "Connect ::recoveryDeviceAdded Adding:"
+ // << QString::fromStdString(device->ecid);
+
+ RecoveryDeviceInfoWidget *deviceWidget =
+ new RecoveryDeviceInfoWidget(device, this);
+
+ // QString tabTitle = QString::fromStdString(device->product);
+
+ m_stackedWidget->addWidget(deviceWidget);
+ // m_deviceWidgets[device->ecid] = std::pair{
+ // deviceWidget, m_sidebar->addToSidebar(tabTitle, device->ecid)};
}
void DeviceManagerWidget::addPendingDevice(const QString &udid, bool locked)
@@ -214,11 +270,6 @@ std::string DeviceManagerWidget::getCurrentDevice() const
return m_currentDeviceUuid;
}
-QWidget *DeviceManagerWidget::getDeviceWidget(int deviceIndex) const
-{
- // return m_deviceWidgets.value(deviceIndex, nullptr);
-}
-
void DeviceManagerWidget::setDeviceNavigation(int deviceIndex,
const QString §ion)
{
diff --git a/src/devicemanagerwidget.h b/src/devicemanagerwidget.h
index 5323c77..107e00d 100644
--- a/src/devicemanagerwidget.h
+++ b/src/devicemanagerwidget.h
@@ -17,18 +17,9 @@ class DeviceManagerWidget : public QWidget
public:
explicit DeviceManagerWidget(QWidget *parent = nullptr);
- void addDevice(iDescriptorDevice *device);
- // TODO:udid or uuid ?
- void addPendingDevice(const QString &udid, bool locked);
- void addPairedDevice(iDescriptorDevice *device);
-
- void removeDevice(const std::string &uuid);
void setCurrentDevice(const std::string &uuid);
std::string getCurrentDevice() const;
- // Get the device widget at a specific index
- QWidget *getDeviceWidget(int deviceIndex) const;
-
// Navigation methods
void setDeviceNavigation(int deviceIndex, const QString §ion);
@@ -45,6 +36,12 @@ private slots:
private:
void setupUI();
+ void addDevice(iDescriptorDevice *device);
+ void removeDevice(const std::string &uuid);
+ void addRecoveryDevice(RecoveryDeviceInfo *device);
+ // TODO:udid or uuid ?
+ void addPendingDevice(const QString &udid, bool locked);
+ void addPairedDevice(iDescriptorDevice *device);
QHBoxLayout *m_mainLayout;
DeviceSidebarWidget *m_sidebar;
QStackedWidget *m_stackedWidget;
diff --git a/src/devicesidebarwidget.cpp b/src/devicesidebarwidget.cpp
index 400bfb9..251dbf7 100644
--- a/src/devicesidebarwidget.cpp
+++ b/src/devicesidebarwidget.cpp
@@ -2,24 +2,17 @@
#include "iDescriptor-ui.h"
#include "loadingspinnerwidget.h"
#include
-#include
// DeviceSidebarItem Implementation
DeviceSidebarItem::DeviceSidebarItem(const QString &deviceName,
const std::string &uuid, QWidget *parent)
: QFrame(parent), m_deviceName(deviceName), m_uuid(uuid), m_selected(false),
- m_collapsed(true)
+ m_collapsed(false)
{
setupUI();
setFrameStyle(QFrame::StyledPanel);
setLineWidth(1);
updateToggleButton();
-
- // Initialize animation
- m_collapseAnimation =
- new QPropertyAnimation(m_optionsWidget, "maximumHeight", this);
- m_collapseAnimation->setDuration(200);
- m_collapseAnimation->setEasingCurve(QEasingCurve::InOutQuad);
}
void DeviceSidebarItem::setupUI()
@@ -122,9 +115,10 @@ void DeviceSidebarItem::setupUI()
m_mainLayout->addWidget(m_optionsWidget);
- // Initially hide options
- m_optionsWidget->setMaximumHeight(0);
- m_optionsWidget->hide();
+ // Initialize UI state to match m_collapsed value
+ // This ensures consistency regardless of initial m_collapsed value
+ updateToggleButton();
+ toggleCollapse();
setStyleSheet("DeviceSidebarItem { border: "
"1px solid #e0e0e0; border-radius: 5px; }");
@@ -153,7 +147,7 @@ void DeviceSidebarItem::setCollapsed(bool collapsed)
m_collapsed = collapsed;
updateToggleButton();
- animateCollapse();
+ toggleCollapse();
}
void DeviceSidebarItem::updateToggleButton()
@@ -165,40 +159,15 @@ void DeviceSidebarItem::updateToggleButton()
}
}
-void DeviceSidebarItem::animateCollapse()
+void DeviceSidebarItem::toggleCollapse()
{
- m_collapseAnimation->stop();
-
if (m_collapsed) {
- // Collapsing
- m_collapseAnimation->setStartValue(m_optionsWidget->height());
- m_collapseAnimation->setEndValue(0);
-
- connect(m_collapseAnimation, &QPropertyAnimation::finished, this,
- [this]() {
- m_optionsWidget->hide();
- disconnect(m_collapseAnimation,
- &QPropertyAnimation::finished, this, nullptr);
- });
+ m_optionsWidget->hide();
+ m_optionsWidget->setMaximumHeight(0);
} else {
- // Expanding
m_optionsWidget->show();
m_optionsWidget->setMaximumHeight(QWIDGETSIZE_MAX);
- int targetHeight = m_optionsWidget->sizeHint().height();
- m_optionsWidget->setMaximumHeight(0);
-
- m_collapseAnimation->setStartValue(0);
- m_collapseAnimation->setEndValue(targetHeight);
-
- connect(m_collapseAnimation, &QPropertyAnimation::finished, this,
- [this]() {
- m_optionsWidget->setMaximumHeight(QWIDGETSIZE_MAX);
- disconnect(m_collapseAnimation,
- &QPropertyAnimation::finished, this, nullptr);
- });
}
-
- m_collapseAnimation->start();
}
void DeviceSidebarItem::onToggleCollapse() { setCollapsed(!m_collapsed); }
diff --git a/src/devicesidebarwidget.h b/src/devicesidebarwidget.h
index 41d7781..b070b9a 100644
--- a/src/devicesidebarwidget.h
+++ b/src/devicesidebarwidget.h
@@ -6,8 +6,6 @@
#include
#include
#include
-#include
-#include
#include
#include
#include
@@ -39,7 +37,7 @@ private slots:
private:
void setupUI();
void updateToggleButton();
- void animateCollapse();
+ void toggleCollapse();
std::string m_uuid;
QString m_deviceName;
@@ -57,8 +55,6 @@ private:
QPushButton *m_galleryButton;
QPushButton *m_filesButton;
QButtonGroup *m_navigationGroup;
-
- QPropertyAnimation *m_collapseAnimation;
};
#ifndef DEVICEPENDINGSIDEBARITEM_H
diff --git a/src/diskusagewidget.cpp b/src/diskusagewidget.cpp
index ee5e5a3..9d925e5 100644
--- a/src/diskusagewidget.cpp
+++ b/src/diskusagewidget.cpp
@@ -36,11 +36,36 @@ void DiskUsageWidget::setupUI()
m_titleLabel->setAlignment(Qt::AlignCenter);
m_mainLayout->addWidget(m_titleLabel);
- // Status label (for loading/error states)
- m_statusLabel = new QLabel(this);
+ // Stacked widget for different states
+ m_stackedWidget = new QStackedWidget(this);
+ m_mainLayout->addWidget(m_stackedWidget);
+
+ // Loading/Error page
+ m_loadingErrorPage = new QWidget();
+ m_loadingErrorLayout = new QVBoxLayout(m_loadingErrorPage);
+ m_loadingErrorLayout->setContentsMargins(0, 0, 0, 0);
+ m_loadingErrorLayout->setSpacing(5);
+
+ m_processIndicator = new QProcessIndicator(m_loadingErrorPage);
+ m_processIndicator->setFixedSize(24, 24);
+ m_processIndicator->start();
+
+ m_statusLabel = new QLabel(m_loadingErrorPage);
m_statusLabel->setAlignment(Qt::AlignCenter);
m_statusLabel->setText("Loading disk usage...");
- m_mainLayout->addWidget(m_statusLabel);
+
+ m_loadingErrorLayout->addStretch();
+ m_loadingErrorLayout->addWidget(m_processIndicator, 0, Qt::AlignCenter);
+ m_loadingErrorLayout->addWidget(m_statusLabel);
+ m_loadingErrorLayout->addStretch();
+
+ m_stackedWidget->addWidget(m_loadingErrorPage);
+
+ // Data page
+ m_dataPage = new QWidget();
+ m_dataLayout = new QVBoxLayout(m_dataPage);
+ m_dataLayout->setContentsMargins(0, 0, 0, 0);
+ m_dataLayout->setSpacing(0);
// Disk usage bar container
m_diskBarContainer = new QWidget(this);
@@ -89,8 +114,7 @@ void DiskUsageWidget::setupUI()
m_diskBarLayout->addWidget(m_othersBar);
m_diskBarLayout->addWidget(m_freeBar);
- m_diskBarContainer->hide(); // Initially hidden
- m_mainLayout->addWidget(m_diskBarContainer);
+ m_dataLayout->addWidget(m_diskBarContainer);
// Legend layout
m_legendLayout = new QHBoxLayout();
@@ -98,11 +122,11 @@ void DiskUsageWidget::setupUI()
m_legendLayout->setContentsMargins(0, 0, 0, 0);
// Legend labels
- m_systemLabel = new QLabel("System", this);
- m_appsLabel = new QLabel("Apps", this);
- m_mediaLabel = new QLabel("Media", this);
- m_othersLabel = new QLabel("Others", this);
- m_freeLabel = new QLabel("Free", this);
+ m_systemLabel = new QLabel("System", m_dataPage);
+ m_appsLabel = new QLabel("Apps", m_dataPage);
+ m_mediaLabel = new QLabel("Media", m_dataPage);
+ m_othersLabel = new QLabel("Others", m_dataPage);
+ m_freeLabel = new QLabel("Free", m_dataPage);
// Style legend labels with colored backgrounds
QString labelStyle =
@@ -120,14 +144,12 @@ void DiskUsageWidget::setupUI()
m_legendLayout->addWidget(m_freeLabel);
m_legendLayout->addStretch();
- // Hide legend initially
- m_systemLabel->hide();
- m_appsLabel->hide();
- m_mediaLabel->hide();
- m_othersLabel->hide();
- m_freeLabel->hide();
+ m_dataLayout->addLayout(m_legendLayout);
- m_mainLayout->addLayout(m_legendLayout);
+ m_stackedWidget->addWidget(m_dataPage);
+
+ // Initially show loading page
+ m_stackedWidget->setCurrentWidget(m_loadingErrorPage);
}
QSize DiskUsageWidget::sizeHint() const { return QSize(400, 80); }
@@ -135,49 +157,28 @@ QSize DiskUsageWidget::sizeHint() const { return QSize(400, 80); }
void DiskUsageWidget::updateUI()
{
if (m_state == Loading) {
+ m_processIndicator->start();
m_statusLabel->setText("Loading disk usage...");
- m_statusLabel->show();
- m_diskBarContainer->hide();
- m_systemLabel->hide();
- m_appsLabel->hide();
- m_mediaLabel->hide();
- m_othersLabel->hide();
- m_freeLabel->hide();
+ m_stackedWidget->setCurrentWidget(m_loadingErrorPage);
return;
}
if (m_state == Error) {
+ m_processIndicator->stop();
m_statusLabel->setText("Error: " + m_errorMessage);
- m_statusLabel->show();
- m_diskBarContainer->hide();
- m_systemLabel->hide();
- m_appsLabel->hide();
- m_mediaLabel->hide();
- m_othersLabel->hide();
- m_freeLabel->hide();
+ m_stackedWidget->setCurrentWidget(m_loadingErrorPage);
return;
}
if (m_totalCapacity == 0) {
+ m_processIndicator->stop();
m_statusLabel->setText("No disk information available.");
- m_statusLabel->show();
- m_diskBarContainer->hide();
- m_systemLabel->hide();
- m_appsLabel->hide();
- m_mediaLabel->hide();
- m_othersLabel->hide();
- m_freeLabel->hide();
+ m_stackedWidget->setCurrentWidget(m_loadingErrorPage);
return;
}
- // Hide status label and show disk bar and legend
- m_statusLabel->hide();
- m_diskBarContainer->show();
- m_systemLabel->show();
- m_appsLabel->show();
- m_mediaLabel->show();
- m_othersLabel->show();
- m_freeLabel->show();
+ // Show data page
+ m_stackedWidget->setCurrentWidget(m_dataPage);
// Calculate proportions for each segment
int totalWidth = m_diskBarContainer->width();
@@ -271,7 +272,35 @@ void DiskUsageWidget::updateUI()
(double)m_othersUsage / m_totalCapacity);
m_freeBar->setUsageInfo("Free", formatSize(m_freeSpace), "#BDC3C7",
(double)m_freeSpace / m_totalCapacity);
+#else
+ m_systemBar->setToolTip(
+ QString("System: %1 (%2%)")
+ .arg(formatSize(m_systemUsage))
+ .arg(QString::number((double)m_systemUsage / m_totalCapacity * 100,
+ 'f', 1)));
+ m_appsBar->setToolTip(
+ QString("Apps: %1 (%2%)")
+ .arg(formatSize(m_appsUsage))
+ .arg(QString::number((double)m_appsUsage / m_totalCapacity * 100,
+ 'f', 1)));
+ m_mediaBar->setToolTip(
+ QString("Media: %1 (%2%)")
+ .arg(formatSize(m_mediaUsage))
+ .arg(QString::number((double)m_mediaUsage / m_totalCapacity * 100,
+ 'f', 1)));
+ m_othersBar->setToolTip(
+ QString("Others: %1 (%2%)")
+ .arg(formatSize(m_othersUsage))
+ .arg(QString::number((double)m_othersUsage / m_totalCapacity * 100,
+ 'f', 1)));
+ m_freeBar->setToolTip(
+ QString("Free: %1 (%2%)")
+ .arg(formatSize(m_freeSpace))
+ .arg(QString::number((double)m_freeSpace / m_totalCapacity * 100,
+ 'f', 1)));
+
#endif
+
// Hide segments with zero usage
// m_systemBar->setVisible(m_systemUsage > 0);
// m_appsBar->setVisible(m_appsUsage > 0);
diff --git a/src/diskusagewidget.h b/src/diskusagewidget.h
index 87fdfc9..8497b56 100644
--- a/src/diskusagewidget.h
+++ b/src/diskusagewidget.h
@@ -2,10 +2,12 @@
#define DISKUSAGEWIDGET_H
#include "diskusagebar.h"
#include "iDescriptor.h"
+#include "qprocessindicator.h"
#include
#include
#include
+#include
#include
#include
#include
@@ -35,7 +37,17 @@ private:
// UI widgets
QVBoxLayout *m_mainLayout;
QLabel *m_titleLabel;
+ QStackedWidget *m_stackedWidget;
+
+ // Loading/Error page
+ QWidget *m_loadingErrorPage;
+ QVBoxLayout *m_loadingErrorLayout;
+ QProcessIndicator *m_processIndicator;
QLabel *m_statusLabel;
+
+ // Data page
+ QWidget *m_dataPage;
+ QVBoxLayout *m_dataLayout;
QWidget *m_diskBarContainer;
QHBoxLayout *m_diskBarLayout;
#ifdef Q_OS_MAC
diff --git a/src/fileexplorerwidget.cpp b/src/fileexplorerwidget.cpp
index 9234909..5aefd31 100644
--- a/src/fileexplorerwidget.cpp
+++ b/src/fileexplorerwidget.cpp
@@ -1,8 +1,10 @@
#include "fileexplorerwidget.h"
#include "afcexplorerwidget.h"
+#include "iDescriptor-ui.h"
#include "iDescriptor.h"
#include "mediapreviewdialog.h"
#include "settingsmanager.h"
+#include
#include
#include
#include
@@ -12,9 +14,12 @@
#include
#include
#include
+#include
+#include
#include
#include
#include
+#include
#include
#include
#include
@@ -24,11 +29,12 @@ FileExplorerWidget::FileExplorerWidget(iDescriptorDevice *device,
QWidget *parent)
: QWidget(parent), device(device), usingAFC2(false)
{
+ m_mainSplitter = new ModernSplitter(Qt::Horizontal, this);
- m_mainSplitter = new QSplitter(Qt::Horizontal, this);
// Main layout
QHBoxLayout *mainLayout = new QHBoxLayout(this);
mainLayout->addWidget(m_mainSplitter);
+ mainLayout->setContentsMargins(0, 0, 0, 0);
setupSidebar();
@@ -47,7 +53,7 @@ void FileExplorerWidget::setupSidebar()
m_sidebarTree = new QTreeWidget();
m_sidebarTree->setHeaderLabel("Files");
m_sidebarTree->setMinimumWidth(50);
- m_sidebarTree->setMaximumWidth(200);
+ m_sidebarTree->setMaximumWidth(250);
// AFC Default section
m_afcDefaultItem = new QTreeWidgetItem(m_sidebarTree);
diff --git a/src/iDescriptor-ui.h b/src/iDescriptor-ui.h
index 44f61c5..98a4cf9 100644
--- a/src/iDescriptor-ui.h
+++ b/src/iDescriptor-ui.h
@@ -1,7 +1,12 @@
#pragma once
+#include
#include
#include
#include
+#include
+#include
+#include
+#include
#include
#ifdef Q_OS_MAC
@@ -73,4 +78,56 @@ enum class iDescriptorTool {
UnmountDevImage,
Unknown,
iFuse
-};
\ No newline at end of file
+};
+
+class ModernSplitterHandle : public QSplitterHandle
+{
+public:
+ ModernSplitterHandle(Qt::Orientation orientation, QSplitter *parent)
+ : QSplitterHandle(orientation, parent)
+ {
+ }
+
+protected:
+ void paintEvent(QPaintEvent *event) override
+ {
+ Q_UNUSED(event)
+
+ QPainter painter(this);
+ painter.setRenderHint(QPainter::Antialiasing);
+
+ QColor buttonColor = QApplication::palette().color(QPalette::Text);
+ buttonColor.setAlpha(60);
+
+ int margin = 10;
+ int availableWidth = width() - (2 * margin);
+ int centerX = margin + availableWidth / 2;
+ int centerY = height() / 2;
+
+ int buttonWidth = 6;
+ int buttonHeight = 50;
+
+ QRect buttonRect(centerX - buttonWidth / 2, centerY - buttonHeight / 2,
+ buttonWidth, buttonHeight);
+
+ painter.setBrush(QBrush(buttonColor));
+ painter.setPen(Qt::NoPen);
+ painter.drawRoundedRect(buttonRect, buttonWidth / 2, buttonWidth / 2);
+ }
+};
+
+class ModernSplitter : public QSplitter
+{
+public:
+ ModernSplitter(Qt::Orientation orientation, QWidget *parent = nullptr)
+ : QSplitter(orientation, parent)
+ {
+ setHandleWidth(10);
+ }
+
+protected:
+ QSplitterHandle *createHandle() override
+ {
+ return new ModernSplitterHandle(orientation(), this);
+ }
+};
diff --git a/src/iDescriptor.h b/src/iDescriptor.h
index 99ec71b..635a99c 100644
--- a/src/iDescriptor.h
+++ b/src/iDescriptor.h
@@ -4,6 +4,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -319,6 +320,7 @@ public:
free(value);
return result;
}
+ plist_t getNode() const { return current_node; }
};
afc_error_t safe_afc_read_directory(afc_client_t afcClient, idevice_t device,
@@ -359,15 +361,16 @@ bool shutdown(idevice_t device);
TakeScreenshotResult take_screenshot(screenshotr_client_t shotr);
-bool mount_dev_image(const char *udid, const char *image_dir_path);
+mobile_image_mounter_error_t mount_dev_image(const char *udid,
+ const char *image_dir_path);
struct GetMountedImageResult {
bool success;
- std::string output;
+ std::string sig;
std::string message;
};
-QPair _get_mounted_image(const char *udid);
+plist_t _get_mounted_image(const char *udid);
bool restart(std::string udid);
diff --git a/src/installedappswidget.cpp b/src/installedappswidget.cpp
index f74b399..8cc2df2 100644
--- a/src/installedappswidget.cpp
+++ b/src/installedappswidget.cpp
@@ -1,5 +1,6 @@
#include "installedappswidget.h"
#include "afcexplorerwidget.h"
+#include "iDescriptor-ui.h"
#include "iDescriptor.h"
#include "qprocessindicator.h"
#include
@@ -19,14 +20,13 @@
#include
#include
-// AppTabWidget Implementation
AppTabWidget::AppTabWidget(const QString &appName, const QString &bundleId,
const QString &version, QWidget *parent)
: QGroupBox(parent), m_appName(appName), m_bundleId(bundleId),
m_version(version), m_selected(false), m_hovered(false)
{
setFixedHeight(60);
- setMinimumWidth(250);
+ setMinimumWidth(100);
setCursor(Qt::PointingHandCursor);
setupUI();
@@ -67,22 +67,19 @@ void AppTabWidget::setSelected(bool selected)
void AppTabWidget::setupUI()
{
- // Create main layout
QHBoxLayout *mainLayout = new QHBoxLayout(this);
mainLayout->setContentsMargins(10, 8, 10, 8);
mainLayout->setSpacing(10);
-
+ // m_defaultBg = this->palette().color(QPalette::Window);
// Icon label
m_iconLabel = new QLabel();
m_iconLabel->setFixedSize(32, 32);
m_iconLabel->setScaledContents(true);
- // Load placeholder icon
QPixmap placeholderIcon = QApplication::style()
->standardIcon(QStyle::SP_ComputerIcon)
.pixmap(32, 32);
m_iconLabel->setPixmap(placeholderIcon);
-
mainLayout->addWidget(m_iconLabel);
// Text container
@@ -92,24 +89,21 @@ void AppTabWidget::setupUI()
// App name label
m_nameLabel = new QLabel();
- m_nameLabel->setFont(QFont(m_nameLabel->font().family(),
- m_nameLabel->font().pointSize(), QFont::Medium));
+ QFont nameFont = m_nameLabel->font();
+ nameFont.setWeight(QFont::Medium);
+ m_nameLabel->setFont(nameFont);
QString displayText = m_appName;
if (displayText.length() > 20) {
displayText = displayText.left(17) + "...";
}
m_nameLabel->setText(displayText);
-
textLayout->addWidget(m_nameLabel);
// Version label
if (!m_version.isEmpty()) {
m_versionLabel = new QLabel(m_version);
- QFont versionFont = m_versionLabel->font();
- versionFont.setPointSize(versionFont.pointSize() - 1);
- m_versionLabel->setFont(versionFont);
- // m_versionLabel->setStyleSheet("color: #666666;");
+ m_versionLabel->setStyleSheet("font-size: 11px;");
textLayout->addWidget(m_versionLabel);
} else {
m_versionLabel = nullptr;
@@ -118,8 +112,7 @@ void AppTabWidget::setupUI()
mainLayout->addLayout(textLayout);
mainLayout->addStretch();
- // Set initial styles
- // updateStyles();
+ updateStyles();
}
void AppTabWidget::mousePressEvent(QMouseEvent *event)
@@ -132,48 +125,31 @@ void AppTabWidget::enterEvent(QEnterEvent *event)
{
Q_UNUSED(event)
m_hovered = true;
- // updateStyles();
+ updateStyles();
}
void AppTabWidget::leaveEvent(QEvent *event)
{
Q_UNUSED(event)
m_hovered = false;
- // updateStyles();
+ updateStyles();
}
void AppTabWidget::updateStyles()
{
- QString backgroundColor;
- QString nameColor = "#000000";
- QString versionColor = "#666666";
QString borderStyle;
-
- if (m_selected) {
- backgroundColor = "#007AFF";
- nameColor = "#ffffff";
- versionColor = "#ffffff";
- borderStyle = "border: 2px solid #007AFF; border-radius: 6px;";
- } else if (m_hovered) {
- backgroundColor = "#f0f0f0";
- borderStyle = "border: 1px solid #e0e0e0; border-radius: 6px;";
- } else {
- backgroundColor = "transparent";
- borderStyle = "border: 1px solid transparent; border-radius: 6px;";
- }
-
- // Update widget background
- // setStyleSheet(QString("AppTabWidget { background-color: %1; %2 }")
- // .arg(backgroundColor, borderStyle));
-
- // Update name label color
- // m_nameLabel->setStyleSheet(QString("color: %1;").arg(nameColor));
-
- // Update version label color if it exists
- // if (m_versionLabel) {
- // m_versionLabel->setStyleSheet(QString("color:
- // %1;").arg(versionColor));
- // }
+ // TODO: for some reason setting a style overrides every other style instead
+ // of adding or overriding
+ // if (m_selected) {
+ // setStyleSheet("border: 2px solid #007AFF;");
+ // }
+ // borderStyle = "border: 2px solid #007AFF;";
+ // } else if (m_hovered) {
+ // borderStyle = "border: 1px solid" + highlightColor.name() + ";";
+ // } else {
+ // borderStyle = "";
+ // }
+ // setStyleSheet(borderStyle);
}
InstalledAppsWidget::InstalledAppsWidget(iDescriptorDevice *device,
@@ -198,158 +174,107 @@ void InstalledAppsWidget::setupUI()
m_mainLayout->setContentsMargins(0, 0, 0, 0);
m_mainLayout->setSpacing(0);
- // Create main splitter
- m_splitter = new QSplitter(Qt::Horizontal, this);
- m_splitter->setChildrenCollapsible(false);
- m_mainLayout->addWidget(m_splitter);
+ // Create stacked widget for different states
+ m_stackedWidget = new QStackedWidget(this);
+ m_mainLayout->addWidget(m_stackedWidget);
- // Left side - Custom tab area with scroll
- QWidget *tabWidget = new QWidget();
- tabWidget->setMinimumWidth(300);
- tabWidget->setMaximumWidth(500);
+ // Create loading widget
+ createLoadingWidget();
- QVBoxLayout *tabWidgetLayout = new QVBoxLayout(tabWidget);
- tabWidgetLayout->setContentsMargins(0, 0, 0, 0);
- tabWidgetLayout->setSpacing(0);
+ // Create error widget
+ createErrorWidget();
- // Search box at the top
- QWidget *searchContainer = new QWidget();
- searchContainer->setFixedHeight(50);
- QHBoxLayout *searchLayout = new QHBoxLayout(searchContainer);
- searchLayout->setContentsMargins(0, 0, 0, 0);
-
- m_searchEdit = new QLineEdit();
- m_searchEdit->setPlaceholderText("Search apps...");
- m_searchEdit->setStyleSheet("QLineEdit { "
- " border: 2px solid #e0e0e0; "
- " border-radius: 6px; "
- " padding: 4px 8px; "
- " font-size: 14px; "
- "} "
- "QLineEdit:focus { "
- " border: 2px solid #007AFF; "
- " outline: none; "
- "} ");
-
- // Add search icon
- // QAction *searchAction = m_searchEdit->addAction(
- // this->style()->standardIcon(QStyle::SP_FileDialogContentsView),
- // QLineEdit::LeadingPosition);
- m_searchEdit->setToolTip("Search");
-
- searchLayout->addWidget(m_searchEdit);
-
- // Add checkbox for file sharing filter
- m_fileSharingCheckBox = new QCheckBox("Show Only File Sharing Enabled");
- m_fileSharingCheckBox->setChecked(true); // Default enabled
- m_fileSharingCheckBox->setStyleSheet("QCheckBox { "
- " font-size: 10px; "
- " margin-left: 5px; "
- "}");
- searchLayout->addWidget(m_fileSharingCheckBox);
-
- tabWidgetLayout->addWidget(searchContainer);
-
- // Add a separator line
- // QFrame *separator = new QFrame();
- // separator->setFrameShape(QFrame::HLine);
- // separator->setFrameShadow(QFrame::Sunken);
- // separator->setStyleSheet("QFrame { color: #e0e0e0; }");
- // tabFrameLayout->addWidget(separator);
-
- m_tabScrollArea = new QScrollArea();
- m_tabScrollArea->setWidgetResizable(true);
- m_tabScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
- m_tabScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
- m_tabScrollArea->setStyleSheet("QScrollArea { border: none; }");
-
- m_tabContainer = new QWidget();
- // m_tabContainer->setStyleSheet("");
- m_tabLayout = new QVBoxLayout(m_tabContainer);
- m_tabLayout->setContentsMargins(0, 0, 10, 0);
- m_tabLayout->setSpacing(10);
- m_tabLayout->addStretch(); // Push tabs to top
-
- m_tabScrollArea->setWidget(m_tabContainer);
- tabWidgetLayout->addWidget(m_tabScrollArea);
-
- // Add tab widget to splitter
- m_splitter->addWidget(tabWidget);
-
- // Right side - Content area
- m_contentWidget = new QWidget();
- // m_contentWidget->setStyleSheet("border: 1px solid #ccc;");
-
- QVBoxLayout *contentLayout = new QVBoxLayout(m_contentWidget);
- contentLayout->setContentsMargins(0, 0, 0, 0);
- contentLayout->setSpacing(0);
-
- m_contentLabel = new QLabel("Select an app to view details");
- m_contentLabel->setAlignment(Qt::AlignCenter);
- // m_contentLabel->setStyleSheet("font-size: 16px; color: #666;");
- contentLayout->addWidget(m_contentLabel);
-
- // Container explorer area
- // QLabel *containerTitle = new QLabel("App Container:");
- // containerTitle->setStyleSheet(
- // "font-size: 14px; font-weight: bold; color: #333;");
- // containerTitle->setVisible(false);
- // contentLayout->addWidget(containerTitle);
-
- m_containerScrollArea = new QScrollArea();
- m_containerScrollArea->setWidgetResizable(true);
- m_containerScrollArea->setMinimumHeight(200);
- // m_containerScrollArea->setStyleSheet(
- // "QScrollArea { border: 1px solid #ddd; border-radius: 4px; }");
- m_containerScrollArea->setVisible(false);
-
- m_containerWidget = new QWidget();
- m_containerLayout = new QVBoxLayout(m_containerWidget);
- m_containerLayout->setContentsMargins(0, 0, 0, 0);
- m_containerLayout->setSpacing(0);
-
- m_containerScrollArea->setWidget(m_containerWidget);
- contentLayout->addWidget(m_containerScrollArea);
-
- // Add content widget to splitter
- m_splitter->addWidget(m_contentWidget);
-
- // Set initial splitter sizes (30% for tabs, 70% for content)
- m_splitter->setSizes({300, 700});
-
- // // Progress bar for loading
- // m_progressBar = new QProgressBar();
- // m_progressBar->setRange(0, 0); // Indeterminate progress
- // m_progressBar->setVisible(false);
- // contentLayout->addWidget(m_progressBar);
-
- // Connect search functionality
- connect(m_searchEdit, &QLineEdit::textChanged, this,
- &InstalledAppsWidget::filterApps);
-
- // Connect file sharing filter
- connect(m_fileSharingCheckBox, &QCheckBox::toggled, this,
- &InstalledAppsWidget::onFileSharingFilterChanged);
+ // Create content widget
+ createContentWidget();
+ // Start in loading state
showLoadingState();
}
void InstalledAppsWidget::showLoadingState()
{
- m_contentLabel->setText("Loading installed apps...");
- // m_progressBar->setVisible(true);
-
- // Clear existing tabs
- qDeleteAll(m_appTabs);
- m_appTabs.clear();
- m_selectedTab = nullptr;
+ m_stackedWidget->setCurrentWidget(m_loadingWidget);
}
void InstalledAppsWidget::showErrorState(const QString &error)
{
- m_contentLabel->setText(QString("Error loading apps: %1").arg(error));
- // m_progressBar->setVisible(false);
+ m_errorLabel->setText(QString("Error loading apps: %1").arg(error));
+ m_stackedWidget->setCurrentWidget(m_errorWidget);
}
+
+void InstalledAppsWidget::createLoadingWidget()
+{
+ m_loadingWidget = new QWidget();
+ QVBoxLayout *loadingLayout = new QVBoxLayout(m_loadingWidget);
+ loadingLayout->setAlignment(Qt::AlignCenter);
+
+ QProcessIndicator *spinner = new QProcessIndicator();
+ spinner->setType(QProcessIndicator::line_rotate);
+ spinner->setFixedSize(48, 48);
+ spinner->start();
+ loadingLayout->addWidget(spinner, 0, Qt::AlignCenter);
+
+ QLabel *loadingLabel = new QLabel("Loading installed apps...");
+ loadingLabel->setAlignment(Qt::AlignCenter);
+ loadingLabel->setStyleSheet(
+ "font-size: 14px; color: #666; margin-top: 10px;");
+ loadingLayout->addWidget(loadingLabel);
+
+ m_stackedWidget->addWidget(m_loadingWidget);
+}
+
+void InstalledAppsWidget::createErrorWidget()
+{
+ m_errorWidget = new QWidget();
+ QVBoxLayout *errorLayout = new QVBoxLayout(m_errorWidget);
+ errorLayout->setAlignment(Qt::AlignCenter);
+
+ m_errorLabel = new QLabel();
+ m_errorLabel->setAlignment(Qt::AlignCenter);
+ m_errorLabel->setStyleSheet(
+ "font-size: 14px; color: #d32f2f; margin: 20px;");
+ m_errorLabel->setWordWrap(true);
+ errorLayout->addWidget(m_errorLabel);
+
+ QPushButton *retryButton = new QPushButton("Retry");
+ retryButton->setFixedSize(100, 30);
+ connect(retryButton, &QPushButton::clicked, this,
+ &InstalledAppsWidget::fetchInstalledApps);
+ errorLayout->addWidget(retryButton, 0, Qt::AlignCenter);
+
+ m_stackedWidget->addWidget(m_errorWidget);
+}
+
+void InstalledAppsWidget::createContentWidget()
+{
+ m_contentWidget = new QWidget();
+ QHBoxLayout *contentLayout = new QHBoxLayout(m_contentWidget);
+ contentLayout->setContentsMargins(0, 0, 0, 0);
+ contentLayout->setSpacing(0);
+
+ // Create main splitter
+ m_splitter = new ModernSplitter(Qt::Horizontal, m_contentWidget);
+ m_splitter->setChildrenCollapsible(false);
+ contentLayout->addWidget(m_splitter);
+
+ // Left side - App list
+ createLeftPanel();
+
+ // Right side - Content area
+ createRightPanel();
+
+ // Set initial splitter sizes (400px for tabs, rest for content)
+ m_splitter->setSizes({400, 600});
+
+ // Connect signals
+ connect(m_searchEdit, &QLineEdit::textChanged, this,
+ &InstalledAppsWidget::filterApps);
+ connect(m_fileSharingCheckBox, &QCheckBox::toggled, this,
+ &InstalledAppsWidget::onFileSharingFilterChanged);
+
+ m_stackedWidget->addWidget(m_contentWidget);
+}
+
// todo: move to services
void InstalledAppsWidget::fetchInstalledApps()
{
@@ -362,6 +287,10 @@ void InstalledAppsWidget::fetchInstalledApps()
QVariantMap result;
QVariantList apps;
+ // result["success"] = true;
+ // result["apps"] = apps;
+ // return result;
+
instproxy_client_t instproxy = nullptr;
lockdownd_client_t lockdownClient = nullptr;
lockdownd_service_descriptor_t lockdowndService = nullptr;
@@ -528,7 +457,6 @@ void InstalledAppsWidget::fetchInstalledApps()
void InstalledAppsWidget::onAppsDataReady()
{
QVariantMap result = m_watcher->result();
- // m_progressBar->setVisible(false);
if (!result.value("success", false).toBool()) {
showErrorState(result.value("error", "Unknown error").toString());
@@ -537,10 +465,13 @@ void InstalledAppsWidget::onAppsDataReady()
QVariantList apps = result.value("apps").toList();
if (apps.isEmpty()) {
- m_contentLabel->setText("No apps found");
+ showErrorState("No apps found");
return;
}
+ // Switch to content view once data is loaded
+ m_stackedWidget->setCurrentWidget(m_contentWidget);
+
// Sort apps by display name
std::sort(apps.begin(), apps.end(),
[](const QVariant &a, const QVariant &b) {
@@ -586,8 +517,8 @@ void InstalledAppsWidget::onAppsDataReady()
createAppTab(tabName, bundleId, version);
}
- m_contentLabel->setText(
- QString("Found %1 installed apps").arg(apps.count()));
+ // m_contentLabel->setText(
+ // QString("Found %1 installed apps").arg(apps.count()));
// Select first tab if available
if (!m_appTabs.isEmpty()) {
@@ -604,7 +535,6 @@ void InstalledAppsWidget::createAppTab(const QString &appName,
connect(tabWidget, &AppTabWidget::clicked, this,
&InstalledAppsWidget::onAppTabClicked);
- // TODO: is this needed ?
// Remove the stretch before adding the new tab
m_tabLayout->removeItem(m_tabLayout->itemAt(m_tabLayout->count() - 1));
@@ -633,26 +563,7 @@ void InstalledAppsWidget::selectAppTab(AppTabWidget *tab)
m_selectedTab = tab;
tab->setSelected(true);
- // Update content
QString bundleId = tab->getBundleId();
- QString version = tab->getVersion();
- QString appName = tab->getAppName();
-
- // Remove the (System) suffix for display
- QString displayName = appName;
- displayName.remove(" (System)");
-
- QString content = QString("%1
"
- "Bundle ID: %2
"
- "Version: %3
"
- "
")
- .arg(displayName, bundleId,
- version.isEmpty() ? "Unknown" : version);
-
- m_contentLabel->setText(content);
- m_contentLabel->setTextFormat(Qt::RichText);
- m_contentLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);
- m_contentLabel->setWordWrap(true);
// Load app container data
loadAppContainer(bundleId);
@@ -695,21 +606,19 @@ void InstalledAppsWidget::loadAppContainer(const QString &bundleId)
delete item;
}
+ // Create a centered loading widget
+ QWidget *loadingWidget = new QWidget();
+ QVBoxLayout *loadingLayout = new QVBoxLayout(loadingWidget);
+ loadingLayout->setAlignment(Qt::AlignCenter);
+
QProcessIndicator *l = new QProcessIndicator();
+ l->setType(QProcessIndicator::line_rotate);
l->setFixedSize(32, 32);
l->start();
- m_containerLayout->addWidget(l);
- m_containerScrollArea->setVisible(true);
+ loadingLayout->addWidget(l, 0, Qt::AlignCenter);
- // // Find container title and make it visible
- // QVBoxLayout *contentLayout =
- // qobject_cast(m_contentWidget->layout());
- // if (contentLayout && contentLayout->count() > 1) {
- // QLayoutItem *titleItem = contentLayout->itemAt(1);
- // if (titleItem && titleItem->widget()) {
- // titleItem->widget()->setVisible(true);
- // }
- // }
+ m_containerLayout->addWidget(loadingWidget);
+ m_containerScrollArea->setVisible(true);
QFuture future = QtConcurrent::run([this, bundleId]()
-> QVariantMap {
@@ -865,9 +774,9 @@ void InstalledAppsWidget::onContainerDataReady()
}
if (!result.value("success", false).toBool()) {
+ qDebug() << "Error loading app container:"
+ << result.value("error").toString();
QLabel *errorLabel = new QLabel("No data available for this app");
- // errorLabel->setStyleSheet(
- // "color: #999; font-style: italic; text-align: center;");
errorLabel->setAlignment(Qt::AlignCenter);
m_containerLayout->addWidget(errorLabel);
return;
@@ -876,29 +785,19 @@ void InstalledAppsWidget::onContainerDataReady()
// Get the AFC clients from the result
afc_client_t afcClient = reinterpret_cast(
result.value("afcClient").value());
- house_arrest_client_t houseArrestClient =
- reinterpret_cast(
- result.value("houseArrestClient").value());
if (!afcClient) {
QLabel *errorLabel =
new QLabel("Failed to get AFC client for app container");
- // errorLabel->setStyleSheet("color: #999; font-style: italic;");
m_containerLayout->addWidget(errorLabel);
return;
}
// Create AfcExplorerWidget with the house arrest AFC client
- AfcExplorerWidget *explorer = new AfcExplorerWidget(
- afcClient,
- [houseArrestClient]() {
- // Cleanup callback when client becomes invalid
- if (houseArrestClient) {
- house_arrest_client_free(houseArrestClient);
- }
- },
- m_device, this);
-
+ // todo:afcClient never gets freed
+ AfcExplorerWidget *explorer =
+ new AfcExplorerWidget(afcClient, []() {}, m_device, this);
+ explorer->setStyleSheet("border :none;");
m_containerLayout->addWidget(explorer);
}
@@ -908,3 +807,86 @@ void InstalledAppsWidget::onFileSharingFilterChanged(bool enabled)
// Refresh the apps list when filter changes
fetchInstalledApps();
}
+
+void InstalledAppsWidget::createLeftPanel()
+{
+ QWidget *tabWidget = new QWidget();
+ tabWidget->setMinimumWidth(100);
+ tabWidget->setMaximumWidth(500);
+
+ QVBoxLayout *tabWidgetLayout = new QVBoxLayout(tabWidget);
+ tabWidgetLayout->setContentsMargins(0, 0, 0, 0);
+ tabWidgetLayout->setSpacing(0);
+
+ // Search container
+ QWidget *searchContainer = new QWidget();
+ searchContainer->setFixedHeight(60);
+ QHBoxLayout *searchLayout = new QHBoxLayout(searchContainer);
+ searchLayout->setContentsMargins(5, 0, 5, 5);
+
+ // Search box
+ m_searchEdit = new QLineEdit();
+ m_searchEdit->setPlaceholderText("Search apps...");
+ m_searchEdit->setStyleSheet("QLineEdit { "
+ " border: 2px solid #e0e0e0; "
+ " border-radius: 6px; "
+ " padding: 8px 12px; "
+ " font-size: 14px; "
+ "} "
+ "QLineEdit:focus { "
+ " border: 2px solid #007AFF; "
+ " outline: none; "
+ "}");
+ searchLayout->addWidget(m_searchEdit);
+
+ // File sharing filter checkbox
+ m_fileSharingCheckBox = new QCheckBox("Show Only File Sharing Enabled");
+ m_fileSharingCheckBox->setChecked(true);
+ m_fileSharingCheckBox->setStyleSheet("QCheckBox { font-size: 10px; }");
+ searchLayout->addWidget(m_fileSharingCheckBox);
+
+ tabWidgetLayout->addWidget(searchContainer);
+
+ // App list scroll area
+ m_tabScrollArea = new QScrollArea();
+ m_tabScrollArea->setWidgetResizable(true);
+ m_tabScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ m_tabScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ m_tabScrollArea->setStyleSheet("QScrollArea { border: none; }");
+
+ m_tabContainer = new QWidget();
+ m_tabLayout = new QVBoxLayout(m_tabContainer);
+ m_tabLayout->setContentsMargins(0, 0, 10, 0);
+ m_tabLayout->setSpacing(10);
+ m_tabLayout->addStretch();
+
+ m_tabScrollArea->setWidget(m_tabContainer);
+ tabWidgetLayout->addWidget(m_tabScrollArea);
+
+ m_splitter->addWidget(tabWidget);
+}
+
+void InstalledAppsWidget::createRightPanel()
+{
+ QWidget *rightContentWidget = new QWidget();
+
+ QVBoxLayout *contentLayout = new QVBoxLayout(rightContentWidget);
+ contentLayout->setContentsMargins(0, 0, 0, 5);
+ contentLayout->setSpacing(0);
+
+ // Container explorer area
+ m_containerScrollArea = new QScrollArea();
+ m_containerScrollArea->setWidgetResizable(true);
+ m_containerScrollArea->setMinimumHeight(200);
+ m_containerScrollArea->setVisible(false);
+
+ m_containerWidget = new QWidget();
+ m_containerLayout = new QVBoxLayout(m_containerWidget);
+ m_containerLayout->setContentsMargins(0, 0, 0, 0);
+ m_containerLayout->setSpacing(0);
+
+ m_containerScrollArea->setWidget(m_containerWidget);
+ contentLayout->addWidget(m_containerScrollArea);
+
+ m_splitter->addWidget(rightContentWidget);
+}
diff --git a/src/installedappswidget.h b/src/installedappswidget.h
index 15c64ac..27bd1e8 100644
--- a/src/installedappswidget.h
+++ b/src/installedappswidget.h
@@ -19,6 +19,7 @@
#include
#include
#include
+#include
#include
#include
@@ -78,6 +79,11 @@ private slots:
private:
void setupUI();
+ void createLoadingWidget();
+ void createErrorWidget();
+ void createContentWidget();
+ void createLeftPanel();
+ void createRightPanel();
void fetchInstalledApps();
void createAppTab(const QString &appName, const QString &bundleId,
const QString &version);
@@ -86,16 +92,20 @@ private:
void selectAppTab(AppTabWidget *tab);
void filterApps(const QString &searchText);
void loadAppContainer(const QString &bundleId);
+ void createHouseArrestAfcClient();
iDescriptorDevice *m_device;
QHBoxLayout *m_mainLayout;
+ QStackedWidget *m_stackedWidget;
+ QWidget *m_loadingWidget;
+ QWidget *m_errorWidget;
+ QWidget *m_contentWidget;
+ QLabel *m_errorLabel;
QLineEdit *m_searchEdit;
QCheckBox *m_fileSharingCheckBox;
QScrollArea *m_tabScrollArea;
QWidget *m_tabContainer;
QVBoxLayout *m_tabLayout;
- QWidget *m_contentWidget;
- QLabel *m_contentLabel;
QProgressBar *m_progressBar;
QScrollArea *m_containerScrollArea;
QWidget *m_containerWidget;
diff --git a/src/logindialog.cpp b/src/logindialog.cpp
index b9eb22a..bccba1c 100644
--- a/src/logindialog.cpp
+++ b/src/logindialog.cpp
@@ -13,9 +13,8 @@
LoginDialog::LoginDialog(QWidget *parent) : QDialog(parent)
{
- setWindowTitle("Login to App Store");
+ setWindowTitle("Login to App Store - iDescriptor");
setModal(true);
- // setFixedSize(400, 250);
setFixedWidth(400);
QVBoxLayout *layout = new QVBoxLayout(this);
@@ -24,7 +23,7 @@ LoginDialog::LoginDialog(QWidget *parent) : QDialog(parent)
// Email
QLabel *emailLabel = new QLabel("Email:");
- emailLabel->setStyleSheet("font-size: 14px; color: #555;");
+ emailLabel->setStyleSheet("font-size: 14px");
layout->addWidget(emailLabel);
m_emailEdit = new QLineEdit();
@@ -35,7 +34,7 @@ LoginDialog::LoginDialog(QWidget *parent) : QDialog(parent)
// Password
QLabel *passwordLabel = new QLabel("Password:");
- passwordLabel->setStyleSheet("font-size: 14px; color: #555;");
+ passwordLabel->setStyleSheet("font-size: 14px");
layout->addWidget(passwordLabel);
m_passwordEdit = new QLineEdit();
@@ -48,8 +47,8 @@ LoginDialog::LoginDialog(QWidget *parent) : QDialog(parent)
// Description
QLabel *descriptionLabel =
new QLabel("Don't worry, your credentials won't be "
- "stored, shared anywhere. This App is open-source.");
- descriptionLabel->setStyleSheet("font-size: 10px; font-weight: bold;");
+ "stored or shared anywhere. This App is open-source.");
+ descriptionLabel->setStyleSheet("font-size: 10px; font-weight: thin;");
descriptionLabel->setAlignment(Qt::AlignLeft);
descriptionLabel->setWordWrap(true); // Add this line
layout->addWidget(descriptionLabel);
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 535353a..48819f5 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -5,48 +5,28 @@
#include "ifusediskunmountbutton.h"
#include "ifusemanager.h"
#include "settingswidget.h"
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
#include "appswidget.h"
#include "devicemanagerwidget.h"
#include "iDescriptor-ui.h"
#include "iDescriptor.h"
+#include "jailbrokenwidget.h"
#include "libirecovery.h"
#include "toolboxwidget.h"
#include
-#include
-#include
-#include
-#include
#include
#include
#include
#include
+#include
#include "appcontext.h"
-#include "deviceinfowidget.h"
-#include "devicemenuwidget.h"
-#include "fileexplorerwidget.h"
-#include "jailbrokenwidget.h"
-#include "recoverydeviceinfowidget.h"
#include "settingsmanager.h"
#include
#include
#include
#include
#include
-#include
-#include
void handleCallback(const idevice_event_t *event, void *userData)
{
@@ -105,15 +85,15 @@ void handleCallbackRecovery(const irecv_device_event_t *event, void *userData)
case IRECV_DEVICE_ADD:
qDebug() << "Recovery device added: ";
// TODO: handle recovery device addition
- // QMetaObject::invokeMethod(ctx->mainWindow, "onRecoveryDeviceAdded",
- // Qt::QueuedConnection,
- // Q_ARG(QObject *, new
- // RecoveryDeviceInfo(event)));
+ QMetaObject::invokeMethod(
+ AppContext::sharedInstance(), "addRecoveryDevice",
+ Qt::QueuedConnection,
+ Q_ARG(RecoveryDeviceInfo *, new RecoveryDeviceInfo(event)));
break;
case IRECV_DEVICE_REMOVE:
qDebug() << "Recovery device removed: ";
QMetaObject::invokeMethod(
- AppContext::sharedInstance(), "onRecoveryDeviceRemoved",
+ AppContext::sharedInstance(), "removeRecoveryDevice",
Qt::QueuedConnection,
Q_ARG(QString, QString::number(event->device_info->ecid)));
break;
@@ -133,7 +113,9 @@ MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
- // setStyleSheet("background-color: white; color: black;");
+
+ setWindowIcon(QIcon(":/icons/icon.png"));
+
// Create custom tab widget
m_customTabWidget = new CustomTabWidget(this);
m_customTabWidget->setAttribute(Qt::WA_ContentsMarginsRespectsSafeArea,
@@ -250,10 +232,9 @@ void MainWindow::createMenus()
QMenu *actionsMenu = menuBar()->addMenu("&Actions");
QAction *aboutAct = new QAction("&About iDescriptor", this);
- connect(aboutAct, &QAction::triggered, this, [=]() {
- QMessageBox::about(this, "About iDescriptor",
- "iDescriptor
"
- "A modern device management tool.");
+ connect(aboutAct, &QAction::triggered, this, [this]() {
+ QMessageBox::about(this, "iDescriptor",
+ "A free and open-source idevice management tool.");
});
actionsMenu->addAction(aboutAct);
#endif
@@ -276,75 +257,6 @@ void MainWindow::updateNoDevicesConnected()
m_mainStackedWidget->setCurrentIndex(1); // Show device list page
}
-void MainWindow::onRecoveryDeviceAdded(QObject *recoveryDeviceInfoObj)
-{
- if (!recoveryDeviceInfoObj)
- // TODO: handle
- return;
- try {
- m_mainStackedWidget->setCurrentIndex(1);
- RecoveryDeviceInfo *device =
- qobject_cast(recoveryDeviceInfoObj);
- if (!device) {
- qDebug() << "Invalid recovery device info object";
- return;
- }
- // IDescriptorInitDeviceResultRecovery initResult=
- // init_idescriptor_recovery_device(deviceInfo);
-
- // IDescriptorInitDeviceResult initResult =
- // init_idescriptor_device(udid.toStdString().c_str());
-
- qDebug() << "Recovery device initialized: " << device->ecid;
-
- std::string added_ecid =
- AppContext::sharedInstance()->addRecoveryDevice(device);
-
- // Create device info widget
- RecoveryDeviceInfoWidget *recoveryDeviceInfoWidget =
- new RecoveryDeviceInfoWidget(device);
- QPixmap recoveryIcon(16, 16);
- recoveryIcon.fill(Qt::transparent);
- QPainter painter(&recoveryIcon);
- painter.setRenderHint(QPainter::Antialiasing);
- painter.setBrush(QColor(255, 59, 48)); // Red for recovery mode
- painter.drawRoundedRect(2, 2, 12, 12, 2, 2);
-
- // int mostRecentDevice =
- // customTabWidget->addTabWithIcon(recoveryDeviceInfoWidget,
- // recoveryIcon, "Recovery Mode");
-
- // m_device_menu_widgets[added_ecid] = recoveryDeviceInfoWidget;
- // Get device icon and product type for tab
- // QString tabTitle =
- // QString::fromStdString(device->product.toStdString());
- QString tabTitle = QString::fromStdString("recovery mode device");
-
- // Add tab with custom icon
- // int mostRecentDevice =
- // m_deviceManager->addDevice(recoveryDeviceInfoWidget, tabTitle);
- // m_deviceManager->setCurrentDevice(mostRecentDevice);
- } catch (const std::exception &e) {
- qDebug() << "Exception in onDeviceAdded: " << e.what();
- QMessageBox::critical(
- this, "Error",
- "An error occurred while processing device information");
- }
-}
-
-void MainWindow::onRecoveryDeviceRemoved(QObject *deviceInfoObj)
-{
- auto *info = qobject_cast(deviceInfoObj);
- if (!info)
- return;
-
- qDebug() << "Recovery device removed: " << info->ecid;
-
- // TODO: Implement proper device removal in DeviceManagerWidget
- // For now, we'll just log the removal
- qDebug() << "Recovery device cleanup not yet implemented";
-}
-
MainWindow::~MainWindow()
{
idevice_event_unsubscribe();
@@ -356,75 +268,5 @@ MainWindow::~MainWindow()
// }
// idescriptor_devices.clear();
delete ui;
- sleep(1); // Give some time for cleanup to finish
-}
-
-void MainWindow::onDeviceInitFailed(QString udid, lockdownd_error_t err)
-{
- QString errorTitle = "Device Connection Error";
- QString errorMessage;
-
- switch (err) {
- case LOCKDOWN_E_PASSWORD_PROTECTED:
- errorMessage =
- QString(
- "Could not validate device %1 because a passcode is set.\n\n"
- "Please enter the passcode on your device and try again.")
- .arg(udid);
- qDebug() << "ERROR: Could not validate with device" << udid
- << "because a passcode is set. Please enter the passcode on "
- "the device and retry.";
- break;
- case LOCKDOWN_E_INVALID_CONF:
- case LOCKDOWN_E_INVALID_HOST_ID:
- errorMessage = QString("Device %1 is not paired with this computer.\n\n"
- "Please check your device settings.")
- .arg(udid);
- qDebug() << "ERROR: Device" << udid << "is not paired with this host";
- break;
- case LOCKDOWN_E_PAIRING_DIALOG_RESPONSE_PENDING:
- errorMessage =
- QString(
- "Trust dialog is waiting for your response.\n\n"
- "Please accept the trust dialog on the screen of device %1,\n"
- "then attempt to pair again.")
- .arg(udid);
- qDebug()
- << "ERROR: Please accept the trust dialog on the screen of device"
- << udid << ", then attempt to pair again.";
- break;
- case LOCKDOWN_E_USER_DENIED_PAIRING:
- errorMessage = QString("Pairing rejected.\n\n"
- "You denied the trust dialog on device %1.")
- .arg(udid);
- qDebug() << "ERROR: Device" << udid
- << "said that the user denied the trust dialog.";
- break;
- case LOCKDOWN_E_PAIRING_FAILED:
- errorMessage = QString("Pairing with device %1 failed.\n\n"
- "Please try again or restart your device.")
- .arg(udid);
- qDebug() << "ERROR: Pairing with device" << udid << "failed.";
- break;
- case LOCKDOWN_E_GET_PROHIBITED:
- case LOCKDOWN_E_PAIRING_PROHIBITED_OVER_THIS_CONNECTION:
- errorMessage = "Pairing is not possible over this connection.\n\n"
- "Please try using a USB connection.";
- qDebug() << "ERROR: Pairing is not possible over this connection.";
- break;
- default:
- errorMessage = QString("Unknown error occurred with device %1.\n\n"
- "Error code: %2")
- .arg(udid)
- .arg(err);
- qDebug() << "ERROR: Device" << udid << "returned unhandled error code"
- << err;
- break;
- }
- QMessageBox errorDialog(this);
- errorDialog.setWindowTitle(errorTitle);
- errorDialog.setText(errorMessage);
- errorDialog.setIcon(QMessageBox::Warning);
- errorDialog.setStandardButtons(QMessageBox::Ok);
- errorDialog.exec();
+ sleep(2); // Give some time for cleanup to finish
}
\ No newline at end of file
diff --git a/src/mainwindow.h b/src/mainwindow.h
index d8da75e..7347203 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -2,11 +2,11 @@
#define MAINWINDOW_H
#include "customtabwidget.h"
#include "devicemanagerwidget.h"
-#include "devicemenuwidget.h"
#include "iDescriptor.h"
#include "libirecovery.h"
#include
#include
+#include
QT_BEGIN_NAMESPACE
namespace Ui
@@ -23,11 +23,7 @@ public:
static MainWindow *sharedInstance();
MainWindow(QWidget *parent = nullptr);
~MainWindow();
- void onRecoveryDeviceAdded(QObject *recoveryDeviceInfoObj);
- void onRecoveryDeviceRemoved(QObject *deviceInfoObj);
-
public slots:
- void onDeviceInitFailed(QString udid, lockdownd_error_t err);
void updateNoDevicesConnected();
private:
diff --git a/src/querymobilegestaltwidget.cpp b/src/querymobilegestaltwidget.cpp
index 1560dc2..469f287 100644
--- a/src/querymobilegestaltwidget.cpp
+++ b/src/querymobilegestaltwidget.cpp
@@ -15,26 +15,24 @@ QueryMobileGestaltWidget::QueryMobileGestaltWidget(iDescriptorDevice *device,
void QueryMobileGestaltWidget::setupUI()
{
- setWindowTitle("MobileGestalt Query Tool");
+ setWindowTitle("Query MobileGestalt - iDescriptor");
setMinimumSize(800, 600);
// Main layout
mainLayout = new QVBoxLayout(this);
// Title
- QLabel *titleLabel = new QLabel("MobileGestalt Query Interface");
- titleLabel->setStyleSheet(
- "font-size: 18px; font-weight: bold; margin: 10px;");
- titleLabel->setAlignment(Qt::AlignCenter);
- mainLayout->addWidget(titleLabel);
+ QLabel *desc = new QLabel("This tool lets you query MobileGestalt keys , "
+ "which provide various device information.");
+ desc->setStyleSheet("margin:5px;");
+ mainLayout->addWidget(desc);
// Selection group
selectionGroup = new QGroupBox("Select MobileGestalt Keys");
- selectionGroup->setStyleSheet(
- "QGroupBox { font-weight: bold; margin-top: 10px; }");
mainLayout->addWidget(selectionGroup);
QVBoxLayout *groupLayout = new QVBoxLayout(selectionGroup);
+ groupLayout->setContentsMargins(0, 0, 0, 0);
// Select/Clear buttons
buttonLayout = new QHBoxLayout();
@@ -45,6 +43,7 @@ void QueryMobileGestaltWidget::setupUI()
buttonLayout->addWidget(selectAllButton);
buttonLayout->addWidget(clearAllButton);
buttonLayout->addStretch();
+ buttonLayout->setContentsMargins(5, 5, 5, 5);
groupLayout->addLayout(buttonLayout);
// Scroll area for checkboxes
@@ -63,23 +62,8 @@ void QueryMobileGestaltWidget::setupUI()
// Query button
queryButton = new QPushButton("Query MobileGestalt");
- queryButton->setStyleSheet("QPushButton {"
- " background-color: #4CAF50;"
- " color: white;"
- " border: none;"
- " padding: 12px 24px;"
- " font-size: 16px;"
- " font-weight: bold;"
- " border-radius: 6px;"
- " margin: 10px;"
- "}"
- "QPushButton:hover {"
- " background-color: #45a049;"
- "}"
- "QPushButton:pressed {"
- " background-color: #3d8b40;"
- "}");
- queryButton->setMaximumWidth(200);
+ queryButton->setProperty("primary", true);
+ queryButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
mainLayout->addWidget(queryButton, 0, Qt::AlignCenter);
// Status label
@@ -87,23 +71,17 @@ void QueryMobileGestaltWidget::setupUI()
statusLabel->setStyleSheet("color: #666; font-style: italic; margin: 5px;");
mainLayout->addWidget(statusLabel);
- // Output text area
- QLabel *outputLabel = new QLabel("Query Results:");
- outputLabel->setStyleSheet("font-weight: bold; margin-top: 10px;");
- mainLayout->addWidget(outputLabel);
-
+ QGroupBox *outputGroup = new QGroupBox("Query Results");
outputTextEdit = new QTextEdit();
outputTextEdit->setReadOnly(true);
- outputTextEdit->setPlaceholderText("Query results will appear here...");
- outputTextEdit->setStyleSheet(
- "QTextEdit {"
- " border: 2px solid #ddd;"
- " border-radius: 5px;"
- " padding: 10px;"
- " font-family: 'Consolas', 'Monaco', monospace;"
- " background-color: #f9f9f9;"
- "}");
- mainLayout->addWidget(outputTextEdit);
+ outputTextEdit->setPlaceholderText("results will appear here...");
+ outputTextEdit->setStyleSheet("QTextEdit {"
+ "border : none;"
+ "}");
+ outputGroup->setLayout(new QVBoxLayout());
+ outputGroup->layout()->setContentsMargins(0, 0, 0, 0);
+ outputGroup->layout()->addWidget(outputTextEdit);
+ mainLayout->addWidget(outputGroup);
// Connect signals
connect(queryButton, &QPushButton::clicked, this,
@@ -1091,8 +1069,6 @@ void QueryMobileGestaltWidget::onQueryButtonClicked()
QString("Querying %1 key(s)...").arg(selectedKeys.size()));
statusLabel->setStyleSheet("color: #4CAF50; font-style: italic;");
- // Call your actual query function here
- // For demonstration, using a mock function
QMap results = queryMobileGestalt(selectedKeys);
displayResults(results);
@@ -1135,7 +1111,6 @@ void QueryMobileGestaltWidget::displayResults(
outputTextEdit->setPlainText(output);
}
-// Mock query function - replace this with your actual implementation
QMap
QueryMobileGestaltWidget::queryMobileGestalt(const QStringList &keys)
{
@@ -1146,8 +1121,6 @@ QueryMobileGestaltWidget::queryMobileGestalt(const QStringList &keys)
qDebug() << "MobileGestalt query failed.";
return {};
}
- // This is a mock implementation
- // Replace this with your actual query function that takes a plist dict
pugi::xml_document infoXml;
pugi::xml_parse_result result = infoXml.load_string(xml);
if (xml)
diff --git a/src/realtimescreen.cpp b/src/realtimescreen.cpp
index 0758b77..95cb416 100644
--- a/src/realtimescreen.cpp
+++ b/src/realtimescreen.cpp
@@ -23,58 +23,6 @@
#ifndef _WIN32
#include
#endif
-static void get_image_filename(char *imgdata, char **filename)
-{
- // If the provided filename already has an extension, use it as is.
- if (*filename) {
- char *last_dot = strrchr(*filename, '.');
- if (last_dot && !strchr(last_dot, '/')) {
- return;
- }
- }
-
- // Find the appropriate file extension for the filename.
- const char *fileext = NULL;
- if (memcmp(imgdata, "\x89PNG", 4) == 0) {
- fileext = ".png";
- } else if (memcmp(imgdata, "MM\x00*", 4) == 0) {
- fileext = ".tiff";
- } else {
- printf("WARNING: screenshot data has unexpected image format.\n");
- fileext = ".dat";
- }
-
- // If a filename without an extension is provided, append the extension.
- // Otherwise, generate a filename based on the current time.
- char *basename = NULL;
- if (*filename) {
- basename = (char *)malloc(strlen(*filename) + 1);
- strcpy(basename, *filename);
- free(*filename);
- *filename = NULL;
- } else {
- time_t now = time(NULL);
- basename = (char *)malloc(32);
- strftime(basename, 31, "screenshot-%Y-%m-%d-%H-%M-%S", gmtime(&now));
- }
-
- // Ensure the filename is unique on disk.
- char *unique_filename =
- (char *)malloc(strlen(basename) + strlen(fileext) + 7);
- sprintf(unique_filename, "%s%s", basename, fileext);
- int i;
- for (i = 2; i < (1 << 16); i++) {
- if (access(unique_filename, F_OK) == -1) {
- *filename = unique_filename;
- break;
- }
- sprintf(unique_filename, "%s-%d%s", basename, i, fileext);
- }
- if (!*filename) {
- free(unique_filename);
- }
- free(basename);
-}
RealtimeScreen::RealtimeScreen(QString udid, QWidget *parent)
: QWidget{parent}, timer(nullptr), capturing(false), shotrClient(nullptr)
@@ -186,46 +134,6 @@ RealtimeScreen::RealtimeScreen(QString udid, QWidget *parent)
} else {
connect(timer, &QTimer::timeout, this,
&RealtimeScreen::updateScreenshot);
- // char *imgdata = NULL;
- // uint64_t imgsize = 0;
- // if (screenshotr_take_screenshot(shotrClient, &imgdata,
- // &imgsize) == SCREENSHOTR_E_SUCCESS)
- // {
- // get_image_filename(imgdata, &filename);
- // if (!filename)
- // {
- // printf("FATAL: Could not find a unique filename!\n");
- // }
- // else
- // {
- // FILE *f = fopen(filename, "wb");
- // if (f)
- // {
- // if (fwrite(imgdata, 1, (size_t)imgsize, f) ==
- // (size_t)imgsize)
- // {
- // printf("Screenshot saved to %s\n", filename);
- // result = 0;
- // }
- // else
- // {
- // printf("Could not save screenshot to file
- // %s!\n", filename);
- // }
- // fclose(f);
- // }
- // else
- // {
- // printf("Could not open %s for writing: %s\n",
- // filename, strerror(errno));
- // }
- // }
- // }
- // else
- // {
- // printf("Could not get screenshot!\n");
- // }
- // screenshotr_client_free(shotrClient);
}
} else {
printf("Could not start screenshotr service: %s\nRemember that you "
diff --git a/src/toolboxwidget.cpp b/src/toolboxwidget.cpp
index c65e578..261b820 100644
--- a/src/toolboxwidget.cpp
+++ b/src/toolboxwidget.cpp
@@ -381,18 +381,10 @@ void ToolboxWidget::onToolboxClicked(iDescriptorTool tool)
virtualLocation->show();
} break;
case iDescriptorTool::Restart: {
- // TODO:WIP
- std::string udid = m_currentDevice->udid;
- AppContext::sharedInstance()->instanceRemoveDevice(
- QString::fromStdString(udid));
- // QMetaObject::invokeMethod(AppContext::sharedInstance(),
- // "removeDevice",
- // Qt::QueuedConnection,
- // Q_ARG(QString, QString(udid.c_str())));
- if (!(restart(udid)))
+ if (!(restart(m_currentDevice->udid)))
warn("Failed to restart device");
else {
- warn("Device services restarted successfully", "Success");
+ warn("Device will restart once unplugged", "Success");
qDebug() << "Restarting device";
}
} break;
diff --git a/src/virtual_location.cpp b/src/virtual_location.cpp
index 59d014a..0e6a6bd 100644
--- a/src/virtual_location.cpp
+++ b/src/virtual_location.cpp
@@ -6,9 +6,11 @@
#include
#include
#include
+#include
#include
#include
#include
+#include
#include
#include
#include
@@ -24,6 +26,7 @@
VirtualLocation::VirtualLocation(iDescriptorDevice *device, QWidget *parent)
: QWidget{parent}, m_device(device)
{
+ setWindowTitle("Virtual Location - iDescriptor");
// Create the main layout
QHBoxLayout *mainLayout = new QHBoxLayout(this);
mainLayout->setContentsMargins(10, 10, 10, 10);
@@ -32,8 +35,6 @@ VirtualLocation::VirtualLocation(iDescriptorDevice *device, QWidget *parent)
// Create left panel for controls
QWidget *rightPanel = new QWidget();
rightPanel->setFixedWidth(250);
- rightPanel->setStyleSheet(
- "QWidget { background-color: #f0f0f0; border-radius: 5px; }");
QVBoxLayout *rightLayout = new QVBoxLayout(rightPanel);
rightLayout->setContentsMargins(15, 15, 15, 15);
@@ -41,54 +42,33 @@ VirtualLocation::VirtualLocation(iDescriptorDevice *device, QWidget *parent)
// Title
QLabel *titleLabel = new QLabel("Virtual Location Settings");
- titleLabel->setStyleSheet(
- "font-size: 14px; font-weight: bold; color: #333;");
+ titleLabel->setStyleSheet("margin-bottom: 10px;");
rightLayout->addWidget(titleLabel);
- // Coordinates section
- QLabel *coordsLabel = new QLabel("Coordinates:");
- coordsLabel->setStyleSheet("font-weight: bold; color: #555;");
- rightLayout->addWidget(coordsLabel);
+ QGroupBox *coordGroup = new QGroupBox("Coordinates");
+ rightLayout->addWidget(coordGroup);
+
+ QVBoxLayout *coordLayout = new QVBoxLayout(coordGroup);
// Latitude input
QLabel *latLabel = new QLabel("Latitude:");
- latLabel->setStyleSheet("color: #666;");
- rightLayout->addWidget(latLabel);
+ coordLayout->addWidget(latLabel);
m_latitudeEdit = new QLineEdit();
m_latitudeEdit->setPlaceholderText("e.g., 59.9139");
m_latitudeEdit->setText("59.9139");
m_latitudeEdit->setValidator(new QDoubleValidator(-90.0, 90.0, 6, this));
- m_latitudeEdit->setStyleSheet(
- "padding: 5px; border: 1px solid #ccc; border-radius: 3px;");
- rightLayout->addWidget(m_latitudeEdit);
+ coordLayout->addWidget(m_latitudeEdit);
// Longitude input
QLabel *lonLabel = new QLabel("Longitude:");
- lonLabel->setStyleSheet("color: #666;");
- rightLayout->addWidget(lonLabel);
+ coordLayout->addWidget(lonLabel);
m_longitudeEdit = new QLineEdit();
m_longitudeEdit->setPlaceholderText("e.g., 10.7522");
m_longitudeEdit->setText("10.7522");
m_longitudeEdit->setValidator(new QDoubleValidator(-180.0, 180.0, 6, this));
- m_longitudeEdit->setStyleSheet(
- "padding: 5px; border: 1px solid #ccc; border-radius: 3px;");
- rightLayout->addWidget(m_longitudeEdit);
-
- // Altitude input
- QLabel *altLabel = new QLabel("Altitude (meters):");
- altLabel->setStyleSheet("color: #666;");
- rightLayout->addWidget(altLabel);
-
- m_altitudeEdit = new QLineEdit();
- m_altitudeEdit->setPlaceholderText("e.g., 100.0");
- m_altitudeEdit->setText("100.0");
- m_altitudeEdit->setValidator(
- new QDoubleValidator(-500.0, 10000.0, 2, this));
- m_altitudeEdit->setStyleSheet(
- "padding: 5px; border: 1px solid #ccc; border-radius: 3px;");
- rightLayout->addWidget(m_altitudeEdit);
+ coordLayout->addWidget(m_longitudeEdit);
// Add some spacing
rightLayout->addItem(
@@ -96,20 +76,7 @@ VirtualLocation::VirtualLocation(iDescriptorDevice *device, QWidget *parent)
// Apply button
m_applyButton = new QPushButton("Apply Settings");
- m_applyButton->setStyleSheet("QPushButton {"
- " background-color: #4CAF50;"
- " color: white;"
- " border: none;"
- " padding: 10px;"
- " border-radius: 5px;"
- " font-weight: bold;"
- "}"
- "QPushButton:hover {"
- " background-color: #45a049;"
- "}"
- "QPushButton:pressed {"
- " background-color: #3d8b40;"
- "}");
+ m_applyButton->setDefault(true);
rightLayout->addWidget(m_applyButton);
// Add stretch to push everything to the top
@@ -136,8 +103,6 @@ VirtualLocation::VirtualLocation(iDescriptorDevice *device, QWidget *parent)
&VirtualLocation::onInputChanged);
connect(m_longitudeEdit, &QLineEdit::textChanged, this,
&VirtualLocation::onInputChanged);
- connect(m_altitudeEdit, &QLineEdit::textChanged, this,
- &VirtualLocation::onInputChanged);
connect(m_applyButton, &QPushButton::clicked, this,
&VirtualLocation::onApplyClicked);
@@ -298,23 +263,34 @@ void VirtualLocation::updateInputsFromMap(double latitude, double longitude)
void VirtualLocation::onApplyClicked()
{
- bool devImgSuccess =
- DevDiskManager::sharedInstance()->mountCompatibleImage(m_device);
- if (!devImgSuccess) {
- warn("Failed to mount developer image on device. Cannot set location.");
- qDebug() << "Failed to mount developer image on device. Cannot set "
- "location.";
+ GetMountedImageResult result =
+ DevDiskManager::sharedInstance()->getMountedImage(
+ m_device->udid.c_str());
+
+ if (!result.success) {
+ QMessageBox::warning(this, "Failure", result.message.c_str());
return;
}
- bool latOk, lonOk, altOk;
+ if (result.success || result.sig.empty()) {
+ bool devImgSuccess =
+ DevDiskManager::sharedInstance()->mountCompatibleImage(m_device);
+ if (!devImgSuccess) {
+ QMessageBox::warning(this, "Failure",
+ "Failed to mount developer image on device. "
+ "Try with a different cable.");
+ qDebug() << "Failed to mount developer image on device. Cannot set "
+ "location.";
+ return;
+ }
+ }
+
+ bool latOk, lonOk;
double latitude = m_latitudeEdit->text().toDouble(&latOk);
double longitude = m_longitudeEdit->text().toDouble(&lonOk);
- double altitude = m_altitudeEdit->text().toDouble(&altOk);
- if (latOk && lonOk && altOk) {
- // Emit signal or perform action with the coordinates
- emit locationChanged(latitude, longitude, altitude);
+ if (latOk && lonOk) {
+ emit locationChanged(latitude, longitude);
// Update map one final time
updateMapFromInputs();
@@ -336,8 +312,11 @@ void VirtualLocation::onApplyClicked()
warn("Failed to set location on device");
qDebug() << "Failed to set location on device";
} else {
+ // todo: thread safe?
+ QMessageBox::information(this, "Success",
+ "Location applied successfully!");
qDebug() << "Applied location settings:" << latitude << ","
- << longitude << "," << altitude;
+ << longitude;
}
} else {
qDebug() << "Invalid coordinate values";
diff --git a/src/virtual_location.h b/src/virtual_location.h
index bfd4ffd..a980d62 100644
--- a/src/virtual_location.h
+++ b/src/virtual_location.h
@@ -17,7 +17,7 @@ public:
QWidget *parent = nullptr);
signals:
- void locationChanged(double latitude, double longitude, double altitude);
+ void locationChanged(double latitude, double longitude);
public slots:
void updateInputsFromMap(double latitude, double longitude);
@@ -33,7 +33,6 @@ private:
QQuickWidget *m_quickWidget;
QLineEdit *m_latitudeEdit;
QLineEdit *m_longitudeEdit;
- QLineEdit *m_altitudeEdit;
QPushButton *m_applyButton;
QTimer m_updateTimer;
bool m_updatingFromInput = false;