implement settings, fix bugs and enhance ui

- Added new settings keys in SettingsManager for download path, auto-check updates, auto-raise window, switch to new device, unmount iFuse on exit, theme, and connection timeout.
- Implemented methods to get and set these new settings.
- Updated SettingsWidget to include UI elements for the new settings, including checkboxes and a combo box for theme selection.
- Refactored loadSettings and saveSettings methods to handle new settings.
- Removed deprecated settings UI elements to streamline the interface.
- Introduced VirtualLocation widget for managing virtual location settings with a map interface.
- Integrated QML for map visualization and input handling for latitude and longitude.
- Added functionality to apply virtual location settings to the connected device.
This commit is contained in:
uncor3
2025-10-17 06:56:02 +00:00
parent 0c23824a4e
commit 8d7b027992
55 changed files with 3602 additions and 1919 deletions
+166 -104
View File
@@ -3,13 +3,16 @@
#include "appcontext.h"
#include "cableinfowidget.h"
#include "devdiskimageswidget.h"
#include "devdiskmanager.h"
#include "iDescriptor-ui.h"
#include "iDescriptor.h"
#ifdef __APPLE__
#include "ifusewidget.h"
#endif
#include "pcfileexplorerwidget.h"
#include "querymobilegestaltwidget.h"
#include "realtimescreen.h"
#include "virtual_location.h"
#include "realtimescreenwidget.h"
#include "virtuallocationwidget.h"
#include <QApplication>
#include <QDebug>
#include <QMessageBox>
@@ -29,41 +32,32 @@ bool enterRecoveryMode(iDescriptorDevice *device)
idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
if (LOCKDOWN_E_SUCCESS !=
(ldret = lockdownd_client_new(device->device, &client,
"EnterRecoveryMode"))) {
(ldret = lockdownd_client_new(device->device, &client, APP_LABEL))) {
printf("ERROR: Could not connect to lockdownd: %s (%d)\n",
lockdownd_strerror(ldret), ldret);
// idevice_free(device);
return 1;
return false;
}
int res = 0;
// printf("Telling device with udid %s to enter recovery mode.\n", udid);
ldret = lockdownd_enter_recovery(client);
if (ldret == LOCKDOWN_E_SESSION_INACTIVE) {
lockdownd_client_free(client);
client = NULL;
if (LOCKDOWN_E_SUCCESS !=
(ldret = lockdownd_client_new_with_handshake(
device->device, &client, "EnterRecoveryMode"))) {
if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(
device->device, &client, APP_LABEL))) {
printf("ERROR: Could not connect to lockdownd: %s (%d)\n",
lockdownd_strerror(ldret), ldret);
// idevice_free(device);
return 1;
return false;
}
ldret = lockdownd_enter_recovery(client);
}
lockdownd_client_free(client);
if (ldret != LOCKDOWN_E_SUCCESS) {
printf("Failed to enter recovery mode.\n");
res = 1;
return false;
} else {
printf("Device is successfully switching to recovery mode.\n");
return true;
}
lockdownd_client_free(client);
// idevice_free(device);
return 0;
}
ToolboxWidget::ToolboxWidget(QWidget *parent) : QWidget{parent}
@@ -103,50 +97,53 @@ void ToolboxWidget::setupUI()
m_scrollArea->viewport()->setStyleSheet("background: transparent;");
m_contentWidget = new QWidget();
m_gridLayout = new QGridLayout(m_contentWidget);
QVBoxLayout *contentLayout = new QVBoxLayout(m_contentWidget);
contentLayout->setSpacing(20);
contentLayout->setContentsMargins(0, 0, 0, 0);
// Main Tools Section
QLabel *mainToolsLabel = new QLabel("Tools");
mainToolsLabel->setStyleSheet(
"font-weight: bold; font-size: 14px; margin-left: 10px");
contentLayout->addWidget(mainToolsLabel);
QWidget *mainToolsWidget = new QWidget();
m_gridLayout = new QGridLayout(mainToolsWidget);
m_gridLayout->setSpacing(10);
QList<iDescriptorToolWidget> toolWidgets;
toolWidgets.append({iDescriptorTool::Airplayer,
"Start an airplayer service to cast your device screen "
"(does not require a device to be connected)",
false, ""});
toolWidgets.append({iDescriptorTool::VirtualLocation,
"Simulate GPS location on your device", true, ""});
toolWidgets.append(
QList<iDescriptorToolWidget> mainToolWidgets;
mainToolWidgets.append(
{iDescriptorTool::Airplayer,
"Start an airplayer service to cast your device screen "
"(does not require a device to be connected)",
false, ""});
mainToolWidgets.append({iDescriptorTool::VirtualLocation,
"Simulate GPS location on your device", true, ""});
mainToolWidgets.append(
{iDescriptorTool::RealtimeScreen,
"View device screen in real-time (wired connection required)", true,
""});
toolWidgets.append(
{iDescriptorTool::Restart, "Restart device services", true, ""});
toolWidgets.append(
{iDescriptorTool::Shutdown, "Shut down the device", true, ""});
toolWidgets.append({iDescriptorTool::RecoveryMode,
"Enter device recovery mode", true, ""});
toolWidgets.append({iDescriptorTool::QueryMobileGestalt,
"Query device hardware information", true, ""});
toolWidgets.append({iDescriptorTool::TouchIdTest,
"Test Touch ID functionality", true, ""});
toolWidgets.append(
{iDescriptorTool::FaceIdTest, "Test Face ID functionality", true, ""});
toolWidgets.append({iDescriptorTool::UnmountDevImage,
"Unmount a developer image", true, ""});
toolWidgets.append({iDescriptorTool::EnterRecoveryMode,
"Enter device recovery mode", true, ""});
toolWidgets.append({iDescriptorTool::DeveloperDiskImages,
"Manage developer disk images", false, ""});
toolWidgets.append({iDescriptorTool::WirelessFileImport,
"Import files wirelessly to your iDevice", false, ""});
toolWidgets.append({iDescriptorTool::MountIphone,
"Mount your iPhone's filesystem on your PC", true, ""});
toolWidgets.append({iDescriptorTool::CableInfoWidget,
"View detailed cable and connection info", true, ""});
toolWidgets.append({iDescriptorTool::NetworkDevices,
"Discover and monitor devices on your network", false,
""});
mainToolWidgets.append({iDescriptorTool::QueryMobileGestalt,
"Query device hardware information", true, ""});
mainToolWidgets.append({iDescriptorTool::DeveloperDiskImages,
"Manage developer disk images", false, ""});
mainToolWidgets.append({iDescriptorTool::WirelessFileImport,
"Import files wirelessly to your iDevice", false,
""});
#ifdef __APPLE__
mainToolWidgets.append({iDescriptorTool::iFuse,
"Mount your iPhone's filesystem on your PC", true,
""});
#endif
mainToolWidgets.append({iDescriptorTool::CableInfoWidget,
"View detailed cable and connection info", true,
""});
mainToolWidgets.append({iDescriptorTool::NetworkDevices,
"Discover and monitor devices on your network",
false, ""});
for (int i = 0; i < toolWidgets.size(); ++i) {
const auto &tool = toolWidgets[i];
for (int i = 0; i < mainToolWidgets.size(); ++i) {
const auto &tool = mainToolWidgets[i];
ClickableWidget *toolbox =
createToolbox(tool.tool, tool.description, tool.requiresDevice);
int row = i / 3;
@@ -154,6 +151,41 @@ void ToolboxWidget::setupUI()
m_gridLayout->addWidget(toolbox, row, col);
}
contentLayout->addWidget(mainToolsWidget);
// More Tools Section
QLabel *moreToolsLabel = new QLabel("More Tools");
moreToolsLabel->setStyleSheet(
"font-weight: bold; font-size: 14px; margin-left: 10px");
contentLayout->addWidget(moreToolsLabel);
QWidget *moreToolsWidget = new QWidget();
QGridLayout *moreGridLayout = new QGridLayout(moreToolsWidget);
moreGridLayout->setSpacing(10);
QList<iDescriptorToolWidget> moreToolWidgets;
moreToolWidgets.append(
{iDescriptorTool::MountDevImage,
"Mount a compatible device image with a single click", true, ""});
moreToolWidgets.append(
{iDescriptorTool::Restart, "Restart device services", true, ""});
moreToolWidgets.append(
{iDescriptorTool::Shutdown, "Shut down the device", true, ""});
moreToolWidgets.append({iDescriptorTool::RecoveryMode,
"Enter device recovery mode", true, ""});
for (int i = 0; i < moreToolWidgets.size(); ++i) {
const auto &tool = moreToolWidgets[i];
ClickableWidget *toolbox =
createToolbox(tool.tool, tool.description, tool.requiresDevice);
int row = i / 3;
int col = i % 3;
moreGridLayout->addWidget(toolbox, row, col);
}
contentLayout->addWidget(moreToolsWidget);
contentLayout->addStretch();
m_scrollArea->setWidget(m_contentWidget);
mainLayout->addWidget(m_scrollArea);
@@ -186,9 +218,6 @@ ClickableWidget *ToolboxWidget::createToolbox(iDescriptorTool tool,
case iDescriptorTool::RealtimeScreen:
title = "Realtime Screen";
break;
case iDescriptorTool::EnterRecoveryMode:
title = "Enter Recovery Mode";
break;
case iDescriptorTool::MountDevImage:
title = "Mount Dev Image";
break;
@@ -213,21 +242,12 @@ ClickableWidget *ToolboxWidget::createToolbox(iDescriptorTool tool,
case iDescriptorTool::WirelessFileImport:
title = "Wireless File Import";
break;
case iDescriptorTool::MountIphone:
case iDescriptorTool::iFuse:
title = "iFuse Mount";
break;
case iDescriptorTool::CableInfoWidget:
title = "Cable Info";
break;
case iDescriptorTool::TouchIdTest:
title = "Touch ID Test";
break;
case iDescriptorTool::FaceIdTest:
title = "Face ID Test";
break;
case iDescriptorTool::UnmountDevImage:
title = "Unmount Dev Image";
break;
case iDescriptorTool::NetworkDevices:
title = "Network Devices";
break;
@@ -316,19 +336,21 @@ void ToolboxWidget::updateUI()
void ToolboxWidget::onDeviceSelectionChanged()
{
// Handle device selection change
QString selectedDevice = m_deviceCombo->currentText();
qDebug() << "Selected device:" << selectedDevice;
QString selectedUdid = m_deviceCombo->currentData().toString();
qDebug() << "Selected device UDID:" << selectedUdid;
// Update m_uuid if a valid device is selected
// Update m_uuid and m_currentDevice if a valid device is selected
QList<iDescriptorDevice *> devices =
AppContext::sharedInstance()->getAllDevices();
for (iDescriptorDevice *device : devices) {
if (QString::fromStdString(device->udid) == selectedDevice) {
if (QString::fromStdString(device->udid) == selectedUdid) {
m_uuid = device->udid;
m_currentDevice = device;
return;
}
}
m_uuid.clear();
m_currentDevice = nullptr;
}
void ToolboxWidget::onToolboxClicked(iDescriptorTool tool)
@@ -344,11 +366,12 @@ void ToolboxWidget::onToolboxClicked(iDescriptorTool tool)
} break;
case iDescriptorTool::RealtimeScreen: {
RealtimeScreen *realtimeScreen =
new RealtimeScreen(QString::fromStdString(m_uuid));
RealtimeScreenWidget *realtimeScreen =
new RealtimeScreenWidget(m_currentDevice);
realtimeScreen->setAttribute(Qt::WA_DeleteOnClose);
realtimeScreen->show();
} break;
case iDescriptorTool::EnterRecoveryMode: {
case iDescriptorTool::RecoveryMode: {
// Handle entering recovery mode
bool success = enterRecoveryMode(m_currentDevice);
QMessageBox msgBox;
@@ -361,25 +384,38 @@ void ToolboxWidget::onToolboxClicked(iDescriptorTool tool)
msgBox.exec();
} break;
case iDescriptorTool::MountDevImage: {
// TODO: Handle mounting device image
// bool success =
// mount_dev_image(const_cast<char
// *>(m_currentDevice->udid.c_str()));
// QMessageBox msgBox;
// msgBox.setWindowTitle("Mount Dev Image");
// if (success) {
// msgBox.setText("Successfully mounted device image.");
// } else {
// msgBox.setText("Failed to mount device image.");
// }
GetMountedImageResult result =
DevDiskManager::sharedInstance()->getMountedImage(
m_currentDevice->udid.c_str());
if (!result.success) {
QMessageBox::warning(this, "Failure", result.message.c_str());
return;
}
if (result.success || result.sig.empty()) {
bool devImgSuccess =
DevDiskManager::sharedInstance()->mountCompatibleImage(
m_currentDevice);
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;
}
}
} break;
case iDescriptorTool::VirtualLocation: {
// Handle virtual location functionality
VirtualLocation *virtualLocation = new VirtualLocation(m_currentDevice);
virtualLocation->setAttribute(
Qt::WA_DeleteOnClose); // Optional: auto cleanup
virtualLocation->setWindowFlag(Qt::Window); // Make it a true window
virtualLocation->resize(800, 600); // Optional: default size
virtualLocation->setAttribute(Qt::WA_DeleteOnClose);
virtualLocation->setWindowFlag(Qt::Window);
virtualLocation->resize(800, 600);
virtualLocation->show();
} break;
case iDescriptorTool::Restart: {
@@ -388,9 +424,6 @@ void ToolboxWidget::onToolboxClicked(iDescriptorTool tool)
case iDescriptorTool::Shutdown: {
shutdownDevice(m_currentDevice);
} break;
case iDescriptorTool::RecoveryMode: {
_enterRecoveryMode(m_currentDevice);
} break;
case iDescriptorTool::QueryMobileGestalt: {
// Handle querying MobileGestalt
QueryMobileGestaltWidget *queryMobileGestaltWidget =
@@ -401,7 +434,6 @@ void ToolboxWidget::onToolboxClicked(iDescriptorTool tool)
queryMobileGestaltWidget->show();
} break;
case iDescriptorTool::DeveloperDiskImages: {
// single instance lock
if (!m_devDiskImagesWidget) {
m_devDiskImagesWidget = new DevDiskImagesWidget(m_currentDevice);
m_devDiskImagesWidget->setAttribute(Qt::WA_DeleteOnClose);
@@ -416,20 +448,21 @@ void ToolboxWidget::onToolboxClicked(iDescriptorTool tool)
}
} break;
case iDescriptorTool::WirelessFileImport: {
// Handle wireless file import
PCFileExplorerWidget *fileExplorer = new PCFileExplorerWidget();
fileExplorer->setAttribute(Qt::WA_DeleteOnClose);
fileExplorer->setWindowFlag(Qt::Window);
fileExplorer->resize(800, 600);
fileExplorer->show();
} break;
case iDescriptorTool::MountIphone: {
#ifdef __APPLE__
case iDescriptorTool::iFuse: {
iFuseWidget *ifuseWidget = new iFuseWidget(m_currentDevice);
ifuseWidget->setAttribute(Qt::WA_DeleteOnClose);
ifuseWidget->setWindowFlag(Qt::Window);
ifuseWidget->resize(600, 400);
ifuseWidget->show();
} break;
#endif
case iDescriptorTool::CableInfoWidget: {
CableInfoWidget *cableInfoWidget = new CableInfoWidget(m_currentDevice);
cableInfoWidget->setAttribute(Qt::WA_DeleteOnClose);
@@ -438,7 +471,6 @@ void ToolboxWidget::onToolboxClicked(iDescriptorTool tool)
cableInfoWidget->show();
} break;
case iDescriptorTool::NetworkDevices: {
// single instance lock
if (!m_networkDevicesWidget) {
m_networkDevicesWidget = new NetworkDevicesWidget();
m_networkDevicesWidget->setAttribute(Qt::WA_DeleteOnClose);
@@ -460,6 +492,14 @@ void ToolboxWidget::onToolboxClicked(iDescriptorTool tool)
void ToolboxWidget::restartDevice(iDescriptorDevice *device)
{
QMessageBox msgBox;
msgBox.setWindowTitle("Restart Device");
msgBox.setText("Are you sure you want to restart the device?");
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
msgBox.setDefaultButton(QMessageBox::No);
int ret = msgBox.exec();
if (ret != QMessageBox::Yes)
return;
if (!device || device->udid.empty()) {
return;
}
@@ -474,11 +514,23 @@ void ToolboxWidget::restartDevice(iDescriptorDevice *device)
void ToolboxWidget::shutdownDevice(iDescriptorDevice *device)
{
QMessageBox msgBox;
msgBox.setWindowTitle("Shutdown Device");
msgBox.setText("Are you sure you want to shutdown the device?");
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
msgBox.setDefaultButton(QMessageBox::No);
int ret = msgBox.exec();
if (ret != QMessageBox::Yes)
return;
if (!device || device->udid.empty()) {
return;
}
if (!(shutdown(device->device)))
// TODO: warn is a safe wrapper for QMessageBox but do we actually need
// it ?
warn("Failed to shutdown device");
else {
warn("Device will shutdown once unplugged", "Success");
@@ -488,17 +540,27 @@ void ToolboxWidget::shutdownDevice(iDescriptorDevice *device)
void ToolboxWidget::_enterRecoveryMode(iDescriptorDevice *device)
{
QMessageBox msgBox;
msgBox.setWindowTitle("Enter Recovery Mode");
msgBox.setText("Are you sure you want to enter recovery mode?");
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
msgBox.setDefaultButton(QMessageBox::No);
int ret = msgBox.exec();
if (ret != QMessageBox::Yes)
return;
if (!device || device->udid.empty()) {
return;
}
bool success = enterRecoveryMode(device);
QMessageBox msgBox;
msgBox.setWindowTitle("Recovery Mode");
QMessageBox _msgBox;
_msgBox.setWindowTitle("Recovery Mode");
if (success) {
msgBox.setText("Successfully entered recovery mode.");
_msgBox.setText("Successfully entered recovery mode.");
} else {
msgBox.setText("Failed to enter recovery mode.");
_msgBox.setText("Failed to enter recovery mode.");
}
msgBox.exec();
_msgBox.exec();
}