diff --git a/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java b/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java index 95dd57e6d..66e9e46e5 100644 --- a/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java +++ b/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java @@ -83,7 +83,7 @@ public class Deobfuscator { public void savePresets() { Path deobfMapFile = deobfPresets.getDeobfMapFile(); if (Files.exists(deobfMapFile) && !args.isDeobfuscationForceSave()) { - LOG.warn("Deobfuscation map file '{}' exists. Use command line option '--deobf-rewrite-cfg' to rewrite it", + LOG.info("Deobfuscation map file '{}' exists. Use command line option '--deobf-rewrite-cfg' to rewrite it", deobfMapFile.toAbsolutePath()); return; } 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 ae3e58fd4..d8bacf945 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java @@ -80,6 +80,8 @@ import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ch.qos.logback.classic.Level; + import jadx.api.JadxArgs; import jadx.api.JavaClass; import jadx.api.JavaNode; @@ -114,9 +116,11 @@ import jadx.gui.ui.dialog.LogViewerDialog; import jadx.gui.ui.dialog.RenameDialog; import jadx.gui.ui.dialog.SearchDialog; import jadx.gui.ui.panel.ContentPanel; +import jadx.gui.ui.panel.IssuesPanel; import jadx.gui.ui.panel.JDebuggerPanel; import jadx.gui.ui.panel.ProgressPanel; import jadx.gui.ui.popupmenu.JPackagePopupMenu; +import jadx.gui.ui.treenodes.SummaryNode; import jadx.gui.update.JadxUpdate; import jadx.gui.update.JadxUpdate.IUpdateCallback; import jadx.gui.update.data.Release; @@ -129,6 +133,7 @@ import jadx.gui.utils.Link; import jadx.gui.utils.NLS; import jadx.gui.utils.SystemInfo; import jadx.gui.utils.UiUtils; +import jadx.gui.utils.logs.LogCollector; import jadx.gui.utils.search.CommentsIndex; import jadx.gui.utils.search.TextSearchIndex; @@ -402,6 +407,7 @@ public class MainWindow extends JFrame { project.setFilePath(paths); clearTree(); BreakpointManager.saveAndExit(); + LogCollector.getInstance().reset(); if (paths.isEmpty()) { return; } @@ -413,11 +419,31 @@ public class MainWindow extends JFrame { UiUtils.errorMessage(this, NLS.str("message.memoryLow")); return; } + checkLoadedStatus(); onOpen(paths); onFinish.run(); }); } + private void checkLoadedStatus() { + if (!wrapper.getClasses().isEmpty()) { + return; + } + int errors = LogCollector.getInstance().getErrors(); + if (errors > 0) { + int result = JOptionPane.showConfirmDialog(this, + NLS.str("message.load_errors", errors), + NLS.str("message.errorTitle"), + JOptionPane.OK_CANCEL_OPTION, + JOptionPane.ERROR_MESSAGE); + if (result == JOptionPane.OK_OPTION) { + LogViewerDialog.openWithLevel(this, Level.ERROR); + } + } else { + UiUtils.showMessageBox(this, NLS.str("message.no_classes")); + } + } + private void onOpen(List paths) { deobfToggleBtn.setSelected(settings.isDeobfuscationOn()); initTree(); @@ -428,6 +454,7 @@ public class MainWindow extends JFrame { private void addTreeCustomNodes() { treeRoot.replaceCustomNode(ApkSignature.getApkSignature(wrapper)); + treeRoot.replaceCustomNode(new SummaryNode(this)); } private boolean ensureProjectIsSaved() { @@ -723,7 +750,7 @@ public class MainWindow extends JFrame { } private void treeRightClickAction(MouseEvent e) { - JNode obj = getJNodeUnderMouse(e); + JNode obj = getJNodeUnderMouse(e, false); if (obj instanceof JPackage) { JPackagePopupMenu menu = new JPackagePopupMenu(this, (JPackage) obj); menu.show(e.getComponent(), e.getX(), e.getY()); @@ -737,8 +764,12 @@ public class MainWindow extends JFrame { } @Nullable - private JNode getJNodeUnderMouse(MouseEvent mouseEvent) { + private JNode getJNodeUnderMouse(MouseEvent mouseEvent, boolean trySelection) { TreePath path = tree.getPathForLocation(mouseEvent.getX(), mouseEvent.getY()); + if (path == null && trySelection) { + // maybe click on node row (mouse pressed event should select this node in tree) + path = tree.getSelectionPath(); + } if (path != null) { Object obj = path.getLastPathComponent(); if (obj instanceof JNode) { @@ -939,7 +970,7 @@ public class MainWindow extends JFrame { Action logAction = new AbstractAction(NLS.str("menu.log"), ICON_LOG) { @Override public void actionPerformed(ActionEvent e) { - new LogViewerDialog(MainWindow.this).setVisible(true); + LogViewerDialog.open(MainWindow.this); } }; logAction.putValue(Action.SHORT_DESCRIPTION, NLS.str("menu.log")); @@ -1096,10 +1127,16 @@ public class MainWindow extends JFrame { ToolTipManager.sharedInstance().registerComponent(tree); tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); tree.addMouseListener(new MouseAdapter() { + + @Override + public void mousePressed(MouseEvent e) { + super.mousePressed(e); + } + @Override public void mouseClicked(MouseEvent e) { if (SwingUtilities.isLeftMouseButton(e)) { - nodeClickAction(getJNodeUnderMouse(e)); + nodeClickAction(getJNodeUnderMouse(e, true)); } else if (SwingUtilities.isRightMouseButton(e)) { treeRightClickAction(e); } @@ -1157,13 +1194,18 @@ public class MainWindow extends JFrame { }); progressPane = new ProgressPanel(this, true); + IssuesPanel issuesPanel = new IssuesPanel(this); JPanel leftPane = new JPanel(new BorderLayout()); JScrollPane treeScrollPane = new JScrollPane(tree); treeScrollPane.setMinimumSize(new Dimension(100, 150)); + JPanel bottomPane = new JPanel(new BorderLayout()); + bottomPane.add(issuesPanel, BorderLayout.PAGE_START); + bottomPane.add(progressPane, BorderLayout.PAGE_END); + leftPane.add(treeScrollPane, BorderLayout.CENTER); - leftPane.add(progressPane, BorderLayout.PAGE_END); + leftPane.add(bottomPane, BorderLayout.PAGE_END); splitPane.setLeftComponent(leftPane); tabbedPane = new TabbedPane(this); diff --git a/jadx-gui/src/main/java/jadx/gui/ui/dialog/LogViewerDialog.java b/jadx-gui/src/main/java/jadx/gui/ui/dialog/LogViewerDialog.java index a2cfac430..c1d7ea146 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/dialog/LogViewerDialog.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/dialog/LogViewerDialog.java @@ -32,7 +32,16 @@ public class LogViewerDialog extends JDialog { private final transient JadxSettings settings; private transient RSyntaxTextArea textPane; - public LogViewerDialog(MainWindow mainWindow) { + public static void open(MainWindow mainWindow) { + openWithLevel(mainWindow, level); + } + + public static void openWithLevel(MainWindow mainWindow, Level newLevel) { + level = newLevel; + new LogViewerDialog(mainWindow).setVisible(true); + } + + private LogViewerDialog(MainWindow mainWindow) { this.settings = mainWindow.getSettings(); initUI(mainWindow); registerLogListener(); diff --git a/jadx-gui/src/main/java/jadx/gui/ui/panel/IssuesPanel.java b/jadx-gui/src/main/java/jadx/gui/ui/panel/IssuesPanel.java new file mode 100644 index 000000000..701512fb6 --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/ui/panel/IssuesPanel.java @@ -0,0 +1,83 @@ +package jadx.gui.ui.panel; + +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; + +import ch.qos.logback.classic.Level; + +import jadx.gui.ui.MainWindow; +import jadx.gui.ui.dialog.LogViewerDialog; +import jadx.gui.utils.NLS; +import jadx.gui.utils.UiUtils; +import jadx.gui.utils.logs.LogCollector; + +public class IssuesPanel extends JPanel { + private static final long serialVersionUID = -7720576036668459218L; + + private static final ImageIcon ERROR_ICON = UiUtils.openSvgIcon("ui/error"); + private static final ImageIcon WARN_ICON = UiUtils.openSvgIcon("ui/warning"); + + private final MainWindow mainWindow; + private JLabel errorLabel; + private JLabel warnLabel; + + public IssuesPanel(MainWindow mainWindow) { + this.mainWindow = mainWindow; + initUI(); + LogCollector.getInstance().registerIssueListener((error, warnings) -> { + SwingUtilities.invokeLater(() -> onUpdate(error, warnings)); + }); + } + + private void initUI() { + JLabel label = new JLabel(NLS.str("issues_panel.label")); + errorLabel = new JLabel(ERROR_ICON); + warnLabel = new JLabel(WARN_ICON); + + String toolTipText = NLS.str("issues_panel.tooltip"); + errorLabel.setToolTipText(toolTipText); + warnLabel.setToolTipText(toolTipText); + + errorLabel.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + LogViewerDialog.openWithLevel(mainWindow, Level.ERROR); + } + }); + warnLabel.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + LogViewerDialog.openWithLevel(mainWindow, Level.WARN); + } + }); + + setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); + setVisible(false); + add(label); + add(Box.createHorizontalGlue()); + add(errorLabel); + add(Box.createHorizontalGlue()); + add(warnLabel); + } + + private void onUpdate(int error, int warnings) { + if (error == 0 && warnings == 0) { + setVisible(false); + return; + } + setVisible(true); + errorLabel.setText(NLS.str("issues_panel.errors", error)); + errorLabel.setVisible(error != 0); + warnLabel.setText(NLS.str("issues_panel.warnings", warnings)); + warnLabel.setVisible(warnings != 0); + } +} diff --git a/jadx-gui/src/main/java/jadx/gui/ui/treenodes/SummaryNode.java b/jadx-gui/src/main/java/jadx/gui/ui/treenodes/SummaryNode.java new file mode 100644 index 000000000..0146262b8 --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/ui/treenodes/SummaryNode.java @@ -0,0 +1,163 @@ +package jadx.gui.ui.treenodes; + +import java.io.File; +import java.io.IOException; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.swing.Icon; +import javax.swing.ImageIcon; + +import org.apache.commons.text.StringEscapeUtils; + +import jadx.api.JadxDecompiler; +import jadx.core.dex.attributes.IAttributeNode; +import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.nodes.MethodNode; +import jadx.core.dex.nodes.ProcessState; +import jadx.core.utils.ErrorsCounter; +import jadx.core.utils.Utils; +import jadx.gui.treemodel.JClass; +import jadx.gui.treemodel.JNode; +import jadx.gui.ui.MainWindow; +import jadx.gui.ui.TabbedPane; +import jadx.gui.ui.panel.ContentPanel; +import jadx.gui.ui.panel.HtmlPanel; +import jadx.gui.utils.UiUtils; + +public class SummaryNode extends JNode { + private static final long serialVersionUID = 4295299814582784805L; + + private static final ImageIcon ICON = UiUtils.openSvgIcon("nodes/detailView"); + + private final MainWindow mainWindow; + + public SummaryNode(MainWindow mainWindow) { + this.mainWindow = mainWindow; + } + + @Override + public String getContent() { + StringEscapeUtils.Builder builder = StringEscapeUtils.builder(StringEscapeUtils.ESCAPE_HTML4); + try { + builder.append(""); + builder.append(""); + writeInputSummary(builder); + writeDecompilationSummary(builder); + builder.append(""); + } catch (Exception e) { + builder.append("Error build summary: "); + builder.append("
");
+			builder.append(Utils.getStackTrace(e));
+			builder.append("
"); + } + return builder.toString(); + } + + private void writeInputSummary(StringEscapeUtils.Builder builder) throws IOException { + builder.append("

Input

"); + JadxDecompiler jadx = mainWindow.getWrapper().getDecompiler(); + builder.append("

Files

"); + builder.append(""); + + List classes = jadx.getRoot().getClasses(true); + List codeSources = classes.stream() + .map(ClassNode::getInputFileName) + .distinct() + .sorted() + .collect(Collectors.toList()); + codeSources.remove("synthetic"); + int codeSourcesCount = codeSources.size(); + builder.append("

Code sources

"); + builder.append("
    "); + if (codeSourcesCount != 1) { + builder.append("
  • Count: " + codeSourcesCount + "
  • "); + } + // dex files list + codeSources.removeIf(f -> !f.endsWith(".dex")); + if (!codeSources.isEmpty()) { + for (String input : codeSources) { + builder.append("
  • "); + builder.escape(input); + builder.append("
  • "); + } + } + builder.append("
"); + + int methodsCount = classes.stream().mapToInt(cls -> cls.getMethods().size()).sum(); + int fieldsCount = classes.stream().mapToInt(cls -> cls.getFields().size()).sum(); + int insnCount = classes.stream().flatMap(cls -> cls.getMethods().stream()).mapToInt(MethodNode::getInsnsCount).sum(); + builder.append("

Counts

"); + builder.append("
    "); + builder.append("
  • Classes: " + classes.size() + "
  • "); + builder.append("
  • Methods: " + methodsCount + "
  • "); + builder.append("
  • Fields: " + fieldsCount + "
  • "); + builder.append("
  • Instructions: " + insnCount + " (units)
  • "); + builder.append("
"); + } + + private void writeDecompilationSummary(StringEscapeUtils.Builder builder) { + builder.append("

Decompilation

"); + JadxDecompiler jadx = mainWindow.getWrapper().getDecompiler(); + List classes = jadx.getRoot().getClasses(false); + int classesCount = classes.size(); + long processedClasses = classes.stream().filter(c -> c.getState() == ProcessState.PROCESS_COMPLETE).count(); + long generatedClasses = classes.stream().filter(c -> c.getState() == ProcessState.GENERATED_AND_UNLOADED).count(); + builder.append("
    "); + builder.append("
  • Top level classes: " + classesCount + "
  • "); + builder.append("
  • At process stage: " + valueAndPercent(processedClasses, classesCount) + "
  • "); + builder.append("
  • Code generated: " + valueAndPercent(generatedClasses, classesCount) + "
  • "); + builder.append("
"); + + ErrorsCounter counter = jadx.getRoot().getErrorsCounter(); + Set problemNodes = new HashSet<>(); + problemNodes.addAll(counter.getErrorNodes()); + problemNodes.addAll(counter.getWarnNodes()); + long problemMethods = problemNodes.stream().filter(MethodNode.class::isInstance).count(); + int methodsCount = classes.stream().mapToInt(cls -> cls.getMethods().size()).sum(); + double methodSuccessRate = (methodsCount - problemMethods) * 100.0 / (double) methodsCount; + + builder.append("

Issues

"); + builder.append("
    "); + builder.append("
  • Errors: " + counter.getErrorCount() + "
  • "); + builder.append("
  • Warnings: " + counter.getWarnsCount() + "
  • "); + builder.append("
  • Nodes with errors: " + counter.getErrorNodes().size() + "
  • "); + builder.append("
  • Nodes with warnings: " + counter.getWarnNodes().size() + "
  • "); + builder.append("
  • Total nodes with issues: " + problemNodes.size() + "
  • "); + builder.append("
  • Methods with issues: " + problemMethods + "
  • "); + builder.append("
  • Methods success rate: " + String.format("%.2f", methodSuccessRate) + "%
  • "); + builder.append("
"); + } + + private String valueAndPercent(long value, int total) { + return String.format("%d (%.2f%%)", value, value * 100 / ((double) total)); + } + + @Override + public ContentPanel getContentPanel(TabbedPane tabbedPane) { + return new HtmlPanel(tabbedPane, this); + } + + @Override + public String makeString() { + return "Summary"; + } + + @Override + public Icon getIcon() { + return ICON; + } + + @Override + public JClass getJParent() { + return null; + } +} diff --git a/jadx-gui/src/main/java/jadx/gui/utils/UiUtils.java b/jadx-gui/src/main/java/jadx/gui/utils/UiUtils.java index 35a89eba8..3a8069b53 100644 --- a/jadx-gui/src/main/java/jadx/gui/utils/UiUtils.java +++ b/jadx-gui/src/main/java/jadx/gui/utils/UiUtils.java @@ -253,10 +253,6 @@ public class UiUtils { return CTRL_BNT_KEY; } - public static void showMessageBox(Component parent, String msg) { - JOptionPane.showMessageDialog(parent, msg); - } - public static void addEscapeShortCutToDispose(JDialog dialog) { KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); dialog.getRootPane().registerKeyboardAction(e -> dialog.dispose(), stroke, JComponent.WHEN_IN_FOCUSED_WINDOW); @@ -292,6 +288,10 @@ public class UiUtils { return envVal; } + public static void showMessageBox(Component parent, String msg) { + JOptionPane.showMessageDialog(parent, msg); + } + public static void errorMessage(Component parent, String message) { JOptionPane.showMessageDialog(parent, message, NLS.str("message.errorTitle"), JOptionPane.ERROR_MESSAGE); diff --git a/jadx-gui/src/main/java/jadx/gui/utils/logs/ILogIssuesListener.java b/jadx-gui/src/main/java/jadx/gui/utils/logs/ILogIssuesListener.java new file mode 100644 index 000000000..a80829cf1 --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/utils/logs/ILogIssuesListener.java @@ -0,0 +1,5 @@ +package jadx.gui.utils.logs; + +public interface ILogIssuesListener { + void onChange(int error, int warnings); +} diff --git a/jadx-gui/src/main/java/jadx/gui/utils/logs/LimitedQueue.java b/jadx-gui/src/main/java/jadx/gui/utils/logs/LimitedQueue.java new file mode 100644 index 000000000..62f6ddf0f --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/utils/logs/LimitedQueue.java @@ -0,0 +1,50 @@ +package jadx.gui.utils.logs; + +import java.util.AbstractQueue; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Iterator; + +public class LimitedQueue extends AbstractQueue { + + private final Deque deque = new ArrayDeque<>(); + private final int limit; + + public LimitedQueue(int limit) { + this.limit = limit; + } + + @Override + public Iterator iterator() { + return deque.iterator(); + } + + @Override + public int size() { + return deque.size(); + } + + @Override + public boolean offer(T t) { + deque.addLast(t); + if (deque.size() > limit) { + deque.removeFirst(); + } + return true; + } + + @Override + public T poll() { + return deque.poll(); + } + + @Override + public T peek() { + return deque.peek(); + } + + @Override + public void clear() { + deque.clear(); + } +} diff --git a/jadx-gui/src/main/java/jadx/gui/utils/logs/LogCollector.java b/jadx-gui/src/main/java/jadx/gui/utils/logs/LogCollector.java index 966a00449..c1fa25df8 100644 --- a/jadx-gui/src/main/java/jadx/gui/utils/logs/LogCollector.java +++ b/jadx-gui/src/main/java/jadx/gui/utils/logs/LogCollector.java @@ -1,7 +1,6 @@ package jadx.gui.utils.logs; -import java.util.Deque; -import java.util.LinkedList; +import java.util.Queue; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -44,8 +43,12 @@ public class LogCollector extends AppenderBase { @Nullable private ILogListener listener; + @Nullable + private ILogIssuesListener issuesListener; + private int errors = 0; + private int warnings = 0; - private final Deque buffer = new LinkedList<>(); + private final Queue buffer = new LimitedQueue<>(BUFFER_SIZE); public LogCollector() { setName("LogCollector"); @@ -60,14 +63,24 @@ public class LogCollector extends AppenderBase { if (listener != null && level.isGreaterOrEqual(listener.getFilterLevel())) { listener.onAppend(msg); } + if (level == Level.ERROR) { + errors++; + issuesUpdated(); + } else if (level == Level.WARN) { + warnings++; + issuesUpdated(); + } + } + } + + private void issuesUpdated() { + if (issuesListener != null) { + issuesListener.onChange(errors, warnings); } } private void store(Level level, String msg) { - buffer.addLast(new LogEvent(level, msg)); - if (buffer.size() > BUFFER_SIZE) { - buffer.pollFirst(); - } + buffer.offer(new LogEvent(level, msg)); } public void setLayout(Layout layout) { @@ -81,10 +94,32 @@ public class LogCollector extends AppenderBase { } } + public void registerIssueListener(@NotNull ILogIssuesListener listener) { + this.issuesListener = listener; + synchronized (this) { + listener.onChange(errors, warnings); + } + } + public void resetListener() { this.listener = null; } + public void reset() { + buffer.clear(); + errors = 0; + warnings = 0; + issuesUpdated(); + } + + public int getErrors() { + return errors; + } + + public int getWarnings() { + return warnings; + } + private String init(Level filterLevel) { StringBuilder sb = new StringBuilder(); for (LogEvent event : buffer) { diff --git a/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties b/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties index 8b12fae0d..886547d76 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties @@ -64,6 +64,8 @@ message.userCancelTask=Aufgabe wurde vom Benutzer abgebrochen. message.memoryLow=Jadx hat zu wenig Speicherplatz. Bitte mit erhöhter maximaler Heap-Größe erneut starten. message.taskError=Die Aufgabe ist durch Fehler fehlgeschlagen (siehe Protokoll für Details). message.errorTitle=Fehler +#message.load_errors=Load failed.\nErrors count: %d\nClick OK to open log viewer" +#message.no_classes=No classes loaded, nothing to decompile! message.saveIncomplete=Speichern unvollständig.
%s
%d Klassen oder Quellen wurden nicht gespeichert! message.indexIncomplete=Index einiger Klassen übersprungen.
%s
%d Klassen wurden nicht indiziert und werden nicht in den Suchergebnissen erscheinen! @@ -241,6 +243,11 @@ apkSignature.warnings=Warnhinweise apkSignature.exception=APK-Verifizierung fehlgeschlagen apkSignature.unprotectedEntry=Dateien, die nicht durch eine Signatur geschützt sind. Unbefugte Änderungen an diesem JAR-Eintrag werden nicht erkannt. +#issues_panel.label=Issues: +#issues_panel.errors=%d errors +#issues_panel.warnings=%d warnings +#issues_panel.tooltip=Open in log viewer + debugger.process_selector=Zu debuggenden Prozess auswählen debugger.step_into=Schritt Into (F7) debugger.step_over=Schritt Over (F8) 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 ecc31344c..21af2dded 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_en_US.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_en_US.properties @@ -64,6 +64,8 @@ message.userCancelTask=Task was canceled by user. message.memoryLow=Jadx is running low on memory. Please restart with increased maximum heap size. message.taskError=Task failed with error (check log for details). message.errorTitle=Error +message.load_errors=Load failed.\nErrors count: %d\nClick OK to open log viewer" +message.no_classes=No classes loaded, nothing to decompile! message.saveIncomplete=Save incomplete.
%s
%d classes or resources were not saved! message.indexIncomplete=Index of some classes skipped.
%s
%d classes were not indexed and will not appear in search results! @@ -241,6 +243,11 @@ apkSignature.warnings=Warnings apkSignature.exception=APK verification failed apkSignature.unprotectedEntry=Files that are not protected by signature. Unauthorized modifications to this JAR entry will not be detected. +issues_panel.label=Issues: +issues_panel.errors=%d errors +issues_panel.warnings=%d warnings +issues_panel.tooltip=Open in log viewer + debugger.process_selector=Select a process to debug debugger.step_into=Step Into (F7) debugger.step_over=Step Over (F8) diff --git a/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties b/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties index 550700d84..3673a68c2 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties @@ -64,6 +64,8 @@ nav.forward=Adelante #message.memoryLow=Jadx is running low on memory. Please restart with increased maximum heap size. #message.taskError=Task failed with error (check log for details). #message.errorTitle=Error +#message.load_errors=Load failed.\nErrors count: %d\nClick OK to open log viewer" +#message.no_classes=No classes loaded, nothing to decompile! #message.saveIncomplete=Save incomplete.
%s
%d classes or resources were not saved! #message.indexIncomplete=Index of some classes skipped.
%s
%d classes were not indexed and will not appear in search results! @@ -241,6 +243,11 @@ certificate.serialPubKeyY=Y #apkSignature.exception= #apkSignature.unprotectedEntry= +#issues_panel.label=Issues: +#issues_panel.errors=%d errors +#issues_panel.warnings=%d warnings +#issues_panel.tooltip=Open in log viewer + #debugger.process_selector=Select a process to debug #debugger.step_into=Step Into (F7) #debugger.step_over=Step Over (F8) diff --git a/jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties b/jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties index 45e517e47..2bbfa2d3b 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties @@ -64,6 +64,8 @@ nav.forward=앞으로 #message.memoryLow=Jadx is running low on memory. Please restart with increased maximum heap size. #message.taskError=Task failed with error (check log for details). #message.errorTitle=Error +#message.load_errors=Load failed.\nErrors count: %d\nClick OK to open log viewer" +#message.no_classes=No classes loaded, nothing to decompile! #message.saveIncomplete=Save incomplete.
%s
%d classes or resources were not saved! #message.indexIncomplete=Index of some classes skipped.
%s
%d classes were not indexed and will not appear in search results! @@ -241,6 +243,11 @@ apkSignature.warnings=경고 apkSignature.exception=APK 검증 실패 apkSignature.unprotectedEntry=서명으로 보호되지 않는 파일. 이 JAR 항목에 대한 승인되지 않은 수정은 감지되지 않습니다. +#issues_panel.label=Issues: +#issues_panel.errors=%d errors +#issues_panel.warnings=%d warnings +#issues_panel.tooltip=Open in log viewer + debugger.process_selector=디버깅 할 프로세스 선택 debugger.step_into=한 단계씩 코드 실행 (F7) debugger.step_over=프로시저 단위 실행 (F8) diff --git a/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties b/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties index a07ecfadb..d8dfe6136 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties @@ -64,6 +64,8 @@ nav.forward=前进 #message.memoryLow=Jadx is running low on memory. Please restart with increased maximum heap size. #message.taskError=Task failed with error (check log for details). #message.errorTitle=Error +#message.load_errors=Load failed.\nErrors count: %d\nClick OK to open log viewer" +#message.no_classes=No classes loaded, nothing to decompile! #message.saveIncomplete=Save incomplete.
%s
%d classes or resources were not saved! #message.indexIncomplete=Index of some classes skipped.
%s
%d classes were not indexed and will not appear in search results! @@ -241,6 +243,11 @@ apkSignature.warnings=警告 apkSignature.exception=APK 验证失败 apkSignature.unprotectedEntry=不受签名保护的文件。不会检测对此 JAR 条目的未经授权的修改。 +#issues_panel.label=Issues: +#issues_panel.errors=%d errors +#issues_panel.warnings=%d warnings +#issues_panel.tooltip=Open in log viewer + #debugger.process_selector=Select a process to debug #debugger.step_into=Step Into (F7) #debugger.step_over=Step Over (F8) diff --git a/jadx-gui/src/main/resources/icons/nodes/detailView.svg b/jadx-gui/src/main/resources/icons/nodes/detailView.svg new file mode 100644 index 000000000..b1f95da82 --- /dev/null +++ b/jadx-gui/src/main/resources/icons/nodes/detailView.svg @@ -0,0 +1,4 @@ + + + + diff --git a/jadx-gui/src/main/resources/icons/ui/error.svg b/jadx-gui/src/main/resources/icons/ui/error.svg new file mode 100644 index 000000000..a7ad8a2d6 --- /dev/null +++ b/jadx-gui/src/main/resources/icons/ui/error.svg @@ -0,0 +1,4 @@ + + + + diff --git a/jadx-gui/src/main/resources/icons/ui/warning.svg b/jadx-gui/src/main/resources/icons/ui/warning.svg new file mode 100644 index 000000000..7ad4cec6d --- /dev/null +++ b/jadx-gui/src/main/resources/icons/ui/warning.svg @@ -0,0 +1,4 @@ + + + +