Commit f60bb6b1 authored by Jan S's avatar Jan S Committed by skylot

fix: various UI improvements (#419)

* fixed wait time for background jobs
* enable multi-threaded decompiling
* added preference for excluding certain packages from decompiling and indexing
* show message dialog in case classes are not indexed because of low memory
* added heap usage bar for visualizing Java memory usage
parent fd3498ad
...@@ -5,6 +5,7 @@ import java.io.File; ...@@ -5,6 +5,7 @@ import java.io.File;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -64,10 +65,34 @@ public class JadxWrapper { ...@@ -64,10 +65,34 @@ public class JadxWrapper {
new Thread(save).start(); new Thread(save).start();
} }
/**
* Get the complete list of classes
* @return
*/
public List<JavaClass> getClasses() { public List<JavaClass> getClasses() {
return decompiler.getClasses(); return decompiler.getClasses();
} }
/**
* Get all classes that are not excluded by the excluded packages settings
* @return
*/
public List<JavaClass> getIncludedClasses() {
List<JavaClass> classList = decompiler.getClasses();
String excludedPackages = settings.getExcludedPackages().trim();
if (excludedPackages.length() == 0)
return classList;
String[] excluded = excludedPackages.split("[ ]+");
return classList.stream().filter(cls -> {
for (String exclude : excluded) {
if (cls.getFullName().startsWith(exclude))
return false;
}
return true;
}).collect(Collectors.toList());
}
public List<JavaPackage> getPackages() { public List<JavaPackage> getPackages() {
return decompiler.getPackages(); return decompiler.getPackages();
} }
...@@ -79,4 +104,8 @@ public class JadxWrapper { ...@@ -79,4 +104,8 @@ public class JadxWrapper {
public File getOpenFile() { public File getOpenFile() {
return openFile; return openFile;
} }
public JadxSettings getSettings() {
return settings;
}
} }
...@@ -3,6 +3,7 @@ package jadx.gui.jobs; ...@@ -3,6 +3,7 @@ package jadx.gui.jobs;
import javax.swing.*; import javax.swing.*;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import jadx.gui.utils.NLS;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -62,6 +63,9 @@ public class BackgroundWorker extends SwingWorker<Void, Void> { ...@@ -62,6 +63,9 @@ public class BackgroundWorker extends SwingWorker<Void, Void> {
if (searchIndex != null && searchIndex.getSkippedCount() > 0) { if (searchIndex != null && searchIndex.getSkippedCount() > 0) {
LOG.warn("Indexing of some classes skipped, count: {}, low memory: {}", LOG.warn("Indexing of some classes skipped, count: {}, low memory: {}",
searchIndex.getSkippedCount(), Utils.memoryInfo()); searchIndex.getSkippedCount(), Utils.memoryInfo());
String msg = NLS.str("message.indexingClassesSkipped");
msg = String.format(msg, searchIndex.getSkippedCount());
JOptionPane.showMessageDialog(null, msg);
} }
} catch (Exception e) { } catch (Exception e) {
LOG.error("Exception in background worker", e); LOG.error("Exception in background worker", e);
......
...@@ -10,7 +10,7 @@ public class DecompileJob extends BackgroundJob { ...@@ -10,7 +10,7 @@ public class DecompileJob extends BackgroundJob {
} }
protected void runJob() { protected void runJob() {
for (final JavaClass cls : wrapper.getClasses()) { for (final JavaClass cls : wrapper.getIncludedClasses()) {
addTask(new Runnable() { addTask(new Runnable() {
@Override @Override
public void run() { public void run() {
......
...@@ -33,7 +33,7 @@ public class IndexJob extends BackgroundJob { ...@@ -33,7 +33,7 @@ public class IndexJob extends BackgroundJob {
final CodeUsageInfo usageInfo = new CodeUsageInfo(nodeCache); final CodeUsageInfo usageInfo = new CodeUsageInfo(nodeCache);
cache.setTextIndex(index); cache.setTextIndex(index);
cache.setUsageInfo(usageInfo); cache.setUsageInfo(usageInfo);
for (final JavaClass cls : wrapper.getClasses()) { for (final JavaClass cls : wrapper.getIncludedClasses()) {
addTask(new Runnable() { addTask(new Runnable() {
@Override @Override
public void run() { public void run() {
......
...@@ -42,6 +42,9 @@ public class JadxSettings extends JadxCLIArgs { ...@@ -42,6 +42,9 @@ public class JadxSettings extends JadxCLIArgs {
private String editorThemePath = ""; private String editorThemePath = "";
private LangLocale langLocale = NLS.defaultLocale(); private LangLocale langLocale = NLS.defaultLocale();
private boolean autoStartJobs = false; private boolean autoStartJobs = false;
protected String excludedPackages = "";
private boolean showHeapUsageBar = true;
private int settingsVersion = 0; private int settingsVersion = 0;
...@@ -147,6 +150,24 @@ public class JadxSettings extends JadxCLIArgs { ...@@ -147,6 +150,24 @@ public class JadxSettings extends JadxCLIArgs {
return true; return true;
} }
public boolean isShowHeapUsageBar() {
return showHeapUsageBar;
}
public void setShowHeapUsageBar(boolean showHeapUsageBar) {
this.showHeapUsageBar = showHeapUsageBar;
partialSync(settings -> settings.showHeapUsageBar = showHeapUsageBar);
}
public String getExcludedPackages() {
return excludedPackages;
}
public void setExcludedPackages(String excludedPackages) {
this.excludedPackages = excludedPackages;
}
public void setThreadsCount(int threadsCount) { public void setThreadsCount(int threadsCount) {
this.threadsCount = threadsCount; this.threadsCount = threadsCount;
} }
......
package jadx.gui.settings; package jadx.gui.settings;
import javax.swing.*; import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import java.awt.*; import java.awt.*;
import java.awt.event.ItemEvent; import java.awt.event.ItemEvent;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
...@@ -242,6 +244,16 @@ public class JadxSettingsWindow extends JDialog { ...@@ -242,6 +244,16 @@ public class JadxSettingsWindow extends JDialog {
needReload(); needReload();
}); });
JButton editExcludedPackages = new JButton(NLS.str("preferences.excludedPackages.button"));
editExcludedPackages.addActionListener( event -> {
String result = JOptionPane.showInputDialog(this, NLS.str("preferences.excludedPackages.editDialog"),
settings.getExcludedPackages());
if (result !=null) {
settings.setExcludedPackages(result);
}
});
JCheckBox autoStartJobs = new JCheckBox(); JCheckBox autoStartJobs = new JCheckBox();
autoStartJobs.setSelected(settings.isAutoStartJobs()); autoStartJobs.setSelected(settings.isAutoStartJobs());
autoStartJobs.addItemListener(e -> settings.setAutoStartJobs(e.getStateChange() == ItemEvent.SELECTED)); autoStartJobs.addItemListener(e -> settings.setAutoStartJobs(e.getStateChange() == ItemEvent.SELECTED));
...@@ -269,6 +281,8 @@ public class JadxSettingsWindow extends JDialog { ...@@ -269,6 +281,8 @@ public class JadxSettingsWindow extends JDialog {
SettingsGroup other = new SettingsGroup(NLS.str("preferences.decompile")); SettingsGroup other = new SettingsGroup(NLS.str("preferences.decompile"));
other.addRow(NLS.str("preferences.threads"), threadsCount); other.addRow(NLS.str("preferences.threads"), threadsCount);
other.addRow(NLS.str("preferences.excludedPackages"), NLS.str("preferences.excludedPackages.tooltip"),
editExcludedPackages);
other.addRow(NLS.str("preferences.start_jobs"), autoStartJobs); other.addRow(NLS.str("preferences.start_jobs"), autoStartJobs);
other.addRow(NLS.str("preferences.showInconsistentCode"), showInconsistentCode); other.addRow(NLS.str("preferences.showInconsistentCode"), showInconsistentCode);
other.addRow(NLS.str("preferences.escapeUnicode"), escapeUnicode); other.addRow(NLS.str("preferences.escapeUnicode"), escapeUnicode);
...@@ -334,6 +348,10 @@ public class JadxSettingsWindow extends JDialog { ...@@ -334,6 +348,10 @@ public class JadxSettingsWindow extends JDialog {
} }
public void addRow(String label, JComponent comp) { public void addRow(String label, JComponent comp) {
addRow(label, null, comp);
}
public void addRow(String label, String tooltip, JComponent comp) {
c.gridy = row++; c.gridy = row++;
JLabel jLabel = new JLabel(label); JLabel jLabel = new JLabel(label);
jLabel.setLabelFor(comp); jLabel.setLabelFor(comp);
...@@ -349,6 +367,12 @@ public class JadxSettingsWindow extends JDialog { ...@@ -349,6 +367,12 @@ public class JadxSettingsWindow extends JDialog {
c.anchor = GridBagConstraints.CENTER; c.anchor = GridBagConstraints.CENTER;
c.weightx = 0.2; c.weightx = 0.2;
c.fill = GridBagConstraints.HORIZONTAL; c.fill = GridBagConstraints.HORIZONTAL;
if (tooltip != null) {
jLabel.setToolTipText(tooltip);
comp.setToolTipText(tooltip);
}
add(comp, c); add(comp, c);
comp.addPropertyChangeListener("enabled", evt -> jLabel.setEnabled((boolean) evt.getNewValue())); comp.addPropertyChangeListener("enabled", evt -> jLabel.setEnabled((boolean) evt.getNewValue()));
......
package jadx.gui.ui;
import jadx.gui.utils.NLS;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class HeapUsageBar extends JProgressBar implements ActionListener {
private static final double TWO_TO_20 = 1048576d; // 1024 * 1024
private final Color GREEN = new Color(0, 180, 0);
private final Color RED = new Color(200, 0, 0);
private final Runtime r;
private String maxHeapStr;
private final Timer timer;
private final double maxGB;
private final String textFormat;
public HeapUsageBar() {
super();
textFormat = NLS.str("heapUsage.text");
r = Runtime.getRuntime();
setBorderPainted(false);
setStringPainted(true);
setValue(10);
int maxKB = (int) (r.maxMemory() / 1024);
setMaximum(maxKB);
maxGB = maxKB / TWO_TO_20;
update();
timer = new Timer(1000, this);
}
public void update() {
long used = r.totalMemory() - r.freeMemory();
int usedKB = (int) (used / 1024);
setValue(usedKB);
setString(String.format(textFormat, (usedKB / TWO_TO_20), maxGB));
if (used > r.totalMemory() * 0.8) {
setForeground(RED);
} else {
setForeground(GREEN);
}
}
@Override
public void actionPerformed(ActionEvent e) {
update();
}
@Override
public void setVisible(boolean aFlag) {
super.setVisible(aFlag);
if (aFlag) {
timer.start();
} else {
timer.stop();
}
}
}
...@@ -90,10 +90,12 @@ public class MainWindow extends JFrame { ...@@ -90,10 +90,12 @@ public class MainWindow extends JFrame {
private DefaultTreeModel treeModel; private DefaultTreeModel treeModel;
private JRoot treeRoot; private JRoot treeRoot;
private TabbedPane tabbedPane; private TabbedPane tabbedPane;
private HeapUsageBar heapUsageBar;
private boolean isFlattenPackage; private boolean isFlattenPackage;
private JToggleButton flatPkgButton; private JToggleButton flatPkgButton;
private JCheckBoxMenuItem flatPkgMenuItem; private JCheckBoxMenuItem flatPkgMenuItem;
private JCheckBoxMenuItem heapUsageBarMenuItem;
private JToggleButton deobfToggleBtn; private JToggleButton deobfToggleBtn;
private JCheckBoxMenuItem deobfMenuItem; private JCheckBoxMenuItem deobfMenuItem;
...@@ -124,6 +126,7 @@ public class MainWindow extends JFrame { ...@@ -124,6 +126,7 @@ public class MainWindow extends JFrame {
public void open() { public void open() {
pack(); pack();
setLocationAndPosition(); setLocationAndPosition();
heapUsageBar.setVisible(settings.isShowHeapUsageBar());
setVisible(true); setVisible(true);
setLocationRelativeTo(null); setLocationRelativeTo(null);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
...@@ -185,7 +188,7 @@ public class MainWindow extends JFrame { ...@@ -185,7 +188,7 @@ public class MainWindow extends JFrame {
protected void resetCache() { protected void resetCache() {
cacheObject.reset(); cacheObject.reset();
// TODO: decompilation freezes sometime with several threads // TODO: decompilation freezes sometime with several threads
int threadsCount = 1; // settings.getThreadsCount(); int threadsCount = settings.getThreadsCount();
cacheObject.setDecompileJob(new DecompileJob(wrapper, threadsCount)); cacheObject.setDecompileJob(new DecompileJob(wrapper, threadsCount));
cacheObject.setIndexJob(new IndexJob(wrapper, cacheObject, threadsCount)); cacheObject.setIndexJob(new IndexJob(wrapper, cacheObject, threadsCount));
} }
...@@ -385,6 +388,13 @@ public class MainWindow extends JFrame { ...@@ -385,6 +388,13 @@ public class MainWindow extends JFrame {
flatPkgMenuItem = new JCheckBoxMenuItem(NLS.str("menu.flatten"), ICON_FLAT_PKG); flatPkgMenuItem = new JCheckBoxMenuItem(NLS.str("menu.flatten"), ICON_FLAT_PKG);
flatPkgMenuItem.setState(isFlattenPackage); flatPkgMenuItem.setState(isFlattenPackage);
heapUsageBarMenuItem = new JCheckBoxMenuItem(NLS.str("menu.heapUsageBar"));
heapUsageBarMenuItem.setState(settings.isShowHeapUsageBar());
heapUsageBarMenuItem.addActionListener(event -> {
settings.setShowHeapUsageBar(!settings.isShowHeapUsageBar());
heapUsageBar.setVisible(settings.isShowHeapUsageBar());
});
Action syncAction = new AbstractAction(NLS.str("menu.sync"), ICON_SYNC) { Action syncAction = new AbstractAction(NLS.str("menu.sync"), ICON_SYNC) {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
...@@ -483,6 +493,7 @@ public class MainWindow extends JFrame { ...@@ -483,6 +493,7 @@ public class MainWindow extends JFrame {
view.setMnemonic(KeyEvent.VK_V); view.setMnemonic(KeyEvent.VK_V);
view.add(flatPkgMenuItem); view.add(flatPkgMenuItem);
view.add(syncAction); view.add(syncAction);
view.add(heapUsageBarMenuItem);
JMenu nav = new JMenu(NLS.str("menu.navigation")); JMenu nav = new JMenu(NLS.str("menu.navigation"));
nav.setMnemonic(KeyEvent.VK_N); nav.setMnemonic(KeyEvent.VK_N);
...@@ -609,6 +620,9 @@ public class MainWindow extends JFrame { ...@@ -609,6 +620,9 @@ public class MainWindow extends JFrame {
new DropTarget(this, DnDConstants.ACTION_COPY, new MainDropTarget(this)); new DropTarget(this, DnDConstants.ACTION_COPY, new MainDropTarget(this));
heapUsageBar = new HeapUsageBar();
mainPanel.add(heapUsageBar, BorderLayout.SOUTH);
setContentPane(mainPanel); setContentPane(mainPanel);
setTitle(DEFAULT_TITLE); setTitle(DEFAULT_TITLE);
} }
......
...@@ -7,6 +7,7 @@ menu.no_recent_files=No recent files ...@@ -7,6 +7,7 @@ menu.no_recent_files=No recent files
menu.preferences=Preferences menu.preferences=Preferences
menu.sync=Sync with editor menu.sync=Sync with editor
menu.flatten=Show flatten packages menu.flatten=Show flatten packages
menu.heapUsageBar=Show memory usage bar
menu.navigation=Navigation menu.navigation=Navigation
menu.text_search=Text search menu.text_search=Text search
menu.class_search=Class search menu.class_search=Class search
...@@ -46,6 +47,10 @@ tabs.closeAll=Close All ...@@ -46,6 +47,10 @@ tabs.closeAll=Close All
nav.back=Back nav.back=Back
nav.forward=Forward nav.forward=Forward
message.indexingClassesSkipped=<html>Jadx is running low on memory. Therefore %d classes were not indexed.<br>If you want all classes to be indexed restart Jadx with increased maximum heap size.</html>
heapUsage.text=JADX memory usage: %.2f GB of %.2f GB
search_dialog.open=Open search_dialog.open=Open
search_dialog.cancel=Cancel search_dialog.cancel=Cancel
search_dialog.open_by_name=Search for text: search_dialog.open_by_name=Search for text:
...@@ -84,6 +89,10 @@ preferences.replaceConsts=Replace constants ...@@ -84,6 +89,10 @@ preferences.replaceConsts=Replace constants
preferences.useImports=Use import statements preferences.useImports=Use import statements
preferences.skipResourcesDecode=Don't decode resources preferences.skipResourcesDecode=Don't decode resources
preferences.threads=Processing threads count preferences.threads=Processing threads count
preferences.excludedPackages=Excluded packages
preferences.excludedPackages.tooltip=List of space separated package names that will not be decompiled or indexed (saves RAM)
preferences.excludedPackages.button=Edit
preferences.excludedPackages.editDialog=<html>List of space separated package names that will not be decompiled or indexed (saves RAM)<br>e.g. <code>android.support</code></html>
preferences.cfg=Generate methods CFG graphs (in 'dot' format) preferences.cfg=Generate methods CFG graphs (in 'dot' format)
preferences.raw_cfg=Generate RAW CFG graphs preferences.raw_cfg=Generate RAW CFG graphs
preferences.font=Editor font preferences.font=Editor font
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment