From 2a86e3090d3bac782de5bd5fc0aba7247d69bd7d Mon Sep 17 00:00:00 2001 From: uncor3 Date: Mon, 3 Nov 2025 12:36:59 +0000 Subject: [PATCH] bug fixes - update app icon fetching to use QNetworkAccessManager - fix App icon label dangling pointer - check updates if autocheckupdates is enabled --- src/appdownloadbasedialog.cpp | 3 +- src/appdownloaddialog.cpp | 3 +- src/appinstalldialog.cpp | 11 +- src/appinstalldialog.h | 2 + src/appstoremanager.cpp | 23 ++-- src/appstoremanager.h | 2 +- src/appswidget.cpp | 106 ++++++++++-------- src/appswidget.h | 2 +- .../helpers/fetch_app_icon_from_apple.cpp | 20 +--- src/iDescriptor.h | 7 +- src/installedappswidget.cpp | 8 +- src/installedappswidget.h | 1 + src/mainwindow.cpp | 4 +- src/qprocessindicator.cpp | 9 +- src/qprocessindicator.h | 1 + src/sponsorappcard.cpp | 8 +- src/sponsorappcard.h | 5 +- 17 files changed, 118 insertions(+), 97 deletions(-) diff --git a/src/appdownloadbasedialog.cpp b/src/appdownloadbasedialog.cpp index e069d91..c858d6c 100644 --- a/src/appdownloadbasedialog.cpp +++ b/src/appdownloadbasedialog.cpp @@ -86,7 +86,8 @@ void AppDownloadBaseDialog::startDownloadProcess(const QString &bundleId, manager->downloadApp( bundleId, outputDir, "", acquireLicense, - [this, outputDir, promptToOpenDir](int result) { + [this, promptToOpenDir, outputDir](int result) { + m_operationInProgress = false; if (result == 0) { // Success emit downloadFinished(true, "Success"); m_progressBar->setValue(100); diff --git a/src/appdownloaddialog.cpp b/src/appdownloaddialog.cpp index baaa4db..ceea7b3 100644 --- a/src/appdownloaddialog.cpp +++ b/src/appdownloaddialog.cpp @@ -83,6 +83,7 @@ void AppDownloadDialog::onDownloadClicked() int buttonIndex = m_layout->indexOf(m_actionButton); layout()->removeWidget(m_actionButton); m_actionButton->deleteLater(); - + qDebug() << "Starting download to" << m_outputDir; + qDebug() << "Bundle ID:" << m_bundleId; startDownloadProcess(m_bundleId, m_outputDir, buttonIndex); } diff --git a/src/appinstalldialog.cpp b/src/appinstalldialog.cpp index 0f29c5c..ec46184 100644 --- a/src/appinstalldialog.cpp +++ b/src/appinstalldialog.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -24,14 +25,13 @@ AppInstallDialog::AppInstallDialog(const QString &appName, setWindowTitle("Install " + appName + " - iDescriptor"); setModal(true); setFixedWidth(500); - + m_manager = new QNetworkAccessManager(this); QVBoxLayout *layout = qobject_cast(this->layout()); // App info section QHBoxLayout *appInfoLayout = new QHBoxLayout(); QLabel *iconLabel = new QLabel(); - fetchAppIconFromApple( - bundleId, - [iconLabel](const QPixmap &pixmap) { + ::fetchAppIconFromApple( + m_manager, bundleId, [iconLabel](const QPixmap &pixmap) { if (!pixmap.isNull()) { QPixmap scaled = pixmap.scaled(64, 64, Qt::KeepAspectRatioByExpanding, @@ -49,8 +49,7 @@ AppInstallDialog::AppInstallDialog(const QString &appName, iconLabel->setPixmap(rounded); } - }, - this); + }); QPixmap icon = QApplication::style() ->standardIcon(QStyle::SP_ComputerIcon) .pixmap(64, 64); diff --git a/src/appinstalldialog.h b/src/appinstalldialog.h index cfcdaf6..79c3afd 100644 --- a/src/appinstalldialog.h +++ b/src/appinstalldialog.h @@ -7,6 +7,7 @@ #include #include #include +#include class AppInstallDialog : public AppDownloadBaseDialog { @@ -30,6 +31,7 @@ private: QLabel *m_statusLabel; QFutureWatcher *m_installWatcher; QTemporaryDir *m_tempDir = nullptr; + QNetworkAccessManager *m_manager = nullptr; void updateDeviceList(); void performInstallation(const QString &ipaPath, const QString &deviceUdid); }; diff --git a/src/appstoremanager.cpp b/src/appstoremanager.cpp index 3187d18..b97ddd2 100644 --- a/src/appstoremanager.cpp +++ b/src/appstoremanager.cpp @@ -160,8 +160,9 @@ void AppStoreManager::searchApps( } void AppStoreManager::downloadApp( - const QString &bundleId, const QString &outputDir, const QString &country, - bool acquireLicense, std::function callback, + const QString &bundleId, const QString &outputDir, + const QString &externalVersionId, bool acquireLicense, + std::function callback, std::function progressCallback) { if (!m_initialized) { @@ -189,15 +190,15 @@ void AppStoreManager::downloadApp( }; } - QFuture future = QtConcurrent::run([bundleId, outputDir, country, - acquireLicense, cProgressCallback, - progressUserData]() { - int result = IpaToolDownloadApp(bundleId.toUtf8().data(), - outputDir.toUtf8().data(), - country.toUtf8().data(), acquireLicense, - cProgressCallback, progressUserData); - return result; - }); + QFuture future = QtConcurrent::run( + [bundleId, outputDir, externalVersionId, acquireLicense, + cProgressCallback, progressUserData]() { + int result = IpaToolDownloadApp( + bundleId.toUtf8().data(), outputDir.toUtf8().data(), + externalVersionId.toUtf8().data(), acquireLicense, + cProgressCallback, progressUserData); + return result; + }); QFutureWatcher *watcher = new QFutureWatcher(this); connect( diff --git a/src/appstoremanager.h b/src/appstoremanager.h index 35b33ca..6e6bd34 100644 --- a/src/appstoremanager.h +++ b/src/appstoremanager.h @@ -25,7 +25,7 @@ public: const QString &searchTerm, int limit, std::function callback); void downloadApp(const QString &bundleId, const QString &outputDir, - const QString &country, bool acquireLicense, + const QString &externalVersionId, bool acquireLicense, std::function callback, std::function progressCallback = nullptr); diff --git a/src/appswidget.cpp b/src/appswidget.cpp index 0abd909..b8e2b55 100644 --- a/src/appswidget.cpp +++ b/src/appswidget.cpp @@ -118,11 +118,11 @@ QJsonObject getVersionedConfig(const QJsonObject &rootObj) } } -// watch for login and logout events +// TODO: watch for login and logout events AppsWidget *AppsWidget::sharedInstance() { - static AppsWidget instance; - return &instance; + static AppsWidget *instance = new AppsWidget(); + return instance; } AppsWidget::AppsWidget(QWidget *parent) : QWidget(parent), m_isLoggedIn(false) @@ -406,9 +406,11 @@ void AppsWidget::populateDefaultApps() QString logoUrl = sponsorObj.value("logo").toString(); QString description = sponsorObj.value("description").toString(); QString url = sponsorObj.value("url").toString(); - + bool useBundleIdForIcon = + sponsorObj.value("useBundleIdForIcon").toBool(true); createAppCard(name, bundleId, description, logoUrl, url, gridLayout, - row, col, SponsorType(SponsorType::Platinum)); + row, col, useBundleIdForIcon, + SponsorType(SponsorType::Platinum)); advanceGridPos(); } @@ -419,8 +421,11 @@ void AppsWidget::populateDefaultApps() QString description = sponsorObj.value("description").toString(); QString logoUrl = sponsorObj.value("logo").toString(); QString url = sponsorObj.value("url").toString(); + bool useBundleIdForIcon = + sponsorObj.value("useBundleIdForIcon").toBool(true); createAppCard(name, bundleId, description, logoUrl, url, gridLayout, - row, col, SponsorType(SponsorType::Gold)); + row, col, useBundleIdForIcon, + SponsorType(SponsorType::Gold)); advanceGridPos(); } @@ -466,9 +471,13 @@ void AppsWidget::populateDefaultApps() QString bundleId = sponsorObj.value("bundleId").toString(); QString description = sponsorObj.value("description").toString(); QString url = sponsorObj.value("url").toString(); + QString logoUrl = sponsorObj.value("logo").toString(); + bool useBundleIdForIcon = + sponsorObj.value("useBundleIdForIcon").toBool(true); - createAppCard(name, bundleId, description, "", url, gridLayout, row, - col, SponsorType(SponsorType::Silver)); + createAppCard(name, bundleId, description, logoUrl, url, gridLayout, + row, col, useBundleIdForIcon, + SponsorType(SponsorType::Silver)); advanceGridPos(); } @@ -478,8 +487,13 @@ void AppsWidget::populateDefaultApps() QString bundleId = sponsorObj.value("bundleId").toString(); QString description = sponsorObj.value("description").toString(); QString url = sponsorObj.value("url").toString(); - createAppCard(name, bundleId, description, "", url, gridLayout, row, - col, SponsorType(SponsorType::Bronze)); + QString logoUrl = sponsorObj.value("logo").toString(); + + bool useBundleIdForIcon = + sponsorObj.value("useBundleIdForIcon").toBool(true); + createAppCard(name, bundleId, description, logoUrl, url, gridLayout, + row, col, useBundleIdForIcon, + SponsorType(SponsorType::Bronze)); advanceGridPos(); } gridLayout->setRowStretch(gridLayout->rowCount(), 1); @@ -526,12 +540,10 @@ void AppsWidget::createSponsorCard(QGridLayout *gridLayout, int row, int col) gridLayout->addWidget(sponsorCard, row, col); } -void AppsWidget::createAppCard(const QString &name, const QString &bundleId, - const QString &description, - const QString &logoUrl, - const QString &websiteUrl, - QGridLayout *gridLayout, int row, int col, - const SponsorType &sponsorType) +void AppsWidget::createAppCard( + const QString &name, const QString &bundleId, const QString &description, + const QString &logoUrl, const QString &websiteUrl, QGridLayout *gridLayout, + int row, int col, bool useBundleIdForIcon, const SponsorType &sponsorType) { QWidget *cardWidget = new QWidget(); @@ -541,6 +553,7 @@ void AppsWidget::createAppCard(const QString &name, const QString &bundleId, // App icon QLabel *iconLabel = new QLabel(); + QPointer safeIconLabel = iconLabel; QPixmap placeholderIcon = QApplication::style() ->standardIcon(QStyle::SP_ComputerIcon) .pixmap(64, 64); @@ -548,41 +561,43 @@ void AppsWidget::createAppCard(const QString &name, const QString &bundleId, iconLabel->setAlignment(Qt::AlignCenter); cardLayout->addWidget(iconLabel); - // If logoUrl is provided and bundleId is empty, use the logo directly - if (!logoUrl.isEmpty()) { + if (!logoUrl.isEmpty() && !useBundleIdForIcon) { QUrl url(logoUrl); QNetworkRequest request(url); QNetworkReply *reply = m_networkManager->get(request); - connect(reply, &QNetworkReply::finished, [reply, iconLabel]() { - if (reply->error() == QNetworkReply::NoError) { - QByteArray data = reply->readAll(); - QPixmap pixmap; - if (pixmap.loadFromData(data)) { - QPixmap scaled = - pixmap.scaled(64, 64, Qt::KeepAspectRatioByExpanding, - Qt::SmoothTransformation); - QPixmap rounded(64, 64); - rounded.fill(Qt::transparent); + // Use Qt's parent-child relationship to auto-cleanup + connect( + reply, &QNetworkReply::finished, this, [reply, safeIconLabel]() { + if (reply->error() == QNetworkReply::NoError && safeIconLabel) { + QByteArray data = reply->readAll(); + QPixmap pixmap; + if (pixmap.loadFromData(data)) { + QPixmap scaled = pixmap.scaled( + 64, 64, Qt::KeepAspectRatioByExpanding, + Qt::SmoothTransformation); + QPixmap rounded(64, 64); + rounded.fill(Qt::transparent); - QPainter painter(&rounded); - painter.setRenderHint(QPainter::Antialiasing); - QPainterPath path; - path.addRoundedRect(QRectF(0, 0, 64, 64), 16, 16); - painter.setClipPath(path); - painter.drawPixmap(0, 0, scaled); - painter.end(); + QPainter painter(&rounded); + painter.setRenderHint(QPainter::Antialiasing); + QPainterPath path; + path.addRoundedRect(QRectF(0, 0, 64, 64), 16, 16); + painter.setClipPath(path); + painter.drawPixmap(0, 0, scaled); + painter.end(); - iconLabel->setPixmap(rounded); + safeIconLabel->setPixmap(rounded); + } } - } - reply->deleteLater(); - }); + reply->deleteLater(); + }); + // Ensure reply is deleted if iconLabel is destroyed + connect(iconLabel, &QObject::destroyed, reply, &QNetworkReply::abort); } else if (!bundleId.isEmpty()) { - // Use Apple's API for app icons fetchAppIconFromApple( - bundleId, - [iconLabel](const QPixmap &pixmap) { - if (!pixmap.isNull()) { + m_networkManager, bundleId, [safeIconLabel](const QPixmap &pixmap) { + // Check if iconLabel still exists + if (safeIconLabel && !pixmap.isNull()) { QPixmap scaled = pixmap.scaled(64, 64, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); @@ -597,10 +612,9 @@ void AppsWidget::createAppCard(const QString &name, const QString &bundleId, painter.drawPixmap(0, 0, scaled); painter.end(); - iconLabel->setPixmap(rounded); + safeIconLabel->setPixmap(rounded); } - }, - cardWidget); + }); } // Vertical layout for name and description diff --git a/src/appswidget.h b/src/appswidget.h index 469dac1..b448cbe 100644 --- a/src/appswidget.h +++ b/src/appswidget.h @@ -79,7 +79,7 @@ private: void createAppCard(const QString &name, const QString &bundleId, const QString &description, const QString &logoUrl, const QString &websiteUrl, QGridLayout *gridLayout, - int row, int col, + int row, int col, bool useBundleIdForIcon = true, const SponsorType &sponsorType = SponsorType()); void setupDefaultAppsPage(); void setupLoadingPage(); diff --git a/src/core/helpers/fetch_app_icon_from_apple.cpp b/src/core/helpers/fetch_app_icon_from_apple.cpp index a7e38a5..53a4327 100644 --- a/src/core/helpers/fetch_app_icon_from_apple.cpp +++ b/src/core/helpers/fetch_app_icon_from_apple.cpp @@ -1,23 +1,19 @@ #include #include #include -#include #include #include -#include -void fetchAppIconFromApple(const QString &bundleId, - std::function callback, - QObject *context) +void fetchAppIconFromApple(QNetworkAccessManager *manager, + const QString &bundleId, + std::function callback) { - QNetworkAccessManager *manager = new QNetworkAccessManager(context); QString url = QString("https://itunes.apple.com/lookup?bundleId=%1").arg(bundleId); QNetworkReply *reply = manager->get(QNetworkRequest(QUrl(url))); QObject::connect( - reply, &QNetworkReply::finished, context, - [reply, callback, manager, context]() { + reply, &QNetworkReply::finished, [reply, callback, manager]() { QByteArray data = reply->readAll(); reply->deleteLater(); @@ -25,7 +21,6 @@ void fetchAppIconFromApple(const QString &bundleId, QJsonDocument doc = QJsonDocument::fromJson(data, &parseError); if (parseError.error != QJsonParseError::NoError) { callback(QPixmap()); - manager->deleteLater(); return; } @@ -33,7 +28,6 @@ void fetchAppIconFromApple(const QString &bundleId, QJsonArray results = obj.value("results").toArray(); if (results.isEmpty()) { callback(QPixmap()); - manager->deleteLater(); return; } @@ -41,21 +35,19 @@ void fetchAppIconFromApple(const QString &bundleId, QString iconUrl = appInfo.value("artworkUrl100").toString(); if (iconUrl.isEmpty()) { callback(QPixmap()); - manager->deleteLater(); return; } // Fetch the icon image QNetworkReply *iconReply = manager->get(QNetworkRequest(QUrl(iconUrl))); - QObject::connect(iconReply, &QNetworkReply::finished, context, - [iconReply, callback, manager]() { + QObject::connect(iconReply, &QNetworkReply::finished, + [iconReply, callback]() { QByteArray iconData = iconReply->readAll(); iconReply->deleteLater(); QPixmap pixmap; pixmap.loadFromData(iconData); callback(pixmap); - manager->deleteLater(); }); }); } diff --git a/src/iDescriptor.h b/src/iDescriptor.h index f6967a7..4706297 100644 --- a/src/iDescriptor.h +++ b/src/iDescriptor.h @@ -1,6 +1,7 @@ #pragma once #include #include +#include #include #include #include @@ -375,9 +376,9 @@ void get_battery_info(std::string productType, idevice_t idevice, void parseOldDeviceBattery(PlistNavigator &ioreg, DeviceInfo &d); void parseDeviceBattery(PlistNavigator &ioreg, DeviceInfo &d); -void fetchAppIconFromApple(const QString &bundleId, - std::function callback, - QObject *context); +void fetchAppIconFromApple(QNetworkAccessManager *manager, + const QString &bundleId, + std::function callback); afc_error_t afc2_client_new(idevice_t device, afc_client_t *afc); diff --git a/src/installedappswidget.cpp b/src/installedappswidget.cpp index d18dcbe..a3ed78a 100644 --- a/src/installedappswidget.cpp +++ b/src/installedappswidget.cpp @@ -36,9 +36,8 @@ AppTabWidget::AppTabWidget(const QString &appName, const QString &bundleId, void AppTabWidget::fetchAppIcon() { - fetchAppIconFromApple( - m_bundleId, - [this](const QPixmap &pixmap) { + ::fetchAppIconFromApple( + m_networkManager, m_bundleId, [this](const QPixmap &pixmap) { if (!pixmap.isNull()) { QPixmap scaled = pixmap.scaled(32, 32, Qt::KeepAspectRatioByExpanding, @@ -56,8 +55,7 @@ void AppTabWidget::fetchAppIcon() m_iconLabel->setPixmap(rounded); } - }, - this); + }); } void AppTabWidget::setSelected(bool selected) diff --git a/src/installedappswidget.h b/src/installedappswidget.h index 2fd738c..6ceeff5 100644 --- a/src/installedappswidget.h +++ b/src/installedappswidget.h @@ -61,6 +61,7 @@ private: QLabel *m_nameLabel; QLabel *m_versionLabel; QList m_appTabs; + QNetworkAccessManager *m_networkManager = new QNetworkAccessManager(this); }; class InstalledAppsWidget : public QWidget diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 5402884..f102ac6 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -265,7 +265,9 @@ MainWindow::MainWindow(QWidget *parent) // installed via package manager on Linux this); qDebug() << "Checking for updates..."; - m_updater->checkForUpdates(); + SettingsManager::sharedInstance()->doIfEnabled( + SettingsManager::Setting::AutoCheckUpdates, + [this]() { m_updater->checkForUpdates(); }); } void MainWindow::createMenus() diff --git a/src/qprocessindicator.cpp b/src/qprocessindicator.cpp index be6a101..cd58167 100644 --- a/src/qprocessindicator.cpp +++ b/src/qprocessindicator.cpp @@ -1,6 +1,6 @@ // https://github.com/raythorn/QProcessIndicator/blob/master/QProcessIndicator/QProcessIndicator.cpp #include "qprocessindicator.h" - +#include #include #include #include @@ -16,6 +16,13 @@ QProcessIndicator::QProcessIndicator(QWidget *parent) m_timer = new QTimer(); connect(m_timer, SIGNAL(timeout()), this, SLOT(onTimeout())); + connect(qApp, &QApplication::paletteChanged, this, + &QProcessIndicator::updateStyle); +} +void QProcessIndicator::updateStyle() +{ + m_color = palette().color(QPalette::Highlight); + update(); } QProcessIndicator::~QProcessIndicator() diff --git a/src/qprocessindicator.h b/src/qprocessindicator.h index bc3d2ca..c312b43 100644 --- a/src/qprocessindicator.h +++ b/src/qprocessindicator.h @@ -47,6 +47,7 @@ private: void drawRotateLine(QPainter *painter); void drawScaleLine(QPainter *painter); void drawRotateBall(QPainter *painter); + void updateStyle(); private: int m_type; diff --git a/src/sponsorappcard.cpp b/src/sponsorappcard.cpp index eca869a..69b5a1b 100644 --- a/src/sponsorappcard.cpp +++ b/src/sponsorappcard.cpp @@ -39,9 +39,8 @@ SponsorAppCard::SponsorAppCard(QWidget *parent) : QWidget{parent} "Create an online store within minutes and start selling."; QString websiteUrl = "https://www.shopify.com"; - fetchAppIconFromApple( - bundleId, - [iconLabel](const QPixmap &pixmap) { + ::fetchAppIconFromApple( + m_networkManager, bundleId, [iconLabel](const QPixmap &pixmap) { if (!pixmap.isNull()) { QPixmap scaled = pixmap.scaled(64, 64, Qt::KeepAspectRatioByExpanding, @@ -59,8 +58,7 @@ SponsorAppCard::SponsorAppCard(QWidget *parent) : QWidget{parent} iconLabel->setPixmap(rounded); } - }, - this); + }); // Vertical layout for name and description QVBoxLayout *textLayout = new QVBoxLayout(); diff --git a/src/sponsorappcard.h b/src/sponsorappcard.h index a44966a..9540ac2 100644 --- a/src/sponsorappcard.h +++ b/src/sponsorappcard.h @@ -1,6 +1,6 @@ #ifndef SPONSORAPPCARD_H #define SPONSORAPPCARD_H - +#include #include class SponsorAppCard : public QWidget @@ -9,6 +9,9 @@ class SponsorAppCard : public QWidget public: explicit SponsorAppCard(QWidget *parent = nullptr); +private: + QNetworkAccessManager *m_networkManager = new QNetworkAccessManager(this); + signals: };