Skip to content

Commit 3aa12f5

Browse files
Use amd64 instead of aarch64 on Linux (#209)
* Use amd64 instead of aarch64 * Fix JAVA_HOME not being set on Linux * Show open project name in window title. * Create desktop entry on Linux * Update desktop database async * Set window icon from main window
1 parent 372138e commit 3aa12f5

8 files changed

Lines changed: 314 additions & 28 deletions

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ set(SOURCES
7474
sources/apksignworker.cpp
7575
sources/appearancesettingswidget.cpp
7676
sources/binarysettingswidget.cpp
77+
sources/desktopdatabaseupdateworker.cpp
7778
sources/devicelistworker.cpp
7879
sources/deviceselectiondialog.cpp
7980
sources/findreplacedialog.cpp
@@ -101,6 +102,7 @@ set(HEADERS
101102
sources/apksignworker.h
102103
sources/appearancesettingswidget.h
103104
sources/binarysettingswidget.h
105+
sources/desktopdatabaseupdateworker.h
104106
sources/devicelistworker.h
105107
sources/deviceselectiondialog.h
106108
sources/findreplacedialog.h
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#include <QDebug>
2+
#include <QObject>
3+
#include <QProcess>
4+
#include "desktopdatabaseupdateworker.h"
5+
6+
DesktopDatabaseUpdateWorker::DesktopDatabaseUpdateWorker(const QString &applicationsDir, QObject *parent)
7+
: QObject(parent), m_ApplicationsDir(applicationsDir)
8+
{
9+
}
10+
11+
void DesktopDatabaseUpdateWorker::updateDatabase()
12+
{
13+
emit started();
14+
15+
// Check if update-desktop-database exists
16+
QProcess checkProcess;
17+
checkProcess.start("which", QStringList() << "update-desktop-database");
18+
if (!checkProcess.waitForFinished(1000)) {
19+
// Command doesn't exist or timed out, just finish silently
20+
emit finished();
21+
return;
22+
}
23+
24+
if (checkProcess.exitCode() != 0) {
25+
// update-desktop-database not found, finish silently
26+
emit finished();
27+
return;
28+
}
29+
30+
// Run update-desktop-database
31+
QProcess updateProcess;
32+
updateProcess.start("update-desktop-database", QStringList() << m_ApplicationsDir);
33+
34+
if (!updateProcess.waitForFinished(10000)) { // Wait max 10 seconds
35+
emit error(QObject::tr("Desktop database update timed out"));
36+
emit finished();
37+
return;
38+
}
39+
40+
if (updateProcess.exitCode() != 0) {
41+
QString errorOutput = QString::fromUtf8(updateProcess.readAllStandardError());
42+
emit error(QObject::tr("Desktop database update failed: %1").arg(errorOutput));
43+
}
44+
45+
emit finished();
46+
}
47+
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#ifndef DESKTOPDATABASEUPDATEWORKER_H
2+
#define DESKTOPDATABASEUPDATEWORKER_H
3+
4+
#include <QObject>
5+
#include <QString>
6+
7+
class DesktopDatabaseUpdateWorker : public QObject
8+
{
9+
Q_OBJECT
10+
public:
11+
explicit DesktopDatabaseUpdateWorker(const QString &applicationsDir, QObject *parent = nullptr);
12+
void updateDatabase();
13+
signals:
14+
void finished();
15+
void error(const QString &message);
16+
void started();
17+
private:
18+
QString m_ApplicationsDir;
19+
};
20+
21+
#endif // DESKTOPDATABASEUPDATEWORKER_H
22+

sources/main.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ int main(int argc, char *argv[])
9191
}
9292
app.setPalette(palette);
9393
#endif
94-
94+
9595
// Check for APK file path in command-line arguments
9696
QString apkFilePath;
9797
if (argc > 1) {

sources/mainwindow.cpp

Lines changed: 190 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,22 @@
88
#include <QDockWidget>
99
#include <QFile>
1010
#include <QFileDialog>
11+
#include <QFileInfo>
1112
#include <QFrame>
1213
#include <QHeaderView>
1314
#include <QInputDialog>
1415
#include <QMenuBar>
1516
#include <QMessageBox>
17+
#include <QPixmap>
1618
#include <QPushButton>
1719
#include <QProcess>
20+
#include <QProcessEnvironment>
1821
#include <QSettings>
1922
#include <QSignalBlocker>
23+
#include <QStandardPaths>
2024
#include <QStatusBar>
2125
#include <QTabWidget>
26+
#include <QTextStream>
2227
#include <QTextDocumentFragment>
2328
#include <QThread>
2429
#include <QTimer>
@@ -29,6 +34,7 @@
2934
#include "apkdecompileworker.h"
3035
#include "apkrecompileworker.h"
3136
#include "apksignworker.h"
37+
#include "desktopdatabaseupdateworker.h"
3238
#include "deviceselectiondialog.h"
3339
#include "findreplacedialog.h"
3440
#include "hexedit.h"
@@ -72,7 +78,14 @@ MainWindow::MainWindow(const QMap<QString, QString> &versions, QWidget *parent)
7278
setMenuBar(buildMenuBar());
7379
setMinimumSize(WINDOW_WIDTH, WINDOW_HEIGHT);
7480
setStatusBar(buildStatusBar(versions));
75-
setWindowTitle(tr("APK Studio").append(" - https://vaibhavpandey.com/apkstudio/"));
81+
updateWindowTitle();
82+
83+
#ifdef Q_OS_LINUX
84+
// Set window icon explicitly for Linux window managers
85+
// This ensures the icon appears in the sidebar/dock when the window is running
86+
setWindowIcon(QIcon(":/images/icon.png"));
87+
#endif
88+
7689
connect(QApplication::clipboard(), &QClipboard::dataChanged, this, &MainWindow::handleClipboardDataChanged);
7790
QSettings settings;
7891
if (settings.value("app_maximized").toBool()) {
@@ -92,7 +105,12 @@ MainWindow::MainWindow(const QMap<QString, QString> &versions, QWidget *parent)
92105
// Ensure main window gets focus instead of search boxes
93106
setFocus();
94107

95-
QTimer::singleShot(100, [=] {
108+
#ifdef Q_OS_LINUX
109+
// Check and install desktop file on Linux (after window is shown)
110+
QTimer::singleShot(500, this, &MainWindow::checkAndInstallDesktopFile);
111+
#endif
112+
113+
QTimer::singleShot(500, [=] {
96114
QSettings settings;
97115
const QStringList files = settings.value("open_files").toStringList();
98116
foreach (const QString &file, files) {
@@ -1445,6 +1463,7 @@ void MainWindow::openProject(const QString &folder, const bool last)
14451463
openFile(manifest);
14461464
}
14471465
}
1466+
updateWindowTitle();
14481467
}
14491468

14501469
void MainWindow::reloadChildren(QTreeWidgetItem *item)
@@ -1488,6 +1507,175 @@ bool MainWindow::saveTab(int i)
14881507
return true;
14891508
}
14901509

1510+
void MainWindow::updateWindowTitle()
1511+
{
1512+
QString title = tr("APK Studio by VPZ");
1513+
1514+
// Get the first (most recent) project from the tree
1515+
if (m_ProjectsTree->topLevelItemCount() > 0) {
1516+
QTreeWidgetItem *firstProject = m_ProjectsTree->topLevelItem(0);
1517+
if (firstProject) {
1518+
QString projectFolder = firstProject->data(0, Qt::UserRole + 2).toString();
1519+
if (!projectFolder.isEmpty()) {
1520+
QFileInfo info(projectFolder);
1521+
title += tr(" - %1").arg(info.fileName());
1522+
}
1523+
}
1524+
}
1525+
1526+
setWindowTitle(title);
1527+
}
1528+
1529+
#ifdef Q_OS_LINUX
1530+
void MainWindow::checkAndInstallDesktopFile()
1531+
{
1532+
QString applicationsDir = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation);
1533+
if (applicationsDir.isEmpty()) {
1534+
// Fallback to ~/.local/share/applications
1535+
applicationsDir = QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + "/.local/share/applications";
1536+
}
1537+
1538+
QString desktopFilePath = applicationsDir + "/apkstudio.desktop";
1539+
QFileInfo desktopFileInfo(desktopFilePath);
1540+
1541+
// Check if .desktop file already exists
1542+
if (desktopFileInfo.exists()) {
1543+
return; // Already installed
1544+
}
1545+
1546+
// Ask user for confirmation
1547+
QMessageBox msgBox(this);
1548+
msgBox.setWindowTitle(tr("Install desktop entry"));
1549+
msgBox.setText(tr("Would you like to install a desktop entry for APK Studio?\n\nThis will allow you to launch APK Studio from your application menu."));
1550+
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
1551+
msgBox.setDefaultButton(QMessageBox::Yes);
1552+
msgBox.setIcon(QMessageBox::Question);
1553+
1554+
if (msgBox.exec() != QMessageBox::Yes) {
1555+
return; // User declined
1556+
}
1557+
1558+
// Get the executable path
1559+
// Check if running from AppImage - if so, use the AppImage file path
1560+
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
1561+
QString executablePath;
1562+
QString appImagePath = env.value("APPIMAGE");
1563+
1564+
if (!appImagePath.isEmpty() && QFile::exists(appImagePath)) {
1565+
// Running from AppImage - use the AppImage file itself
1566+
executablePath = appImagePath;
1567+
} else {
1568+
// Not running from AppImage - use the regular executable path
1569+
executablePath = QApplication::applicationFilePath();
1570+
QFileInfo exeInfo(executablePath);
1571+
if (!exeInfo.exists()) {
1572+
// If the executable path doesn't exist (e.g., running from build directory),
1573+
// try to use argv[0] or a generic command
1574+
executablePath = "apkstudio";
1575+
} else {
1576+
executablePath = exeInfo.absoluteFilePath();
1577+
}
1578+
}
1579+
1580+
// Extract icon from bundled resources and save it to a location the desktop file can use
1581+
QString iconPath = "apkstudio"; // Fallback to generic name
1582+
1583+
// Try to extract icon from bundled resources
1584+
QPixmap iconPixmap(":/images/icon.png");
1585+
if (!iconPixmap.isNull()) {
1586+
// Save icon to ~/.local/share/icons/apkstudio.png
1587+
QString iconsDir = QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + "/.local/share/icons";
1588+
QDir iconsDirObj;
1589+
if (iconsDirObj.mkpath(iconsDir)) {
1590+
QString iconFilePath = iconsDir + "/apkstudio.png";
1591+
if (iconPixmap.save(iconFilePath, "PNG")) {
1592+
iconPath = iconFilePath;
1593+
} else {
1594+
// Fallback: try ~/.local/share/apkstudio/icon.png
1595+
QString appDataDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
1596+
if (appDataDir.isEmpty()) {
1597+
appDataDir = QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + "/.apkstudio";
1598+
}
1599+
if (iconsDirObj.mkpath(appDataDir)) {
1600+
iconFilePath = appDataDir + "/icon.png";
1601+
if (iconPixmap.save(iconFilePath, "PNG")) {
1602+
iconPath = iconFilePath;
1603+
}
1604+
}
1605+
}
1606+
}
1607+
}
1608+
1609+
// If icon extraction failed, try to find it relative to executable (for non-AppImage installations)
1610+
QFileInfo exeInfo(executablePath);
1611+
if (iconPath == "apkstudio" && exeInfo.exists() && appImagePath.isEmpty()) {
1612+
QString exeDir = exeInfo.absolutePath();
1613+
QFileInfo iconInfo(exeDir + "/../share/pixmaps/apkstudio.png");
1614+
if (iconInfo.exists()) {
1615+
iconPath = iconInfo.absoluteFilePath();
1616+
} else {
1617+
// Try relative to executable
1618+
iconInfo.setFile(exeDir + "/../share/apkstudio/icon.png");
1619+
if (iconInfo.exists()) {
1620+
iconPath = iconInfo.absoluteFilePath();
1621+
}
1622+
}
1623+
}
1624+
1625+
// Create the applications directory if it doesn't exist
1626+
QDir dir;
1627+
if (!dir.mkpath(applicationsDir)) {
1628+
QMessageBox::warning(this, tr("Error"),
1629+
tr("Failed to create applications directory:\n%1").arg(applicationsDir));
1630+
return;
1631+
}
1632+
1633+
// Create the .desktop file content
1634+
QFile desktopFile(desktopFilePath);
1635+
if (!desktopFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
1636+
QMessageBox::warning(this, tr("Error"),
1637+
tr("Failed to create desktop file:\n%1").arg(desktopFilePath));
1638+
return;
1639+
}
1640+
1641+
QTextStream out(&desktopFile);
1642+
out << "[Desktop Entry]\n";
1643+
out << "Type=Application\n";
1644+
out << "Name=APK Studio\n";
1645+
out << "Name[en]=APK Studio\n";
1646+
out << "Comment=Android APK reverse engineering tool\n";
1647+
out << "Comment[en]=Android APK reverse engineering tool\n";
1648+
// Escape the executable path if it contains spaces
1649+
QString escapedExecPath = executablePath;
1650+
if (executablePath.contains(" ")) {
1651+
escapedExecPath = "\"" + executablePath + "\"";
1652+
}
1653+
out << "Exec=" << escapedExecPath << " %F\n";
1654+
out << "Icon=" << iconPath << "\n";
1655+
out << "Terminal=false\n";
1656+
out << "Categories=Development;Utility;\n";
1657+
out << "StartupNotify=true\n";
1658+
out << "StartupWMClass=apkstudio\n";
1659+
desktopFile.close();
1660+
1661+
// Set appropriate permissions (readable by user, group, and others)
1662+
desktopFile.setPermissions(QFile::ReadUser | QFile::WriteUser |
1663+
QFile::ReadGroup | QFile::ReadOther);
1664+
1665+
// Update desktop database to make it appear in Ubuntu launcher (async using worker)
1666+
QThread *thread = new QThread(this);
1667+
DesktopDatabaseUpdateWorker *worker = new DesktopDatabaseUpdateWorker(applicationsDir);
1668+
worker->moveToThread(thread);
1669+
1670+
connect(thread, &QThread::started, worker, &DesktopDatabaseUpdateWorker::updateDatabase);
1671+
connect(worker, &DesktopDatabaseUpdateWorker::finished, thread, &QThread::quit);
1672+
connect(worker, &DesktopDatabaseUpdateWorker::finished, worker, &QObject::deleteLater);
1673+
connect(thread, &QThread::finished, thread, &QObject::deleteLater);
1674+
// Note: We don't show error messages for database update failures as they're not critical
1675+
thread->start();
1676+
}
1677+
#endif
1678+
14911679
MainWindow::~MainWindow()
14921680
{
14931681
}

sources/mainwindow.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ private slots:
136136
void openProject(const QString &folder, const bool last = false);
137137
void reloadChildren(QTreeWidgetItem *item);
138138
void filterProjectTreeItems(QTreeWidgetItem *item, const QString &filter);
139+
void updateWindowTitle();
140+
#ifdef Q_OS_LINUX
141+
void checkAndInstallDesktopFile();
142+
#endif
139143
private:
140144
bool saveTab(int index);
141145
};

0 commit comments

Comments
 (0)