diff --git a/jadx-gui/src/main/java/jadx/gui/JadxWrapper.java b/jadx-gui/src/main/java/jadx/gui/JadxWrapper.java index 5e79fc30113..f11a0bcf105 100644 --- a/jadx-gui/src/main/java/jadx/gui/JadxWrapper.java +++ b/jadx-gui/src/main/java/jadx/gui/JadxWrapper.java @@ -5,6 +5,7 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.ThreadPoolExecutor; +import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -64,10 +65,34 @@ public void run() { new Thread(save).start(); } + /** + * Get the complete list of classes + * @return + */ public List getClasses() { return decompiler.getClasses(); } + /** + * Get all classes that are not excluded by the excluded packages settings + * @return + */ + public List getIncludedClasses() { + List 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 getPackages() { return decompiler.getPackages(); } @@ -79,4 +104,8 @@ public List getResources() { public File getOpenFile() { return openFile; } + + public JadxSettings getSettings() { + return settings; + } } diff --git a/jadx-gui/src/main/java/jadx/gui/jobs/BackgroundWorker.java b/jadx-gui/src/main/java/jadx/gui/jobs/BackgroundWorker.java index 00e13f9516c..59ad04e3929 100644 --- a/jadx-gui/src/main/java/jadx/gui/jobs/BackgroundWorker.java +++ b/jadx-gui/src/main/java/jadx/gui/jobs/BackgroundWorker.java @@ -3,6 +3,7 @@ import javax.swing.*; import java.util.concurrent.Future; +import jadx.gui.utils.NLS; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -62,6 +63,9 @@ protected Void doInBackground() throws Exception { if (searchIndex != null && searchIndex.getSkippedCount() > 0) { LOG.warn("Indexing of some classes skipped, count: {}, low memory: {}", searchIndex.getSkippedCount(), Utils.memoryInfo()); + String msg = NLS.str("message.indexingClassesSkipped"); + msg = String.format(msg, searchIndex.getSkippedCount()); + JOptionPane.showMessageDialog(null, msg); } } catch (Exception e) { LOG.error("Exception in background worker", e); diff --git a/jadx-gui/src/main/java/jadx/gui/jobs/DecompileJob.java b/jadx-gui/src/main/java/jadx/gui/jobs/DecompileJob.java index df4af302fea..983165067c8 100644 --- a/jadx-gui/src/main/java/jadx/gui/jobs/DecompileJob.java +++ b/jadx-gui/src/main/java/jadx/gui/jobs/DecompileJob.java @@ -10,7 +10,7 @@ public DecompileJob(JadxWrapper wrapper, int threadsCount) { } protected void runJob() { - for (final JavaClass cls : wrapper.getClasses()) { + for (final JavaClass cls : wrapper.getIncludedClasses()) { addTask(new Runnable() { @Override public void run() { diff --git a/jadx-gui/src/main/java/jadx/gui/jobs/IndexJob.java b/jadx-gui/src/main/java/jadx/gui/jobs/IndexJob.java index 2d94456ab50..ed1da813f13 100644 --- a/jadx-gui/src/main/java/jadx/gui/jobs/IndexJob.java +++ b/jadx-gui/src/main/java/jadx/gui/jobs/IndexJob.java @@ -33,7 +33,7 @@ protected void runJob() { final CodeUsageInfo usageInfo = new CodeUsageInfo(nodeCache); cache.setTextIndex(index); cache.setUsageInfo(usageInfo); - for (final JavaClass cls : wrapper.getClasses()) { + for (final JavaClass cls : wrapper.getIncludedClasses()) { addTask(new Runnable() { @Override public void run() { diff --git a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java index 56e1ba0d8c2..c88a6ba3ec6 100644 --- a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java +++ b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java @@ -42,6 +42,9 @@ public class JadxSettings extends JadxCLIArgs { private String editorThemePath = ""; private LangLocale langLocale = NLS.defaultLocale(); private boolean autoStartJobs = false; + protected String excludedPackages = ""; + + private boolean showHeapUsageBar = true; private int settingsVersion = 0; @@ -147,6 +150,24 @@ public boolean loadWindowPos(Window window) { 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) { this.threadsCount = threadsCount; } diff --git a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java index af2bd3f139e..e61027f746c 100644 --- a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java @@ -1,6 +1,8 @@ package jadx.gui.settings; import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; import java.awt.*; import java.awt.event.ItemEvent; import java.awt.event.MouseAdapter; @@ -242,6 +244,16 @@ private SettingsGroup makeDecompilationGroup() { 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(); autoStartJobs.setSelected(settings.isAutoStartJobs()); autoStartJobs.addItemListener(e -> settings.setAutoStartJobs(e.getStateChange() == ItemEvent.SELECTED)); @@ -269,6 +281,8 @@ private SettingsGroup makeDecompilationGroup() { SettingsGroup other = new SettingsGroup(NLS.str("preferences.decompile")); 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.showInconsistentCode"), showInconsistentCode); other.addRow(NLS.str("preferences.escapeUnicode"), escapeUnicode); @@ -334,6 +348,10 @@ public SettingsGroup(String title) { } public void addRow(String label, JComponent comp) { + addRow(label, null, comp); + } + + public void addRow(String label, String tooltip, JComponent comp) { c.gridy = row++; JLabel jLabel = new JLabel(label); jLabel.setLabelFor(comp); @@ -349,6 +367,12 @@ public void addRow(String label, JComponent comp) { c.anchor = GridBagConstraints.CENTER; c.weightx = 0.2; c.fill = GridBagConstraints.HORIZONTAL; + + if (tooltip != null) { + jLabel.setToolTipText(tooltip); + comp.setToolTipText(tooltip); + } + add(comp, c); comp.addPropertyChangeListener("enabled", evt -> jLabel.setEnabled((boolean) evt.getNewValue())); diff --git a/jadx-gui/src/main/java/jadx/gui/ui/HeapUsageBar.java b/jadx-gui/src/main/java/jadx/gui/ui/HeapUsageBar.java new file mode 100644 index 00000000000..29abbdb4543 --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/ui/HeapUsageBar.java @@ -0,0 +1,68 @@ +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(); + } + } +} diff --git a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java index 5b8bf67c8e0..7315cc82849 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java @@ -90,10 +90,12 @@ public class MainWindow extends JFrame { private DefaultTreeModel treeModel; private JRoot treeRoot; private TabbedPane tabbedPane; + private HeapUsageBar heapUsageBar; private boolean isFlattenPackage; private JToggleButton flatPkgButton; private JCheckBoxMenuItem flatPkgMenuItem; + private JCheckBoxMenuItem heapUsageBarMenuItem; private JToggleButton deobfToggleBtn; private JCheckBoxMenuItem deobfMenuItem; @@ -124,6 +126,7 @@ private void applySettings() { public void open() { pack(); setLocationAndPosition(); + heapUsageBar.setVisible(settings.isShowHeapUsageBar()); setVisible(true); setLocationRelativeTo(null); setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); @@ -185,7 +188,7 @@ public void openFile(File file) { protected void resetCache() { cacheObject.reset(); // TODO: decompilation freezes sometime with several threads - int threadsCount = 1; // settings.getThreadsCount(); + int threadsCount = settings.getThreadsCount(); cacheObject.setDecompileJob(new DecompileJob(wrapper, threadsCount)); cacheObject.setIndexJob(new IndexJob(wrapper, cacheObject, threadsCount)); } @@ -385,6 +388,13 @@ public void actionPerformed(ActionEvent e) { flatPkgMenuItem = new JCheckBoxMenuItem(NLS.str("menu.flatten"), ICON_FLAT_PKG); 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) { @Override public void actionPerformed(ActionEvent e) { @@ -483,6 +493,7 @@ public void actionPerformed(ActionEvent e) { view.setMnemonic(KeyEvent.VK_V); view.add(flatPkgMenuItem); view.add(syncAction); + view.add(heapUsageBarMenuItem); JMenu nav = new JMenu(NLS.str("menu.navigation")); nav.setMnemonic(KeyEvent.VK_N); @@ -609,6 +620,9 @@ public void treeWillCollapse(TreeExpansionEvent event) throws ExpandVetoExceptio new DropTarget(this, DnDConstants.ACTION_COPY, new MainDropTarget(this)); + heapUsageBar = new HeapUsageBar(); + mainPanel.add(heapUsageBar, BorderLayout.SOUTH); + setContentPane(mainPanel); setTitle(DEFAULT_TITLE); } diff --git a/jadx-gui/src/main/resources/i18n/Messages_en_US.properties b/jadx-gui/src/main/resources/i18n/Messages_en_US.properties index a0705904d41..839443e449e 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_en_US.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_en_US.properties @@ -7,6 +7,7 @@ menu.no_recent_files=No recent files menu.preferences=Preferences menu.sync=Sync with editor menu.flatten=Show flatten packages +menu.heapUsageBar=Show memory usage bar menu.navigation=Navigation menu.text_search=Text search menu.class_search=Class search @@ -46,6 +47,10 @@ tabs.closeAll=Close All nav.back=Back nav.forward=Forward +message.indexingClassesSkipped=Jadx is running low on memory. Therefore %d classes were not indexed.
If you want all classes to be indexed restart Jadx with increased maximum heap size. + +heapUsage.text=JADX memory usage: %.2f GB of %.2f GB + search_dialog.open=Open search_dialog.cancel=Cancel search_dialog.open_by_name=Search for text: @@ -84,6 +89,10 @@ preferences.replaceConsts=Replace constants preferences.useImports=Use import statements preferences.skipResourcesDecode=Don't decode resources 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=List of space separated package names that will not be decompiled or indexed (saves RAM)
e.g. android.support preferences.cfg=Generate methods CFG graphs (in 'dot' format) preferences.raw_cfg=Generate RAW CFG graphs preferences.font=Editor font