abstract file explorer logic into AfcExplorerWidget

This commit is contained in:
uncor3
2025-10-03 20:49:24 +00:00
parent 15a70c62ca
commit d8439c7591
8 changed files with 742 additions and 629 deletions
+516
View File
@@ -0,0 +1,516 @@
#include "afcexplorerwidget.h"
#include "iDescriptor.h"
#include "mediapreviewdialog.h"
#include "settingsmanager.h"
#include <QDebug>
#include <QDesktopServices>
#include <QFileDialog>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QIcon>
#include <QInputDialog>
#include <QMenu>
#include <QMessageBox>
#include <QPushButton>
#include <QSignalBlocker>
#include <QSplitter>
#include <QTreeWidget>
#include <QVariant>
#include <libimobiledevice/afc.h>
#include <libimobiledevice/libimobiledevice.h>
AfcExplorerWidget::AfcExplorerWidget(afc_client_t afcClient,
std::function<void()> 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<QPushButton *>(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<QListWidgetItem *> selectedItems = m_fileList->selectedItems();
QList<QListWidgetItem *> 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<QListWidgetItem *> selectedItems = m_fileList->selectedItems();
if (selectedItems.isEmpty())
return;
// Only files (not directories)
QList<QListWidgetItem *> 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<QListWidgetItem *> 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<uint32_t>(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<bool, QString> 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<SidebarItemData>().first;
QString path = item->data(0, Qt::UserRole).value<SidebarItemData>().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();
}
+74
View File
@@ -0,0 +1,74 @@
#ifndef AFCEXPLORER_H
#define AFCEXPLORER_H
#include "iDescriptor.h"
#include <QHBoxLayout>
#include <QInputDialog>
#include <QLabel>
#include <QListWidget>
#include <QMenu>
#include <QPushButton>
#include <QSplitter>
#include <QStack>
#include <QString>
#include <QTreeWidget>
#include <QVBoxLayout>
#include <QWidget>
#include <libimobiledevice/afc.h>
class AfcExplorerWidget : public QWidget
{
Q_OBJECT
public:
explicit AfcExplorerWidget(
afc_client_t afcClient = nullptr,
std::function<void()> 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<QString> 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
+44 -525
View File
@@ -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<QPushButton *>(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<QListWidgetItem *> selectedItems = fileList->selectedItems();
QList<QListWidgetItem *> 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<QListWidgetItem *> selectedItems = fileList->selectedItems();
if (selectedItems.isEmpty())
return;
// Only files (not directories)
QList<QListWidgetItem *> 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<QListWidgetItem *> 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<uint32_t>(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<bool, QString> 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<SidebarItemData>().first;
QString path = item->data(0, Qt::UserRole).value<SidebarItemData>().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();
}
}
+8 -48
View File
@@ -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<QString> 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
+80 -46
View File
@@ -1,4 +1,5 @@
#include "installedappswidget.h"
#include "afcexplorerwidget.h"
#include "iDescriptor.h"
#include <QAction>
#include <QApplication>
@@ -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<void *>(afcClient));
result["houseArrestClient"] = QVariant::fromValue(
reinterpret_cast<void *>(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<afc_client_t>(
result.value("afcClient").value<void *>());
house_arrest_client_t houseArrestClient =
reinterpret_cast<house_arrest_client_t>(
result.value("houseArrestClient").value<void *>());
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();
}
+3
View File
@@ -2,6 +2,7 @@
#define INSTALLEDAPPSWIDGET_H
#include "iDescriptor.h"
#include <QCheckBox>
#include <QEnterEvent>
#include <QFrame>
#include <QFutureWatcher>
@@ -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;
+8 -8
View File
@@ -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();
+9 -2
View File
@@ -24,7 +24,7 @@
#include <QWheelEvent>
#include <QtConcurrent/QtConcurrent>
#include <QtGlobal>
// 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,