Skip to content

Commit

Permalink
fix(gui): confirm directory loading on file open (#1462)
Browse files Browse the repository at this point in the history
  • Loading branch information
skylot committed Apr 25, 2022
1 parent 4586015 commit e02434d
Show file tree
Hide file tree
Showing 8 changed files with 227 additions and 109 deletions.
157 changes: 48 additions & 109 deletions jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import java.awt.Font;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.HeadlessException;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.dnd.DnDConstants;
Expand Down Expand Up @@ -46,8 +45,6 @@
import javax.swing.Box;
import javax.swing.ImageIcon;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
Expand All @@ -67,7 +64,6 @@
import javax.swing.event.MenuListener;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeWillExpandListener;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
Expand All @@ -88,6 +84,7 @@
import jadx.api.ResourceFile;
import jadx.api.plugins.utils.CommonFileUtils;
import jadx.core.Jadx;
import jadx.core.utils.ListUtils;
import jadx.core.utils.StringUtils;
import jadx.core.utils.Utils;
import jadx.core.utils.files.FileUtils;
Expand Down Expand Up @@ -118,6 +115,7 @@
import jadx.gui.ui.codearea.EditorViewState;
import jadx.gui.ui.dialog.ADBDialog;
import jadx.gui.ui.dialog.AboutDialog;
import jadx.gui.ui.dialog.FileDialog;
import jadx.gui.ui.dialog.LogViewerDialog;
import jadx.gui.ui.dialog.RenameDialog;
import jadx.gui.ui.dialog.SearchDialog;
Expand All @@ -144,7 +142,6 @@

import static io.reactivex.internal.functions.Functions.EMPTY_RUNNABLE;
import static jadx.gui.utils.FileUtils.fileNamesToPaths;
import static jadx.gui.utils.FileUtils.toPaths;
import static javax.swing.KeyStroke.getKeyStroke;

public class MainWindow extends JFrame {
Expand Down Expand Up @@ -284,63 +281,22 @@ public void onUpdate(Release r) {
}

public void openFileOrProject() {
String title = NLS.str("file.open_title");
JFileChooser fileChooser = buildFileChooser(false, title);
int ret = fileChooser.showDialog(this, title);
if (ret == JFileChooser.APPROVE_OPTION) {
settings.setLastOpenFilePath(fileChooser.getCurrentDirectory().toPath());
open(toPaths(fileChooser.getSelectedFiles()));
FileDialog fileDialog = new FileDialog(this, FileDialog.OpenMode.OPEN);
List<Path> openPaths = fileDialog.show();
if (!openPaths.isEmpty()) {
settings.setLastOpenFilePath(fileDialog.getCurrentDir());
open(openPaths);
}
}

public void addFiles() {
String title = NLS.str("file.add_files_action");
JFileChooser fileChooser = buildFileChooser(true, title);
int ret = fileChooser.showDialog(this, title);
if (ret == JFileChooser.APPROVE_OPTION) {
List<Path> paths = new ArrayList<>(wrapper.getOpenPaths());
paths.addAll(toPaths(fileChooser.getSelectedFiles()));
open(paths);
FileDialog fileDialog = new FileDialog(this, FileDialog.OpenMode.ADD);
List<Path> addPaths = fileDialog.show();
if (!addPaths.isEmpty()) {
open(ListUtils.distinctMergeSortedLists(addPaths, wrapper.getOpenPaths()));
}
}

private JFileChooser buildFileChooser(boolean addFiles, String toolTipText) {
String[] exts;
if (addFiles) {
exts = new String[] { "apk", "dex", "jar", "class", "smali", "zip", "aar", "arsc" };
} else {
exts = new String[] { JadxProject.PROJECT_EXTENSION, "apk", "dex", "jar", "class", "smali", "zip", "aar", "arsc", "aab" };
}
String description = "Supported files: (" + Utils.arrayToStr(exts) + ')';

JFileChooser fileChooser = new JFileChooser() {
@Override
protected JDialog createDialog(Component parent) throws HeadlessException {
JDialog dialog = super.createDialog(parent);
dialog.setLocationRelativeTo(null);
settings.loadWindowPos(dialog);
dialog.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
settings.saveWindowPos(dialog);
super.windowClosed(e);
}
});
return dialog;
}
};
fileChooser.setAcceptAllFileFilterUsed(true);
fileChooser.setFileFilter(new FileNameExtensionFilter(description, exts));
fileChooser.setMultiSelectionEnabled(true);
fileChooser.setToolTipText(toolTipText);
fileChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
Path currentDirectory = settings.getLastOpenFilePath();
if (currentDirectory != null) {
fileChooser.setCurrentDirectory(currentDirectory.toFile());
}
return fileChooser;
}

private void newProject() {
if (!ensureProjectIsSaved()) {
return;
Expand All @@ -359,45 +315,35 @@ private void saveProject() {
}

private void saveProjectAs() {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setAcceptAllFileFilterUsed(true);
String[] exts = { JadxProject.PROJECT_EXTENSION };
String description = "supported files: " + Arrays.toString(exts).replace('[', '(').replace(']', ')');
fileChooser.setFileFilter(new FileNameExtensionFilter(description, exts));
fileChooser.setToolTipText(NLS.str("file.save_project"));
Path currentDirectory = settings.getLastSaveProjectPath();
if (currentDirectory != null) {
fileChooser.setCurrentDirectory(currentDirectory.toFile());
}
if (this.project.getFilePaths().size() == 1) {
FileDialog fileDialog = new FileDialog(this, FileDialog.OpenMode.SAVE_PROJECT);
if (project.getFilePaths().size() == 1) {
// If there is only one file loaded we suggest saving the jadx project file next to the loaded file
Path loadedFile = this.project.getFilePaths().get(0);
String fileName = loadedFile.getFileName() + "." + JadxProject.PROJECT_EXTENSION;
fileChooser.setSelectedFile(loadedFile.resolveSibling(fileName).toFile());
fileDialog.setSelectedFile(loadedFile.resolveSibling(fileName));
}
int ret = fileChooser.showSaveDialog(mainPanel);
if (ret == JFileChooser.APPROVE_OPTION) {
settings.setLastSaveProjectPath(fileChooser.getCurrentDirectory().toPath());

Path path = fileChooser.getSelectedFile().toPath();
if (!path.getFileName().toString().toLowerCase(Locale.ROOT).endsWith(JadxProject.PROJECT_EXTENSION)) {
path = path.resolveSibling(path.getFileName() + "." + JadxProject.PROJECT_EXTENSION);
}

if (Files.exists(path)) {
int res = JOptionPane.showConfirmDialog(
this,
NLS.str("confirm.save_as_message", path.getFileName()),
NLS.str("confirm.save_as_title"),
JOptionPane.YES_NO_OPTION);
if (res == JOptionPane.NO_OPTION) {
return;
}
List<Path> saveFiles = fileDialog.show();
if (saveFiles.isEmpty()) {
return;
}
settings.setLastSaveProjectPath(fileDialog.getCurrentDir());
Path savePath = saveFiles.get(0);
if (!savePath.getFileName().toString().toLowerCase(Locale.ROOT).endsWith(JadxProject.PROJECT_EXTENSION)) {
savePath = savePath.resolveSibling(savePath.getFileName() + "." + JadxProject.PROJECT_EXTENSION);
}
if (Files.exists(savePath)) {
int res = JOptionPane.showConfirmDialog(
this,
NLS.str("confirm.save_as_message", savePath.getFileName()),
NLS.str("confirm.save_as_title"),
JOptionPane.YES_NO_OPTION);
if (res == JOptionPane.NO_OPTION) {
return;
}
project.saveAs(path);
settings.addRecentProject(path);
update();
}
project.saveAs(savePath);
settings.addRecentProject(savePath);
update();
}

void open(List<Path> paths) {
Expand Down Expand Up @@ -646,29 +592,22 @@ public void reOpenFile() {
}

private void saveAll(boolean export) {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
fileChooser.setToolTipText(NLS.str("file.save_all_msg"));

Path currentDirectory = settings.getLastSaveFilePath();
if (currentDirectory != null) {
fileChooser.setCurrentDirectory(currentDirectory.toFile());
FileDialog fileDialog = new FileDialog(this, FileDialog.OpenMode.EXPORT);
List<Path> saveDirs = fileDialog.show();
if (saveDirs.isEmpty()) {
return;
}

int ret = fileChooser.showSaveDialog(mainPanel);
if (ret == JFileChooser.APPROVE_OPTION) {
JadxArgs decompilerArgs = wrapper.getArgs();
decompilerArgs.setExportAsGradleProject(export);
if (export) {
decompilerArgs.setSkipSources(false);
decompilerArgs.setSkipResources(false);
} else {
decompilerArgs.setSkipSources(settings.isSkipSources());
decompilerArgs.setSkipResources(settings.isSkipResources());
}
settings.setLastSaveFilePath(fileChooser.getCurrentDirectory().toPath());
backgroundExecutor.execute(new ExportTask(this, wrapper, fileChooser.getSelectedFile()));
JadxArgs decompilerArgs = wrapper.getArgs();
decompilerArgs.setExportAsGradleProject(export);
if (export) {
decompilerArgs.setSkipSources(false);
decompilerArgs.setSkipResources(false);
} else {
decompilerArgs.setSkipSources(settings.isSkipSources());
decompilerArgs.setSkipResources(settings.isSkipResources());
}
settings.setLastSaveFilePath(fileDialog.getCurrentDir());
backgroundExecutor.execute(new ExportTask(this, wrapper, saveDirs.get(0).toFile()));
}

public void initTree() {
Expand Down
155 changes: 155 additions & 0 deletions jadx-gui/src/main/java/jadx/gui/ui/dialog/FileDialog.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package jadx.gui.ui.dialog;

import java.awt.Component;
import java.awt.HeadlessException;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileNameExtensionFilter;

import org.jetbrains.annotations.Nullable;

import jadx.core.utils.Utils;
import jadx.gui.settings.JadxProject;
import jadx.gui.ui.MainWindow;
import jadx.gui.utils.FileUtils;
import jadx.gui.utils.NLS;

public class FileDialog {

public enum OpenMode {
OPEN, ADD, SAVE_PROJECT, EXPORT
}

private final MainWindow mainWindow;

private boolean isOpen;
private String title;
private List<String> fileExtList;
private int selectionMode = JFileChooser.FILES_AND_DIRECTORIES;
private @Nullable Path currentDir;
private @Nullable Path selectedFile;

public FileDialog(MainWindow mainWindow, OpenMode mode) {
this.mainWindow = mainWindow;
initForMode(mode);
}

public List<Path> show() {
FileChooser fileChooser = buildFileChooser();
int ret = isOpen ? fileChooser.showOpenDialog(mainWindow) : fileChooser.showSaveDialog(mainWindow);
if (ret != JFileChooser.APPROVE_OPTION) {
return Collections.emptyList();
}
currentDir = fileChooser.getCurrentDirectory().toPath();
return FileUtils.toPaths(fileChooser.getSelectedFiles());
}

public Path getCurrentDir() {
return currentDir;
}

public void setSelectedFile(Path path) {
this.selectedFile = path;
}

private void initForMode(OpenMode mode) {
switch (mode) {
case OPEN:
case ADD:
fileExtList = new ArrayList<>(Arrays.asList("apk", "dex", "jar", "class", "smali", "zip", "aar", "arsc"));
if (mode == OpenMode.OPEN) {
fileExtList.addAll(Arrays.asList(JadxProject.PROJECT_EXTENSION, "aab"));
title = NLS.str("file.open_title");
} else {
title = NLS.str("file.add_files_action");
}
selectionMode = JFileChooser.FILES_AND_DIRECTORIES;
currentDir = mainWindow.getSettings().getLastOpenFilePath();
isOpen = true;
break;

case SAVE_PROJECT:
title = NLS.str("file.save_project");
fileExtList = Collections.singletonList(JadxProject.PROJECT_EXTENSION);
selectionMode = JFileChooser.FILES_ONLY;
currentDir = mainWindow.getSettings().getLastSaveFilePath();
isOpen = false;
break;

case EXPORT:
title = NLS.str("file.save_all_msg");
fileExtList = Collections.emptyList();
selectionMode = JFileChooser.DIRECTORIES_ONLY;
currentDir = mainWindow.getSettings().getLastSaveFilePath();
isOpen = false;
break;
}
}

private FileChooser buildFileChooser() {
FileChooser fileChooser = new FileChooser();
fileChooser.setToolTipText(title);
fileChooser.setFileSelectionMode(selectionMode);
fileChooser.setMultiSelectionEnabled(isOpen);
fileChooser.setAcceptAllFileFilterUsed(true);
if (!fileExtList.isEmpty()) {
String description = NLS.str("file_dialog.supported_files") + ": (" + Utils.listToString(fileExtList) + ')';
fileChooser.setFileFilter(new FileNameExtensionFilter(description, fileExtList.toArray(new String[0])));
}
if (currentDir != null) {
fileChooser.setCurrentDirectory(currentDir.toFile());
}
if (selectedFile != null) {
fileChooser.setSelectedFile(selectedFile.toFile());
}
return fileChooser;
}

private class FileChooser extends JFileChooser {
@Override
protected JDialog createDialog(Component parent) throws HeadlessException {
JDialog dialog = super.createDialog(parent);
dialog.setTitle(title);
dialog.setLocationRelativeTo(null);
mainWindow.getSettings().loadWindowPos(dialog);
dialog.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
mainWindow.getSettings().saveWindowPos(dialog);
super.windowClosed(e);
}
});
return dialog;
}

@Override
public void approveSelection() {
if (selectionMode == FILES_AND_DIRECTORIES) {
File currentFile = getSelectedFile();
if (currentFile.isDirectory()) {
int option = JOptionPane.showConfirmDialog(
mainWindow,
NLS.str("file_dialog.load_dir_confirm") + "\n " + currentFile,
NLS.str("file_dialog.load_dir_title"),
JOptionPane.YES_NO_OPTION);
if (option != JOptionPane.YES_OPTION) {
this.setCurrentDirectory(currentFile);
this.updateUI();
return;
}
}
}
super.approveSelection();
}
}
}
Loading

0 comments on commit e02434d

Please sign in to comment.