diff --git a/jadx-core/src/main/java/jadx/api/Decompiler.java b/jadx-core/src/main/java/jadx/api/Decompiler.java index f51db74b0..da66a1756 100644 --- a/jadx-core/src/main/java/jadx/api/Decompiler.java +++ b/jadx-core/src/main/java/jadx/api/Decompiler.java @@ -21,6 +21,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; @@ -45,7 +46,9 @@ public final class Decompiler { try { loadInput(); parseDex(); - saveAll(); + ExecutorService ex = saveAll(args.getOutDir()); + ex.awaitTermination(100, TimeUnit.DAYS); + LOG.info("done"); } catch (Throwable e) { LOG.error("jadx error:", e); } finally { @@ -88,16 +91,16 @@ public final class Decompiler { return Collections.unmodifiableList(packages); } - public void saveAll() throws InterruptedException { + public ThreadPoolExecutor saveAll(File dir) throws InterruptedException { int threadsCount = args.getThreadsCount(); LOG.debug("processing threads count: {}", threadsCount); ArrayList passList = new ArrayList(passes); - SaveCode savePass = new SaveCode(args); + SaveCode savePass = new SaveCode(dir, args); passList.add(savePass); LOG.info("processing ..."); - ExecutorService executor = Executors.newFixedThreadPool(threadsCount); + ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(threadsCount); for (ClassNode cls : root.getClasses()) { if (cls.getCode() == null) { ProcessClass job = new ProcessClass(cls, passList); @@ -111,7 +114,7 @@ public final class Decompiler { } } executor.shutdown(); - executor.awaitTermination(100, TimeUnit.DAYS); + return executor; } private void loadInput() throws IOException, DecodeException { diff --git a/jadx-core/src/main/java/jadx/core/codegen/CodeWriter.java b/jadx-core/src/main/java/jadx/core/codegen/CodeWriter.java index c0ebb31b3..88cb1c0c9 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/CodeWriter.java +++ b/jadx-core/src/main/java/jadx/core/codegen/CodeWriter.java @@ -224,7 +224,6 @@ public class CodeWriter { } finally { if (out != null) out.close(); - buf = null; } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/SaveCode.java b/jadx-core/src/main/java/jadx/core/dex/visitors/SaveCode.java index 170d7cd22..8836386bd 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/SaveCode.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/SaveCode.java @@ -12,9 +12,9 @@ public class SaveCode extends AbstractVisitor { private final File dir; private final IJadxArgs args; - public SaveCode(IJadxArgs args) { + public SaveCode(File dir, IJadxArgs args) { this.args = args; - this.dir = args.getOutDir(); + this.dir = dir; } @Override diff --git a/jadx-gui/src/main/java/jadx/gui/JadxWrapper.java b/jadx-gui/src/main/java/jadx/gui/JadxWrapper.java index 7f5f4c2b9..2361e42a8 100644 --- a/jadx-gui/src/main/java/jadx/gui/JadxWrapper.java +++ b/jadx-gui/src/main/java/jadx/gui/JadxWrapper.java @@ -6,11 +6,18 @@ import jadx.api.JavaClass; import jadx.api.JavaPackage; import jadx.core.utils.exceptions.DecodeException; +import javax.swing.ProgressMonitor; import java.io.File; import java.io.IOException; import java.util.List; +import java.util.concurrent.ThreadPoolExecutor; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class JadxWrapper { + private static final Logger LOG = LoggerFactory.getLogger(JadxWrapper.class); + private final Decompiler decompiler; private File openFile; @@ -29,6 +36,28 @@ public class JadxWrapper { } } + public void saveAll(final File dir, final ProgressMonitor progressMonitor) { + Runnable save = new Runnable() { + @Override + public void run() { + try { + ThreadPoolExecutor ex = decompiler.saveAll(dir); + while (ex.isTerminating()) { + long total = ex.getTaskCount(); + long done = ex.getCompletedTaskCount(); + progressMonitor.setProgress((int) (done * 100.0 / (double) total)); + Thread.sleep(500); + } + progressMonitor.close(); + LOG.info("done"); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }; + new Thread(save).start(); + } + public List getClasses() { return decompiler.getClasses(); } diff --git a/jadx-gui/src/main/java/jadx/gui/MainWindow.java b/jadx-gui/src/main/java/jadx/gui/MainWindow.java index ed94933b0..10fe16a57 100644 --- a/jadx-gui/src/main/java/jadx/gui/MainWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/MainWindow.java @@ -22,6 +22,7 @@ import javax.swing.JToggleButton; import javax.swing.JToolBar; import javax.swing.JTree; import javax.swing.KeyStroke; +import javax.swing.ProgressMonitor; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.filechooser.FileNameExtensionFilter; @@ -52,6 +53,7 @@ public class MainWindow extends JFrame { private static final Color BACKGROUND = new Color(0xf7f7f7); private static final ImageIcon ICON_OPEN = Utils.openIcon("folder"); + private static final ImageIcon ICON_SAVE_ALL = Utils.openIcon("disk_multiple"); private static final ImageIcon ICON_CLOSE = Utils.openIcon("cross"); private static final ImageIcon ICON_FLAT_PKG = Utils.openIcon("empty_logical_package_obj"); private static final ImageIcon ICON_SEARCH = Utils.openIcon("magnifier"); @@ -77,6 +79,18 @@ public class MainWindow extends JFrame { setTitle(DEFAULT_TITLE + " - " + file.getName()); } + private void saveAllAction() { + JFileChooser fileChooser = new JFileChooser(); + fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + int ret = fileChooser.showDialog(mainPanel, NLS.str("file.save_all_msg")); + if (ret == JFileChooser.APPROVE_OPTION) { + + ProgressMonitor progressMonitor = new ProgressMonitor(mainPanel, "Saving sources", "", 0, 100); + progressMonitor.setMillisToPopup(500); + wrapper.saveAll(fileChooser.getSelectedFile(), progressMonitor); + } + } + private void initTree() { JRoot treeRoot = new JRoot(wrapper); treeModel.setRoot(treeRoot); @@ -129,21 +143,29 @@ public class MainWindow extends JFrame { JMenu file = new JMenu("File"); file.setMnemonic(KeyEvent.VK_F); - JMenuItem exit = new JMenuItem("Exit", ICON_CLOSE); + JMenuItem exit = new JMenuItem(NLS.str("file.exit"), ICON_CLOSE); exit.setMnemonic(KeyEvent.VK_E); - exit.setToolTipText("Exit application"); exit.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { System.exit(0); } }); - JMenuItem open = new JMenuItem("Open", ICON_OPEN); - open.setMnemonic(KeyEvent.VK_E); - open.setToolTipText("Open file"); + JMenuItem open = new JMenuItem(NLS.str("file.open"), ICON_OPEN); + open.setMnemonic(KeyEvent.VK_O); open.addActionListener(new OpenListener()); + JMenuItem saveAll = new JMenuItem(NLS.str("file.save_all"), ICON_SAVE_ALL); + saveAll.setMnemonic(KeyEvent.VK_S); + saveAll.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + saveAllAction(); + } + }); + file.add(open); + file.add(saveAll); file.addSeparator(); file.add(exit); @@ -153,11 +175,21 @@ public class MainWindow extends JFrame { JToolBar toolbar = new JToolBar(); toolbar.setFloatable(false); - JButton openButton = new JButton(ICON_OPEN); + 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() { + @Override + public void actionPerformed(ActionEvent e) { + saveAllAction(); + } + }); + saveAllButton.setToolTipText(NLS.str("file.save_all")); + toolbar.add(saveAllButton); + toolbar.addSeparator(); final JToggleButton flatPkgButton = new JToggleButton(ICON_FLAT_PKG); @@ -235,6 +267,7 @@ public class MainWindow extends JFrame { textArea.setBackground(BACKGROUND); textArea.setCodeFoldingEnabled(true); textArea.setAntiAliasingEnabled(true); + textArea.setEditable(false); // textArea.setHyperlinksEnabled(true); textArea.setTabSize(4); 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 b9d0370df..eb79eb615 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_en_US.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_en_US.properties @@ -1,4 +1,8 @@ file.open=Open file +file.save=Save file +file.save_all=Save all +file.save_all_msg=Select directory for save decompiled sources +file.exit=Exit tree.flatten=Flatten packages diff --git a/jadx-gui/src/main/resources/icons-16/disk_multiple.png b/jadx-gui/src/main/resources/icons-16/disk_multiple.png new file mode 100644 index 000000000..fc5a52f5e Binary files /dev/null and b/jadx-gui/src/main/resources/icons-16/disk_multiple.png differ