bug fixes

- update app icon fetching to use QNetworkAccessManager
- fix App icon label dangling pointer
- check updates if autocheckupdates is enabled
This commit is contained in:
uncor3
2025-11-03 12:36:59 +00:00
parent fa430efdcf
commit 2a86e3090d
17 changed files with 118 additions and 97 deletions
+2 -1
View File
@@ -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);
+2 -1
View File
@@ -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);
}
+5 -6
View File
@@ -8,6 +8,7 @@
#include <QFutureWatcher>
#include <QLabel>
#include <QMessageBox>
#include <QNetworkAccessManager>
#include <QPainter>
#include <QPainterPath>
#include <QPushButton>
@@ -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<QVBoxLayout *>(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);
+2
View File
@@ -7,6 +7,7 @@
#include <QFutureWatcher>
#include <QLabel>
#include <QTemporaryDir>
#include <QNetworkAccessManager>
class AppInstallDialog : public AppDownloadBaseDialog
{
@@ -30,6 +31,7 @@ private:
QLabel *m_statusLabel;
QFutureWatcher<int> *m_installWatcher;
QTemporaryDir *m_tempDir = nullptr;
QNetworkAccessManager *m_manager = nullptr;
void updateDeviceList();
void performInstallation(const QString &ipaPath, const QString &deviceUdid);
};
+12 -11
View File
@@ -160,8 +160,9 @@ void AppStoreManager::searchApps(
}
void AppStoreManager::downloadApp(
const QString &bundleId, const QString &outputDir, const QString &country,
bool acquireLicense, std::function<void(int result)> callback,
const QString &bundleId, const QString &outputDir,
const QString &externalVersionId, bool acquireLicense,
std::function<void(int result)> callback,
std::function<void(long long current, long long total)> progressCallback)
{
if (!m_initialized) {
@@ -189,15 +190,15 @@ void AppStoreManager::downloadApp(
};
}
QFuture<int> 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<int> 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<int> *watcher = new QFutureWatcher<int>(this);
connect(
+1 -1
View File
@@ -25,7 +25,7 @@ public:
const QString &searchTerm, int limit,
std::function<void(bool success, const QString &results)> callback);
void downloadApp(const QString &bundleId, const QString &outputDir,
const QString &country, bool acquireLicense,
const QString &externalVersionId, bool acquireLicense,
std::function<void(int result)> callback,
std::function<void(long long current, long long total)>
progressCallback = nullptr);
+60 -46
View File
@@ -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<QLabel> 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
+1 -1
View File
@@ -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();
+6 -14
View File
@@ -1,23 +1,19 @@
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QPixmap>
#include <QtConcurrent/QtConcurrent>
void fetchAppIconFromApple(const QString &bundleId,
std::function<void(const QPixmap &)> callback,
QObject *context)
void fetchAppIconFromApple(QNetworkAccessManager *manager,
const QString &bundleId,
std::function<void(const QPixmap &)> 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();
});
});
}
+4 -3
View File
@@ -1,6 +1,7 @@
#pragma once
#include <QDebug>
#include <QImage>
#include <QNetworkAccessManager>
#include <QtCore/QObject>
#include <libimobiledevice/afc.h>
#include <libimobiledevice/installation_proxy.h>
@@ -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<void(const QPixmap &)> callback,
QObject *context);
void fetchAppIconFromApple(QNetworkAccessManager *manager,
const QString &bundleId,
std::function<void(const QPixmap &)> callback);
afc_error_t afc2_client_new(idevice_t device, afc_client_t *afc);
+3 -5
View File
@@ -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)
+1
View File
@@ -61,6 +61,7 @@ private:
QLabel *m_nameLabel;
QLabel *m_versionLabel;
QList<AppTabWidget *> m_appTabs;
QNetworkAccessManager *m_networkManager = new QNetworkAccessManager(this);
};
class InstalledAppsWidget : public QWidget
+3 -1
View File
@@ -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()
+8 -1
View File
@@ -1,6 +1,6 @@
// https://github.com/raythorn/QProcessIndicator/blob/master/QProcessIndicator/QProcessIndicator.cpp
#include "qprocessindicator.h"
#include <QApplication>
#include <QDebug>
#include <QPoint>
#include <QtGlobal>
@@ -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()
+1
View File
@@ -47,6 +47,7 @@ private:
void drawRotateLine(QPainter *painter);
void drawScaleLine(QPainter *painter);
void drawRotateBall(QPainter *painter);
void updateStyle();
private:
int m_type;
+3 -5
View File
@@ -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();
+4 -1
View File
@@ -1,6 +1,6 @@
#ifndef SPONSORAPPCARD_H
#define SPONSORAPPCARD_H
#include <QNetworkAccessManager>
#include <QWidget>
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:
};