diff --git a/src/afcexplorerwidget.cpp b/src/afcexplorerwidget.cpp new file mode 100644 index 0000000..46ac91e --- /dev/null +++ b/src/afcexplorerwidget.cpp @@ -0,0 +1,516 @@ +#include "afcexplorerwidget.h" +#include "iDescriptor.h" +#include "mediapreviewdialog.h" +#include "settingsmanager.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +AfcExplorerWidget::AfcExplorerWidget(afc_client_t afcClient, + std::function onClientInvalidCb, + iDescriptorDevice *device, QWidget *parent) + : QWidget(parent), m_currentAfcClient(afcClient), m_device(device) +{ + // Initialize current AFC client to default + m_currentAfcClient = afcClient; + + // Setup file explorer + setupFileExplorer(); + + // Main layout + QHBoxLayout *mainLayout = new QHBoxLayout(this); + setLayout(mainLayout); + mainLayout->addWidget(m_explorer); + + // Initialize + m_history.push("/"); + loadPath("/"); + + setupContextMenu(); + connect(SettingsManager::sharedInstance(), + &SettingsManager::favoritePlacesChanged, this, + &AfcExplorerWidget::refreshFavoritePlaces); +} + +void AfcExplorerWidget::goBack() +{ + if (m_history.size() > 1) { + m_history.pop(); + QString prevPath = m_history.top(); + loadPath(prevPath); + } +} + +void AfcExplorerWidget::onItemDoubleClicked(QListWidgetItem *item) +{ + QVariant data = item->data(Qt::UserRole); + bool isDir = data.toBool(); + QString name = item->text(); + + // Use breadcrumb to get current path + QString currPath = "/"; + if (!m_history.isEmpty()) + currPath = m_history.top(); + + if (!currPath.endsWith("/")) + currPath += "/"; + QString nextPath = currPath == "/" ? "/" + name : currPath + name; + + if (isDir) { + m_history.push(nextPath); + loadPath(nextPath); + } else { + auto *previewDialog = new MediaPreviewDialog(m_device, nextPath, this); + previewDialog->setAttribute(Qt::WA_DeleteOnClose); + previewDialog->show(); + // TODO: we need this ? + emit fileSelected(nextPath); + } +} + +void AfcExplorerWidget::onBreadcrumbClicked() +{ + QPushButton *btn = qobject_cast(sender()); + if (!btn) + return; + QString path = btn->property("fullPath").toString(); + // pathLabel removed, compare with m_history.top() + if (!m_history.isEmpty() && path == m_history.top()) + return; + m_history.push(path); + loadPath(path); +} + +void AfcExplorerWidget::updateBreadcrumb(const QString &path) +{ + // Remove old breadcrumb buttons + QLayoutItem *child; + while ((child = m_breadcrumbLayout->takeAt(0)) != nullptr) { + if (child->widget()) { + child->widget()->deleteLater(); + } + delete child; + } + + QStringList parts = path.split("/", Qt::SkipEmptyParts); + QString currPath = ""; + int idx = 0; + // Add root + QPushButton *rootBtn = new QPushButton("/"); + rootBtn->setFlat(true); + rootBtn->setProperty("fullPath", "/"); + connect(rootBtn, &QPushButton::clicked, this, + &AfcExplorerWidget::onBreadcrumbClicked); + m_breadcrumbLayout->addWidget(rootBtn); + + for (const QString &part : parts) { + currPath += part; + if (idx > 0) { + QLabel *sep = new QLabel(" / "); + m_breadcrumbLayout->addWidget(sep); + } + + QPushButton *btn = new QPushButton(part); + btn->setFlat(true); + btn->setProperty("fullPath", currPath); + connect(btn, &QPushButton::clicked, this, + &AfcExplorerWidget::onBreadcrumbClicked); + m_breadcrumbLayout->addWidget(btn); + idx++; + } + m_breadcrumbLayout->addStretch(); +} + +void AfcExplorerWidget::loadPath(const QString &path) +{ + m_fileList->clear(); + // pathLabel->setText(path); // removed + + updateBreadcrumb(path); + + MediaFileTree tree = + get_file_tree(m_currentAfcClient, m_device->device, path.toStdString()); + if (!tree.success) { + m_fileList->addItem("Failed to load directory"); + return; + } + + for (const auto &entry : tree.entries) { + QListWidgetItem *item = + new QListWidgetItem(QString::fromStdString(entry.name)); + item->setData(Qt::UserRole, entry.isDir); + if (entry.isDir) + item->setIcon(QIcon::fromTheme("folder")); + else + item->setIcon(QIcon::fromTheme("text-x-generic")); + m_fileList->addItem(item); + } +} + +void AfcExplorerWidget::setupContextMenu() +{ + m_fileList->setContextMenuPolicy(Qt::CustomContextMenu); + connect(m_fileList, &QListWidget::customContextMenuRequested, this, + &AfcExplorerWidget::onFileListContextMenu); +} + +void AfcExplorerWidget::onFileListContextMenu(const QPoint &pos) +{ + QListWidgetItem *item = m_fileList->itemAt(pos); + if (!item) + return; + + bool isDir = item->data(Qt::UserRole).toBool(); + if (isDir) + return; // Only export files + + QMenu menu; + QAction *exportAction = menu.addAction("Export"); + QAction *selectedAction = + menu.exec(m_fileList->viewport()->mapToGlobal(pos)); + if (selectedAction == exportAction) { + QList selectedItems = m_fileList->selectedItems(); + QList filesToExport; + if (selectedItems.isEmpty()) + filesToExport.append(item); // fallback: just the clicked one + else { + for (QListWidgetItem *selItem : selectedItems) { + if (!selItem->data(Qt::UserRole).toBool()) + filesToExport.append(selItem); + } + } + if (filesToExport.isEmpty()) + return; + QString dir = + QFileDialog::getExistingDirectory(this, "Select Export Directory"); + if (dir.isEmpty()) + return; + for (QListWidgetItem *selItem : filesToExport) { + exportSelectedFile(selItem, dir); + } + } +} + +void AfcExplorerWidget::onExportClicked() +{ + QList selectedItems = m_fileList->selectedItems(); + if (selectedItems.isEmpty()) + return; + + // Only files (not directories) + QList filesToExport; + for (QListWidgetItem *item : selectedItems) { + if (!item->data(Qt::UserRole).toBool()) + filesToExport.append(item); + } + if (filesToExport.isEmpty()) + return; + + // Ask user for a directory to save all files + QString dir = + QFileDialog::getExistingDirectory(this, "Select Export Directory"); + if (dir.isEmpty()) + return; + + for (QListWidgetItem *item : filesToExport) { + exportSelectedFile(item, dir); + } +} + +void AfcExplorerWidget::onExportDeleteClicked() +{ + // Placeholder for future implementation + QList selectedItems = m_fileList->selectedItems(); + if (selectedItems.isEmpty()) + return; + // TODO: Implement export & delete logic + return; +} + +void AfcExplorerWidget::exportSelectedFile(QListWidgetItem *item, + const QString &directory) +{ + QString fileName = item->text(); + QString currPath = "/"; + if (!m_history.isEmpty()) + currPath = m_history.top(); + if (!currPath.endsWith("/")) + currPath += "/"; + qDebug() << "Current path:" << currPath; + QString devicePath = currPath == "/" ? "/" + fileName : currPath + fileName; + qDebug() << "Exporting file:" << devicePath; + + // Save to selected directory + QString savePath = directory + "/" + fileName; + + // Get device info and check connections + qDebug() << "Using device index:" << m_device->udid.c_str(); + qDebug() << "Device UDID:" << QString::fromStdString(m_device->udid); + qDebug() << "Device Product Type:" + << QString::fromStdString(m_device->deviceInfo.productType); + + // Export file using the validated connections + int result = export_file_to_path(m_currentAfcClient, + devicePath.toStdString().c_str(), + savePath.toStdString().c_str()); + + qDebug() << "Export result:" << result; + + if (result == 0) { + qDebug() << "Exported" << devicePath << "to" << savePath; + + QMessageBox::StandardButton reply; + reply = QMessageBox::question( + this, "Export Successful", + "File exported successfully. Would you like to see the directory?", + QMessageBox::Yes | QMessageBox::No); + if (reply == QMessageBox::Yes) { + QDesktopServices::openUrl(QUrl::fromLocalFile(directory)); + } + } else { + qDebug() << "Failed to export" << devicePath; + QMessageBox::warning(this, "Export Failed", + "Failed to export the file from the device"); + } +} + +// Helper function to export to a specific local path +int AfcExplorerWidget::export_file_to_path(afc_client_t afc, + const char *device_path, + 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; + return -1; + } + FILE *out = fopen(local_path, "wb"); + if (!out) { + qDebug() << "Failed to open local file:" << local_path; + afc_file_close(afc, handle); + return -1; + } + + 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) { + fwrite(buffer, 1, bytes_read, out); + } + + fclose(out); + // TODO: implement safe_afc_file_close + afc_file_close(afc, handle); + return 0; +} + +void AfcExplorerWidget::onImportClicked() +{ + // TODO: check devices + + // Select one or more files to import + QStringList fileNames = QFileDialog::getOpenFileNames(this, "Import Files"); + if (fileNames.isEmpty()) + return; + + // Use current breadcrumb directory as target + QString currPath = "/"; + if (!m_history.isEmpty()) + currPath = m_history.top(); + if (!currPath.endsWith("/")) + currPath += "/"; + + // if (!device || !client || !serviceDesc) + // { + // qDebug() << "Failed to connect to device or lockdown service"; + // return; + // } + + // Import each file + for (const QString &localPath : fileNames) { + QFileInfo fi(localPath); + QString devicePath = currPath + fi.fileName(); + int result = import_file_to_device(m_currentAfcClient, + devicePath.toStdString().c_str(), + localPath.toStdString().c_str()); + if (result == 0) + qDebug() << "Imported" << localPath << "to" << devicePath; + else + qDebug() << "Failed to import" << localPath; + } + + // Refresh file list + loadPath(currPath); +} + +// Helper function to import a file from a local path to the device +int AfcExplorerWidget::import_file_to_device(afc_client_t afc, + const char *device_path, + const char *local_path) +{ + QFile in(local_path); + if (!in.open(QIODevice::ReadOnly)) { + qDebug() << "Failed to open local file for import:" << local_path; + return -1; + } + + uint64_t handle = 0; + if (afc_file_open(afc, device_path, AFC_FOPEN_WRONLY, &handle) != + AFC_E_SUCCESS) { + qDebug() << "Failed to open file on device for writing:" << device_path; + return -1; + } + + char buffer[4096]; + qint64 bytesRead; + while ((bytesRead = in.read(buffer, sizeof(buffer))) > 0) { + uint32_t bytesWritten = 0; + if (afc_file_write(afc, handle, buffer, + static_cast(bytesRead), + &bytesWritten) != AFC_E_SUCCESS || + bytesWritten != bytesRead) { + qDebug() << "Failed to write to device file:" << device_path; + afc_file_close(afc, handle); + in.close(); + return -1; + } + } + + afc_file_close(afc, handle); + in.close(); + return 0; +} + +// useAFC2 ,path, +typedef QPair SidebarItemData; + +void AfcExplorerWidget::setupFileExplorer() +{ + m_explorer = new QWidget(); + QVBoxLayout *explorerLayout = new QVBoxLayout(m_explorer); + + // Export/Import buttons layout + QHBoxLayout *exportLayout = new QHBoxLayout(); + m_exportBtn = new QPushButton("Export"); + m_exportDeleteBtn = new QPushButton("Export & Delete"); + m_importBtn = new QPushButton("Import"); + m_addToFavoritesBtn = new QPushButton("Add to Favorites"); + exportLayout->addWidget(m_exportBtn); + exportLayout->addWidget(m_exportDeleteBtn); + exportLayout->addWidget(m_importBtn); + exportLayout->addWidget(m_addToFavoritesBtn); + exportLayout->addStretch(); + explorerLayout->addLayout(exportLayout); + + // Navigation layout (Back + Breadcrumb) + QHBoxLayout *navLayout = new QHBoxLayout(); + m_backBtn = new QPushButton("Back"); + m_breadcrumbLayout = new QHBoxLayout(); + m_breadcrumbLayout->setSpacing(0); + navLayout->addWidget(m_backBtn); + navLayout->addLayout(m_breadcrumbLayout); + navLayout->addStretch(); + explorerLayout->addLayout(navLayout); + + // File list + m_fileList = new QListWidget(); + m_fileList->setSelectionMode(QAbstractItemView::ExtendedSelection); + explorerLayout->addWidget(m_fileList); + + // Connect buttons + connect(m_backBtn, &QPushButton::clicked, this, &AfcExplorerWidget::goBack); + connect(m_fileList, &QListWidget::itemDoubleClicked, this, + &AfcExplorerWidget::onItemDoubleClicked); + connect(m_exportBtn, &QPushButton::clicked, this, + &AfcExplorerWidget::onExportClicked); + connect(m_exportDeleteBtn, &QPushButton::clicked, this, + &AfcExplorerWidget::onExportDeleteClicked); + connect(m_importBtn, &QPushButton::clicked, this, + &AfcExplorerWidget::onImportClicked); + connect(m_addToFavoritesBtn, &QPushButton::clicked, this, + &AfcExplorerWidget::onAddToFavoritesClicked); +} + +void AfcExplorerWidget::onSidebarItemClicked(QTreeWidgetItem *item, int column) +{ + Q_UNUSED(column) + + bool useAfc2 = item->data(0, Qt::UserRole).value().first; + QString path = item->data(0, Qt::UserRole).value().second; + + // if (itemType == "try_install_afc2") { + // onTryInstallAFC2Clicked(); + // return; + // } + + switchToAFC(useAfc2); + loadPath(path); +} + +void AfcExplorerWidget::onAddToFavoritesClicked() +{ + QString currentPath = "/"; + if (!m_history.isEmpty()) + currentPath = m_history.top(); + + bool ok; + QString alias = QInputDialog::getText( + this, "Add to Favorites", + "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; + } +} + +void AfcExplorerWidget::saveFavoritePlace(const QString &path, + const QString &alias) +{ + qDebug() << "Saving favorite place:" << alias << "->" << 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(); +} diff --git a/src/afcexplorerwidget.h b/src/afcexplorerwidget.h new file mode 100644 index 0000000..b06903e --- /dev/null +++ b/src/afcexplorerwidget.h @@ -0,0 +1,74 @@ +#ifndef AFCEXPLORER_H +#define AFCEXPLORER_H + +#include "iDescriptor.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class AfcExplorerWidget : public QWidget +{ + Q_OBJECT +public: + explicit AfcExplorerWidget( + afc_client_t afcClient = nullptr, + std::function onClientInvalidCb = nullptr, + iDescriptorDevice *device = nullptr, QWidget *parent = nullptr); +signals: + void fileSelected(const QString &filePath); + +private slots: + void goBack(); + void onItemDoubleClicked(QListWidgetItem *item); + void onBreadcrumbClicked(); + void onFileListContextMenu(const QPoint &pos); + void onExportClicked(); + void onExportDeleteClicked(); + void onImportClicked(); + void onSidebarItemClicked(QTreeWidgetItem *item, int column); + void onAddToFavoritesClicked(); + void onTryInstallAFC2Clicked(); + +private: + QWidget *m_explorer; + QPushButton *m_backBtn; + QPushButton *m_exportBtn; + QPushButton *m_exportDeleteBtn; + QPushButton *m_importBtn; + QPushButton *m_addToFavoritesBtn; + QListWidget *m_fileList; + QStack m_history; + QHBoxLayout *m_breadcrumbLayout; + 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); + void exportSelectedFile(QListWidgetItem *item, const QString &directory); + int export_file_to_path(afc_client_t afc, const char *device_path, + const char *local_path); + int import_file_to_device(afc_client_t afc, const char *device_path, + const char *local_path); +}; + +#endif // AFCEXPLORER_H diff --git a/src/fileexplorerwidget.cpp b/src/fileexplorerwidget.cpp index 0689237..368abe3 100644 --- a/src/fileexplorerwidget.cpp +++ b/src/fileexplorerwidget.cpp @@ -1,4 +1,5 @@ #include "fileexplorerwidget.h" +#include "afcexplorerwidget.h" #include "iDescriptor.h" #include "mediapreviewdialog.h" #include "settingsmanager.h" @@ -23,410 +24,41 @@ FileExplorerWidget::FileExplorerWidget(iDescriptorDevice *device, QWidget *parent) : QWidget(parent), device(device), usingAFC2(false) { - // Initialize current AFC client to default - currentAfcClient = device->afcClient; - qDebug() << "AFC2 available:" << (device->afc2Client != nullptr); - // Create main splitter - mainSplitter = new QSplitter(Qt::Horizontal, this); - - // Setup sidebar - setupSidebar(); - - // Setup file explorer - setupFileExplorer(); - - // Add widgets to splitter - mainSplitter->addWidget(sidebarTree); - mainSplitter->addWidget(fileExplorerWidget); - mainSplitter->setSizes({400, 800}); + m_mainSplitter = new QSplitter(Qt::Horizontal, this); // Main layout QHBoxLayout *mainLayout = new QHBoxLayout(this); - mainLayout->addWidget(mainSplitter); + mainLayout->addWidget(m_mainSplitter); + + setupSidebar(); + + // Add widgets to splitter + m_mainSplitter->addWidget(m_sidebarTree); + m_mainSplitter->addWidget( + new AfcExplorerWidget(device->afcClient, nullptr, device)); + m_mainSplitter->setSizes({400, 800}); setLayout(mainLayout); - - // Initialize - history.push("/"); - loadPath("/"); - - setupContextMenu(); - connect(SettingsManager::sharedInstance(), - &SettingsManager::favoritePlacesChanged, this, - &FileExplorerWidget::refreshFavoritePlaces); } - -void FileExplorerWidget::goBack() -{ - if (history.size() > 1) { - history.pop(); - QString prevPath = history.top(); - loadPath(prevPath); - } -} - -void FileExplorerWidget::onItemDoubleClicked(QListWidgetItem *item) -{ - QVariant data = item->data(Qt::UserRole); - bool isDir = data.toBool(); - QString name = item->text(); - - // Use breadcrumb to get current path - QString currPath = "/"; - if (!history.isEmpty()) - currPath = history.top(); - - if (!currPath.endsWith("/")) - currPath += "/"; - QString nextPath = currPath == "/" ? "/" + name : currPath + name; - - if (isDir) { - history.push(nextPath); - loadPath(nextPath); - } else { - auto *previewDialog = new MediaPreviewDialog(device, nextPath, this); - previewDialog->setAttribute(Qt::WA_DeleteOnClose); - previewDialog->show(); - // TODO: we need this ? - emit fileSelected(nextPath); - } -} - -void FileExplorerWidget::onBreadcrumbClicked() -{ - QPushButton *btn = qobject_cast(sender()); - if (!btn) - return; - QString path = btn->property("fullPath").toString(); - // pathLabel removed, compare with history.top() - if (!history.isEmpty() && path == history.top()) - return; - history.push(path); - loadPath(path); -} - -void FileExplorerWidget::updateBreadcrumb(const QString &path) -{ - // Remove old breadcrumb buttons - QLayoutItem *child; - while ((child = breadcrumbLayout->takeAt(0)) != nullptr) { - if (child->widget()) { - child->widget()->deleteLater(); - } - delete child; - } - - QStringList parts = path.split("/", Qt::SkipEmptyParts); - QString currPath = ""; - int idx = 0; - // Add root - QPushButton *rootBtn = new QPushButton("/"); - rootBtn->setFlat(true); - rootBtn->setProperty("fullPath", "/"); - connect(rootBtn, &QPushButton::clicked, this, - &FileExplorerWidget::onBreadcrumbClicked); - breadcrumbLayout->addWidget(rootBtn); - - for (const QString &part : parts) { - currPath += part; - if (idx > 0) { - QLabel *sep = new QLabel(" / "); - breadcrumbLayout->addWidget(sep); - } - - QPushButton *btn = new QPushButton(part); - btn->setFlat(true); - btn->setProperty("fullPath", currPath); - connect(btn, &QPushButton::clicked, this, - &FileExplorerWidget::onBreadcrumbClicked); - breadcrumbLayout->addWidget(btn); - idx++; - } - breadcrumbLayout->addStretch(); -} - -void FileExplorerWidget::loadPath(const QString &path) -{ - fileList->clear(); - // pathLabel->setText(path); // removed - - updateBreadcrumb(path); - - MediaFileTree tree = - get_file_tree(currentAfcClient, device->device, path.toStdString()); - if (!tree.success) { - fileList->addItem("Failed to load directory"); - return; - } - - for (const auto &entry : tree.entries) { - QListWidgetItem *item = - new QListWidgetItem(QString::fromStdString(entry.name)); - item->setData(Qt::UserRole, entry.isDir); - if (entry.isDir) - item->setIcon(QIcon::fromTheme("folder")); - else - item->setIcon(QIcon::fromTheme("text-x-generic")); - fileList->addItem(item); - } -} - -void FileExplorerWidget::setupContextMenu() -{ - fileList->setContextMenuPolicy(Qt::CustomContextMenu); - connect(fileList, &QListWidget::customContextMenuRequested, this, - &FileExplorerWidget::onFileListContextMenu); -} - -void FileExplorerWidget::onFileListContextMenu(const QPoint &pos) -{ - QListWidgetItem *item = fileList->itemAt(pos); - if (!item) - return; - - bool isDir = item->data(Qt::UserRole).toBool(); - if (isDir) - return; // Only export files - - QMenu menu; - QAction *exportAction = menu.addAction("Export"); - QAction *selectedAction = menu.exec(fileList->viewport()->mapToGlobal(pos)); - if (selectedAction == exportAction) { - QList selectedItems = fileList->selectedItems(); - QList filesToExport; - if (selectedItems.isEmpty()) - filesToExport.append(item); // fallback: just the clicked one - else { - for (QListWidgetItem *selItem : selectedItems) { - if (!selItem->data(Qt::UserRole).toBool()) - filesToExport.append(selItem); - } - } - if (filesToExport.isEmpty()) - return; - QString dir = - QFileDialog::getExistingDirectory(this, "Select Export Directory"); - if (dir.isEmpty()) - return; - for (QListWidgetItem *selItem : filesToExport) { - exportSelectedFile(selItem, dir); - } - } -} - -void FileExplorerWidget::onExportClicked() -{ - QList selectedItems = fileList->selectedItems(); - if (selectedItems.isEmpty()) - return; - - // Only files (not directories) - QList filesToExport; - for (QListWidgetItem *item : selectedItems) { - if (!item->data(Qt::UserRole).toBool()) - filesToExport.append(item); - } - if (filesToExport.isEmpty()) - return; - - // Ask user for a directory to save all files - QString dir = - QFileDialog::getExistingDirectory(this, "Select Export Directory"); - if (dir.isEmpty()) - return; - - for (QListWidgetItem *item : filesToExport) { - exportSelectedFile(item, dir); - } -} - -void FileExplorerWidget::onExportDeleteClicked() -{ - // Placeholder for future implementation - QList selectedItems = fileList->selectedItems(); - if (selectedItems.isEmpty()) - return; - // TODO: Implement export & delete logic - return; -} - -void FileExplorerWidget::exportSelectedFile(QListWidgetItem *item, - const QString &directory) -{ - QString fileName = item->text(); - QString currPath = "/"; - if (!history.isEmpty()) - currPath = history.top(); - if (!currPath.endsWith("/")) - currPath += "/"; - qDebug() << "Current path:" << currPath; - QString devicePath = currPath == "/" ? "/" + fileName : currPath + fileName; - qDebug() << "Exporting file:" << devicePath; - - // Save to selected directory - QString savePath = directory + "/" + fileName; - - // Get device info and check connections - qDebug() << "Using device index:" << device->udid.c_str(); - qDebug() << "Device UDID:" << QString::fromStdString(device->udid); - qDebug() << "Device Product Type:" - << QString::fromStdString(device->deviceInfo.productType); - - // Export file using the validated connections - int result = - export_file_to_path(currentAfcClient, devicePath.toStdString().c_str(), - savePath.toStdString().c_str()); - - qDebug() << "Export result:" << result; - - if (result == 0) { - qDebug() << "Exported" << devicePath << "to" << savePath; - - QMessageBox::StandardButton reply; - reply = QMessageBox::question( - this, "Export Successful", - "File exported successfully. Would you like to see the directory?", - QMessageBox::Yes | QMessageBox::No); - if (reply == QMessageBox::Yes) { - QDesktopServices::openUrl(QUrl::fromLocalFile(directory)); - } - } else { - qDebug() << "Failed to export" << devicePath; - QMessageBox::warning(this, "Export Failed", - "Failed to export the file from the device"); - } -} - -// Helper function to export to a specific local path -int FileExplorerWidget::export_file_to_path(afc_client_t afc, - const char *device_path, - 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; - return -1; - } - FILE *out = fopen(local_path, "wb"); - if (!out) { - qDebug() << "Failed to open local file:" << local_path; - afc_file_close(afc, handle); - return -1; - } - - 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) { - fwrite(buffer, 1, bytes_read, out); - } - - fclose(out); - // TODO: implement safe_afc_file_close - afc_file_close(afc, handle); - return 0; -} - -void FileExplorerWidget::onImportClicked() -{ - // TODO: check devices - - // Select one or more files to import - QStringList fileNames = QFileDialog::getOpenFileNames(this, "Import Files"); - if (fileNames.isEmpty()) - return; - - // Use current breadcrumb directory as target - QString currPath = "/"; - if (!history.isEmpty()) - currPath = history.top(); - if (!currPath.endsWith("/")) - currPath += "/"; - - // if (!device || !client || !serviceDesc) - // { - // qDebug() << "Failed to connect to device or lockdown service"; - // return; - // } - - // Import each file - for (const QString &localPath : fileNames) { - QFileInfo fi(localPath); - QString devicePath = currPath + fi.fileName(); - int result = import_file_to_device(currentAfcClient, - devicePath.toStdString().c_str(), - localPath.toStdString().c_str()); - if (result == 0) - qDebug() << "Imported" << localPath << "to" << devicePath; - else - qDebug() << "Failed to import" << localPath; - } - - // Refresh file list - loadPath(currPath); -} - -// Helper function to import a file from a local path to the device -int FileExplorerWidget::import_file_to_device(afc_client_t afc, - const char *device_path, - const char *local_path) -{ - QFile in(local_path); - if (!in.open(QIODevice::ReadOnly)) { - qDebug() << "Failed to open local file for import:" << local_path; - return -1; - } - - uint64_t handle = 0; - if (afc_file_open(afc, device_path, AFC_FOPEN_WRONLY, &handle) != - AFC_E_SUCCESS) { - qDebug() << "Failed to open file on device for writing:" << device_path; - return -1; - } - - char buffer[4096]; - qint64 bytesRead; - while ((bytesRead = in.read(buffer, sizeof(buffer))) > 0) { - uint32_t bytesWritten = 0; - if (afc_file_write(afc, handle, buffer, - static_cast(bytesRead), - &bytesWritten) != AFC_E_SUCCESS || - bytesWritten != bytesRead) { - qDebug() << "Failed to write to device file:" << device_path; - afc_file_close(afc, handle); - in.close(); - return -1; - } - } - - afc_file_close(afc, handle); - in.close(); - return 0; -} - // useAFC2 ,path, typedef QPair SidebarItemData; void FileExplorerWidget::setupSidebar() { - sidebarTree = new QTreeWidget(); - sidebarTree->setHeaderLabel("Files"); - sidebarTree->setMinimumWidth(350); - sidebarTree->setMaximumWidth(400); + m_sidebarTree = new QTreeWidget(); + m_sidebarTree->setHeaderLabel("Files"); + m_sidebarTree->setMinimumWidth(350); + m_sidebarTree->setMaximumWidth(400); // AFC Default section - afcDefaultItem = new QTreeWidgetItem(sidebarTree); - afcDefaultItem->setText(0, "Explorer"); - afcDefaultItem->setIcon(0, QIcon::fromTheme("folder")); - afcDefaultItem->setData(0, Qt::UserRole, - QVariant::fromValue(SidebarItemData(false, "/"))); - afcDefaultItem->setExpanded(true); + 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); // Add root folder under Default - QTreeWidgetItem *rootItem = new QTreeWidgetItem(afcDefaultItem); + QTreeWidgetItem *rootItem = new QTreeWidgetItem(m_afcDefaultItem); rootItem->setText(0, "Default"); rootItem->setIcon(0, QIcon::fromTheme("folder")); rootItem->setData(0, Qt::UserRole, @@ -434,24 +66,24 @@ void FileExplorerWidget::setupSidebar() rootItem->setData(0, Qt::UserRole + 1, QVariant::fromValue(false)); // AFC2 Jailbroken section - afcJailbrokenItem = new QTreeWidgetItem(afcDefaultItem); - afcJailbrokenItem->setText(0, "Jailbroken (AFC2)"); - afcJailbrokenItem->setIcon(0, QIcon::fromTheme("applications-system")); - afcJailbrokenItem->setData(0, Qt::UserRole, - QVariant::fromValue(SidebarItemData(true, "/"))); - afcJailbrokenItem->setExpanded(false); + 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); // Common Places section - commonPlacesItem = new QTreeWidgetItem(sidebarTree); - commonPlacesItem->setText(0, "Common Places"); - commonPlacesItem->setIcon(0, QIcon::fromTheme("places-bookmarks")); - commonPlacesItem->setData( + 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"))); - commonPlacesItem->setExpanded(true); + m_commonPlacesItem->setExpanded(true); - QTreeWidgetItem *wallpapersItem = new QTreeWidgetItem(commonPlacesItem); + QTreeWidgetItem *wallpapersItem = new QTreeWidgetItem(m_commonPlacesItem); wallpapersItem->setText(0, "Wallpapers"); wallpapersItem->setIcon(0, QIcon::fromTheme("image-x-generic")); wallpapersItem->setData( @@ -462,113 +94,18 @@ void FileExplorerWidget::setupSidebar() QVariant::fromValue(false)); // Default AFC // Favorite Places section - favoritePlacesItem = new QTreeWidgetItem(sidebarTree); - favoritePlacesItem->setText(0, "Favorite Places"); - favoritePlacesItem->setIcon(0, QIcon::fromTheme("user-bookmarks")); - favoritePlacesItem->setData( + 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, "/"))); - favoritePlacesItem->setExpanded(true); + m_favoritePlacesItem->setExpanded(true); loadFavoritePlaces(); - connect(sidebarTree, &QTreeWidget::itemClicked, this, - &FileExplorerWidget::onSidebarItemClicked); -} - -void FileExplorerWidget::setupFileExplorer() -{ - fileExplorerWidget = new QWidget(); - QVBoxLayout *explorerLayout = new QVBoxLayout(fileExplorerWidget); - - // Export/Import buttons layout - QHBoxLayout *exportLayout = new QHBoxLayout(); - exportBtn = new QPushButton("Export"); - exportDeleteBtn = new QPushButton("Export & Delete"); - importBtn = new QPushButton("Import"); - addToFavoritesBtn = new QPushButton("Add to Favorites"); - exportLayout->addWidget(exportBtn); - exportLayout->addWidget(exportDeleteBtn); - exportLayout->addWidget(importBtn); - exportLayout->addWidget(addToFavoritesBtn); - exportLayout->addStretch(); - explorerLayout->addLayout(exportLayout); - - // Navigation layout (Back + Breadcrumb) - QHBoxLayout *navLayout = new QHBoxLayout(); - backBtn = new QPushButton("Back"); - breadcrumbLayout = new QHBoxLayout(); - breadcrumbLayout->setSpacing(0); - navLayout->addWidget(backBtn); - navLayout->addLayout(breadcrumbLayout); - navLayout->addStretch(); - explorerLayout->addLayout(navLayout); - - // File list - fileList = new QListWidget(); - fileList->setSelectionMode(QAbstractItemView::ExtendedSelection); - explorerLayout->addWidget(fileList); - - // Connect buttons - connect(backBtn, &QPushButton::clicked, this, &FileExplorerWidget::goBack); - connect(fileList, &QListWidget::itemDoubleClicked, this, - &FileExplorerWidget::onItemDoubleClicked); - connect(exportBtn, &QPushButton::clicked, this, - &FileExplorerWidget::onExportClicked); - connect(exportDeleteBtn, &QPushButton::clicked, this, - &FileExplorerWidget::onExportDeleteClicked); - connect(importBtn, &QPushButton::clicked, this, - &FileExplorerWidget::onImportClicked); - connect(addToFavoritesBtn, &QPushButton::clicked, this, - &FileExplorerWidget::onAddToFavoritesClicked); -} - -void FileExplorerWidget::onSidebarItemClicked(QTreeWidgetItem *item, int column) -{ - Q_UNUSED(column) - - bool useAfc2 = item->data(0, Qt::UserRole).value().first; - QString path = item->data(0, Qt::UserRole).value().second; - - // if (itemType == "try_install_afc2") { - // onTryInstallAFC2Clicked(); - // return; - // } - - switchToAFC(useAfc2); - loadPath(path); -} - -void FileExplorerWidget::onAddToFavoritesClicked() -{ - QString currentPath = "/"; - if (!history.isEmpty()) - currentPath = history.top(); - - bool ok; - QString alias = QInputDialog::getText( - this, "Add to Favorites", - "Enter alias for this location:", QLineEdit::Normal, currentPath, &ok); - if (ok && !alias.isEmpty()) { - saveFavoritePlace(currentPath, alias); - refreshFavoritePlaces(); - } -} - -void FileExplorerWidget::onTryInstallAFC2Clicked() -{ - qDebug() << "Clicked on try to install AFC2"; -} - -void FileExplorerWidget::switchToAFC(bool useAFC2) -{ - if (useAFC2 && device->afc2Client) { - usingAFC2 = true; - currentAfcClient = device->afc2Client; - } else { - usingAFC2 = false; - currentAfcClient = device->afcClient; - } + // connect(m_sidebarTree, &QTreeWidget::itemClicked, this, + // &FileExplorerWidget::onSidebarItemClicked); } void FileExplorerWidget::loadFavoritePlaces() @@ -581,7 +118,8 @@ void FileExplorerWidget::loadFavoritePlaces() QString alias = favorite.second; qDebug() << "Favorite:" << alias << "->" << path; - QTreeWidgetItem *favoriteItem = new QTreeWidgetItem(favoritePlacesItem); + QTreeWidgetItem *favoriteItem = + new QTreeWidgetItem(m_favoritePlacesItem); favoriteItem->setText(0, alias); favoriteItem->setIcon(0, QIcon::fromTheme("folder-favorites")); favoriteItem->setData( @@ -589,23 +127,4 @@ void FileExplorerWidget::loadFavoritePlaces() favoriteItem->setData(0, Qt::UserRole + 1, QVariant::fromValue(false)); // Default to AFC } -} - -void FileExplorerWidget::saveFavoritePlace(const QString &path, - const QString &alias) -{ - qDebug() << "Saving favorite place:" << alias << "->" << path; - SettingsManager *settings = SettingsManager::sharedInstance(); - settings->saveFavoritePlace(path, alias); -} - -void FileExplorerWidget::refreshFavoritePlaces() -{ - // Clear existing favorite items - while (favoritePlacesItem->childCount() > 0) { - delete favoritePlacesItem->takeChild(0); - } - - // Reload favorite places - loadFavoritePlaces(); -} +} \ No newline at end of file diff --git a/src/fileexplorerwidget.h b/src/fileexplorerwidget.h index c345af6..826b927 100644 --- a/src/fileexplorerwidget.h +++ b/src/fileexplorerwidget.h @@ -23,61 +23,21 @@ public: explicit FileExplorerWidget(iDescriptorDevice *device, QWidget *parent = nullptr); -signals: - void fileSelected(const QString &filePath); - -private slots: - void goBack(); - void onItemDoubleClicked(QListWidgetItem *item); - void onBreadcrumbClicked(); - void onFileListContextMenu(const QPoint &pos); - void onExportClicked(); - void onExportDeleteClicked(); - void onImportClicked(); - void onSidebarItemClicked(QTreeWidgetItem *item, int column); - void onAddToFavoritesClicked(); - void onTryInstallAFC2Clicked(); - private: - QSplitter *mainSplitter; - QTreeWidget *sidebarTree; - QWidget *fileExplorerWidget; - QPushButton *backBtn; - QPushButton *exportBtn; - QPushButton *exportDeleteBtn; - QPushButton *importBtn; - QPushButton *addToFavoritesBtn; - QListWidget *fileList; - QStack history; - QHBoxLayout *breadcrumbLayout; - iDescriptorDevice *device; - - // Current AFC mode - bool usingAFC2; + QSplitter *m_mainSplitter; afc_client_t currentAfcClient; + QTreeWidget *m_sidebarTree; + iDescriptorDevice *device; + bool usingAFC2; // Tree items - QTreeWidgetItem *afcDefaultItem; - QTreeWidgetItem *afcJailbrokenItem; - QTreeWidgetItem *commonPlacesItem; - QTreeWidgetItem *favoritePlacesItem; + QTreeWidgetItem *m_afcDefaultItem; + QTreeWidgetItem *m_afcJailbrokenItem; + QTreeWidgetItem *m_commonPlacesItem; + QTreeWidgetItem *m_favoritePlacesItem; void setupSidebar(); - void setupFileExplorer(); - void loadPath(const QString &path); - void updateBreadcrumb(const QString &path); void loadFavoritePlaces(); - void saveFavoritePlace(const QString &path, const QString &alias); - void refreshFavoritePlaces(); - void switchToAFC(bool useAFC2); - - void setupContextMenu(); - void exportSelectedFile(QListWidgetItem *item); - void exportSelectedFile(QListWidgetItem *item, const QString &directory); - int export_file_to_path(afc_client_t afc, const char *device_path, - const char *local_path); - int import_file_to_device(afc_client_t afc, const char *device_path, - const char *local_path); }; #endif // FILEEXPLORERWIDGET_H diff --git a/src/installedappswidget.cpp b/src/installedappswidget.cpp index 90bf3fe..3ea0cfa 100644 --- a/src/installedappswidget.cpp +++ b/src/installedappswidget.cpp @@ -1,4 +1,5 @@ #include "installedappswidget.h" +#include "afcexplorerwidget.h" #include "iDescriptor.h" #include #include @@ -207,6 +208,17 @@ void InstalledAppsWidget::setupUI() searchAction->setToolTip("Search"); searchLayout->addWidget(m_searchEdit); + + // Add checkbox for file sharing filter + m_fileSharingCheckBox = new QCheckBox("File Sharing Enabled"); + m_fileSharingCheckBox->setChecked(true); // Default enabled + m_fileSharingCheckBox->setStyleSheet("QCheckBox { " + " font-size: 12px; " + " color: #333; " + " margin-left: 5px; " + "}"); + searchLayout->addWidget(m_fileSharingCheckBox); + tabFrameLayout->addWidget(searchContainer); // Add a separator line @@ -280,6 +292,10 @@ void InstalledAppsWidget::setupUI() connect(m_searchEdit, &QLineEdit::textChanged, this, &InstalledAppsWidget::filterApps); + // Connect file sharing filter + connect(m_fileSharingCheckBox, &QCheckBox::toggled, this, + &InstalledAppsWidget::onFileSharingFilterChanged); + showLoadingState(); } @@ -361,6 +377,8 @@ void InstalledAppsWidget::fetchInstalledApps() plist_new_string("CFBundleShortVersionString")); plist_array_append_item(return_attrs, plist_new_string("CFBundleVersion")); + plist_array_append_item( + return_attrs, plist_new_string("UIFileSharingEnabled")); plist_dict_set_item(client_opts, "ReturnAttributes", return_attrs); @@ -422,6 +440,21 @@ void InstalledAppsWidget::fetchInstalledApps() } } + // Get file sharing enabled status + plist_t file_sharing = plist_dict_get_item( + app_info, "UIFileSharingEnabled"); + if (file_sharing && + plist_get_node_type(file_sharing) == + PLIST_BOOLEAN) { + uint8_t file_sharing_enabled = 0; + plist_get_bool_val(file_sharing, + &file_sharing_enabled); + appData["fileSharingEnabled"] = + (file_sharing_enabled != 0); + } else { + appData["fileSharingEnabled"] = false; + } + appData["type"] = appType; if (!appData["bundleId"].toString().isEmpty()) { @@ -497,6 +530,13 @@ void InstalledAppsWidget::onAppsDataReady() QString bundleId = appData.value("bundleId").toString(); QString version = appData.value("version").toString(); QString appType = appData.value("type").toString(); + bool fileSharingEnabled = + appData.value("fileSharingEnabled", false).toBool(); + + // Filter by file sharing status if checkbox is checked + if (m_fileSharingCheckBox->isChecked() && !fileSharingEnabled) { + continue; + } if (displayName.isEmpty()) { displayName = bundleId; @@ -673,10 +713,10 @@ void InstalledAppsWidget::loadAppContainer(const QString &bundleId) lockdowndService = nullptr; // Send vendor container command - if (house_arrest_send_command(houseArrestClient, "VendContainer", + if (house_arrest_send_command(houseArrestClient, "VendDocuments", bundleId.toUtf8().constData()) != HOUSE_ARREST_E_SUCCESS) { - result["error"] = "Could not send VendContainer command"; + result["error"] = "Could not send VendDocuments command"; house_arrest_client_free(houseArrestClient); lockdownd_client_free(lockdownClient); return result; @@ -743,12 +783,17 @@ void InstalledAppsWidget::loadAppContainer(const QString &bundleId) } afc_dictionary_free(list); } - + qDebug() << "App container files:" << files; result["files"] = files; + result["afcClient"] = + QVariant::fromValue(reinterpret_cast(afcClient)); + result["houseArrestClient"] = QVariant::fromValue( + reinterpret_cast(houseArrestClient)); result["success"] = true; - afc_client_free(afcClient); - house_arrest_client_free(houseArrestClient); + // Don't free the clients here - they will be used by + // AfcExplorerWidget afc_client_free(afcClient); + // house_arrest_client_free(houseArrestClient); lockdownd_client_free(lockdownClient); } catch (const std::exception &e) { @@ -792,49 +837,38 @@ void InstalledAppsWidget::onContainerDataReady() return; } - QStringList files = result.value("files").toStringList(); - if (files.isEmpty()) { - QLabel *emptyLabel = new QLabel("App container is empty"); - emptyLabel->setStyleSheet("color: #999; font-style: italic;"); - m_containerLayout->addWidget(emptyLabel); + // Get the AFC clients from the result + afc_client_t afcClient = reinterpret_cast( + result.value("afcClient").value()); + house_arrest_client_t houseArrestClient = + reinterpret_cast( + result.value("houseArrestClient").value()); + + if (!afcClient) { + QLabel *errorLabel = + new QLabel("Failed to get AFC client for app container"); + errorLabel->setStyleSheet("color: #999; font-style: italic;"); + m_containerLayout->addWidget(errorLabel); return; } - // Sort files/directories - files.sort(); + // Create AfcExplorerWidget with the house arrest AFC client + AfcExplorerWidget *explorer = new AfcExplorerWidget( + afcClient, + [houseArrestClient]() { + // Cleanup callback when client becomes invalid + if (houseArrestClient) { + house_arrest_client_free(houseArrestClient); + } + }, + m_device, this); - // Add files/directories to the container view - for (const QString &fileName : files) { - QLabel *fileLabel = new QLabel(); - - // Determine if it's likely a directory (simple heuristic) - QString displayText = fileName; - QIcon icon; - if (!fileName.contains('.') || fileName.endsWith("/")) { - icon = this->style()->standardIcon(QStyle::SP_DirIcon); - displayText = "📁 " + fileName; - } else { - icon = this->style()->standardIcon(QStyle::SP_FileIcon); - displayText = "📄 " + fileName; - } - - fileLabel->setText(displayText); - fileLabel->setStyleSheet("QLabel { " - " padding: 4px 8px; " - " border: 1px solid transparent; " - " border-radius: 3px; " - " font-family: monospace; " - " font-size: 13px; " - "} " - "QLabel:hover { " - " background-color: #f0f0f0; " - " border: 1px solid #ddd; " - "}"); - fileLabel->setWordWrap(true); - - m_containerLayout->addWidget(fileLabel); - } - - // Add stretch to push items to top - m_containerLayout->addStretch(); + m_containerLayout->addWidget(explorer); +} + +void InstalledAppsWidget::onFileSharingFilterChanged(bool enabled) +{ + Q_UNUSED(enabled) + // Refresh the apps list when filter changes + fetchInstalledApps(); } diff --git a/src/installedappswidget.h b/src/installedappswidget.h index d7f24a9..ab8d211 100644 --- a/src/installedappswidget.h +++ b/src/installedappswidget.h @@ -2,6 +2,7 @@ #define INSTALLEDAPPSWIDGET_H #include "iDescriptor.h" +#include #include #include #include @@ -68,6 +69,7 @@ private slots: void onAppsDataReady(); void onAppTabClicked(); void onContainerDataReady(); + void onFileSharingFilterChanged(bool enabled); private: void setupUI(); @@ -83,6 +85,7 @@ private: iDescriptorDevice *m_device; QHBoxLayout *m_mainLayout; QLineEdit *m_searchEdit; + QCheckBox *m_fileSharingCheckBox; QScrollArea *m_tabScrollArea; QWidget *m_tabContainer; QVBoxLayout *m_tabLayout; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 2aef7a7..535353a 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -180,14 +180,14 @@ MainWindow::MainWindow(QWidget *parent) m_customTabWidget->addTab(jailbrokenWidget, "Jailbroken"); m_customTabWidget->finalizeStyles(); - connect( - m_customTabWidget, &CustomTabWidget::currentChanged, this, - [this, jailbrokenWidget](int index) { - if (index == 3) { // Jailbroken tab - jailbrokenWidget->initWidget(); - } - }, - Qt::SingleShotConnection); + // connect( + // m_customTabWidget, &CustomTabWidget::currentChanged, this, + // [this, jailbrokenWidget](int index) { + // if (index == 3) { // Jailbroken tab + // jailbrokenWidget->initWidget(); + // } + // }, + // Qt::SingleShotConnection); // settings button QPushButton *settingsButton = new QPushButton(); diff --git a/src/mediapreviewdialog.cpp b/src/mediapreviewdialog.cpp index dd3e406..195752f 100644 --- a/src/mediapreviewdialog.cpp +++ b/src/mediapreviewdialog.cpp @@ -24,7 +24,7 @@ #include #include #include - +// todo : need to pass afc as well MediaPreviewDialog::MediaPreviewDialog(iDescriptorDevice *device, const QString &filePath, QWidget *parent) : QDialog(parent), m_device(device), m_filePath(filePath), @@ -164,7 +164,14 @@ void MediaPreviewDialog::setupVideoView() &MediaPreviewDialog::onMediaPlayerPositionChanged); connect(m_mediaPlayer, &QMediaPlayer::playbackStateChanged, this, &MediaPreviewDialog::onMediaPlayerStateChanged); - + connect(m_mediaPlayer, &QMediaPlayer::errorOccurred, this, + [this](QMediaPlayer::Error error, const QString &errorString) { + qDebug() << "MediaPlayer Error:" << error << errorString; + m_statusLabel->setText("Error: " + errorString); + m_loadingLabel->setText("Error: " + errorString); + m_loadingLabel->show(); + m_videoWidget->hide(); + }); // Setup progress timer for smooth updates m_progressTimer = new QTimer(this); connect(m_progressTimer, &QTimer::timeout, this,