gui: add preferences dialog
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
package jadx.gui;
|
||||
|
||||
import jadx.cli.JadxCLIArgs;
|
||||
import jadx.gui.settings.JadxSettings;
|
||||
import jadx.gui.settings.JadxSettingsAdapter;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.WindowConstants;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -15,26 +15,16 @@ public class JadxGUI {
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
final JadxCLIArgs jadxArgs = new JadxCLIArgs();
|
||||
final JadxSettings jadxArgs = JadxSettingsAdapter.load();
|
||||
// overwrite loaded settings by command line arguments
|
||||
if (!jadxArgs.processArgs(args)) {
|
||||
return;
|
||||
}
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
JadxWrapper wrapper = new JadxWrapper(jadxArgs);
|
||||
MainWindow window = new MainWindow(wrapper);
|
||||
window.pack();
|
||||
window.setLocationAndPosition();
|
||||
window.setVisible(true);
|
||||
window.setLocationRelativeTo(null);
|
||||
window.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
|
||||
|
||||
if (jadxArgs.getInput().isEmpty()) {
|
||||
window.openFile();
|
||||
} else {
|
||||
window.openFile(jadxArgs.getInput().get(0));
|
||||
}
|
||||
MainWindow window = new MainWindow(jadxArgs);
|
||||
window.open();
|
||||
}
|
||||
});
|
||||
} catch (Throwable e) {
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
package jadx.gui.settings;
|
||||
|
||||
import jadx.cli.JadxCLIArgs;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class JadxSettings extends JadxCLIArgs {
|
||||
|
||||
private static final String USER_HOME = System.getProperty("user.home");
|
||||
private static final int RECENT_FILES_COUNT = 15;
|
||||
|
||||
static final Set<String> SKIP_FIELDS = new HashSet<String>(Arrays.asList(
|
||||
"files", "input", "outputDir", "printHelp"
|
||||
));
|
||||
|
||||
private String lastOpenFilePath = USER_HOME;
|
||||
private String lastSaveFilePath = USER_HOME;
|
||||
private boolean flattenPackage = false;
|
||||
private boolean checkForUpdates = true;
|
||||
private List<String> recentFiles = new ArrayList<String>();
|
||||
|
||||
public void sync() {
|
||||
JadxSettingsAdapter.store(this);
|
||||
}
|
||||
|
||||
public String getLastOpenFilePath() {
|
||||
return lastOpenFilePath;
|
||||
}
|
||||
|
||||
public void setLastOpenFilePath(String lastOpenFilePath) {
|
||||
this.lastOpenFilePath = lastOpenFilePath;
|
||||
sync();
|
||||
}
|
||||
|
||||
public String getLastSaveFilePath() {
|
||||
return lastSaveFilePath;
|
||||
}
|
||||
|
||||
public void setLastSaveFilePath(String lastSaveFilePath) {
|
||||
this.lastSaveFilePath = lastSaveFilePath;
|
||||
sync();
|
||||
}
|
||||
|
||||
public boolean isFlattenPackage() {
|
||||
return flattenPackage;
|
||||
}
|
||||
|
||||
public void setFlattenPackage(boolean flattenPackage) {
|
||||
this.flattenPackage = flattenPackage;
|
||||
sync();
|
||||
}
|
||||
|
||||
public boolean isCheckForUpdates() {
|
||||
return checkForUpdates;
|
||||
}
|
||||
|
||||
public void setCheckForUpdates(boolean checkForUpdates) {
|
||||
this.checkForUpdates = checkForUpdates;
|
||||
sync();
|
||||
}
|
||||
|
||||
public Iterable<String> getRecentFiles() {
|
||||
return recentFiles;
|
||||
}
|
||||
|
||||
public void addRecentFile(String filePath) {
|
||||
if (recentFiles.contains(filePath)) {
|
||||
return;
|
||||
}
|
||||
recentFiles.add(filePath);
|
||||
int count = recentFiles.size();
|
||||
if (count > RECENT_FILES_COUNT) {
|
||||
recentFiles.subList(0, count - RECENT_FILES_COUNT).clear();
|
||||
}
|
||||
sync();
|
||||
}
|
||||
|
||||
public void setThreadsCount(int threadsCount) {
|
||||
this.threadsCount = threadsCount;
|
||||
}
|
||||
|
||||
public void setFallbackMode(boolean fallbackMode) {
|
||||
this.fallbackMode = fallbackMode;
|
||||
}
|
||||
|
||||
public void setSkipResources(boolean skipResources) {
|
||||
this.skipResources = skipResources;
|
||||
}
|
||||
|
||||
public void setSkipSources(boolean skipSources) {
|
||||
this.skipSources = skipSources;
|
||||
}
|
||||
|
||||
public void setShowInconsistentCode(boolean showInconsistentCode) {
|
||||
this.showInconsistentCode = showInconsistentCode;
|
||||
}
|
||||
|
||||
public void setCfgOutput(boolean cfgOutput) {
|
||||
this.cfgOutput = cfgOutput;
|
||||
}
|
||||
|
||||
public void setRawCfgOutput(boolean rawCfgOutput) {
|
||||
this.rawCfgOutput = rawCfgOutput;
|
||||
}
|
||||
|
||||
public void setVerbose(boolean verbose) {
|
||||
this.verbose = verbose;
|
||||
}
|
||||
|
||||
public void setDeobfuscationOn(boolean deobfuscationOn) {
|
||||
this.deobfuscationOn = deobfuscationOn;
|
||||
}
|
||||
|
||||
public void setDeobfuscationMinLength(int deobfuscationMinLength) {
|
||||
this.deobfuscationMinLength = deobfuscationMinLength;
|
||||
}
|
||||
|
||||
public void setDeobfuscationMaxLength(int deobfuscationMaxLength) {
|
||||
this.deobfuscationMaxLength = deobfuscationMaxLength;
|
||||
}
|
||||
|
||||
public void setDeobfuscationForceSave(boolean deobfuscationForceSave) {
|
||||
this.deobfuscationForceSave = deobfuscationForceSave;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package jadx.gui.settings;
|
||||
|
||||
import jadx.gui.JadxGUI;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.prefs.Preferences;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.ExclusionStrategy;
|
||||
import com.google.gson.FieldAttributes;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.InstanceCreator;
|
||||
|
||||
public class JadxSettingsAdapter {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JadxSettingsAdapter.class);
|
||||
|
||||
private static final String JADX_GUI_KEY = "jadx.gui.settings";
|
||||
|
||||
private static final Preferences PREFS = Preferences.userNodeForPackage(JadxGUI.class);
|
||||
|
||||
private static ExclusionStrategy EXCLUDE_FIELDS = new ExclusionStrategy() {
|
||||
@Override
|
||||
public boolean shouldSkipField(FieldAttributes f) {
|
||||
return JadxSettings.SKIP_FIELDS.contains(f.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldSkipClass(Class<?> clazz) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
private static final GsonBuilder GSON_BUILDER = new GsonBuilder().setExclusionStrategies(EXCLUDE_FIELDS);
|
||||
private static final Gson GSON = GSON_BUILDER.create();
|
||||
|
||||
private JadxSettingsAdapter() {
|
||||
}
|
||||
|
||||
public static JadxSettings load() {
|
||||
try {
|
||||
String jsonSettings = PREFS.get(JADX_GUI_KEY, "");
|
||||
JadxSettings settings = fromString(jsonSettings);
|
||||
if (settings == null) {
|
||||
return new JadxSettings();
|
||||
}
|
||||
LOG.info("Loaded settings: {}", makeString(settings));
|
||||
return settings;
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error load settings", e);
|
||||
return new JadxSettings();
|
||||
}
|
||||
}
|
||||
|
||||
public static void store(JadxSettings settings) {
|
||||
try {
|
||||
String jsonSettings = makeString(settings);
|
||||
LOG.debug("Saving settings: {}", jsonSettings);
|
||||
PREFS.put(JADX_GUI_KEY, jsonSettings);
|
||||
PREFS.sync();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error store settings", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static JadxSettings fromString(String jsonSettings) {
|
||||
return GSON.fromJson(jsonSettings, JadxSettings.class);
|
||||
}
|
||||
|
||||
public static String makeString(JadxSettings settings) {
|
||||
return GSON.toJson(settings);
|
||||
}
|
||||
|
||||
public static void fill(JadxSettings settings, String jsonStr) {
|
||||
populate(GSON_BUILDER, jsonStr, JadxSettings.class, settings);
|
||||
}
|
||||
|
||||
private static <T> void populate(GsonBuilder builder, String json, Class<T> type, final T into) {
|
||||
builder.registerTypeAdapter(type, new InstanceCreator<T>() {
|
||||
@Override
|
||||
public T createInstance(Type t) {
|
||||
return into;
|
||||
}
|
||||
}).create().fromJson(json, type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
package jadx.gui.settings;
|
||||
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.utils.NLS;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JSpinner;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.WindowConstants;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.Insets;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.ItemEvent;
|
||||
import java.awt.event.ItemListener;
|
||||
|
||||
public class JadxSettingsWindow extends JDialog {
|
||||
private static final long serialVersionUID = -1804570470377354148L;
|
||||
|
||||
private final MainWindow mainWindow;
|
||||
private final JadxSettings settings;
|
||||
private final String startSettings;
|
||||
|
||||
private boolean needReload = false;
|
||||
|
||||
public JadxSettingsWindow(MainWindow mainWindow, JadxSettings settings) {
|
||||
this.mainWindow = mainWindow;
|
||||
this.settings = settings;
|
||||
this.startSettings = JadxSettingsAdapter.makeString(settings);
|
||||
|
||||
initUI();
|
||||
}
|
||||
|
||||
private void initUI() {
|
||||
JPanel panel = new JPanel();
|
||||
panel.setLayout(new GridLayout(0, 1, 10, 5));
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||
panel.add(makeDeobfuscationGroup());
|
||||
panel.add(makeOtherGroup());
|
||||
|
||||
JButton saveBtn = new JButton(NLS.str("preferences.save"));
|
||||
saveBtn.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
settings.sync();
|
||||
if (needReload) {
|
||||
mainWindow.reOpenFile();
|
||||
}
|
||||
dispose();
|
||||
}
|
||||
});
|
||||
JButton cancelButton = new JButton(NLS.str("preferences.cancel"));
|
||||
cancelButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
JadxSettingsAdapter.fill(settings, startSettings);
|
||||
dispose();
|
||||
}
|
||||
});
|
||||
|
||||
JPanel buttonPane = new JPanel();
|
||||
buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));
|
||||
buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
|
||||
buttonPane.add(Box.createHorizontalGlue());
|
||||
buttonPane.add(saveBtn);
|
||||
buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
|
||||
buttonPane.add(cancelButton);
|
||||
|
||||
Container contentPane = getContentPane();
|
||||
contentPane.add(panel, BorderLayout.CENTER);
|
||||
contentPane.add(buttonPane, BorderLayout.PAGE_END);
|
||||
getRootPane().setDefaultButton(saveBtn);
|
||||
|
||||
setTitle(NLS.str("preferences.title"));
|
||||
setSize(400, 550);
|
||||
setLocationRelativeTo(null);
|
||||
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
|
||||
setModalityType(ModalityType.APPLICATION_MODAL);
|
||||
pack();
|
||||
}
|
||||
|
||||
private SettingsGroup makeDeobfuscationGroup() {
|
||||
|
||||
JCheckBox deobfOn = new JCheckBox();
|
||||
deobfOn.setSelected(settings.isDeobfuscationOn());
|
||||
deobfOn.addItemListener(new ItemListener() {
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
settings.setDeobfuscationOn(e.getStateChange() == ItemEvent.SELECTED);
|
||||
needReload();
|
||||
}
|
||||
});
|
||||
|
||||
JCheckBox deobfForce = new JCheckBox();
|
||||
deobfForce.setSelected(settings.isDeobfuscationForceSave());
|
||||
deobfForce.addItemListener(new ItemListener() {
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
settings.setDeobfuscationForceSave(e.getStateChange() == ItemEvent.SELECTED);
|
||||
needReload();
|
||||
}
|
||||
});
|
||||
|
||||
final JSpinner minLen = new JSpinner();
|
||||
minLen.setValue(settings.getDeobfuscationMinLength());
|
||||
minLen.addChangeListener(new ChangeListener() {
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
settings.setDeobfuscationMinLength((Integer) minLen.getValue());
|
||||
needReload();
|
||||
}
|
||||
});
|
||||
|
||||
final JSpinner maxLen = new JSpinner();
|
||||
maxLen.setValue(settings.getDeobfuscationMaxLength());
|
||||
maxLen.addChangeListener(new ChangeListener() {
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
settings.setDeobfuscationMaxLength((Integer) maxLen.getValue());
|
||||
needReload();
|
||||
}
|
||||
});
|
||||
|
||||
SettingsGroup deobfGroup = new SettingsGroup(NLS.str("preferences.deobfuscation"));
|
||||
deobfGroup.addRow(NLS.str("preferences.deobfuscation_on"), deobfOn);
|
||||
deobfGroup.addRow(NLS.str("preferences.deobfuscation_force"), deobfForce);
|
||||
deobfGroup.addRow(NLS.str("preferences.deobfuscation_min_len"), minLen);
|
||||
deobfGroup.addRow(NLS.str("preferences.deobfuscation_max_len"), maxLen);
|
||||
deobfGroup.end();
|
||||
return deobfGroup;
|
||||
}
|
||||
|
||||
private SettingsGroup makeOtherGroup() {
|
||||
JCheckBox update = new JCheckBox();
|
||||
update.setSelected(settings.isCheckForUpdates());
|
||||
update.addItemListener(new ItemListener() {
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
settings.setCheckForUpdates(e.getStateChange() == ItemEvent.SELECTED);
|
||||
}
|
||||
});
|
||||
|
||||
JCheckBox fallback = new JCheckBox();
|
||||
fallback.setSelected(settings.isFallbackMode());
|
||||
fallback.addItemListener(new ItemListener() {
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
settings.setFallbackMode(e.getStateChange() == ItemEvent.SELECTED);
|
||||
needReload();
|
||||
}
|
||||
});
|
||||
|
||||
JCheckBox showInconsistentCode = new JCheckBox();
|
||||
showInconsistentCode.setSelected(settings.isShowInconsistentCode());
|
||||
showInconsistentCode.addItemListener(new ItemListener() {
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
settings.setShowInconsistentCode(e.getStateChange() == ItemEvent.SELECTED);
|
||||
needReload();
|
||||
}
|
||||
});
|
||||
|
||||
JCheckBox resourceDecode = new JCheckBox();
|
||||
resourceDecode.setSelected(settings.isSkipResources());
|
||||
resourceDecode.addItemListener(new ItemListener() {
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
settings.setSkipResources(e.getStateChange() == ItemEvent.SELECTED);
|
||||
needReload();
|
||||
}
|
||||
});
|
||||
|
||||
final JSpinner threadsCount = new JSpinner();
|
||||
threadsCount.setValue(settings.getThreadsCount());
|
||||
threadsCount.addChangeListener(new ChangeListener() {
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
settings.setThreadsCount((Integer) threadsCount.getValue());
|
||||
}
|
||||
});
|
||||
|
||||
JCheckBox cfg = new JCheckBox();
|
||||
cfg.setSelected(settings.isCFGOutput());
|
||||
cfg.addItemListener(new ItemListener() {
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
settings.setCfgOutput(e.getStateChange() == ItemEvent.SELECTED);
|
||||
needReload();
|
||||
}
|
||||
});
|
||||
|
||||
JCheckBox rawCfg = new JCheckBox();
|
||||
rawCfg.setSelected(settings.isRawCFGOutput());
|
||||
rawCfg.addItemListener(new ItemListener() {
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
settings.setRawCfgOutput(e.getStateChange() == ItemEvent.SELECTED);
|
||||
needReload();
|
||||
}
|
||||
});
|
||||
|
||||
SettingsGroup other = new SettingsGroup(NLS.str("preferences.other"));
|
||||
other.addRow(NLS.str("preferences.check_for_updates"), update);
|
||||
other.addRow(NLS.str("preferences.threads"), threadsCount);
|
||||
other.addRow(NLS.str("preferences.fallback"), fallback);
|
||||
other.addRow(NLS.str("preferences.showInconsistentCode"), showInconsistentCode);
|
||||
other.addRow(NLS.str("preferences.skipResourcesDecode"), resourceDecode);
|
||||
other.addRow(NLS.str("preferences.cfg"), cfg);
|
||||
other.addRow(NLS.str("preferences.raw_cfg"), rawCfg);
|
||||
return other;
|
||||
}
|
||||
|
||||
private void needReload() {
|
||||
needReload = true;
|
||||
}
|
||||
|
||||
private static class SettingsGroup extends JPanel {
|
||||
private static final long serialVersionUID = -6487309975896192544L;
|
||||
|
||||
private final GridBagConstraints c;
|
||||
private int row;
|
||||
|
||||
public SettingsGroup(String title) {
|
||||
setBorder(BorderFactory.createTitledBorder(title));
|
||||
setLayout(new GridBagLayout());
|
||||
c = new GridBagConstraints();
|
||||
c.insets = new Insets(5, 5, 5, 5);
|
||||
c.weighty = 1.0;
|
||||
}
|
||||
|
||||
public void addRow(String label, JComponent comp) {
|
||||
c.gridy = row++;
|
||||
JLabel jLabel = new JLabel(label);
|
||||
jLabel.setLabelFor(comp);
|
||||
jLabel.setHorizontalAlignment(SwingConstants.LEFT);
|
||||
c.gridx = 0;
|
||||
c.gridwidth = 1;
|
||||
c.anchor = GridBagConstraints.LINE_START;
|
||||
c.weightx = 0.8;
|
||||
c.fill = GridBagConstraints.NONE;
|
||||
add(jLabel, c);
|
||||
c.gridx = 1;
|
||||
c.gridwidth = GridBagConstraints.REMAINDER;
|
||||
c.anchor = GridBagConstraints.CENTER;
|
||||
c.weightx = 0.2;
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
add(comp, c);
|
||||
}
|
||||
|
||||
public void end() {
|
||||
add(Box.createVerticalGlue());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package jadx.gui.ui;
|
||||
|
||||
import jadx.gui.JadxWrapper;
|
||||
import jadx.gui.settings.JadxSettings;
|
||||
import jadx.gui.settings.JadxSettingsWindow;
|
||||
import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.treemodel.JResource;
|
||||
@@ -8,7 +10,6 @@ import jadx.gui.treemodel.JRoot;
|
||||
import jadx.gui.update.JadxUpdate;
|
||||
import jadx.gui.update.JadxUpdate.IUpdateCallback;
|
||||
import jadx.gui.update.data.Release;
|
||||
import jadx.gui.utils.JadxPreferences;
|
||||
import jadx.gui.utils.Link;
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.Position;
|
||||
@@ -31,6 +32,9 @@ import javax.swing.JToolBar;
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.ProgressMonitor;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.WindowConstants;
|
||||
import javax.swing.event.MenuEvent;
|
||||
import javax.swing.event.MenuListener;
|
||||
import javax.swing.event.TreeExpansionEvent;
|
||||
import javax.swing.event.TreeWillExpandListener;
|
||||
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||
@@ -53,6 +57,7 @@ import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -76,8 +81,11 @@ public class MainWindow extends JFrame {
|
||||
private static final ImageIcon ICON_FIND = Utils.openIcon("magnifier");
|
||||
private static final ImageIcon ICON_BACK = Utils.openIcon("icon_back");
|
||||
private static final ImageIcon ICON_FORWARD = Utils.openIcon("icon_forward");
|
||||
private static final ImageIcon ICON_PREF = Utils.openIcon("wrench");
|
||||
private static final ImageIcon ICON_DEOBF = Utils.openIcon("lock_edit");
|
||||
|
||||
private final JadxWrapper wrapper;
|
||||
private final JadxSettings settings;
|
||||
|
||||
private JPanel mainPanel;
|
||||
|
||||
@@ -88,19 +96,37 @@ public class MainWindow extends JFrame {
|
||||
|
||||
private JCheckBoxMenuItem flatPkgMenuItem;
|
||||
private JToggleButton flatPkgButton;
|
||||
private JToggleButton deobfToggleBtn;
|
||||
private boolean isFlattenPackage;
|
||||
private Link updateLink;
|
||||
|
||||
public MainWindow(JadxWrapper wrapper) {
|
||||
this.wrapper = wrapper;
|
||||
public MainWindow(JadxSettings settings) {
|
||||
this.wrapper = new JadxWrapper(settings);
|
||||
this.settings = settings;
|
||||
|
||||
initUI();
|
||||
initMenuAndToolbar();
|
||||
checkForUpdate();
|
||||
}
|
||||
|
||||
public void open() {
|
||||
pack();
|
||||
setLocationAndPosition();
|
||||
setVisible(true);
|
||||
setLocationRelativeTo(null);
|
||||
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
|
||||
|
||||
if (settings.getInput().isEmpty()) {
|
||||
openFile();
|
||||
} else {
|
||||
openFile(settings.getInput().get(0));
|
||||
}
|
||||
}
|
||||
|
||||
private void checkForUpdate() {
|
||||
// TODO: add option for disable update checks
|
||||
if (!settings.isCheckForUpdates()) {
|
||||
return;
|
||||
}
|
||||
JadxUpdate.check(new IUpdateCallback() {
|
||||
@Override
|
||||
public void onUpdate(final Release r) {
|
||||
@@ -118,38 +144,50 @@ public class MainWindow extends JFrame {
|
||||
public void openFile() {
|
||||
JFileChooser fileChooser = new JFileChooser();
|
||||
fileChooser.setAcceptAllFileFilterUsed(true);
|
||||
fileChooser.setFileFilter(new FileNameExtensionFilter("supported files", "dex", "apk", "jar"));
|
||||
String[] exts = {"apk", "dex", "jar", "class", "zip"};
|
||||
String description = "supported files: " + Arrays.toString(exts).replace('[', '(').replace(']', ')');
|
||||
fileChooser.setFileFilter(new FileNameExtensionFilter(description, exts));
|
||||
fileChooser.setToolTipText(NLS.str("file.open"));
|
||||
String currentDirectory = JadxPreferences.getLastOpenFilePath();
|
||||
String currentDirectory = settings.getLastOpenFilePath();
|
||||
if (!currentDirectory.isEmpty()) {
|
||||
fileChooser.setCurrentDirectory(new File(currentDirectory));
|
||||
}
|
||||
int ret = fileChooser.showDialog(mainPanel, NLS.str("file.open"));
|
||||
if (ret == JFileChooser.APPROVE_OPTION) {
|
||||
JadxPreferences.putLastOpenFilePath(fileChooser.getCurrentDirectory().getPath());
|
||||
settings.setLastOpenFilePath(fileChooser.getCurrentDirectory().getPath());
|
||||
openFile(fileChooser.getSelectedFile());
|
||||
}
|
||||
}
|
||||
|
||||
public void openFile(File file) {
|
||||
wrapper.openFile(file);
|
||||
deobfToggleBtn.setSelected(settings.isDeobfuscationOn());
|
||||
settings.addRecentFile(file.getAbsolutePath());
|
||||
initTree();
|
||||
setTitle(DEFAULT_TITLE + " - " + file.getName());
|
||||
}
|
||||
|
||||
public void reOpenFile() {
|
||||
File openedFile = wrapper.getOpenFile();
|
||||
if (openedFile != null) {
|
||||
tabbedPane.closeAllTabs();
|
||||
openFile(openedFile);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveAllAction() {
|
||||
JFileChooser fileChooser = new JFileChooser();
|
||||
fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
|
||||
fileChooser.setToolTipText(NLS.str("file.save_all_msg"));
|
||||
|
||||
String currentDirectory = JadxPreferences.getLastSaveFilePath();
|
||||
String currentDirectory = settings.getLastSaveFilePath();
|
||||
if (!currentDirectory.isEmpty()) {
|
||||
fileChooser.setCurrentDirectory(new File(currentDirectory));
|
||||
}
|
||||
|
||||
int ret = fileChooser.showDialog(mainPanel, NLS.str("file.select"));
|
||||
if (ret == JFileChooser.APPROVE_OPTION) {
|
||||
JadxPreferences.putLastSaveFilePath(fileChooser.getCurrentDirectory().getPath());
|
||||
settings.setLastSaveFilePath(fileChooser.getCurrentDirectory().getPath());
|
||||
ProgressMonitor progressMonitor = new ProgressMonitor(mainPanel, NLS.str("msg.saving_sources"), "", 0, 100);
|
||||
progressMonitor.setMillisToPopup(500);
|
||||
wrapper.saveAll(fileChooser.getSelectedFile(), progressMonitor);
|
||||
@@ -174,7 +212,7 @@ public class MainWindow extends JFrame {
|
||||
|
||||
private void setFlattenPackage(boolean value) {
|
||||
isFlattenPackage = value;
|
||||
JadxPreferences.putFlattenPackage(isFlattenPackage);
|
||||
settings.setFlattenPackage(isFlattenPackage);
|
||||
|
||||
flatPkgButton.setSelected(isFlattenPackage);
|
||||
flatPkgMenuItem.setState(isFlattenPackage);
|
||||
@@ -265,15 +303,35 @@ public class MainWindow extends JFrame {
|
||||
}
|
||||
});
|
||||
|
||||
JMenu recentFiles = new JMenu(NLS.str("menu.recent_files"));
|
||||
recentFiles.addMenuListener(new RecentFilesMenuListener(recentFiles));
|
||||
|
||||
JMenuItem preferences = new JMenuItem(NLS.str("menu.preferences"), ICON_PREF);
|
||||
ActionListener prefAction = new ActionListener() {
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
final JadxSettingsWindow dialog = new JadxSettingsWindow(MainWindow.this, settings);
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
dialog.setVisible(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
preferences.addActionListener(prefAction);
|
||||
|
||||
file.add(open);
|
||||
file.add(saveAll);
|
||||
file.addSeparator();
|
||||
file.add(recentFiles);
|
||||
file.addSeparator();
|
||||
file.add(preferences);
|
||||
file.addSeparator();
|
||||
file.add(exit);
|
||||
|
||||
JMenu view = new JMenu(NLS.str("menu.view"));
|
||||
view.setMnemonic(KeyEvent.VK_V);
|
||||
|
||||
isFlattenPackage = JadxPreferences.getFlattenPackage();
|
||||
isFlattenPackage = settings.isFlattenPackage();
|
||||
|
||||
flatPkgMenuItem = new JCheckBoxMenuItem(NLS.str("menu.flatten"), ICON_FLAT_PKG);
|
||||
view.add(flatPkgMenuItem);
|
||||
@@ -334,13 +392,9 @@ public class MainWindow extends JFrame {
|
||||
menuBar.add(help);
|
||||
setJMenuBar(menuBar);
|
||||
|
||||
JToolBar toolbar = new JToolBar();
|
||||
toolbar.setFloatable(false);
|
||||
|
||||
final JButton openButton = new JButton(ICON_OPEN);
|
||||
openButton.addActionListener(new OpenListener());
|
||||
openButton.setToolTipText(NLS.str("file.open"));
|
||||
toolbar.add(openButton);
|
||||
|
||||
final JButton saveAllButton = new JButton(ICON_SAVE_ALL);
|
||||
saveAllButton.addActionListener(new ActionListener() {
|
||||
@@ -350,9 +404,6 @@ public class MainWindow extends JFrame {
|
||||
}
|
||||
});
|
||||
saveAllButton.setToolTipText(NLS.str("file.save_all"));
|
||||
toolbar.add(saveAllButton);
|
||||
|
||||
toolbar.addSeparator();
|
||||
|
||||
final JButton syncButton = new JButton(ICON_SYNC);
|
||||
syncButton.addActionListener(new ActionListener() {
|
||||
@@ -362,7 +413,6 @@ public class MainWindow extends JFrame {
|
||||
}
|
||||
});
|
||||
syncButton.setToolTipText(NLS.str("menu.sync"));
|
||||
toolbar.add(syncButton);
|
||||
|
||||
flatPkgButton = new JToggleButton(ICON_FLAT_PKG);
|
||||
flatPkgButton.setSelected(isFlattenPackage);
|
||||
@@ -376,20 +426,14 @@ public class MainWindow extends JFrame {
|
||||
flatPkgMenuItem.addActionListener(flatPkgAction);
|
||||
|
||||
flatPkgButton.setToolTipText(NLS.str("menu.flatten"));
|
||||
toolbar.add(flatPkgButton);
|
||||
toolbar.addSeparator();
|
||||
|
||||
final JButton searchButton = new JButton(ICON_SEARCH);
|
||||
searchButton.addActionListener(searchAction);
|
||||
searchButton.setToolTipText(NLS.str("menu.search"));
|
||||
toolbar.add(searchButton);
|
||||
|
||||
final JButton findButton = new JButton(ICON_FIND);
|
||||
findButton.addActionListener(findAction);
|
||||
findButton.setToolTipText(NLS.str("menu.find_in_file"));
|
||||
toolbar.add(findButton);
|
||||
|
||||
toolbar.addSeparator();
|
||||
|
||||
final JButton backButton = new JButton(ICON_BACK);
|
||||
backButton.addActionListener(new ActionListener() {
|
||||
@@ -399,7 +443,6 @@ public class MainWindow extends JFrame {
|
||||
}
|
||||
});
|
||||
backButton.setToolTipText(NLS.str("nav.back"));
|
||||
toolbar.add(backButton);
|
||||
|
||||
final JButton forwardButton = new JButton(ICON_FORWARD);
|
||||
forwardButton.addActionListener(new ActionListener() {
|
||||
@@ -409,11 +452,52 @@ public class MainWindow extends JFrame {
|
||||
}
|
||||
});
|
||||
forwardButton.setToolTipText(NLS.str("nav.forward"));
|
||||
toolbar.add(forwardButton);
|
||||
|
||||
toolbar.add(Box.createHorizontalGlue());
|
||||
final JButton prefButton = new JButton(ICON_PREF);
|
||||
prefButton.addActionListener(prefAction);
|
||||
prefButton.setToolTipText(NLS.str("menu.preferences"));
|
||||
|
||||
deobfToggleBtn = new JToggleButton(ICON_DEOBF);
|
||||
deobfToggleBtn.setSelected(settings.isDeobfuscationOn());
|
||||
deobfToggleBtn.setToolTipText(NLS.str("preferences.deobfuscation"));
|
||||
deobfToggleBtn.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
settings.setDeobfuscationOn(deobfToggleBtn.isSelected());
|
||||
settings.sync();
|
||||
reOpenFile();
|
||||
}
|
||||
});
|
||||
|
||||
updateLink = new Link("", JadxUpdate.JADX_RELEASES_URL);
|
||||
updateLink.setVisible(false);
|
||||
|
||||
JToolBar toolbar = new JToolBar();
|
||||
toolbar.setFloatable(false);
|
||||
|
||||
toolbar.add(openButton);
|
||||
toolbar.add(saveAllButton);
|
||||
toolbar.addSeparator();
|
||||
|
||||
toolbar.add(syncButton);
|
||||
toolbar.add(flatPkgButton);
|
||||
toolbar.addSeparator();
|
||||
|
||||
toolbar.add(searchButton);
|
||||
toolbar.add(findButton);
|
||||
toolbar.addSeparator();
|
||||
|
||||
toolbar.add(backButton);
|
||||
toolbar.add(forwardButton);
|
||||
toolbar.addSeparator();
|
||||
|
||||
toolbar.add(deobfToggleBtn);
|
||||
toolbar.addSeparator();
|
||||
|
||||
toolbar.add(prefButton);
|
||||
toolbar.addSeparator();
|
||||
|
||||
toolbar.add(Box.createHorizontalGlue());
|
||||
toolbar.add(updateLink);
|
||||
|
||||
mainPanel.add(toolbar, BorderLayout.NORTH);
|
||||
@@ -495,4 +579,40 @@ public class MainWindow extends JFrame {
|
||||
openFile();
|
||||
}
|
||||
}
|
||||
|
||||
private class RecentFilesMenuListener implements MenuListener {
|
||||
private final JMenu recentFiles;
|
||||
|
||||
public RecentFilesMenuListener(JMenu recentFiles) {
|
||||
this.recentFiles = recentFiles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void menuSelected(MenuEvent e) {
|
||||
recentFiles.removeAll();
|
||||
File openFile = wrapper.getOpenFile();
|
||||
String currentFile = openFile == null ? "" : openFile.getAbsolutePath();
|
||||
for (final String file : settings.getRecentFiles()) {
|
||||
if (file.equals(currentFile)) {
|
||||
continue;
|
||||
}
|
||||
JMenuItem menuItem = new JMenuItem(file);
|
||||
recentFiles.add(menuItem);
|
||||
menuItem.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
openFile(new File(file));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void menuDeselected(MenuEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void menuCanceled(MenuEvent e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,7 +299,7 @@ public class SearchDialog extends JDialog {
|
||||
buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));
|
||||
buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
|
||||
buttonPane.add(busyBar);
|
||||
searchPane.add(Box.createRigidArea(new Dimension(5, 0)));
|
||||
buttonPane.add(Box.createRigidArea(new Dimension(5, 0)));
|
||||
buttonPane.add(Box.createHorizontalGlue());
|
||||
buttonPane.add(openBtn);
|
||||
buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
|
||||
|
||||
@@ -200,10 +200,7 @@ class TabbedPane extends JTabbedPane {
|
||||
closeAll.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
List<ContentPanel> contentPanels = new ArrayList<ContentPanel>(openTabs.values());
|
||||
for (ContentPanel panel : contentPanels) {
|
||||
closeCodePanel(panel);
|
||||
}
|
||||
closeAllTabs();
|
||||
}
|
||||
});
|
||||
menu.add(closeAll);
|
||||
@@ -230,4 +227,11 @@ class TabbedPane extends JTabbedPane {
|
||||
}
|
||||
return menu;
|
||||
}
|
||||
|
||||
public void closeAllTabs() {
|
||||
List<ContentPanel> contentPanels = new ArrayList<ContentPanel>(openTabs.values());
|
||||
for (ContentPanel panel : contentPanels) {
|
||||
closeCodePanel(panel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
package jadx.gui.utils;
|
||||
|
||||
import java.util.prefs.Preferences;
|
||||
|
||||
public class JadxPreferences {
|
||||
|
||||
private static final String KEY_LAST_OPEN_FILE_PATH = "lastOpenFilePath";
|
||||
private static final String KEY_LAST_SAVE_FILE_PATH = "lastSaveFilePath";
|
||||
private static final String KEY_FLATTEN_PACKAGE = "flattenPackage";
|
||||
|
||||
private static Preferences prefs = null;
|
||||
|
||||
public static String getLastOpenFilePath() {
|
||||
String result = "";
|
||||
try {
|
||||
result = getPreferences().get(KEY_LAST_OPEN_FILE_PATH, "");
|
||||
if (result.isEmpty()) {
|
||||
result = System.getProperty("user.home");
|
||||
}
|
||||
} catch (Exception anyEx) {
|
||||
/* do nothing, no preferences */
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void putLastOpenFilePath(String path) {
|
||||
try {
|
||||
Preferences prefs = getPreferences();
|
||||
prefs.put(KEY_LAST_OPEN_FILE_PATH, path);
|
||||
prefs.sync();
|
||||
} catch (Exception anyEx) {
|
||||
/* do nothing, no preferences */
|
||||
}
|
||||
}
|
||||
|
||||
public static String getLastSaveFilePath() {
|
||||
String result = "";
|
||||
try {
|
||||
result = getPreferences().get(KEY_LAST_SAVE_FILE_PATH, "");
|
||||
if (result.isEmpty()) {
|
||||
result = getLastOpenFilePath();
|
||||
}
|
||||
} catch (Exception anyEx) {
|
||||
/* do nothing, no preferences */
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void putLastSaveFilePath(String path) {
|
||||
try {
|
||||
Preferences prefs = getPreferences();
|
||||
prefs.put(KEY_LAST_SAVE_FILE_PATH, path);
|
||||
prefs.sync();
|
||||
} catch (Exception anyEx) {
|
||||
/* do nothing, no preferences */
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean getFlattenPackage() {
|
||||
boolean result = false;
|
||||
try {
|
||||
Preferences prefs = getPreferences();
|
||||
result = prefs.getBoolean(KEY_FLATTEN_PACKAGE, false);
|
||||
} catch (Exception anyEx) {
|
||||
/* do nothing, no preferences */
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void putFlattenPackage(boolean value) {
|
||||
try {
|
||||
Preferences prefs = getPreferences();
|
||||
prefs.putBoolean(KEY_FLATTEN_PACKAGE, value);
|
||||
prefs.sync();
|
||||
} catch (Exception anyEx) {
|
||||
/* do nothing, no preferences */
|
||||
}
|
||||
}
|
||||
|
||||
private static Preferences getPreferences() {
|
||||
if (prefs == null) {
|
||||
prefs = Preferences.userRoot();
|
||||
}
|
||||
return prefs;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -42,10 +42,10 @@ public class OverlayIcon implements Icon {
|
||||
|
||||
icon.paintIcon(c, g, x, y);
|
||||
int k = 0;
|
||||
for (Icon icon : icons) {
|
||||
int dx = (int) (OVERLAY_POS[k++] * (w - icon.getIconWidth()));
|
||||
int dy = (int) (OVERLAY_POS[k++] * (h - icon.getIconHeight()));
|
||||
icon.paintIcon(c, g, x + dx, y + dy);
|
||||
for (Icon subIcon : icons) {
|
||||
int dx = (int) (OVERLAY_POS[k++] * (w - subIcon.getIconWidth()));
|
||||
int dy = (int) (OVERLAY_POS[k++] * (h - subIcon.getIconHeight()));
|
||||
subIcon.paintIcon(c, g, x + dx, y + dy);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user