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;