Skip to content

Commit b28c59d

Browse files
Pass additional (optional) args to apktool (#205)
1 parent c3cadbc commit b28c59d

9 files changed

Lines changed: 72 additions & 11 deletions

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Open-source, cross platform [Qt6](https://www.qt.io/) based IDE for reverse-engi
3232
- **Automatic tool download & installation** - APK Studio can automatically download and install required tools (Java, Apktool, JADX, ADB, Uber APK Signer)
3333
- **Framework support** - Install and use manufacturer-specific framework files (e.g., HTC, LG, Samsung) with optional tagging for decompiling and recompiling APKs
3434
- **Command-line APK opening** - Open APK files directly from the file system via "Open with" context menu or command-line arguments
35+
- **Extra apktool arguments** - Provide additional command-line arguments (e.g., `--force-all`, `--no-res`) for decompile and recompile operations
3536
- **Search functionality** - Quick search in open files and project tree to find items by name
3637
- Built-in code editor (\*.java; \*.smali; \*.xml; \*.yml) w/ syntax highlighting
3738
- Built-in viewer for image (\*.gif; \*.jpg; \*.jpeg; \*.png) files

sources/apkdecompiledialog.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ ApkDecompileDialog::ApkDecompileDialog(const QString &apk, QWidget *parent)
1414
layout->addWidget(buildButtonBox());
1515
layout->setContentsMargins(4, 4, 4, 4);
1616
layout->setSpacing(2);
17-
setMinimumSize(340, 160);
17+
setMinimumSize(340, 200);
1818
#ifdef Q_OS_WIN
1919
setWindowIcon(QIcon(":/icons/fugue/android.png"));
2020
#endif
@@ -59,6 +59,9 @@ QLayout *ApkDecompileDialog::buildForm(const QString &apk)
5959
layout->addRow(tr("Framework tag (optional)"), m_EditFrameworkTag = new QLineEdit(this));
6060
m_EditFrameworkTag->setPlaceholderText(tr("e.g., hero, desire, samsung"));
6161
m_EditFrameworkTag->setToolTip(tr("Specify a framework tag if you installed frameworks with a tag. Leave empty to use default framework."));
62+
layout->addRow(tr("Extra arguments (optional)"), m_EditExtraArguments = new QLineEdit(this));
63+
m_EditExtraArguments->setPlaceholderText(tr("e.g., --force-all, --no-res"));
64+
m_EditExtraArguments->setToolTip(tr("Additional apktool command-line arguments. Separate multiple arguments with spaces."));
6265
layout->setSpacing(2);
6366
return layout;
6467
}
@@ -99,3 +102,8 @@ QString ApkDecompileDialog::frameworkTag() const
99102
{
100103
return m_EditFrameworkTag->text().trimmed();
101104
}
105+
106+
QString ApkDecompileDialog::extraArguments() const
107+
{
108+
return m_EditExtraArguments->text().trimmed();
109+
}

sources/apkdecompiledialog.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class ApkDecompileDialog : public QDialog
1717
bool resources() const;
1818
bool smali() const;
1919
QString frameworkTag() const;
20+
QString extraArguments() const;
2021
private:
2122
QDialogButtonBox *m_ButtonBox;
2223
QCheckBox *m_CheckJava;
@@ -25,6 +26,7 @@ class ApkDecompileDialog : public QDialog
2526
QLineEdit *m_EditApk;
2627
QLineEdit *m_EditFolder;
2728
QLineEdit *m_EditFrameworkTag;
29+
QLineEdit *m_EditExtraArguments;
2830
QWidget *buildButtonBox();
2931
QLayout *buildForm(const QString &apk);
3032
private slots:

sources/apkdecompileworker.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
#include <QDebug>
2+
#include <QRegularExpression>
23
#include "apkdecompileworker.h"
34
#include "processutils.h"
45

5-
ApkDecompileWorker::ApkDecompileWorker(const QString &apk, const QString &folder, const bool smali, const bool resources, const bool java, const QString &frameworkTag, QObject *parent)
6-
: QObject(parent), m_Apk(apk), m_Folder(folder), m_Java(java), m_Resources(resources), m_Smali(smali), m_FrameworkTag(frameworkTag)
6+
ApkDecompileWorker::ApkDecompileWorker(const QString &apk, const QString &folder, const bool smali, const bool resources, const bool java, const QString &frameworkTag, const QString &extraArguments, QObject *parent)
7+
: QObject(parent), m_Apk(apk), m_Folder(folder), m_Java(java), m_Resources(resources), m_Smali(smali), m_FrameworkTag(frameworkTag), m_ExtraArguments(extraArguments)
78
{
89
}
910

@@ -34,6 +35,11 @@ void ApkDecompileWorker::decompile()
3435
if (!m_FrameworkTag.isEmpty()) {
3536
args << "-t" << m_FrameworkTag;
3637
}
38+
// Parse and add extra arguments
39+
if (!m_ExtraArguments.isEmpty()) {
40+
QStringList extraArgs = m_ExtraArguments.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts);
41+
args << extraArgs;
42+
}
3743
args << "-o" << m_Folder << m_Apk;
3844
ProcessResult result = ProcessUtils::runCommand(java, args);
3945
#ifdef QT_DEBUG

sources/apkdecompileworker.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ class ApkDecompileWorker : public QObject
77
{
88
Q_OBJECT
99
public:
10-
explicit ApkDecompileWorker(const QString &apk, const QString &folder, const bool smali, const bool resources, const bool java, const QString &frameworkTag = QString(), QObject *parent = nullptr);
10+
explicit ApkDecompileWorker(const QString &apk, const QString &folder, const bool smali, const bool resources, const bool java, const QString &frameworkTag = QString(), const QString &extraArguments = QString(), QObject *parent = nullptr);
1111
void decompile();
1212
private:
1313
QString m_Apk;
@@ -16,6 +16,7 @@ class ApkDecompileWorker : public QObject
1616
bool m_Resources;
1717
bool m_Smali;
1818
QString m_FrameworkTag;
19+
QString m_ExtraArguments;
1920
signals:
2021
void decompileFailed(const QString &apk);
2122
void decompileFinished(const QString &apk, const QString &folder);

sources/apkrecompileworker.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
#include <QDebug>
2+
#include <QRegularExpression>
23
#include "apkrecompileworker.h"
34
#include "processutils.h"
45

5-
ApkRecompileWorker::ApkRecompileWorker(const QString &folder, bool aapt2, QObject *parent)
6-
: QObject(parent), m_Aapt2(aapt2), m_Folder(folder)
6+
ApkRecompileWorker::ApkRecompileWorker(const QString &folder, bool aapt2, const QString &extraArguments, QObject *parent)
7+
: QObject(parent), m_Aapt2(aapt2), m_Folder(folder), m_ExtraArguments(extraArguments)
78
{
89
}
910

@@ -28,6 +29,11 @@ void ApkRecompileWorker::recompile()
2829
if (!m_Aapt2) {
2930
args << "--use-aapt1";
3031
}
32+
// Parse and add extra arguments
33+
if (!m_ExtraArguments.isEmpty()) {
34+
QStringList extraArgs = m_ExtraArguments.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts);
35+
args << extraArgs;
36+
}
3137
ProcessResult result = ProcessUtils::runCommand(java, args);
3238
#ifdef QT_DEBUG
3339
qDebug() << "Apktool returned code" << result.code;

sources/apkrecompileworker.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@ class ApkRecompileWorker : public QObject
77
{
88
Q_OBJECT
99
public:
10-
explicit ApkRecompileWorker(const QString &folder, bool aapt2, QObject *parent = nullptr);
10+
explicit ApkRecompileWorker(const QString &folder, bool aapt2, const QString &extraArguments = QString(), QObject *parent = nullptr);
1111
void recompile();
1212
private:
1313
bool m_Aapt2;
1414
QString m_Folder;
15+
QString m_ExtraArguments;
1516
signals:
1617
void finished();
1718
void recompileFailed(const QString &folder);

sources/mainwindow.cpp

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <QCloseEvent>
44
#include <QDebug>
55
#include <QDesktopServices>
6+
#include <QEvent>
67
#include <QDir>
78
#include <QDockWidget>
89
#include <QFile>
@@ -21,7 +22,6 @@
2122
#include <QTextDocumentFragment>
2223
#include <QThread>
2324
#include <QTimer>
24-
#include <QToolBar>
2525
#include <QTreeWidgetItem>
2626
#include <QUrl>
2727
#include "adbinstallworker.h"
@@ -64,7 +64,9 @@ MainWindow::MainWindow(const QMap<QString, QString> &versions, QWidget *parent)
6464
addDockWidget(Qt::LeftDockWidgetArea, m_DockProject = buildProjectsDock());
6565
addDockWidget(Qt::LeftDockWidgetArea, m_DockFiles = buildFilesDock());
6666
addDockWidget(Qt::BottomDockWidgetArea, m_DockConsole = buildConsoleDock());
67-
addToolBar(Qt::LeftToolBarArea, buildMainToolBar());
67+
addToolBar(Qt::LeftToolBarArea, m_MainToolBar = buildMainToolBar());
68+
// Install event filter to sync menu action when toolbar is hidden/shown via context menu
69+
m_MainToolBar->installEventFilter(this);
6870
setCentralWidget(buildCentralWidget());
6971
setMenuBar(buildMenuBar());
7072
setMinimumSize(WINDOW_WIDTH, WINDOW_HEIGHT);
@@ -84,6 +86,7 @@ MainWindow::MainWindow(const QMap<QString, QString> &versions, QWidget *parent)
8486
m_ActionViewProject->setChecked(m_DockProject->isVisible());
8587
m_ActionViewFiles->setChecked(m_DockFiles->isVisible());
8688
m_ActionViewConsole->setChecked(m_DockConsole->isVisible());
89+
m_ActionViewToolBar->setChecked(m_MainToolBar->isVisible());
8790

8891
// Ensure main window gets focus instead of search boxes
8992
setFocus();
@@ -317,6 +320,10 @@ QMenuBar *MainWindow::buildMenuBar()
317320
m_ActionViewConsole->setCheckable(true);
318321
connect(m_ActionViewConsole, &QAction::toggled, m_DockConsole, &QDockWidget::setVisible);
319322
connect(m_DockConsole, &QDockWidget::visibilityChanged, m_ActionViewConsole, &QAction::setChecked);
323+
view->addSeparator();
324+
m_ActionViewToolBar = view->addAction(tr("Sidebar"));
325+
m_ActionViewToolBar->setCheckable(true);
326+
connect(m_ActionViewToolBar, &QAction::toggled, m_MainToolBar, &QToolBar::setVisible);
320327
auto project = menubar->addMenu(tr("Project"));
321328
m_ActionBuild1 = project->addAction(tr("Build"), this, &MainWindow::handleActionBuild);
322329
m_ActionBuild1->setEnabled(false);
@@ -398,6 +405,17 @@ QStatusBar *MainWindow::buildStatusBar(const QMap<QString, QString> &versions)
398405
return statusbar;
399406
}
400407

408+
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
409+
{
410+
// Sync toolbar menu action when toolbar is shown/hidden via context menu
411+
if (obj == m_MainToolBar && m_ActionViewToolBar) {
412+
if (event->type() == QEvent::Show || event->type() == QEvent::Hide) {
413+
m_ActionViewToolBar->setChecked(m_MainToolBar->isVisible());
414+
}
415+
}
416+
return QMainWindow::eventFilter(obj, event);
417+
}
418+
401419
void MainWindow::closeEvent(QCloseEvent *event)
402420
{
403421
Q_UNUSED(event)
@@ -477,7 +495,7 @@ void MainWindow::openApkFile(const QString &apkPath)
477495
auto dialog = new ApkDecompileDialog(QDir::toNativeSeparators(apkPath), this);
478496
if (dialog->exec() == QDialog::Accepted) {
479497
auto thread = new QThread();
480-
auto worker = new ApkDecompileWorker(dialog->apk(), dialog->folder(), dialog->smali(), dialog->resources(), dialog->java(), dialog->frameworkTag());
498+
auto worker = new ApkDecompileWorker(dialog->apk(), dialog->folder(), dialog->smali(), dialog->resources(), dialog->java(), dialog->frameworkTag(), dialog->extraArguments());
481499
worker->moveToThread(thread);
482500
connect(worker, &ApkDecompileWorker::decompileFailed, this, &MainWindow::handleDecompileFailed);
483501
connect(worker, &ApkDecompileWorker::decompileFinished, this, &MainWindow::handleDecompileFinished);
@@ -508,8 +526,22 @@ void MainWindow::handleActionBuild()
508526
}
509527
QSettings settings;
510528
auto appt2 = settings.value("use_aapt2", true).toBool();
529+
530+
// Ask for extra arguments (optional)
531+
bool ok;
532+
const QString extraArgs = QInputDialog::getText(this,
533+
tr("Extra arguments"),
534+
tr("Enter additional apktool arguments (optional):\nSeparate multiple arguments with spaces.\nExample: --force-all --no-res"),
535+
QLineEdit::Normal,
536+
QString(),
537+
&ok);
538+
QString extraArguments;
539+
if (ok && !extraArgs.trimmed().isEmpty()) {
540+
extraArguments = extraArgs.trimmed();
541+
}
542+
511543
auto thread = new QThread();
512-
auto worker = new ApkRecompileWorker(active->data(0, Qt::UserRole + 2).toString(), appt2);
544+
auto worker = new ApkRecompileWorker(active->data(0, Qt::UserRole + 2).toString(), appt2, extraArguments);
513545
worker->moveToThread(thread);
514546
connect(worker, &ApkRecompileWorker::recompileFailed, this, &MainWindow::handleRecompileFailed);
515547
connect(worker, &ApkRecompileWorker::recompileFinished, this, &MainWindow::handleRecompileFinished);

sources/mainwindow.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <QSortFilterProxyModel>
1414
#include <QStandardItemModel>
1515
#include <QTextEdit>
16+
#include <QToolBar>
1617
#include <QTreeWidget>
1718
#include <QVBoxLayout>
1819
#include "findreplacedialog.h"
@@ -32,6 +33,7 @@ class MainWindow : public QMainWindow
3233
void openApkFile(const QString &apkPath);
3334
protected:
3435
void closeEvent(QCloseEvent *event);
36+
bool eventFilter(QObject *obj, QEvent *event) override;
3537
private:
3638
QAction *m_ActionBuild1;
3739
QAction *m_ActionBuild2;
@@ -53,6 +55,7 @@ class MainWindow : public QMainWindow
5355
QAction *m_ActionViewProject;
5456
QAction *m_ActionViewFiles;
5557
QAction *m_ActionViewConsole;
58+
QAction *m_ActionViewToolBar;
5659
QStackedWidget *m_CentralStack;
5760
QDockWidget *m_DockProject;
5861
QDockWidget *m_DockFiles;
@@ -64,6 +67,7 @@ class MainWindow : public QMainWindow
6467
QLineEdit *m_SearchFiles;
6568
QLineEdit *m_SearchProjects;
6669
QListView *m_ListOpenFiles;
70+
QToolBar *m_MainToolBar;
6771
QStandardItemModel *m_ModelOpenFiles;
6872
QSortFilterProxyModel *m_FilesProxyModel;
6973
QProgressDialog *m_ProgressDialog;

0 commit comments

Comments
 (0)