mirror of
https://github.com/iDescriptor/iDescriptor.git
synced 2026-06-21 19:35:49 +08:00
feat: recognize recovery devices & add heic support
- Added RecoveryDeviceSidebarItem class for managing recovery devices in the sidebar. - Unified device selection structure with DeviceSelection to handle normal, recovery, and pending devices. - Updated DeviceSidebarWidget to support adding and removing recovery devices. - Refactored sidebar navigation and selection handling to accommodate new device types. refactor: Enhance Disk Usage Widget UI - Improved styling and layout of disk usage bars for better visual representation. - Removed unnecessary paint event override and adjusted layout settings. - Added object names for easier styling and debugging. fix: Update File Explorer Widget for AFC2 Support - Integrated a stacked widget to switch between normal and AFC2 explorers. - Simplified sidebar setup and item handling for better maintainability. - Connected sidebar item clicks to switch between AFC explorers. feat: Implement InfoLabel for Copying Text - Created InfoLabel class to display text that can be copied to the clipboard. - Added hover effects and temporary text change on copy action. chore: Clean up Unused Code and Comments - Removed commented-out code and unnecessary forward declarations across multiple files. - Streamlined includes and improved code readability. fix: Improve Recovery Device Info Widget - Updated RecoveryDeviceInfoWidget to display device information more clearly. - Added error handling for recovery mode exit operations with user feedback. feat: Add Responsive QLabel for Image Display - Introduced ResponsiveQLabel to handle responsive image scaling in the UI. - Replaced static image display with responsive label in JailbrokenWidget for better adaptability.
This commit is contained in:
+3
-1
@@ -81,7 +81,7 @@ find_library(TATSU_LIBRARY
|
||||
|
||||
# Add QR code generation library
|
||||
pkg_check_modules(QRENCODE REQUIRED IMPORTED_TARGET libqrencode)
|
||||
|
||||
pkg_check_modules(HEIF REQUIRED IMPORTED_TARGET libheif)
|
||||
find_library(IRECOVERY_LIBRARY
|
||||
NAMES irecovery-1.0
|
||||
PATHS ${CUSTOM_LIB_PATH}
|
||||
@@ -230,6 +230,7 @@ target_link_libraries(iDescriptor PRIVATE
|
||||
${SSL_LIBRARY}
|
||||
${CRYPTO_LIBRARY}
|
||||
${SSH_LIBRARY}
|
||||
${HEIF_LIBRARIES}
|
||||
# ${FRIDA_LIBRARY}
|
||||
# ${ZIP_LIBRARY}
|
||||
PkgConfig::PUGIXML
|
||||
@@ -237,6 +238,7 @@ target_link_libraries(iDescriptor PRIVATE
|
||||
PkgConfig::PLIST
|
||||
PkgConfig::QRENCODE
|
||||
PkgConfig::QTERMWIDGET
|
||||
PkgConfig::HEIF
|
||||
airplay
|
||||
ipatool-go
|
||||
)
|
||||
|
||||
@@ -43,9 +43,6 @@ AfcExplorerWidget::AfcExplorerWidget(afc_client_t afcClient,
|
||||
loadPath("/");
|
||||
|
||||
setupContextMenu();
|
||||
connect(SettingsManager::sharedInstance(),
|
||||
&SettingsManager::favoritePlacesChanged, this,
|
||||
&AfcExplorerWidget::refreshFavoritePlaces);
|
||||
}
|
||||
|
||||
void AfcExplorerWidget::goBack()
|
||||
@@ -175,7 +172,7 @@ void AfcExplorerWidget::loadPath(const QString &path)
|
||||
|
||||
updateBreadcrumb(path);
|
||||
|
||||
MediaFileTree tree =
|
||||
AFCFileTree tree =
|
||||
get_file_tree(m_currentAfcClient, m_device->device, path.toStdString());
|
||||
if (!tree.success) {
|
||||
m_fileList->addItem("Failed to load directory");
|
||||
@@ -317,7 +314,6 @@ int AfcExplorerWidget::export_file_to_path(afc_client_t afc,
|
||||
const char *local_path)
|
||||
{
|
||||
uint64_t handle = 0;
|
||||
// TODO: implement safe_afc_file_open
|
||||
if (afc_file_open(afc, device_path, AFC_FOPEN_RDONLY, &handle) !=
|
||||
AFC_E_SUCCESS) {
|
||||
qDebug() << "Failed to open file on device:" << device_path;
|
||||
@@ -332,7 +328,6 @@ int AfcExplorerWidget::export_file_to_path(afc_client_t afc,
|
||||
|
||||
char buffer[4096];
|
||||
uint32_t bytes_read = 0;
|
||||
// TODO: implement safe_afc_file_read
|
||||
while (afc_file_read(afc, handle, buffer, sizeof(buffer), &bytes_read) ==
|
||||
AFC_E_SUCCESS &&
|
||||
bytes_read > 0) {
|
||||
@@ -340,7 +335,6 @@ int AfcExplorerWidget::export_file_to_path(afc_client_t afc,
|
||||
}
|
||||
|
||||
fclose(out);
|
||||
// TODO: implement safe_afc_file_close
|
||||
afc_file_close(afc, handle);
|
||||
return 0;
|
||||
}
|
||||
@@ -422,9 +416,6 @@ int AfcExplorerWidget::import_file_to_device(afc_client_t afc,
|
||||
return 0;
|
||||
}
|
||||
|
||||
// useAFC2 ,path,
|
||||
typedef QPair<bool, QString> SidebarItemData;
|
||||
|
||||
void AfcExplorerWidget::setupFileExplorer()
|
||||
{
|
||||
m_explorer = new QWidget();
|
||||
@@ -471,22 +462,7 @@ void AfcExplorerWidget::setupFileExplorer()
|
||||
&AfcExplorerWidget::onAddToFavoritesClicked);
|
||||
}
|
||||
|
||||
void AfcExplorerWidget::onSidebarItemClicked(QTreeWidgetItem *item, int column)
|
||||
{
|
||||
Q_UNUSED(column)
|
||||
|
||||
bool useAfc2 = item->data(0, Qt::UserRole).value<SidebarItemData>().first;
|
||||
QString path = item->data(0, Qt::UserRole).value<SidebarItemData>().second;
|
||||
|
||||
// if (itemType == "try_install_afc2") {
|
||||
// onTryInstallAFC2Clicked();
|
||||
// return;
|
||||
// }
|
||||
|
||||
switchToAFC(useAfc2);
|
||||
loadPath(path);
|
||||
}
|
||||
|
||||
// todo: implement
|
||||
void AfcExplorerWidget::onAddToFavoritesClicked()
|
||||
{
|
||||
QString currentPath = "/";
|
||||
@@ -499,23 +475,6 @@ void AfcExplorerWidget::onAddToFavoritesClicked()
|
||||
"Enter alias for this location:", QLineEdit::Normal, currentPath, &ok);
|
||||
if (ok && !alias.isEmpty()) {
|
||||
saveFavoritePlace(currentPath, alias);
|
||||
refreshFavoritePlaces();
|
||||
}
|
||||
}
|
||||
|
||||
void AfcExplorerWidget::onTryInstallAFC2Clicked()
|
||||
{
|
||||
qDebug() << "Clicked on try to install AFC2";
|
||||
}
|
||||
|
||||
void AfcExplorerWidget::switchToAFC(bool useAFC2)
|
||||
{
|
||||
if (useAFC2 && m_device->afc2Client) {
|
||||
m_usingAFC2 = true;
|
||||
m_currentAfcClient = m_device->afc2Client;
|
||||
} else {
|
||||
m_usingAFC2 = false;
|
||||
m_currentAfcClient = m_device->afcClient;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -526,14 +485,3 @@ void AfcExplorerWidget::saveFavoritePlace(const QString &path,
|
||||
SettingsManager *settings = SettingsManager::sharedInstance();
|
||||
settings->saveFavoritePlace(path, alias);
|
||||
}
|
||||
|
||||
void AfcExplorerWidget::refreshFavoritePlaces()
|
||||
{
|
||||
// // Clear existing favorite items
|
||||
// while (m_favoritePlacesItem->childCount() > 0) {
|
||||
// delete m_favoritePlacesItem->takeChild(0);
|
||||
// }
|
||||
|
||||
// // Reload favorite places
|
||||
// loadFavoritePlaces();
|
||||
}
|
||||
|
||||
@@ -34,9 +34,7 @@ private slots:
|
||||
void onFileListContextMenu(const QPoint &pos);
|
||||
void onExportClicked();
|
||||
void onImportClicked();
|
||||
void onSidebarItemClicked(QTreeWidgetItem *item, int column);
|
||||
void onAddToFavoritesClicked();
|
||||
void onTryInstallAFC2Clicked();
|
||||
|
||||
private:
|
||||
QWidget *m_explorer;
|
||||
@@ -50,15 +48,12 @@ private:
|
||||
iDescriptorDevice *m_device;
|
||||
|
||||
// Current AFC mode
|
||||
bool m_usingAFC2;
|
||||
afc_client_t m_currentAfcClient;
|
||||
|
||||
void setupFileExplorer();
|
||||
void loadPath(const QString &path);
|
||||
void updateBreadcrumb(const QString &path);
|
||||
void saveFavoritePlace(const QString &path, const QString &alias);
|
||||
void refreshFavoritePlaces();
|
||||
void switchToAFC(bool useAFC2);
|
||||
|
||||
void setupContextMenu();
|
||||
void exportSelectedFile(QListWidgetItem *item);
|
||||
|
||||
+65
-36
@@ -4,6 +4,7 @@
|
||||
#include <QDBusMessage>
|
||||
#include <QDebug>
|
||||
#include <QMessageBox>
|
||||
#include <QTimer>
|
||||
#include <QUuid>
|
||||
|
||||
AppContext *AppContext::sharedInstance()
|
||||
@@ -75,26 +76,38 @@ void AppContext::addDevice(QString udid, idevice_connection_type conn_type,
|
||||
// return onDeviceInitFailed(udid, initResult.error);
|
||||
if (initResult.error == LOCKDOWN_E_PASSWORD_PROTECTED) {
|
||||
if (addType == AddType::Regular) {
|
||||
// FIXME: if a device never gets paired, it will stay in
|
||||
// this
|
||||
m_pendingDevices.append(udid);
|
||||
emit devicePasswordProtected(udid);
|
||||
QTimer::singleShot(30000, this, [this, udid]() {
|
||||
// After 30 seconds, if the device is still pending,
|
||||
// consider the pairing expired
|
||||
qDebug()
|
||||
<< "Pairing timer fired for device UDID: " << udid;
|
||||
if (m_pendingDevices.contains(udid)) {
|
||||
qDebug()
|
||||
<< "Pairing expired for device UDID: " << udid;
|
||||
m_pendingDevices.removeAll(udid);
|
||||
emit devicePairingExpired(udid);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if (initResult.error ==
|
||||
LOCKDOWN_E_PAIRING_DIALOG_RESPONSE_PENDING) {
|
||||
m_pendingDevices.append(udid);
|
||||
// FIXME: if a device never gets paired, it will stay in this
|
||||
// list forever
|
||||
emit devicePairPending(udid);
|
||||
|
||||
// warn("Device with UDID " + udid +
|
||||
// " is not trusted. Please trust this computer on the
|
||||
// " "device and try again.",
|
||||
// "Warning");
|
||||
QTimer::singleShot(30000, this, [this, udid]() {
|
||||
// After 30 seconds, if the device is still pending,
|
||||
// consider the pairing expired
|
||||
qDebug() << "Pairing timer fired for device UDID: " << udid;
|
||||
if (m_pendingDevices.contains(udid)) {
|
||||
qDebug() << "Pairing expired for device UDID: " << udid;
|
||||
m_pendingDevices.removeAll(udid);
|
||||
emit devicePairingExpired(udid);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
warn("Failed to initialize device with UDID " + udid +
|
||||
". Error code: " + QString::number(initResult.error),
|
||||
"Warning");
|
||||
qDebug() << "Unhandled error for device UDID: " << udid
|
||||
<< " Error code: " << initResult.error;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -116,8 +129,6 @@ void AppContext::addDevice(QString udid, idevice_connection_type conn_type,
|
||||
|
||||
} catch (const std::exception &e) {
|
||||
qDebug() << "Exception in onDeviceAdded: " << e.what();
|
||||
// QMessageBox::critical(this, "Error", "An error occurred while
|
||||
// processing device information");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,13 +141,23 @@ 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.";
|
||||
qDebug() << "AppContext::removeDevice device with UUID:"
|
||||
<< QString::fromStdString(uuid);
|
||||
|
||||
if (m_pendingDevices.contains(_udid)) {
|
||||
m_pendingDevices.removeAll(_udid);
|
||||
emit devicePairingExpired(_udid);
|
||||
return;
|
||||
} else {
|
||||
qDebug() << "Device with UUID " + _udid +
|
||||
" not found in pending devices.";
|
||||
}
|
||||
|
||||
qDebug() << "Removing device with UUID:" << QString::fromStdString(uuid);
|
||||
if (!m_devices.contains(uuid)) {
|
||||
qDebug() << "Device with UUID " + _udid +
|
||||
" not found in normal devices.";
|
||||
return;
|
||||
}
|
||||
|
||||
iDescriptorDevice *device = m_devices[uuid];
|
||||
m_devices.remove(uuid);
|
||||
@@ -148,22 +169,20 @@ void AppContext::removeDevice(QString _udid)
|
||||
delete device;
|
||||
}
|
||||
|
||||
void AppContext::removeRecoveryDevice(QString ecid)
|
||||
void AppContext::removeRecoveryDevice(uint64_t ecid)
|
||||
{
|
||||
std::string std_ecid = ecid.toStdString();
|
||||
if (!m_recoveryDevices.contains(std_ecid)) {
|
||||
qDebug() << "Device with ECID " + ecid +
|
||||
if (!m_recoveryDevices.contains(ecid)) {
|
||||
qDebug() << "Device with ECID " + QString::number(ecid) +
|
||||
" not found. Please report this issue.";
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << "Removing recovery device with ECID:"
|
||||
<< QString::fromStdString(std_ecid);
|
||||
|
||||
RecoveryDeviceInfo *deviceInfo = m_recoveryDevices[std_ecid];
|
||||
m_recoveryDevices.remove(std_ecid);
|
||||
qDebug() << "Removing recovery device with ECID:" << ecid;
|
||||
|
||||
m_recoveryDevices.remove(ecid);
|
||||
emit recoveryDeviceRemoved(ecid);
|
||||
iDescriptorRecoveryDevice *deviceInfo = m_recoveryDevices[ecid];
|
||||
|
||||
delete deviceInfo;
|
||||
}
|
||||
|
||||
@@ -177,7 +196,7 @@ QList<iDescriptorDevice *> AppContext::getAllDevices()
|
||||
return m_devices.values();
|
||||
}
|
||||
|
||||
QList<RecoveryDeviceInfo *> AppContext::getAllRecoveryDevices()
|
||||
QList<iDescriptorRecoveryDevice *> AppContext::getAllRecoveryDevices()
|
||||
{
|
||||
return m_recoveryDevices.values();
|
||||
}
|
||||
@@ -189,17 +208,27 @@ bool AppContext::noDevicesConnected() const
|
||||
m_pendingDevices.isEmpty());
|
||||
}
|
||||
|
||||
void AppContext::addRecoveryDevice(RecoveryDeviceInfo *deviceInfo)
|
||||
void AppContext::addRecoveryDevice(uint64_t ecid)
|
||||
{
|
||||
// Generate a unique ID for the recovery device
|
||||
// std::string uuid =
|
||||
// QUuid::createUuid().toString(QUuid::WithoutBraces).toStdString();
|
||||
IDescriptorInitDeviceResultRecovery res =
|
||||
init_idescriptor_recovery_device(ecid);
|
||||
|
||||
// Add the device to the map
|
||||
// uint64_t to std::string
|
||||
m_recoveryDevices[std::to_string(deviceInfo->ecid)] = deviceInfo;
|
||||
if (!res.success) {
|
||||
qDebug() << "Failed to initialize recovery device with ECID: "
|
||||
<< QString::number(ecid);
|
||||
qDebug() << "Error code: " << res.error;
|
||||
return;
|
||||
}
|
||||
|
||||
emit recoveryDeviceAdded(deviceInfo);
|
||||
iDescriptorRecoveryDevice *recoveryDevice = new iDescriptorRecoveryDevice();
|
||||
recoveryDevice->ecid = res.deviceInfo.ecid;
|
||||
recoveryDevice->mode = res.mode;
|
||||
recoveryDevice->cpid = res.deviceInfo.cpid;
|
||||
recoveryDevice->bdid = res.deviceInfo.bdid;
|
||||
recoveryDevice->displayName = res.displayName;
|
||||
|
||||
m_recoveryDevices[res.deviceInfo.ecid] = recoveryDevice;
|
||||
emit recoveryDeviceAdded(recoveryDevice);
|
||||
}
|
||||
|
||||
AppContext::~AppContext()
|
||||
|
||||
+7
-6
@@ -18,30 +18,31 @@ public:
|
||||
bool noDevicesConnected() const;
|
||||
|
||||
// Returns whether there are any devices connected (regular or recovery)
|
||||
QList<RecoveryDeviceInfo *> getAllRecoveryDevices();
|
||||
QList<iDescriptorRecoveryDevice *> getAllRecoveryDevices();
|
||||
~AppContext();
|
||||
int getConnectedDeviceCount() const;
|
||||
|
||||
private:
|
||||
QMap<std::string, iDescriptorDevice *> m_devices;
|
||||
QMap<std::string, RecoveryDeviceInfo *> m_recoveryDevices;
|
||||
QMap<uint64_t, iDescriptorRecoveryDevice *> m_recoveryDevices;
|
||||
QStringList m_pendingDevices;
|
||||
signals:
|
||||
void deviceAdded(iDescriptorDevice *device);
|
||||
void deviceRemoved(const std::string &udid);
|
||||
void devicePaired(iDescriptorDevice *device);
|
||||
void devicePasswordProtected(const QString &udid);
|
||||
void recoveryDeviceAdded(RecoveryDeviceInfo *deviceInfo);
|
||||
void recoveryDeviceRemoved(const QString &udid);
|
||||
void recoveryDeviceAdded(const iDescriptorRecoveryDevice *deviceInfo);
|
||||
void recoveryDeviceRemoved(uint64_t ecid);
|
||||
void devicePairPending(const QString &udid);
|
||||
void devicePairingExpired(const QString &udid);
|
||||
void systemSleepStarting();
|
||||
void systemWakeup();
|
||||
public slots:
|
||||
void removeDevice(QString udid);
|
||||
void addDevice(QString udid, idevice_connection_type connType,
|
||||
AddType addType);
|
||||
void addRecoveryDevice(RecoveryDeviceInfo *deviceInfo);
|
||||
void removeRecoveryDevice(QString ecid);
|
||||
void addRecoveryDevice(uint64_t ecid);
|
||||
void removeRecoveryDevice(uint64_t ecid);
|
||||
};
|
||||
|
||||
#endif // APPCONTEXT_H
|
||||
|
||||
@@ -5,22 +5,22 @@
|
||||
#include <libimobiledevice/lockdown.h>
|
||||
#include <string.h>
|
||||
|
||||
MediaFileTree get_file_tree(afc_client_t afcClient, idevice_t device,
|
||||
const std::string &path)
|
||||
AFCFileTree get_file_tree(afc_client_t afcClient, idevice_t device,
|
||||
const std::string &path)
|
||||
{
|
||||
|
||||
MediaFileTree result;
|
||||
AFCFileTree result;
|
||||
result.currentPath = path;
|
||||
|
||||
if (afcClient == nullptr) {
|
||||
qDebug() << "AFC client is not initialized in get_file_tree";
|
||||
result.success = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
char **dirs = NULL;
|
||||
if (safe_afc_read_directory(afcClient, device, path.c_str(), &dirs) !=
|
||||
AFC_E_SUCCESS) {
|
||||
// afc_client_free(afcClient);
|
||||
// lockdownd_service_descriptor_free(lockdownService);
|
||||
result.success = false;
|
||||
return result;
|
||||
}
|
||||
@@ -30,21 +30,35 @@ MediaFileTree get_file_tree(afc_client_t afcClient, idevice_t device,
|
||||
if (entryName == "." || entryName == "..")
|
||||
continue;
|
||||
|
||||
// Determine if entry is a directory
|
||||
char **info = NULL;
|
||||
std::string fullPath = path;
|
||||
if (fullPath.back() != '/')
|
||||
fullPath += "/";
|
||||
fullPath += entryName;
|
||||
|
||||
bool isDir = false;
|
||||
if (afc_get_file_info(afcClient, fullPath.c_str(), &info) ==
|
||||
AFC_E_SUCCESS &&
|
||||
info) {
|
||||
if (entryName == "var") {
|
||||
qDebug() << "File info for var:" << info[0] << info[1]
|
||||
<< info[2] << info[3] << info[4] << info[5];
|
||||
}
|
||||
for (int j = 0; info[j]; j += 2) {
|
||||
if (strcmp(info[j], "st_ifmt") == 0) {
|
||||
if (strcmp(info[j + 1], "S_IFDIR") == 0)
|
||||
if (strcmp(info[j + 1], "S_IFDIR") == 0) {
|
||||
isDir = true;
|
||||
} else if (strcmp(info[j + 1], "S_IFLNK") == 0) {
|
||||
/*symlink*/
|
||||
char **dir_contents = NULL;
|
||||
if (afc_read_directory(afcClient, fullPath.c_str(),
|
||||
&dir_contents) ==
|
||||
AFC_E_SUCCESS) {
|
||||
isDir = true;
|
||||
if (dir_contents) {
|
||||
afc_dictionary_free(dir_contents);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -52,10 +66,9 @@ MediaFileTree get_file_tree(afc_client_t afcClient, idevice_t device,
|
||||
}
|
||||
result.entries.push_back({entryName, isDir});
|
||||
}
|
||||
free(dirs);
|
||||
// TODO : Freed when device is disconnected
|
||||
// afc_client_free(afc);
|
||||
// lockdownd_service_descriptor_free(service);
|
||||
if (dirs) {
|
||||
afc_dictionary_free(dirs);
|
||||
}
|
||||
result.success = true;
|
||||
return result;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
#include "../../iDescriptor.h"
|
||||
#include <string>
|
||||
|
||||
std::string parse_product_type(const std::string &productType)
|
||||
{
|
||||
// Check if the product type is in the DEVICE_MAP
|
||||
auto it = DEVICE_MAP.find(productType);
|
||||
if (it != DEVICE_MAP.end()) {
|
||||
return it->second; // Return the marketing name if found
|
||||
}
|
||||
return "Unknown Device"; // Return a default value if not found
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#include "../../iDescriptor.h"
|
||||
#include <QByteArray>
|
||||
#include <QDebug>
|
||||
|
||||
QByteArray read_afc_file_to_byte_array(afc_client_t afcClient, const char *path)
|
||||
{
|
||||
uint64_t fd_handle = 0;
|
||||
afc_error_t fd_err =
|
||||
afc_file_open(afcClient, path, AFC_FOPEN_RDONLY, &fd_handle);
|
||||
|
||||
if (fd_err != AFC_E_SUCCESS) {
|
||||
qDebug() << "Could not open file" << path;
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
// TODO: is this necessary
|
||||
char **info = NULL;
|
||||
afc_get_file_info(afcClient, path, &info);
|
||||
uint64_t fileSize = 0;
|
||||
if (info) {
|
||||
for (int i = 0; info[i]; i += 2) {
|
||||
if (strcmp(info[i], "st_size") == 0) {
|
||||
fileSize = std::stoull(info[i + 1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
afc_dictionary_free(info);
|
||||
}
|
||||
|
||||
if (fileSize == 0) {
|
||||
afc_file_close(afcClient, fd_handle);
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
QByteArray buffer;
|
||||
|
||||
buffer.resize(fileSize);
|
||||
uint32_t bytesRead = 0;
|
||||
afc_file_read(afcClient, fd_handle, buffer.data(), buffer.size(),
|
||||
&bytesRead);
|
||||
|
||||
if (bytesRead != fileSize) {
|
||||
qDebug() << "AFC Error: Read mismatch for file" << path;
|
||||
return QByteArray(); // Read failed
|
||||
}
|
||||
|
||||
return buffer;
|
||||
};
|
||||
@@ -1,9 +1,11 @@
|
||||
#include "../../devicedatabase.h"
|
||||
#include "../../iDescriptor.h"
|
||||
#include "libirecovery.h"
|
||||
#include <QDebug>
|
||||
#include <libimobiledevice/diagnostics_relay.h>
|
||||
#include <libimobiledevice/libimobiledevice.h>
|
||||
#include <libimobiledevice/lockdown.h>
|
||||
#include <string.h>
|
||||
|
||||
std::string safeGetXML(const char *key, pugi::xml_node dict)
|
||||
{
|
||||
@@ -201,7 +203,11 @@ DeviceInfo fullDeviceInfo(const pugi::xml_document &doc,
|
||||
}
|
||||
// TODO:RegionInfo: LL/A
|
||||
std::string rawProductType = safeGet("ProductType");
|
||||
d.productType = parse_product_type(rawProductType);
|
||||
const DeviceDatabaseInfo *info =
|
||||
DeviceDatabase::findByIdentifier(rawProductType);
|
||||
d.productType =
|
||||
info ? info->displayName ? info->marketingName : "Unknown Device"
|
||||
: "Unknown Device";
|
||||
d.rawProductType = rawProductType;
|
||||
d.jailbroken = detect_jailbroken(afcClient);
|
||||
d.is_iPhone = safeGet("DeviceClass") == "iPhone";
|
||||
@@ -382,22 +388,52 @@ IDescriptorInitDeviceResult init_idescriptor_device(const char *udid)
|
||||
}
|
||||
|
||||
IDescriptorInitDeviceResultRecovery
|
||||
init_idescriptor_recovery_device(irecv_device_info *info)
|
||||
init_idescriptor_recovery_device(uint64_t ecid)
|
||||
{
|
||||
IDescriptorInitDeviceResultRecovery result;
|
||||
result.deviceInfo = *info;
|
||||
uint64_t ecid = info->ecid;
|
||||
// irecv_client_t client = nullptr;
|
||||
// Docs say that clients are not long-lived, so instead of storing, we
|
||||
// create a new one each time we need it. irecv_error_t ret =
|
||||
// irecv_open_with_ecid_and_attempts(&client, ecid,
|
||||
// RECOVERY_CLIENT_CONNECTION_TRIES);
|
||||
irecv_client_t client = nullptr;
|
||||
irecv_error_t ret = IRECV_E_UNKNOWN_ERROR;
|
||||
ret = irecv_open_with_ecid_and_attempts(&client, ecid,
|
||||
RECOVERY_CLIENT_CONNECTION_TRIES);
|
||||
|
||||
// if (ret != IRECV_E_SUCCESS)
|
||||
// {
|
||||
// return result;
|
||||
// }
|
||||
if (ret != IRECV_E_SUCCESS) {
|
||||
result.error = ret;
|
||||
return result;
|
||||
}
|
||||
ret = irecv_get_mode(client, (int *)&result.mode);
|
||||
|
||||
if (ret != IRECV_E_SUCCESS) {
|
||||
result.error = ret;
|
||||
irecv_close(client);
|
||||
return result;
|
||||
}
|
||||
|
||||
const irecv_device_info *deviceInfo = irecv_get_device_info(client);
|
||||
if (!deviceInfo) {
|
||||
result.error = IRECV_E_UNKNOWN_ERROR;
|
||||
irecv_close(client);
|
||||
return result;
|
||||
}
|
||||
|
||||
irecv_device_t device = nullptr;
|
||||
const DeviceDatabaseInfo *info = nullptr;
|
||||
if (irecv_devices_get_device_by_client(client, &device) ==
|
||||
IRECV_E_SUCCESS &&
|
||||
device && device->hardware_model) {
|
||||
qDebug() << "Recovery device hardware_model: "
|
||||
<< device->hardware_model;
|
||||
info =
|
||||
DeviceDatabase::findByHwModel(std::string(device->hardware_model));
|
||||
} else {
|
||||
qDebug() << "Could not resolve hardware_model from client.";
|
||||
}
|
||||
|
||||
result.displayName =
|
||||
info ? (info->displayName ? info->displayName : info->marketingName)
|
||||
: "Unknown Device";
|
||||
|
||||
result.deviceInfo = *deviceInfo;
|
||||
result.success = true;
|
||||
irecv_close(client);
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
#include "../../iDescriptor.h"
|
||||
#include <QByteArray>
|
||||
#include <QDebug>
|
||||
#include <QImage>
|
||||
#include <QPixmap>
|
||||
#include <libheif/heif.h>
|
||||
|
||||
QPixmap load_heic(const QByteArray &imageData)
|
||||
{
|
||||
heif_context *ctx = heif_context_alloc();
|
||||
if (!ctx) {
|
||||
qWarning() << "Failed to allocate heif_context";
|
||||
return QPixmap();
|
||||
}
|
||||
|
||||
heif_error err = heif_context_read_from_memory(ctx, imageData.constData(),
|
||||
imageData.size(), nullptr);
|
||||
if (err.code != heif_error_Ok) {
|
||||
qWarning() << "Failed to read HEIC from memory:" << err.message;
|
||||
heif_context_free(ctx);
|
||||
return QPixmap();
|
||||
}
|
||||
|
||||
heif_image_handle *handle;
|
||||
err = heif_context_get_primary_image_handle(ctx, &handle);
|
||||
if (err.code != heif_error_Ok) {
|
||||
qWarning() << "Failed to get primary image handle:" << err.message;
|
||||
heif_context_free(ctx);
|
||||
return QPixmap();
|
||||
}
|
||||
|
||||
heif_image *img;
|
||||
err = heif_decode_image(handle, &img, heif_colorspace_RGB,
|
||||
heif_chroma_interleaved_RGB, nullptr);
|
||||
if (err.code != heif_error_Ok) {
|
||||
qWarning() << "Failed to decode HEIC image:" << err.message;
|
||||
heif_image_handle_release(handle);
|
||||
heif_context_free(ctx);
|
||||
return QPixmap();
|
||||
}
|
||||
|
||||
int width = heif_image_get_width(img, heif_channel_interleaved);
|
||||
int height = heif_image_get_height(img, heif_channel_interleaved);
|
||||
size_t stride;
|
||||
const uint8_t *data =
|
||||
heif_image_get_plane_readonly2(img, heif_channel_interleaved, &stride);
|
||||
|
||||
if (!data) {
|
||||
qWarning() << "Failed to get image plane data";
|
||||
heif_image_release(img);
|
||||
heif_image_handle_release(handle);
|
||||
heif_context_free(ctx);
|
||||
return QPixmap();
|
||||
}
|
||||
|
||||
QImage qimg(data, width, height, stride, QImage::Format_RGB888);
|
||||
QPixmap result = QPixmap::fromImage(qimg);
|
||||
|
||||
heif_image_release(img);
|
||||
heif_image_handle_release(handle);
|
||||
heif_context_free(ctx);
|
||||
|
||||
return result;
|
||||
}
|
||||
+16
-31
@@ -4,12 +4,10 @@
|
||||
#include "fileexplorerwidget.h"
|
||||
#include "iDescriptor-ui.h"
|
||||
#include "iDescriptor.h"
|
||||
#include "infolabel.h"
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
#include <QGraphicsDropShadowEffect>
|
||||
#include <QGraphicsPixmapItem>
|
||||
#include <QGraphicsScene>
|
||||
#include <QGraphicsView>
|
||||
#include <QGridLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QHBoxLayout>
|
||||
@@ -32,30 +30,22 @@ DeviceInfoWidget::DeviceInfoWidget(iDescriptorDevice *device, QWidget *parent)
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout(this);
|
||||
mainLayout->setContentsMargins(0, 0, 10, 0);
|
||||
mainLayout->setSpacing(1);
|
||||
m_graphicsScene = new QGraphicsScene(this); // no parent
|
||||
QGraphicsPixmapItem *pixmapItem =
|
||||
new QGraphicsPixmapItem(QPixmap(":/resources/iphone.png"));
|
||||
m_graphicsScene->addItem(pixmapItem);
|
||||
|
||||
m_graphicsView = new ResponsiveGraphicsView(m_graphicsScene, this);
|
||||
m_graphicsView->setRenderHint(QPainter::Antialiasing);
|
||||
m_graphicsView->setMinimumWidth(200);
|
||||
m_graphicsView->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Expanding);
|
||||
m_graphicsView->setStyleSheet("background: transparent; border: none;");
|
||||
// Create responsive image label
|
||||
m_deviceImageLabel = new ResponsiveQLabel(this);
|
||||
m_deviceImageLabel->setPixmap(QPixmap(":/resources/iphone.png"));
|
||||
m_deviceImageLabel->setMinimumWidth(200);
|
||||
m_deviceImageLabel->setSizePolicy(QSizePolicy::Ignored,
|
||||
QSizePolicy::Expanding);
|
||||
m_deviceImageLabel->setStyleSheet("background: transparent; border: none;");
|
||||
|
||||
mainLayout->addWidget(m_graphicsView, 1); // Stretch factor 1
|
||||
mainLayout->addWidget(m_deviceImageLabel, 1); // Stretch factor 1
|
||||
|
||||
// Right side: Info Table
|
||||
QWidget *infoContainer = new QWidget();
|
||||
// infoContainer->setObjectName("infoContainer");
|
||||
// infoContainer->setStyleSheet("QWidget#infoContainer { "
|
||||
// " border: 1px solid #ccc; "
|
||||
// " border-radius: 6px; "
|
||||
// "}");
|
||||
infoContainer->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
|
||||
infoContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
|
||||
|
||||
QVBoxLayout *infoLayout = new QVBoxLayout(infoContainer);
|
||||
// infoLayout->setSpacing(10);
|
||||
|
||||
// Header
|
||||
QGroupBox *headerWidget = new QGroupBox();
|
||||
@@ -128,6 +118,8 @@ DeviceInfoWidget::DeviceInfoWidget(iDescriptorDevice *device, QWidget *parent)
|
||||
infoLayout->addStretch();
|
||||
|
||||
QGroupBox *gridContainer = new QGroupBox("Device Information");
|
||||
gridContainer->setSizePolicy(QSizePolicy::Expanding,
|
||||
QSizePolicy::Preferred);
|
||||
QGridLayout *gridLayout = new QGridLayout(); // Set layout on gridWidget
|
||||
gridLayout->setSpacing(8);
|
||||
gridLayout->setColumnStretch(1, 1); // Allow value column to stretch
|
||||
@@ -138,7 +130,7 @@ DeviceInfoWidget::DeviceInfoWidget(iDescriptorDevice *device, QWidget *parent)
|
||||
QList<QPair<QString, QWidget *>> infoItems;
|
||||
|
||||
auto createValueLabel = [](const QString &text) {
|
||||
return new QLabel(text);
|
||||
return new InfoLabel(text);
|
||||
};
|
||||
|
||||
infoItems.append({"iOS Version:", createValueLabel(QString::fromStdString(
|
||||
@@ -259,23 +251,16 @@ DeviceInfoWidget::DeviceInfoWidget(iDescriptorDevice *device, QWidget *parent)
|
||||
rightSideLayout->setSpacing(10);
|
||||
rightSideLayout->addWidget(infoContainer);
|
||||
rightSideLayout->addWidget(new DiskUsageWidget(device, this));
|
||||
rightSideLayout->setAlignment(Qt::AlignCenter);
|
||||
// TODO: layout shift cause ?
|
||||
// rightSideLayout->setAlignment(Qt::AlignCenter);
|
||||
mainLayout->addLayout(rightSideLayout, 2); // Stretch factor 2
|
||||
|
||||
m_updateTimer = new QTimer(this);
|
||||
connect(m_updateTimer, &QTimer::timeout, this,
|
||||
&DeviceInfoWidget::updateBatteryInfo);
|
||||
m_updateTimer->start(30000); // Update every 30 seconds
|
||||
}
|
||||
|
||||
DeviceInfoWidget::~DeviceInfoWidget()
|
||||
{
|
||||
if (m_graphicsView) {
|
||||
m_graphicsView->setScene(
|
||||
nullptr); // prevents QGraphicsScene from calling into view during
|
||||
// its destructor only needed on macos ?
|
||||
}
|
||||
}
|
||||
DeviceInfoWidget::~DeviceInfoWidget() {}
|
||||
|
||||
void DeviceInfoWidget::onBatteryMoreClicked()
|
||||
{
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
#define DEVICEINFOWIDGET_H
|
||||
#include "batterywidget.h"
|
||||
#include "iDescriptor.h"
|
||||
#include <QGraphicsScene>
|
||||
#include <QGraphicsView>
|
||||
#include "responsiveqlabel.h"
|
||||
#include <QLabel>
|
||||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
@@ -29,8 +28,7 @@ private:
|
||||
BatteryWidget *m_batteryWidget;
|
||||
QLabel *m_lightningIconLabel;
|
||||
|
||||
QGraphicsView *m_graphicsView = nullptr;
|
||||
QGraphicsScene *m_graphicsScene = nullptr;
|
||||
ResponsiveQLabel *m_deviceImageLabel = nullptr;
|
||||
};
|
||||
|
||||
#endif // DEVICEINFOWIDGET_H
|
||||
|
||||
+125
-105
@@ -42,54 +42,22 @@ DeviceManagerWidget::DeviceManagerWidget(QWidget *parent)
|
||||
});
|
||||
|
||||
connect(AppContext::sharedInstance(), &AppContext::recoveryDeviceAdded,
|
||||
this, [this](QObject *recoveryDeviceInfoObj) {
|
||||
if (!recoveryDeviceInfoObj)
|
||||
return;
|
||||
try {
|
||||
RecoveryDeviceInfo *device =
|
||||
qobject_cast<RecoveryDeviceInfo *>(
|
||||
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";
|
||||
}
|
||||
this, [this](const iDescriptorRecoveryDevice *recoveryDeviceInfo) {
|
||||
addRecoveryDevice(recoveryDeviceInfo);
|
||||
emit updateNoDevicesConnected();
|
||||
});
|
||||
|
||||
// connect(AppContext::sharedInstance(), &AppContext::recoveryDeviceRemoved,
|
||||
// this, [this](const QString &ecid) {
|
||||
// qDebug() << "Removing:" << ecid;
|
||||
// std::string ecidStr = ecid.toStdString();
|
||||
// DeviceMenuWidget *deviceWidget =
|
||||
// qobject_cast<DeviceMenuWidget *>(
|
||||
// m_device_menu_widgets[ecidStr]);
|
||||
connect(AppContext::sharedInstance(), &AppContext::recoveryDeviceRemoved,
|
||||
this, [this](uint64_t ecid) {
|
||||
removeRecoveryDevice(ecid);
|
||||
emit updateNoDevicesConnected();
|
||||
});
|
||||
|
||||
// if (deviceWidget) {
|
||||
// // TODO: Implement proper removal by device index
|
||||
// m_device_menu_widgets.erase(ecidStr);
|
||||
// delete deviceWidget;
|
||||
// }
|
||||
// emit updateNoDevicesConnected();
|
||||
// });
|
||||
connect(AppContext::sharedInstance(), &AppContext::devicePairingExpired,
|
||||
this, [this](const QString &udid) {
|
||||
removePendingDevice(udid);
|
||||
emit updateNoDevicesConnected();
|
||||
});
|
||||
}
|
||||
|
||||
void DeviceManagerWidget::setupUI()
|
||||
@@ -109,10 +77,8 @@ void DeviceManagerWidget::setupUI()
|
||||
m_mainLayout->addWidget(m_stackedWidget);
|
||||
|
||||
// Connect signals
|
||||
connect(m_sidebar, &DeviceSidebarWidget::sidebarDeviceChanged, this,
|
||||
&DeviceManagerWidget::onSidebarDeviceChanged);
|
||||
connect(m_sidebar, &DeviceSidebarWidget::sidebarNavigationChanged, this,
|
||||
&DeviceManagerWidget::onSidebarNavigationChanged);
|
||||
connect(m_sidebar, &DeviceSidebarWidget::deviceSelectionChanged, this,
|
||||
&DeviceManagerWidget::onDeviceSelectionChanged);
|
||||
}
|
||||
|
||||
void DeviceManagerWidget::addDevice(iDescriptorDevice *device)
|
||||
@@ -130,8 +96,8 @@ void DeviceManagerWidget::addDevice(iDescriptorDevice *device)
|
||||
QString tabTitle = QString::fromStdString(device->deviceInfo.productType);
|
||||
|
||||
m_stackedWidget->addWidget(deviceWidget);
|
||||
m_deviceWidgets[device->udid] = std::pair{
|
||||
deviceWidget, m_sidebar->addToSidebar(tabTitle, device->udid)};
|
||||
m_deviceWidgets[device->udid] =
|
||||
std::pair{deviceWidget, m_sidebar->addDevice(tabTitle, device->udid)};
|
||||
|
||||
// todo
|
||||
// If this is the first device, make it current
|
||||
@@ -140,24 +106,48 @@ void DeviceManagerWidget::addDevice(iDescriptorDevice *device)
|
||||
// }
|
||||
}
|
||||
|
||||
void DeviceManagerWidget::addRecoveryDevice(RecoveryDeviceInfo *device)
|
||||
void DeviceManagerWidget::addRecoveryDevice(
|
||||
const iDescriptorRecoveryDevice *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);
|
||||
try {
|
||||
// Create device info widget
|
||||
RecoveryDeviceInfoWidget *recoveryDeviceInfoWidget =
|
||||
new RecoveryDeviceInfoWidget(device);
|
||||
m_recoveryDeviceWidgets.insert(
|
||||
device->ecid,
|
||||
std::pair{recoveryDeviceInfoWidget,
|
||||
m_sidebar->addRecoveryDevice(device->ecid)});
|
||||
m_stackedWidget->addWidget(recoveryDeviceInfoWidget);
|
||||
|
||||
} catch (...) {
|
||||
qDebug() << "Error initializing recovery device";
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceManagerWidget::removeRecoveryDevice(uint64_t ecid)
|
||||
{
|
||||
qDebug() << "Removing recovery device with ECID:" << ecid;
|
||||
if (!m_recoveryDeviceWidgets.contains(ecid)) {
|
||||
qDebug() << "Recovery device with ECID" + QString::number(ecid) +
|
||||
" not found. Please report this issue.";
|
||||
return;
|
||||
}
|
||||
|
||||
RecoveryDeviceInfoWidget *deviceWidget =
|
||||
new RecoveryDeviceInfoWidget(device, this);
|
||||
m_recoveryDeviceWidgets[ecid].first;
|
||||
RecoveryDeviceSidebarItem *sidebarItem =
|
||||
m_recoveryDeviceWidgets[ecid].second;
|
||||
|
||||
// QString tabTitle = QString::fromStdString(device->product);
|
||||
if (deviceWidget != nullptr && sidebarItem != nullptr) {
|
||||
qDebug() << "Recovery device exists removing:" << QString::number(ecid);
|
||||
|
||||
m_stackedWidget->addWidget(deviceWidget);
|
||||
// m_deviceWidgets[device->ecid] = std::pair{
|
||||
// deviceWidget, m_sidebar->addToSidebar(tabTitle, device->ecid)};
|
||||
m_recoveryDeviceWidgets.remove(ecid);
|
||||
m_stackedWidget->removeWidget(deviceWidget);
|
||||
m_sidebar->removeRecoveryDevice(ecid);
|
||||
deviceWidget->deleteLater();
|
||||
|
||||
emit updateNoDevicesConnected();
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceManagerWidget::addPendingDevice(const QString &udid, bool locked)
|
||||
@@ -180,12 +170,28 @@ void DeviceManagerWidget::addPendingDevice(const QString &udid, bool locked)
|
||||
DevicePendingWidget *pendingWidget = new DevicePendingWidget(locked, this);
|
||||
m_stackedWidget->addWidget(pendingWidget);
|
||||
m_pendingDeviceWidgets[udid.toStdString()] =
|
||||
std::pair{pendingWidget, m_sidebar->addPendingToSidebar(udid)};
|
||||
std::pair{pendingWidget, m_sidebar->addPendingDevice(udid)};
|
||||
}
|
||||
|
||||
// If this is the first device, make it current
|
||||
// if (m_currentDeviceIndex == -1) {
|
||||
// setCurrentDevice(deviceIndex);
|
||||
// }
|
||||
void DeviceManagerWidget::removePendingDevice(const QString &udid)
|
||||
{
|
||||
qDebug() << "Removing pending device:" << udid;
|
||||
if (!m_pendingDeviceWidgets.contains(udid.toStdString())) {
|
||||
qDebug() << "Pending device not found:" << udid;
|
||||
return;
|
||||
}
|
||||
std::string udidStr = udid.toStdString();
|
||||
DevicePendingWidget *deviceWidget = m_pendingDeviceWidgets[udidStr].first;
|
||||
DevicePendingSidebarItem *sidebarItem =
|
||||
m_pendingDeviceWidgets[udidStr].second;
|
||||
|
||||
if (deviceWidget != nullptr && sidebarItem != nullptr) {
|
||||
qDebug() << "Pending device exists removing:" << udid;
|
||||
m_pendingDeviceWidgets.remove(udidStr);
|
||||
m_stackedWidget->removeWidget(deviceWidget);
|
||||
m_sidebar->removePendingDevice(udidStr);
|
||||
deviceWidget->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceManagerWidget::addPairedDevice(iDescriptorDevice *device)
|
||||
@@ -201,7 +207,7 @@ void DeviceManagerWidget::addPairedDevice(iDescriptorDevice *device)
|
||||
if (pair.second) {
|
||||
qDebug() << "Removing pending device from sidebar:"
|
||||
<< QString::fromStdString(device->udid);
|
||||
m_sidebar->removePendingFromSidebar(pair.second);
|
||||
m_sidebar->removePendingDevice(device->udid);
|
||||
}
|
||||
|
||||
// Clean up widget if it exists
|
||||
@@ -230,13 +236,12 @@ void DeviceManagerWidget::removeDevice(const std::string &uuid)
|
||||
// TODO: cleanups
|
||||
m_deviceWidgets.remove(uuid);
|
||||
m_stackedWidget->removeWidget(deviceWidget);
|
||||
m_sidebar->removeFromSidebar(sidebarItem);
|
||||
m_sidebar->removeDevice(uuid);
|
||||
deviceWidget->deleteLater();
|
||||
// delete d.second;
|
||||
|
||||
// TODO:
|
||||
if (m_deviceWidgets.count() > 0) {
|
||||
setCurrentDevice(m_deviceWidgets.firstKey());
|
||||
m_sidebar->updateSidebar(m_deviceWidgets.firstKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -252,16 +257,15 @@ void DeviceManagerWidget::setCurrentDevice(const std::string &uuid)
|
||||
return;
|
||||
}
|
||||
|
||||
// m_currentDeviceIndex = deviceIndex;
|
||||
m_currentDeviceUuid = uuid;
|
||||
|
||||
// // Update sidebar selection
|
||||
// m_sidebar->setCurrentDevice(deviceIndex);
|
||||
|
||||
// // Update stacked widget
|
||||
// Update stacked widget
|
||||
QWidget *widget = m_deviceWidgets[uuid].first;
|
||||
m_stackedWidget->setCurrentWidget(widget);
|
||||
|
||||
// Update sidebar selection
|
||||
m_sidebar->setCurrentSelection(DeviceSelection(uuid));
|
||||
|
||||
emit deviceChanged(uuid);
|
||||
}
|
||||
|
||||
@@ -270,35 +274,51 @@ std::string DeviceManagerWidget::getCurrentDevice() const
|
||||
return m_currentDeviceUuid;
|
||||
}
|
||||
|
||||
void DeviceManagerWidget::setDeviceNavigation(int deviceIndex,
|
||||
const QString §ion)
|
||||
void DeviceManagerWidget::onDeviceSelectionChanged(
|
||||
const DeviceSelection &selection)
|
||||
{
|
||||
m_sidebar->setDeviceNavigationSection(deviceIndex, section);
|
||||
// emit deviceNavigationChanged(deviceIndex, section);
|
||||
}
|
||||
// Update sidebar selection
|
||||
m_sidebar->setCurrentSelection(selection);
|
||||
|
||||
void DeviceManagerWidget::onSidebarDeviceChanged(std::string deviceUuid)
|
||||
{
|
||||
setCurrentDevice(deviceUuid);
|
||||
}
|
||||
switch (selection.type) {
|
||||
case DeviceSelection::Normal:
|
||||
if (m_deviceWidgets.contains(selection.uuid)) {
|
||||
if (m_currentDeviceUuid != selection.uuid) {
|
||||
setCurrentDevice(selection.uuid);
|
||||
}
|
||||
|
||||
void DeviceManagerWidget::onSidebarNavigationChanged(std::string deviceUuid,
|
||||
const QString §ion)
|
||||
{
|
||||
if (deviceUuid != m_currentDeviceUuid) {
|
||||
setCurrentDevice(deviceUuid);
|
||||
// Handle navigation section
|
||||
QWidget *tabWidget = m_deviceWidgets[selection.uuid].first;
|
||||
DeviceMenuWidget *deviceMenuWidget =
|
||||
qobject_cast<DeviceMenuWidget *>(tabWidget);
|
||||
qDebug() << "Switching to tab:" << selection.section
|
||||
<< deviceMenuWidget;
|
||||
if (deviceMenuWidget && !selection.section.isEmpty()) {
|
||||
deviceMenuWidget->switchToTab(selection.section);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case DeviceSelection::Recovery:
|
||||
if (m_recoveryDeviceWidgets.contains(selection.ecid)) {
|
||||
QWidget *tabWidget = m_recoveryDeviceWidgets[selection.ecid].first;
|
||||
if (tabWidget) {
|
||||
m_stackedWidget->setCurrentWidget(tabWidget);
|
||||
// Clear current device since we're viewing recovery device
|
||||
m_currentDeviceUuid = "";
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case DeviceSelection::Pending:
|
||||
if (m_pendingDeviceWidgets.contains(selection.uuid)) {
|
||||
QWidget *tabWidget = m_pendingDeviceWidgets[selection.uuid].first;
|
||||
if (tabWidget) {
|
||||
m_stackedWidget->setCurrentWidget(tabWidget);
|
||||
// Clear current device since we're viewing pending device
|
||||
m_currentDeviceUuid = "";
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
QWidget *tabWidget = m_deviceWidgets[deviceUuid].first;
|
||||
DeviceMenuWidget *deviceMenuWidget =
|
||||
qobject_cast<DeviceMenuWidget *>(tabWidget);
|
||||
|
||||
if (deviceMenuWidget) {
|
||||
// Call a method to change the internal tab
|
||||
deviceMenuWidget->switchToTab(section);
|
||||
}
|
||||
// if (deviceIndex != m_currentDeviceIndex) {
|
||||
// setCurrentDevice(deviceIndex);
|
||||
// }
|
||||
// emit sidebarNavigationChanged(deviceUuid, section);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "devicependingwidget.h"
|
||||
#include "devicesidebarwidget.h"
|
||||
#include "iDescriptor.h"
|
||||
#include "recoverydeviceinfowidget.h"
|
||||
#include <QHBoxLayout>
|
||||
#include <QMap>
|
||||
#include <QStackedWidget>
|
||||
@@ -20,28 +21,25 @@ public:
|
||||
void setCurrentDevice(const std::string &uuid);
|
||||
std::string getCurrentDevice() const;
|
||||
|
||||
// Navigation methods
|
||||
void setDeviceNavigation(int deviceIndex, const QString §ion);
|
||||
|
||||
signals:
|
||||
void deviceChanged(std::string deviceUuid);
|
||||
void sidebarNavigationChanged(std::string deviceUuid,
|
||||
const QString §ion);
|
||||
void updateNoDevicesConnected();
|
||||
|
||||
private slots:
|
||||
void onSidebarDeviceChanged(std::string deviceUuid);
|
||||
void onSidebarNavigationChanged(std::string deviceUuid,
|
||||
const QString §ion);
|
||||
void onDeviceSelectionChanged(const DeviceSelection &selection);
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
|
||||
void addDevice(iDescriptorDevice *device);
|
||||
void removeDevice(const std::string &uuid);
|
||||
void addRecoveryDevice(RecoveryDeviceInfo *device);
|
||||
void addRecoveryDevice(const iDescriptorRecoveryDevice *device);
|
||||
void removeRecoveryDevice(uint64_t ecid);
|
||||
// TODO:udid or uuid ?
|
||||
void addPendingDevice(const QString &udid, bool locked);
|
||||
void addPairedDevice(iDescriptorDevice *device);
|
||||
void removePendingDevice(const QString &udid);
|
||||
|
||||
QHBoxLayout *m_mainLayout;
|
||||
DeviceSidebarWidget *m_sidebar;
|
||||
QStackedWidget *m_stackedWidget;
|
||||
@@ -53,6 +51,10 @@ private:
|
||||
std::pair<DevicePendingWidget *, DevicePendingSidebarItem *>>
|
||||
m_pendingDeviceWidgets; // Map to store devices by UDID
|
||||
|
||||
QMap<uint64_t,
|
||||
std::pair<RecoveryDeviceInfoWidget *, RecoveryDeviceSidebarItem *>>
|
||||
m_recoveryDeviceWidgets; // Map to store recovery devices by ECID
|
||||
|
||||
std::string m_currentDeviceUuid;
|
||||
};
|
||||
|
||||
|
||||
+134
-93
@@ -1,6 +1,7 @@
|
||||
#include "devicesidebarwidget.h"
|
||||
#include "iDescriptor-ui.h"
|
||||
#include "loadingspinnerwidget.h"
|
||||
#include "qprocessindicator.h"
|
||||
#include <QDebug>
|
||||
|
||||
// DeviceSidebarItem Implementation
|
||||
@@ -26,8 +27,6 @@ void DeviceSidebarItem::setupUI()
|
||||
QVBoxLayout *headerLayout = new QVBoxLayout(m_headerWidget);
|
||||
headerLayout->setContentsMargins(0, 0, 0, 0);
|
||||
headerLayout->setSpacing(2);
|
||||
m_headerWidget->setStyleSheet(
|
||||
"ClickableWidget { background-color: #ff0000ff; }");
|
||||
connect(m_headerWidget, &ClickableWidget::clicked, this,
|
||||
[this]() { emit deviceSelected(m_uuid); });
|
||||
|
||||
@@ -176,15 +175,66 @@ void DeviceSidebarItem::onNavigationButtonClicked()
|
||||
{
|
||||
QPushButton *button = qobject_cast<QPushButton *>(sender());
|
||||
if (button) {
|
||||
// Only emit navigationRequested - this will handle both device
|
||||
// selection and tab switching
|
||||
emit navigationRequested(m_uuid, button->text());
|
||||
emit deviceSelected(m_uuid);
|
||||
// Remove this line: emit deviceSelected(m_uuid);
|
||||
}
|
||||
}
|
||||
|
||||
const std::string &DeviceSidebarItem::getDeviceUuid() const { return m_uuid; }
|
||||
|
||||
RecoveryDeviceSidebarItem::RecoveryDeviceSidebarItem(uint64_t ecid,
|
||||
QWidget *parent)
|
||||
: QFrame(parent), m_ecid(ecid)
|
||||
{
|
||||
setupUI();
|
||||
}
|
||||
|
||||
void RecoveryDeviceSidebarItem::setupUI()
|
||||
{
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout(this);
|
||||
mainLayout->setContentsMargins(5, 5, 5, 5);
|
||||
mainLayout->setSpacing(5);
|
||||
|
||||
ClickableWidget *headerWidget = new ClickableWidget();
|
||||
connect(headerWidget, &ClickableWidget::clicked, this,
|
||||
[this]() { emit recoveryDeviceSelected(m_ecid); });
|
||||
QVBoxLayout *headerLayout = new QVBoxLayout(headerWidget);
|
||||
headerLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
QLabel *titleLabel = new QLabel("Recovery Mode");
|
||||
titleLabel->setStyleSheet("QLabel { font-weight: bold; }");
|
||||
titleLabel->setWordWrap(true);
|
||||
headerLayout->addWidget(titleLabel);
|
||||
|
||||
mainLayout->addWidget(headerWidget);
|
||||
|
||||
// Set initial style
|
||||
// Set initial style
|
||||
setStyleSheet("RecoveryDeviceSidebarItem { border: "
|
||||
"1px solid #e0e0e0; border-radius: 5px; }");
|
||||
}
|
||||
|
||||
void RecoveryDeviceSidebarItem::setSelected(bool selected)
|
||||
{
|
||||
if (m_selected == selected)
|
||||
return;
|
||||
|
||||
m_selected = selected;
|
||||
|
||||
if (selected) {
|
||||
setStyleSheet("RecoveryDeviceSidebarItem { border: "
|
||||
"2px solid #2196f3; border-radius: 5px; }");
|
||||
} else {
|
||||
setStyleSheet("RecoveryDeviceSidebarItem { border: "
|
||||
"1px solid #e0e0e0; border-radius: 5px; }");
|
||||
}
|
||||
}
|
||||
|
||||
// DeviceSidebarWidget Implementation
|
||||
DeviceSidebarWidget::DeviceSidebarWidget(QWidget *parent) : QWidget(parent)
|
||||
DeviceSidebarWidget::DeviceSidebarWidget(QWidget *parent)
|
||||
: QWidget(parent), m_currentSelection(DeviceSelection(""))
|
||||
{
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout(this);
|
||||
mainLayout->setContentsMargins(10, 10, 10, 10);
|
||||
@@ -219,119 +269,108 @@ DeviceSidebarWidget::DeviceSidebarWidget(QWidget *parent) : QWidget(parent)
|
||||
setMaximumWidth(250);
|
||||
}
|
||||
|
||||
DeviceSidebarItem *DeviceSidebarWidget::addToSidebar(const QString &deviceName,
|
||||
const std::string &uuid)
|
||||
DeviceSidebarItem *DeviceSidebarWidget::addDevice(const QString &deviceName,
|
||||
const std::string &uuid)
|
||||
{
|
||||
DeviceSidebarItem *item = new DeviceSidebarItem(deviceName, uuid, this);
|
||||
|
||||
// Connect to unified handler
|
||||
connect(item, &DeviceSidebarItem::deviceSelected, this,
|
||||
&DeviceSidebarWidget::onDeviceSelected);
|
||||
[this](const std::string &uuid) {
|
||||
onItemSelected(DeviceSelection(uuid));
|
||||
});
|
||||
connect(item, &DeviceSidebarItem::navigationRequested, this,
|
||||
&DeviceSidebarWidget::onSidebarNavigationChanged);
|
||||
[this](const std::string &uuid, const QString §ion) {
|
||||
onItemSelected(DeviceSelection(uuid, section));
|
||||
});
|
||||
|
||||
// TODO
|
||||
m_currentDeviceUuid = uuid;
|
||||
// item->setSelected(true);
|
||||
m_deviceSidebarItems.append(item);
|
||||
updateSelection();
|
||||
// m_deviceItems.append(item);
|
||||
m_contentLayout->insertWidget(m_contentLayout->count() - 1,
|
||||
item); // Insert before stretch
|
||||
|
||||
// Auto-select first device
|
||||
// if (m_currentDeviceIndex == -1) {
|
||||
// setCurrentDevice(deviceIndex);
|
||||
// }
|
||||
m_deviceItems[uuid] = item;
|
||||
m_contentLayout->insertWidget(m_contentLayout->count() - 1, item);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
void DeviceSidebarWidget::removeFromSidebar(DeviceSidebarItem *item)
|
||||
{
|
||||
m_deviceSidebarItems.removeAll(item);
|
||||
m_contentLayout->removeWidget(item);
|
||||
item->deleteLater();
|
||||
}
|
||||
|
||||
DevicePendingSidebarItem *
|
||||
DeviceSidebarWidget::addPendingToSidebar(const QString &uuid)
|
||||
DeviceSidebarWidget::addPendingDevice(const QString &uuid)
|
||||
{
|
||||
DevicePendingSidebarItem *item = new DevicePendingSidebarItem(uuid, this);
|
||||
m_devicePendingSidebarItems.append(item);
|
||||
m_contentLayout->insertWidget(m_contentLayout->count() - 1,
|
||||
item); // Insert before stretch
|
||||
m_pendingItems[uuid.toStdString()] = item;
|
||||
m_contentLayout->insertWidget(m_contentLayout->count() - 1, item);
|
||||
return item;
|
||||
}
|
||||
|
||||
void DeviceSidebarWidget::removePendingFromSidebar(
|
||||
DevicePendingSidebarItem *item)
|
||||
RecoveryDeviceSidebarItem *DeviceSidebarWidget::addRecoveryDevice(uint64_t ecid)
|
||||
{
|
||||
m_devicePendingSidebarItems.removeAll(item);
|
||||
m_contentLayout->removeWidget(item);
|
||||
item->deleteLater();
|
||||
RecoveryDeviceSidebarItem *item = new RecoveryDeviceSidebarItem(ecid, this);
|
||||
|
||||
// Connect to unified handler
|
||||
connect(item, &RecoveryDeviceSidebarItem::recoveryDeviceSelected, this,
|
||||
[this](uint64_t ecid) { onItemSelected(DeviceSelection(ecid)); });
|
||||
|
||||
m_recoveryItems[ecid] = item;
|
||||
m_contentLayout->insertWidget(m_contentLayout->count() - 1, item);
|
||||
return item;
|
||||
}
|
||||
|
||||
void DeviceSidebarWidget::setCurrentDevice(std::string uuid)
|
||||
void DeviceSidebarWidget::removeDevice(const std::string &uuid)
|
||||
{
|
||||
if (m_currentDeviceUuid == uuid)
|
||||
return;
|
||||
|
||||
m_currentDeviceUuid = uuid;
|
||||
updateSelection();
|
||||
emit sidebarDeviceChanged(uuid);
|
||||
}
|
||||
|
||||
void DeviceSidebarWidget::setDeviceNavigationSection(int deviceIndex,
|
||||
const QString §ion)
|
||||
{
|
||||
// for (DeviceSidebarItem *item : m_deviceItems) {
|
||||
// if (item->getDeviceIndex() == deviceIndex) {
|
||||
// // Find and check the appropriate button
|
||||
// QPushButton *targetButton = nullptr;
|
||||
// if (section == "Info")
|
||||
// targetButton = item->findChild<QPushButton *>();
|
||||
// else if (section == "Apps")
|
||||
// targetButton = item->findChildren<QPushButton *>().value(1);
|
||||
// else if (section == "Gallery")
|
||||
// targetButton = item->findChildren<QPushButton *>().value(2);
|
||||
// else if (section == "Files")
|
||||
// targetButton = item->findChildren<QPushButton *>().value(3);
|
||||
|
||||
// if (targetButton) {
|
||||
// targetButton->setChecked(true);
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
void DeviceSidebarWidget::onDeviceSelected(std::string uuid)
|
||||
{
|
||||
setCurrentDevice(uuid);
|
||||
}
|
||||
|
||||
void DeviceSidebarWidget::onSidebarNavigationChanged(std::string uuid,
|
||||
const QString §ion)
|
||||
{
|
||||
if (uuid != m_currentDeviceUuid) {
|
||||
setCurrentDevice(uuid);
|
||||
if (m_deviceItems.contains(uuid)) {
|
||||
DeviceSidebarItem *item = m_deviceItems[uuid];
|
||||
m_deviceItems.remove(uuid);
|
||||
m_contentLayout->removeWidget(item);
|
||||
item->deleteLater();
|
||||
}
|
||||
emit sidebarNavigationChanged(uuid, section);
|
||||
}
|
||||
|
||||
void DeviceSidebarWidget::updateSidebar(std::string uuid)
|
||||
void DeviceSidebarWidget::removePendingDevice(const std::string &uuid)
|
||||
{
|
||||
// TODO : need a proper check
|
||||
if (m_deviceSidebarItems.isEmpty())
|
||||
return;
|
||||
m_currentDeviceUuid = uuid;
|
||||
if (m_pendingItems.contains(uuid)) {
|
||||
DevicePendingSidebarItem *item = m_pendingItems[uuid];
|
||||
m_pendingItems.remove(uuid);
|
||||
m_contentLayout->removeWidget(item);
|
||||
item->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceSidebarWidget::removeRecoveryDevice(uint64_t ecid)
|
||||
{
|
||||
if (m_recoveryItems.contains(ecid)) {
|
||||
RecoveryDeviceSidebarItem *item = m_recoveryItems[ecid];
|
||||
m_recoveryItems.remove(ecid);
|
||||
m_contentLayout->removeWidget(item);
|
||||
item->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceSidebarWidget::setCurrentSelection(const DeviceSelection &selection)
|
||||
{
|
||||
m_currentSelection = selection;
|
||||
updateSelection();
|
||||
}
|
||||
|
||||
void DeviceSidebarWidget::onItemSelected(const DeviceSelection &selection)
|
||||
{
|
||||
setCurrentSelection(selection);
|
||||
emit deviceSelectionChanged(selection);
|
||||
}
|
||||
|
||||
void DeviceSidebarWidget::updateSelection()
|
||||
{
|
||||
for (DeviceSidebarItem *item : m_deviceSidebarItems) {
|
||||
item->setSelected(item->getDeviceUuid() == m_currentDeviceUuid);
|
||||
// Clear all selections first
|
||||
for (auto item : m_deviceItems) {
|
||||
item->setSelected(false);
|
||||
}
|
||||
for (auto item : m_recoveryItems) {
|
||||
item->setSelected(false);
|
||||
}
|
||||
|
||||
// Set selection based on current selection
|
||||
if (m_currentSelection.type == DeviceSelection::Normal &&
|
||||
m_deviceItems.contains(m_currentSelection.uuid)) {
|
||||
m_deviceItems[m_currentSelection.uuid]->setSelected(true);
|
||||
} else if (m_currentSelection.type == DeviceSelection::Recovery &&
|
||||
m_recoveryItems.contains(m_currentSelection.ecid)) {
|
||||
m_recoveryItems[m_currentSelection.ecid]->setSelected(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,15 +378,17 @@ DevicePendingSidebarItem::DevicePendingSidebarItem(const QString &udid,
|
||||
QWidget *parent)
|
||||
: QFrame(parent)
|
||||
{
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
QHBoxLayout *layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->setSpacing(5);
|
||||
layout->setSpacing(1);
|
||||
|
||||
LoadingSpinnerWidget *spinner = new LoadingSpinnerWidget(this);
|
||||
spinner->setFixedSize(16, 16); // Make it a bit smaller
|
||||
spinner->setColor(QColor("#0d6efd")); // Use a theme color
|
||||
QProcessIndicator *spinner = new QProcessIndicator(this);
|
||||
spinner->setFixedSize(32, 32);
|
||||
spinner->setType(QProcessIndicator::line_rotate);
|
||||
spinner->start();
|
||||
|
||||
QLabel *label = new QLabel("Pairing...", this);
|
||||
|
||||
layout->addWidget(label);
|
||||
layout->addWidget(spinner);
|
||||
|
||||
|
||||
+61
-17
@@ -69,41 +69,85 @@ signals:
|
||||
};
|
||||
#endif // DEVICEPENDINGSIDEBARITEM_H
|
||||
|
||||
#ifndef RECOVERYDEVICESIDEBARITEM_H
|
||||
#define RECOVERYDEVICESIDEBARITEM_H
|
||||
class RecoveryDeviceSidebarItem : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit RecoveryDeviceSidebarItem(uint64_t ecid,
|
||||
QWidget *parent = nullptr);
|
||||
|
||||
void setSelected(bool selected);
|
||||
bool isSelected() const { return m_selected; }
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
uint64_t m_ecid;
|
||||
bool m_selected = false;
|
||||
signals:
|
||||
void recoveryDeviceSelected(uint64_t ecid);
|
||||
};
|
||||
#endif // RECOVERYDEVICESIDEBARITEM_H
|
||||
|
||||
// Unified device selection data
|
||||
struct DeviceSelection {
|
||||
enum Type { Normal, Recovery, Pending };
|
||||
Type type;
|
||||
std::string uuid;
|
||||
uint64_t ecid = 0;
|
||||
QString section = "Info";
|
||||
|
||||
DeviceSelection(const std::string &deviceUuid, const QString &nav = "")
|
||||
: type(Normal), uuid(deviceUuid), section(nav)
|
||||
{
|
||||
}
|
||||
DeviceSelection(uint64_t recoveryEcid) : type(Recovery), ecid(recoveryEcid)
|
||||
{
|
||||
}
|
||||
static DeviceSelection pending(const std::string &deviceUuid)
|
||||
{
|
||||
DeviceSelection sel(deviceUuid);
|
||||
sel.type = Pending;
|
||||
return sel;
|
||||
}
|
||||
};
|
||||
|
||||
class DeviceSidebarWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DeviceSidebarWidget(QWidget *parent = nullptr);
|
||||
std::string getUuid() const;
|
||||
|
||||
DeviceSidebarItem *addToSidebar(const QString &deviceName,
|
||||
const std::string &uuid);
|
||||
void removeFromSidebar(DeviceSidebarItem *item);
|
||||
DevicePendingSidebarItem *addPendingToSidebar(const QString &uuid);
|
||||
void removePendingFromSidebar(DevicePendingSidebarItem *item);
|
||||
void setDeviceNavigationSection(int deviceIndex, const QString §ion);
|
||||
void updateSidebar(std::string uuid);
|
||||
// Unified interface
|
||||
DeviceSidebarItem *addDevice(const QString &deviceName,
|
||||
const std::string &uuid);
|
||||
DevicePendingSidebarItem *addPendingDevice(const QString &uuid);
|
||||
RecoveryDeviceSidebarItem *addRecoveryDevice(uint64_t ecid);
|
||||
|
||||
void removeDevice(const std::string &uuid);
|
||||
void removePendingDevice(const std::string &uuid);
|
||||
void removeRecoveryDevice(uint64_t ecid);
|
||||
|
||||
void setCurrentSelection(const DeviceSelection &selection);
|
||||
|
||||
public slots:
|
||||
void onSidebarNavigationChanged(std::string uuid, const QString §ion);
|
||||
void onItemSelected(const DeviceSelection &selection);
|
||||
|
||||
signals:
|
||||
void sidebarNavigationChanged(std::string uuid, const QString §ion);
|
||||
void deviceNavigationChanged(std::string uuid, const QString §ion);
|
||||
void sidebarDeviceChanged(std::string uuid);
|
||||
void deviceSelectionChanged(const DeviceSelection &selection);
|
||||
|
||||
private:
|
||||
void updateSelection();
|
||||
void onDeviceSelected(std::string uuid);
|
||||
void setCurrentDevice(std::string uuid);
|
||||
QScrollArea *m_scrollArea;
|
||||
QWidget *m_contentWidget;
|
||||
QVBoxLayout *m_contentLayout;
|
||||
|
||||
std::string m_currentDeviceUuid;
|
||||
QList<DeviceSidebarItem *> m_deviceSidebarItems;
|
||||
QList<DevicePendingSidebarItem *> m_devicePendingSidebarItems;
|
||||
DeviceSelection m_currentSelection;
|
||||
QMap<std::string, DeviceSidebarItem *> m_deviceItems;
|
||||
QMap<std::string, DevicePendingSidebarItem *> m_pendingItems;
|
||||
QMap<uint64_t, RecoveryDeviceSidebarItem *> m_recoveryItems;
|
||||
};
|
||||
|
||||
#endif // DEVICESIDEBARWIDGET_H
|
||||
+21
-17
@@ -71,9 +71,9 @@ void DiskUsageWidget::setupUI()
|
||||
m_diskBarContainer = new QWidget(this);
|
||||
m_diskBarContainer->setMinimumHeight(20);
|
||||
m_diskBarContainer->setMaximumHeight(20);
|
||||
m_diskBarContainer->setObjectName("diskBarContainer");
|
||||
m_diskBarContainer->setStyleSheet(
|
||||
"QWidget#diskBarContainer { margin: 0; padding: 0; border: none; }");
|
||||
m_diskBarContainer->setObjectName("diskBarContainer");
|
||||
m_diskBarLayout = new QHBoxLayout(m_diskBarContainer);
|
||||
m_diskBarLayout->setContentsMargins(0, 0, 0, 0);
|
||||
m_diskBarLayout->setSpacing(0);
|
||||
@@ -92,21 +92,30 @@ void DiskUsageWidget::setupUI()
|
||||
m_othersBar = new QWidget();
|
||||
m_freeBar = new QWidget();
|
||||
#endif
|
||||
// required for tooltips to have default styling
|
||||
m_systemBar->setObjectName("systemBar");
|
||||
m_appsBar->setObjectName("appsBar");
|
||||
m_mediaBar->setObjectName("mediaBar");
|
||||
m_othersBar->setObjectName("othersBar");
|
||||
m_freeBar->setObjectName("freeBar");
|
||||
|
||||
// Set colors
|
||||
m_systemBar->setStyleSheet(
|
||||
"background-color: #a1384d; border: 1px solid"
|
||||
"QWidget#systemBar { background-color: #a1384d; border: 1px solid"
|
||||
"#e64a5b; padding: 0; margin: 0; border-top-left-radius: 3px; "
|
||||
"border-bottom-left-radius: 3px;");
|
||||
m_appsBar->setStyleSheet("background-color: #4f869f; border: 1px solid "
|
||||
"#63b4da; padding: 0; margin: 0;");
|
||||
m_mediaBar->setStyleSheet(
|
||||
"background-color: #2ECC71; border: none; padding: 0; margin: 0;");
|
||||
m_othersBar->setStyleSheet("background-color: #a28729; border: 1px solid "
|
||||
"#c4a32d; padding: 0; margin: 0;");
|
||||
"border-bottom-left-radius: 3px; }");
|
||||
m_appsBar->setStyleSheet(
|
||||
"QWidget#appsBar { background-color: #4f869f; border: 1px solid "
|
||||
"#63b4da; padding: 0; margin: 0; }");
|
||||
m_mediaBar->setStyleSheet("QWidget#mediaBar { background-color: #2ECC71; "
|
||||
"border: none; padding: 0; margin: 0; }");
|
||||
m_othersBar->setStyleSheet(
|
||||
"QWidget#othersBar { background-color: #a28729; border: 1px solid "
|
||||
"#c4a32d; padding: 0; margin: 0; }");
|
||||
m_freeBar->setStyleSheet(
|
||||
"background-color: #474747; border: 1px solid "
|
||||
"QWidget#freeBar { background-color: #474747; border: 1px solid "
|
||||
"#4f4f4f; padding: 0; margin: 0; border-top-right-radius: 3px; "
|
||||
"border-bottom-right-radius: 3px;");
|
||||
"border-bottom-right-radius: 3px; }");
|
||||
|
||||
m_diskBarLayout->addWidget(m_systemBar);
|
||||
m_diskBarLayout->addWidget(m_appsBar);
|
||||
@@ -145,6 +154,7 @@ void DiskUsageWidget::setupUI()
|
||||
m_legendLayout->addStretch();
|
||||
|
||||
m_dataLayout->addLayout(m_legendLayout);
|
||||
// m_dataLayout->addStretch();
|
||||
|
||||
m_stackedWidget->addWidget(m_dataPage);
|
||||
|
||||
@@ -309,12 +319,6 @@ void DiskUsageWidget::updateUI()
|
||||
// m_freeBar->setVisible(m_freeSpace > 0);
|
||||
}
|
||||
|
||||
void DiskUsageWidget::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
// No custom painting needed - using widgets and layouts
|
||||
}
|
||||
|
||||
void DiskUsageWidget::fetchData()
|
||||
{
|
||||
auto *watcher = new QFutureWatcher<QVariantMap>(this);
|
||||
|
||||
@@ -20,9 +20,6 @@ public:
|
||||
QWidget *parent = nullptr);
|
||||
QSize sizeHint() const override;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
private:
|
||||
void fetchData();
|
||||
void setupUI();
|
||||
|
||||
+48
-51
@@ -27,7 +27,7 @@
|
||||
|
||||
FileExplorerWidget::FileExplorerWidget(iDescriptorDevice *device,
|
||||
QWidget *parent)
|
||||
: QWidget(parent), device(device), usingAFC2(false)
|
||||
: QWidget(parent), m_device(device)
|
||||
{
|
||||
m_mainSplitter = new ModernSplitter(Qt::Horizontal, this);
|
||||
|
||||
@@ -38,16 +38,26 @@ FileExplorerWidget::FileExplorerWidget(iDescriptorDevice *device,
|
||||
|
||||
setupSidebar();
|
||||
|
||||
// Create stacked widget with AFC explorers
|
||||
m_stackedWidget = new QStackedWidget();
|
||||
|
||||
// Add normal AFC explorer (index 0)
|
||||
m_stackedWidget->addWidget(
|
||||
new AfcExplorerWidget(m_device->afcClient, nullptr, m_device));
|
||||
|
||||
// Add AFC2 explorer (index 1)
|
||||
m_stackedWidget->addWidget(
|
||||
new AfcExplorerWidget(m_device->afc2Client, nullptr, m_device));
|
||||
|
||||
// Start with normal AFC client
|
||||
m_stackedWidget->setCurrentIndex(0);
|
||||
|
||||
// Add widgets to splitter
|
||||
m_mainSplitter->addWidget(m_sidebarTree);
|
||||
m_mainSplitter->addWidget(
|
||||
new AfcExplorerWidget(device->afcClient, nullptr, device));
|
||||
m_mainSplitter->addWidget(m_stackedWidget);
|
||||
m_mainSplitter->setSizes({400, 800});
|
||||
setLayout(mainLayout);
|
||||
}
|
||||
// useAFC2 ,path,
|
||||
typedef QPair<bool, QString> SidebarItemData;
|
||||
|
||||
void FileExplorerWidget::setupSidebar()
|
||||
{
|
||||
m_sidebarTree = new QTreeWidget();
|
||||
@@ -55,63 +65,41 @@ void FileExplorerWidget::setupSidebar()
|
||||
m_sidebarTree->setMinimumWidth(50);
|
||||
m_sidebarTree->setMaximumWidth(250);
|
||||
|
||||
// AFC Default section
|
||||
m_afcDefaultItem = new QTreeWidgetItem(m_sidebarTree);
|
||||
m_afcDefaultItem->setText(0, "Explorer");
|
||||
m_afcDefaultItem->setIcon(0, QIcon::fromTheme("folder"));
|
||||
m_afcDefaultItem->setData(0, Qt::UserRole,
|
||||
QVariant::fromValue(SidebarItemData(false, "/")));
|
||||
m_afcDefaultItem->setExpanded(true);
|
||||
QTreeWidgetItem *explorersRoot = new QTreeWidgetItem(m_sidebarTree);
|
||||
explorersRoot->setText(0, "Explorer");
|
||||
explorersRoot->setIcon(0, QIcon::fromTheme("folder"));
|
||||
explorersRoot->setExpanded(true);
|
||||
|
||||
// Add root folder under Default
|
||||
QTreeWidgetItem *rootItem = new QTreeWidgetItem(m_afcDefaultItem);
|
||||
rootItem->setText(0, "Default");
|
||||
rootItem->setIcon(0, QIcon::fromTheme("folder"));
|
||||
rootItem->setData(0, Qt::UserRole,
|
||||
QVariant::fromValue(SidebarItemData(false, "/")));
|
||||
rootItem->setData(0, Qt::UserRole + 1, QVariant::fromValue(false));
|
||||
m_defaultAfcItem = new QTreeWidgetItem(explorersRoot);
|
||||
m_defaultAfcItem->setText(0, "Default");
|
||||
m_defaultAfcItem->setIcon(0, QIcon::fromTheme("folder"));
|
||||
|
||||
// AFC2 Jailbroken section
|
||||
m_afcJailbrokenItem = new QTreeWidgetItem(m_afcDefaultItem);
|
||||
m_afcJailbrokenItem->setText(0, "Jailbroken (AFC2)");
|
||||
m_afcJailbrokenItem->setIcon(0, QIcon::fromTheme("applications-system"));
|
||||
m_afcJailbrokenItem->setData(
|
||||
0, Qt::UserRole, QVariant::fromValue(SidebarItemData(true, "/")));
|
||||
m_afcJailbrokenItem->setExpanded(false);
|
||||
m_jailbrokenAfcItem = new QTreeWidgetItem(explorersRoot);
|
||||
m_jailbrokenAfcItem->setText(0, "Jailbroken (AFC2)");
|
||||
m_jailbrokenAfcItem->setIcon(0, QIcon::fromTheme("applications-system"));
|
||||
|
||||
// Common Places section
|
||||
m_commonPlacesItem = new QTreeWidgetItem(m_sidebarTree);
|
||||
m_commonPlacesItem->setText(0, "Common Places");
|
||||
m_commonPlacesItem->setIcon(0, QIcon::fromTheme("places-bookmarks"));
|
||||
m_commonPlacesItem->setData(
|
||||
0, Qt::UserRole,
|
||||
QVariant::fromValue(
|
||||
SidebarItemData(false, "../../../var/mobile/Library/Wallpapers")));
|
||||
m_commonPlacesItem->setExpanded(true);
|
||||
QTreeWidgetItem *commonPlacesItem = new QTreeWidgetItem(m_sidebarTree);
|
||||
commonPlacesItem->setText(0, "Common Places");
|
||||
commonPlacesItem->setIcon(0, QIcon::fromTheme("places-bookmarks"));
|
||||
commonPlacesItem->setExpanded(true);
|
||||
|
||||
QTreeWidgetItem *wallpapersItem = new QTreeWidgetItem(m_commonPlacesItem);
|
||||
QTreeWidgetItem *wallpapersItem = new QTreeWidgetItem(commonPlacesItem);
|
||||
wallpapersItem->setText(0, "Wallpapers");
|
||||
wallpapersItem->setIcon(0, QIcon::fromTheme("image-x-generic"));
|
||||
wallpapersItem->setData(
|
||||
0, Qt::UserRole,
|
||||
QVariant::fromValue(
|
||||
SidebarItemData(false, "../../../var/mobile/Library/Wallpapers")));
|
||||
wallpapersItem->setData(0, Qt::UserRole + 1,
|
||||
QVariant::fromValue(false)); // Default AFC
|
||||
wallpapersItem->setData(0, Qt::UserRole,
|
||||
"../../../var/mobile/Library/Wallpapers");
|
||||
|
||||
// Favorite Places section
|
||||
m_favoritePlacesItem = new QTreeWidgetItem(m_sidebarTree);
|
||||
m_favoritePlacesItem->setText(0, "Favorite Places");
|
||||
m_favoritePlacesItem->setIcon(0, QIcon::fromTheme("user-bookmarks"));
|
||||
m_favoritePlacesItem->setData(
|
||||
// todo:implement
|
||||
0, Qt::UserRole, QVariant::fromValue(SidebarItemData(false, "/")));
|
||||
m_favoritePlacesItem->setExpanded(true);
|
||||
|
||||
loadFavoritePlaces();
|
||||
|
||||
// connect(m_sidebarTree, &QTreeWidget::itemClicked, this,
|
||||
// &FileExplorerWidget::onSidebarItemClicked);
|
||||
connect(m_sidebarTree, &QTreeWidget::itemClicked, this,
|
||||
&FileExplorerWidget::onSidebarItemClicked);
|
||||
}
|
||||
|
||||
void FileExplorerWidget::loadFavoritePlaces()
|
||||
@@ -128,9 +116,18 @@ void FileExplorerWidget::loadFavoritePlaces()
|
||||
new QTreeWidgetItem(m_favoritePlacesItem);
|
||||
favoriteItem->setText(0, alias);
|
||||
favoriteItem->setIcon(0, QIcon::fromTheme("folder-favorites"));
|
||||
favoriteItem->setData(
|
||||
0, Qt::UserRole, QVariant::fromValue(SidebarItemData(false, path)));
|
||||
favoriteItem->setData(0, Qt::UserRole + 1,
|
||||
QVariant::fromValue(false)); // Default to AFC
|
||||
favoriteItem->setData(0, Qt::UserRole, QVariant::fromValue(path));
|
||||
}
|
||||
}
|
||||
|
||||
void FileExplorerWidget::onSidebarItemClicked(QTreeWidgetItem *item, int column)
|
||||
{
|
||||
Q_UNUSED(column);
|
||||
|
||||
if (item == m_defaultAfcItem) {
|
||||
m_stackedWidget->setCurrentIndex(0);
|
||||
} else if (item == m_jailbrokenAfcItem) {
|
||||
m_stackedWidget->setCurrentIndex(1);
|
||||
}
|
||||
// TODO: implement favorite places
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <QPushButton>
|
||||
#include <QSplitter>
|
||||
#include <QStack>
|
||||
#include <QStackedWidget>
|
||||
#include <QString>
|
||||
#include <QTreeWidget>
|
||||
#include <QVBoxLayout>
|
||||
@@ -23,17 +24,19 @@ public:
|
||||
explicit FileExplorerWidget(iDescriptorDevice *device,
|
||||
QWidget *parent = nullptr);
|
||||
|
||||
private slots:
|
||||
void onSidebarItemClicked(QTreeWidgetItem *item, int column);
|
||||
|
||||
private:
|
||||
QSplitter *m_mainSplitter;
|
||||
QStackedWidget *m_stackedWidget;
|
||||
afc_client_t currentAfcClient;
|
||||
QTreeWidget *m_sidebarTree;
|
||||
iDescriptorDevice *device;
|
||||
bool usingAFC2;
|
||||
iDescriptorDevice *m_device;
|
||||
|
||||
// Tree items
|
||||
QTreeWidgetItem *m_afcDefaultItem;
|
||||
QTreeWidgetItem *m_afcJailbrokenItem;
|
||||
QTreeWidgetItem *m_commonPlacesItem;
|
||||
QTreeWidgetItem *m_defaultAfcItem;
|
||||
QTreeWidgetItem *m_jailbrokenAfcItem;
|
||||
QTreeWidgetItem *m_favoritePlacesItem;
|
||||
|
||||
void setupSidebar();
|
||||
|
||||
@@ -17,52 +17,6 @@
|
||||
#include <QVBoxLayout>
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
|
||||
// todo: create a service
|
||||
QByteArray read_afc_file_to_byte_array(afc_client_t afcClient, const char *path)
|
||||
{
|
||||
uint64_t fd_handle = 0;
|
||||
afc_error_t fd_err =
|
||||
afc_file_open(afcClient, path, AFC_FOPEN_RDONLY, &fd_handle);
|
||||
|
||||
if (fd_err != AFC_E_SUCCESS) {
|
||||
qDebug() << "Could not open file" << path;
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
// TODO: is this necessary
|
||||
char **info = NULL;
|
||||
afc_get_file_info(afcClient, path, &info);
|
||||
uint64_t fileSize = 0;
|
||||
if (info) {
|
||||
for (int i = 0; info[i]; i += 2) {
|
||||
if (strcmp(info[i], "st_size") == 0) {
|
||||
fileSize = std::stoull(info[i + 1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
afc_dictionary_free(info);
|
||||
}
|
||||
|
||||
if (fileSize == 0) {
|
||||
afc_file_close(afcClient, fd_handle);
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
QByteArray buffer;
|
||||
|
||||
buffer.resize(fileSize);
|
||||
uint32_t bytesRead = 0;
|
||||
afc_file_read(afcClient, fd_handle, buffer.data(), buffer.size(),
|
||||
&bytesRead);
|
||||
|
||||
if (bytesRead != fileSize) {
|
||||
qDebug() << "AFC Error: Read mismatch for file" << path;
|
||||
return QByteArray(); // Read failed
|
||||
}
|
||||
|
||||
return buffer;
|
||||
};
|
||||
|
||||
void GalleryWidget::load()
|
||||
{
|
||||
if (m_loaded)
|
||||
|
||||
@@ -123,6 +123,7 @@ public:
|
||||
: QSplitter(orientation, parent)
|
||||
{
|
||||
setHandleWidth(10);
|
||||
setCursor(Qt::SplitHCursor);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
+13
-83
@@ -140,10 +140,6 @@ struct iDescriptorDevice {
|
||||
idevice_connection_type conn_type;
|
||||
idevice_t device;
|
||||
DeviceInfo deviceInfo;
|
||||
/*
|
||||
inital afc client to start the file explorer and gallery with
|
||||
clients are not long lived, so do not assume this will be valid
|
||||
*/
|
||||
afc_client_t afcClient;
|
||||
afc_client_t afc2Client;
|
||||
bool is_iPhone;
|
||||
@@ -158,85 +154,12 @@ struct IDescriptorInitDeviceResult {
|
||||
afc_client_t afc2Client;
|
||||
};
|
||||
|
||||
// Device model identifier to marketing name mapping
|
||||
const std::unordered_map<std::string, std::string> DEVICE_MAP = {
|
||||
{"iPhone1,1", "iPhone 2G"},
|
||||
{"iPhone1,2", "iPhone 3G"},
|
||||
{"iPhone2,1", "iPhone 3GS"},
|
||||
{"iPhone3,1", "iPhone 4 (GSM)"},
|
||||
{"iPhone3,2", "iPhone 4 (GSM Rev A)"},
|
||||
{"iPhone3,3", "iPhone 4 (CDMA)"},
|
||||
{"iPhone4,1", "iPhone 4S"},
|
||||
{"iPhone5,1", "iPhone 5 (GSM)"},
|
||||
{"iPhone5,2", "iPhone 5 (GSM+CDMA)"},
|
||||
{"iPhone5,3", "iPhone 5c (GSM)"},
|
||||
{"iPhone5,4", "iPhone 5c (GSM+CDMA)"},
|
||||
{"iPhone6,1", "iPhone 5s (GSM)"},
|
||||
{"iPhone6,2", "iPhone 5s (GSM+CDMA)"},
|
||||
{"iPhone7,1", "iPhone 6 Plus"},
|
||||
{"iPhone7,2", "iPhone 6"},
|
||||
{"iPhone8,1", "iPhone 6s"},
|
||||
{"iPhone8,2", "iPhone 6s Plus"},
|
||||
{"iPhone8,4", "iPhone SE (1st generation)"},
|
||||
{"iPhone9,1", "iPhone 7 (GSM)"},
|
||||
{"iPhone9,2", "iPhone 7 Plus (GSM)"},
|
||||
{"iPhone9,3", "iPhone 7 (GSM+CDMA)"},
|
||||
{"iPhone9,4", "iPhone 7 Plus (GSM+CDMA)"},
|
||||
{"iPhone10,1", "iPhone 8 (GSM)"},
|
||||
{"iPhone10,2", "iPhone 8 Plus (GSM)"},
|
||||
{"iPhone10,3", "iPhone X (GSM)"},
|
||||
{"iPhone10,4", "iPhone 8 (GSM+CDMA)"},
|
||||
{"iPhone10,5", "iPhone 8 Plus (GSM+CDMA)"},
|
||||
{"iPhone10,6", "iPhone X (GSM+CDMA)"},
|
||||
{"iPhone11,2", "iPhone XS"},
|
||||
{"iPhone11,4", "iPhone XS Max"},
|
||||
{"iPhone11,6", "iPhone XS Max (China)"},
|
||||
{"iPhone11,8", "iPhone XR"},
|
||||
{"iPhone12,1", "iPhone 11"},
|
||||
{"iPhone12,3", "iPhone 11 Pro"},
|
||||
{"iPhone12,5", "iPhone 11 Pro Max"},
|
||||
{"iPhone12,8", "iPhone SE (2nd generation)"},
|
||||
{"iPhone13,1", "iPhone 12 mini"},
|
||||
{"iPhone13,2", "iPhone 12"},
|
||||
{"iPhone13,3", "iPhone 12 Pro"},
|
||||
{"iPhone13,4", "iPhone 12 Pro Max"},
|
||||
{"iPhone14,4", "iPhone 13 mini"},
|
||||
{"iPhone14,5", "iPhone 13"},
|
||||
{"iPhone14,2", "iPhone 13 Pro"},
|
||||
{"iPhone14,3", "iPhone 13 Pro Max"},
|
||||
{"iPhone14,6", "iPhone SE (3rd generation)"},
|
||||
{"iPhone15,2", "iPhone 14 Pro"},
|
||||
{"iPhone15,3", "iPhone 14 Pro Max"},
|
||||
{"iPad1,1", "iPad 1st generation"},
|
||||
{"iPad2,1", "iPad 2 (WiFi)"},
|
||||
{"iPad2,2", "iPad 2 (GSM)"},
|
||||
{"iPad2,3", "iPad 2 (CDMA)"},
|
||||
{"iPad2,4", "iPad 2 (Rev A)"},
|
||||
{"iPad3,1", "iPad 3rd generation (WiFi)"},
|
||||
{"iPad3,2", "iPad 3rd generation (GSM)"},
|
||||
};
|
||||
|
||||
struct RecoveryDeviceInfo : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
RecoveryDeviceInfo(const irecv_device_event_t *event,
|
||||
QObject *parent = nullptr)
|
||||
: QObject(parent)
|
||||
{
|
||||
if (event && event->device_info) {
|
||||
ecid = event->device_info->ecid;
|
||||
mode = event->mode;
|
||||
cpid = event->device_info->cpid;
|
||||
bdid = event->device_info->bdid;
|
||||
}
|
||||
}
|
||||
struct iDescriptorRecoveryDevice {
|
||||
uint64_t ecid;
|
||||
irecv_mode mode;
|
||||
uint32_t cpid;
|
||||
uint32_t bdid;
|
||||
QString product;
|
||||
QString model;
|
||||
QString board_id;
|
||||
const char *displayName;
|
||||
};
|
||||
|
||||
struct TakeScreenshotResult {
|
||||
@@ -247,8 +170,10 @@ struct TakeScreenshotResult {
|
||||
struct IDescriptorInitDeviceResultRecovery {
|
||||
irecv_client_t client = nullptr;
|
||||
irecv_device_info deviceInfo;
|
||||
irecv_error_t error;
|
||||
bool success = false;
|
||||
irecv_mode mode = IRECV_K_RECOVERY_MODE_1;
|
||||
const char *displayName = nullptr;
|
||||
};
|
||||
|
||||
void warn(const QString &message, const QString &title = "Warning",
|
||||
@@ -335,14 +260,14 @@ struct MediaEntry {
|
||||
bool isDir;
|
||||
};
|
||||
|
||||
struct MediaFileTree {
|
||||
struct AFCFileTree {
|
||||
std::vector<MediaEntry> entries;
|
||||
bool success;
|
||||
std::string currentPath;
|
||||
};
|
||||
|
||||
MediaFileTree get_file_tree(afc_client_t afcClient, idevice_t device,
|
||||
const std::string &path = "/");
|
||||
AFCFileTree get_file_tree(afc_client_t afcClient, idevice_t device,
|
||||
const std::string &path = "/");
|
||||
|
||||
bool detect_jailbroken(afc_client_t afc);
|
||||
|
||||
@@ -353,7 +278,7 @@ void get_device_info_xml(const char *udid, int use_network, int simple,
|
||||
IDescriptorInitDeviceResult init_idescriptor_device(const char *udid);
|
||||
|
||||
IDescriptorInitDeviceResultRecovery
|
||||
init_idescriptor_recovery_device(irecv_device_info *info);
|
||||
init_idescriptor_recovery_device(uint64_t ecid);
|
||||
|
||||
bool set_location(idevice_t device, char *lat, char *lon);
|
||||
|
||||
@@ -467,3 +392,8 @@ struct NetworkDevice {
|
||||
return name == other.name && address == other.address;
|
||||
}
|
||||
};
|
||||
|
||||
QPixmap load_heic(const QByteArray &data);
|
||||
|
||||
QByteArray read_afc_file_to_byte_array(afc_client_t afcClient,
|
||||
const char *path);
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
#include "infolabel.h"
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QMouseEvent>
|
||||
|
||||
InfoLabel::InfoLabel(const QString &text, QWidget *parent)
|
||||
: QLabel(text, parent), m_originalText(text)
|
||||
{
|
||||
setCursor(Qt::PointingHandCursor);
|
||||
setStyleSheet("QLabel:hover { background-color: rgba(255, 255, 255, 0.1); "
|
||||
"border-radius: 2px; }");
|
||||
|
||||
m_restoreTimer = new QTimer(this);
|
||||
m_restoreTimer->setSingleShot(true);
|
||||
connect(m_restoreTimer, &QTimer::timeout, this,
|
||||
&InfoLabel::restoreOriginalText);
|
||||
}
|
||||
|
||||
void InfoLabel::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
QClipboard *clipboard = QApplication::clipboard();
|
||||
clipboard->setText(m_originalText);
|
||||
|
||||
setText("Copied!");
|
||||
setStyleSheet("QLabel { color: #4CAF50; font-weight: bold; } "
|
||||
"QLabel:hover { background-color: rgba(255, 255, 255, "
|
||||
"0.1); border-radius: 2px; }");
|
||||
|
||||
m_restoreTimer->start(1000); // Show "Copied!" for 1 second
|
||||
}
|
||||
QLabel::mousePressEvent(event);
|
||||
}
|
||||
|
||||
void InfoLabel::enterEvent(QEnterEvent *event)
|
||||
{
|
||||
if (!m_restoreTimer->isActive()) {
|
||||
setStyleSheet("QLabel:hover { background-color: rgba(255, 255, 255, "
|
||||
"0.1); border-radius: 2px; }");
|
||||
}
|
||||
QLabel::enterEvent(event);
|
||||
}
|
||||
|
||||
void InfoLabel::leaveEvent(QEvent *event)
|
||||
{
|
||||
if (!m_restoreTimer->isActive()) {
|
||||
setStyleSheet("QLabel:hover { background-color: rgba(255, 255, 255, "
|
||||
"0.1); border-radius: 2px; }");
|
||||
}
|
||||
QLabel::leaveEvent(event);
|
||||
}
|
||||
|
||||
void InfoLabel::restoreOriginalText()
|
||||
{
|
||||
setText(m_originalText);
|
||||
setStyleSheet("QLabel:hover { background-color: rgba(255, 255, 255, 0.1); "
|
||||
"border-radius: 2px; }");
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef INFOLABEL_H
|
||||
#define INFOLABEL_H
|
||||
|
||||
#include <QLabel>
|
||||
#include <QTimer>
|
||||
|
||||
class InfoLabel : public QLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit InfoLabel(const QString &text = QString(),
|
||||
QWidget *parent = nullptr);
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void enterEvent(QEnterEvent *event);
|
||||
void leaveEvent(QEvent *event) override;
|
||||
|
||||
private slots:
|
||||
void restoreOriginalText();
|
||||
|
||||
private:
|
||||
QString m_originalText;
|
||||
QTimer *m_restoreTimer;
|
||||
};
|
||||
|
||||
#endif // INFOLABEL_H
|
||||
+10
-14
@@ -1,5 +1,6 @@
|
||||
#include "jailbrokenwidget.h"
|
||||
#include "appcontext.h"
|
||||
#include "responsiveqlabel.h"
|
||||
#include "sshterminalwidget.h"
|
||||
|
||||
#ifdef __linux__
|
||||
@@ -12,8 +13,6 @@
|
||||
#include "iDescriptor.h"
|
||||
#include <QButtonGroup>
|
||||
#include <QDebug>
|
||||
#include <QGraphicsPixmapItem>
|
||||
#include <QGraphicsView>
|
||||
#include <QGroupBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
@@ -29,18 +28,15 @@ JailbrokenWidget::JailbrokenWidget(QWidget *parent) : QWidget{parent}
|
||||
mainLayout->setContentsMargins(2, 2, 2, 2);
|
||||
mainLayout->setSpacing(2);
|
||||
|
||||
QGraphicsScene *scene = new QGraphicsScene(this);
|
||||
QGraphicsPixmapItem *pixmapItem =
|
||||
new QGraphicsPixmapItem(QPixmap(":/resources/iphone.png"));
|
||||
scene->addItem(pixmapItem);
|
||||
// Create responsive image label
|
||||
ResponsiveQLabel *deviceImageLabel = new ResponsiveQLabel(this);
|
||||
deviceImageLabel->setPixmap(QPixmap(":/resources/iphone.png"));
|
||||
deviceImageLabel->setMinimumWidth(200);
|
||||
deviceImageLabel->setSizePolicy(QSizePolicy::Ignored,
|
||||
QSizePolicy::Expanding);
|
||||
deviceImageLabel->setStyleSheet("background: transparent; border: none;");
|
||||
|
||||
QGraphicsView *graphicsView = new ResponsiveGraphicsView(scene, this);
|
||||
graphicsView->setRenderHint(QPainter::Antialiasing);
|
||||
graphicsView->setMinimumWidth(200);
|
||||
graphicsView->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Expanding);
|
||||
graphicsView->setStyleSheet("background:transparent; border: none;");
|
||||
|
||||
mainLayout->addWidget(graphicsView, 1);
|
||||
mainLayout->addWidget(deviceImageLabel, 1);
|
||||
|
||||
// Connect to AppContext for device events
|
||||
connect(AppContext::sharedInstance(), &AppContext::deviceAdded, this,
|
||||
@@ -48,7 +44,7 @@ JailbrokenWidget::JailbrokenWidget(QWidget *parent) : QWidget{parent}
|
||||
connect(AppContext::sharedInstance(), &AppContext::deviceRemoved, this,
|
||||
&JailbrokenWidget::onWiredDeviceRemoved);
|
||||
|
||||
#ifdef __linux____
|
||||
#ifdef __linux__
|
||||
m_wirelessProvider = new AvahiService(this);
|
||||
connect(m_wirelessProvider, &AvahiService::deviceAdded, this,
|
||||
&JailbrokenWidget::onWirelessDeviceAdded);
|
||||
|
||||
+6
-9
@@ -84,18 +84,15 @@ void handleCallbackRecovery(const irecv_device_event_t *event, void *userData)
|
||||
switch (event->type) {
|
||||
case IRECV_DEVICE_ADD:
|
||||
qDebug() << "Recovery device added: ";
|
||||
// TODO: handle recovery device addition
|
||||
QMetaObject::invokeMethod(
|
||||
AppContext::sharedInstance(), "addRecoveryDevice",
|
||||
Qt::QueuedConnection,
|
||||
Q_ARG(RecoveryDeviceInfo *, new RecoveryDeviceInfo(event)));
|
||||
QMetaObject::invokeMethod(AppContext::sharedInstance(),
|
||||
"addRecoveryDevice", Qt::QueuedConnection,
|
||||
Q_ARG(uint64_t, event->device_info->ecid));
|
||||
break;
|
||||
case IRECV_DEVICE_REMOVE:
|
||||
qDebug() << "Recovery device removed: ";
|
||||
QMetaObject::invokeMethod(
|
||||
AppContext::sharedInstance(), "removeRecoveryDevice",
|
||||
Qt::QueuedConnection,
|
||||
Q_ARG(QString, QString::number(event->device_info->ecid)));
|
||||
QMetaObject::invokeMethod(AppContext::sharedInstance(),
|
||||
"removeRecoveryDevice", Qt::QueuedConnection,
|
||||
Q_ARG(uint64_t, event->device_info->ecid));
|
||||
break;
|
||||
default:
|
||||
printf("Unhandled recovery event: %d\n", event->type);
|
||||
|
||||
@@ -52,8 +52,8 @@ MediaPreviewDialog::MediaPreviewDialog(iDescriptorDevice *device,
|
||||
resize(screenSize);
|
||||
|
||||
// Add window transparency
|
||||
setAttribute(Qt::WA_TranslucentBackground);
|
||||
setWindowOpacity(0.99);
|
||||
// looks way too bad on linux - maybe enable only on macOS and Windows
|
||||
// setAttribute(Qt::WA_TranslucentBackground);
|
||||
|
||||
setupUI();
|
||||
loadMedia();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "mediastreamer.h"
|
||||
#include <QtGlobal>
|
||||
|
||||
#include "iDescriptor.h"
|
||||
#include <QDebug>
|
||||
#include <QFileInfo>
|
||||
#include <QHostAddress>
|
||||
@@ -10,10 +11,6 @@
|
||||
#include <libimobiledevice/afc.h>
|
||||
#include <memory>
|
||||
|
||||
// Forward declare AFC helper function
|
||||
QByteArray read_afc_file_to_byte_array(afc_client_t afcClient,
|
||||
const char *path);
|
||||
|
||||
MediaStreamer::MediaStreamer(iDescriptorDevice *device, afc_client_t afcClient,
|
||||
const QString &filePath, QObject *parent)
|
||||
: QTcpServer(parent), m_device(device), m_afcClient(afcClient),
|
||||
|
||||
+14
-3
@@ -1,5 +1,6 @@
|
||||
|
||||
#include "photomodel.h"
|
||||
#include "iDescriptor.h"
|
||||
#include "mediastreamermanager.h"
|
||||
#include <QDebug>
|
||||
#include <QEventLoop>
|
||||
@@ -14,9 +15,6 @@
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
#include <algorithm>
|
||||
|
||||
// Forward declare your helper function
|
||||
QByteArray read_afc_file_to_byte_array(afc_client_t afcClient,
|
||||
const char *path);
|
||||
PhotoModel::PhotoModel(iDescriptorDevice *device, QObject *parent)
|
||||
: QAbstractListModel(parent), m_device(device), m_thumbnailSize(256, 256),
|
||||
m_sortOrder(NewestFirst), m_filterType(All)
|
||||
@@ -343,6 +341,12 @@ QPixmap PhotoModel::loadThumbnailFromDevice(iDescriptorDevice *device,
|
||||
return QPixmap(); // Return empty pixmap on error
|
||||
}
|
||||
|
||||
if (filePath.endsWith(".HEIC")) {
|
||||
qDebug() << "Loading HEIC image from data for:" << filePath;
|
||||
QPixmap img = load_heic(imageData);
|
||||
return img.isNull() ? QPixmap() : img;
|
||||
}
|
||||
|
||||
// Load pixmap from data
|
||||
QPixmap original;
|
||||
if (!original.loadFromData(imageData)) {
|
||||
@@ -384,6 +388,13 @@ QPixmap PhotoModel::loadImage(iDescriptorDevice *device,
|
||||
return QPixmap(); // Return empty pixmap on error
|
||||
}
|
||||
|
||||
if (filePath.endsWith(".HEIC")) {
|
||||
qDebug() << "Loading HEIC image from data for:" << filePath;
|
||||
QPixmap img = load_heic(imageData);
|
||||
return img.isNull() ? QPixmap() : img;
|
||||
}
|
||||
|
||||
// TODO
|
||||
// Load pixmap from data
|
||||
QPixmap original;
|
||||
if (!original.loadFromData(imageData)) {
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
#include "libirecovery.h"
|
||||
#include <QDebug>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
RecoveryDeviceInfoWidget::RecoveryDeviceInfoWidget(RecoveryDeviceInfo *info,
|
||||
QWidget *parent)
|
||||
RecoveryDeviceInfoWidget::RecoveryDeviceInfoWidget(
|
||||
const iDescriptorRecoveryDevice *info, QWidget *parent)
|
||||
: QWidget{parent}
|
||||
{
|
||||
ecid = info->ecid; // Assuming ecid is unique for each device
|
||||
@@ -22,13 +23,13 @@ RecoveryDeviceInfoWidget::RecoveryDeviceInfoWidget(RecoveryDeviceInfo *info,
|
||||
QString::fromStdString(parse_recovery_mode(info->mode))));
|
||||
devLayout->addWidget(new QLabel("ECID: " + QString::number(info->ecid)));
|
||||
devLayout->addWidget(new QLabel("CPID: " + QString::number(info->cpid)));
|
||||
devLayout->addWidget(new QLabel("BDID: " + QString::number(info->bdid)));
|
||||
devLayout->addWidget(new QLabel(info->displayName));
|
||||
|
||||
QPushButton *exitRecoveyMode = new QPushButton("Exit Recovery Mode");
|
||||
connect(exitRecoveyMode, &QPushButton::clicked, this, [this, info]() {
|
||||
irecv_client_t client = NULL;
|
||||
irecv_error_t ierr =
|
||||
irecv_open_with_ecid_and_attempts(&client, info->ecid, 3);
|
||||
irecv_error_t ierr = irecv_open_with_ecid_and_attempts(
|
||||
&client, info->ecid, RECOVERY_CLIENT_CONNECTION_TRIES);
|
||||
irecv_error_t error = IRECV_E_SUCCESS;
|
||||
if (ierr != IRECV_E_SUCCESS) {
|
||||
printf("Failed to open device with ECID %llu: %s\n", info->ecid,
|
||||
@@ -45,12 +46,17 @@ RecoveryDeviceInfoWidget::RecoveryDeviceInfoWidget(RecoveryDeviceInfo *info,
|
||||
|
||||
error = irecv_setenv(client, "auto-boot", "true");
|
||||
if (error != IRECV_E_SUCCESS) {
|
||||
QMessageBox::critical(
|
||||
this, "Error",
|
||||
"Failed to set environment variable 'auto-boot' to 'true'");
|
||||
qDebug() << "Failed to set environment variable: "
|
||||
<< irecv_strerror(error);
|
||||
}
|
||||
|
||||
error = irecv_saveenv(client);
|
||||
if (error != IRECV_E_SUCCESS) {
|
||||
QMessageBox::critical(this, "Error",
|
||||
"Failed to save environment variables");
|
||||
qDebug() << "Failed to save environment variables: "
|
||||
<< irecv_strerror(error);
|
||||
}
|
||||
@@ -58,13 +64,13 @@ RecoveryDeviceInfoWidget::RecoveryDeviceInfoWidget(RecoveryDeviceInfo *info,
|
||||
error = irecv_reboot(client);
|
||||
if (error != IRECV_E_SUCCESS) {
|
||||
// debug("%s\n", irecv_strerror(error));
|
||||
qDebug() << "Failed to send reboot command: "
|
||||
QMessageBox::critical(this, "Error",
|
||||
"Failed to send reboot command");
|
||||
qDebug() << "critical to send reboot command: "
|
||||
<< irecv_strerror(error);
|
||||
} else {
|
||||
// debug("%s\n", irecv_strerror(error));
|
||||
}
|
||||
|
||||
irecv_close(client);
|
||||
});
|
||||
devLayout->addWidget(exitRecoveyMode);
|
||||
devLayout->addWidget(exitRecoveyMode, 0, Qt::AlignCenter);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ class RecoveryDeviceInfoWidget : public QWidget
|
||||
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit RecoveryDeviceInfoWidget(RecoveryDeviceInfo *info,
|
||||
explicit RecoveryDeviceInfoWidget(const iDescriptorRecoveryDevice *info,
|
||||
QWidget *parent = nullptr);
|
||||
uint64_t ecid; // Assuming ecid is unique for each device
|
||||
signals:
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
#include "responsiveqlabel.h"
|
||||
#include <QDebug>
|
||||
#include <QPainter>
|
||||
|
||||
ResponsiveQLabel::ResponsiveQLabel(QWidget *parent) : QLabel(parent)
|
||||
{
|
||||
setAlignment(Qt::AlignCenter);
|
||||
setScaledContents(false);
|
||||
setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Expanding);
|
||||
setMinimumSize(100, 100);
|
||||
}
|
||||
|
||||
void ResponsiveQLabel::setPixmap(const QPixmap &pixmap)
|
||||
{
|
||||
m_originalPixmap = pixmap;
|
||||
updateScaledPixmap();
|
||||
}
|
||||
|
||||
void ResponsiveQLabel::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
QLabel::resizeEvent(event);
|
||||
if (!m_originalPixmap.isNull()) {
|
||||
updateScaledPixmap();
|
||||
}
|
||||
}
|
||||
|
||||
void ResponsiveQLabel::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
if (m_scaledPixmap.isNull()) {
|
||||
QLabel::paintEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
QPainter painter(this);
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
painter.setRenderHint(QPainter::SmoothPixmapTransform);
|
||||
|
||||
// Calculate position to center the pixmap
|
||||
int x = (width() - m_scaledPixmap.width()) / 2;
|
||||
int y = (height() - m_scaledPixmap.height()) / 2;
|
||||
|
||||
painter.drawPixmap(x, y, m_scaledPixmap);
|
||||
}
|
||||
|
||||
void ResponsiveQLabel::updateScaledPixmap()
|
||||
{
|
||||
if (m_originalPixmap.isNull() || size().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Scale the pixmap while maintaining aspect ratio
|
||||
m_scaledPixmap = m_originalPixmap.scaled(size(), Qt::KeepAspectRatio,
|
||||
Qt::SmoothTransformation);
|
||||
|
||||
update();
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef RESPONSIVEQLABEL_H
|
||||
#define RESPONSIVEQLABEL_H
|
||||
|
||||
#include <QLabel>
|
||||
#include <QPixmap>
|
||||
#include <QResizeEvent>
|
||||
|
||||
class ResponsiveQLabel : public QLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ResponsiveQLabel(QWidget *parent = nullptr);
|
||||
void setPixmap(const QPixmap &pixmap);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
private:
|
||||
void updateScaledPixmap();
|
||||
|
||||
QPixmap m_originalPixmap;
|
||||
QPixmap m_scaledPixmap;
|
||||
};
|
||||
|
||||
#endif // RESPONSIVEQLABEL_H
|
||||
Reference in New Issue
Block a user