feat(gui): allow view and edit input smali files
This commit is contained in:
@@ -51,7 +51,7 @@ public class TabStateViewAdapter {
|
||||
viewState.setPreviewTab(tvs.isPreviewTab());
|
||||
return viewState;
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to load tab state: " + tvs, e);
|
||||
LOG.error("Failed to load tab state: {}", tvs, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package jadx.gui.treemodel;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JPopupMenu;
|
||||
@@ -15,11 +16,15 @@ public class JInputFile extends JNode {
|
||||
private final Path filePath;
|
||||
|
||||
public JInputFile(Path filePath) {
|
||||
this.filePath = filePath;
|
||||
this.filePath = Objects.requireNonNull(filePath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JPopupMenu onTreePopupMenu(MainWindow mainWindow) {
|
||||
return buildInputFilePopupMenu(mainWindow, filePath);
|
||||
}
|
||||
|
||||
public static JPopupMenu buildInputFilePopupMenu(MainWindow mainWindow, Path filePath) {
|
||||
JPopupMenu menu = new JPopupMenu();
|
||||
menu.add(new SimpleMenuItem(NLS.str("popup.add_files"), mainWindow::addFiles));
|
||||
menu.add(new SimpleMenuItem(NLS.str("popup.remove"), () -> mainWindow.removeInput(filePath)));
|
||||
@@ -46,4 +51,22 @@ public class JInputFile extends JNode {
|
||||
public String getTooltip() {
|
||||
return filePath.normalize().toAbsolutePath().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return filePath.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
return ((JInputFile) o).filePath.equals(filePath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "JInputFile{" + filePath + '}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,12 @@ public class JInputFiles extends JNode {
|
||||
|
||||
public JInputFiles(List<Path> files) {
|
||||
for (Path file : files) {
|
||||
add(new JInputFile(file));
|
||||
String fileName = file.getFileName().toString();
|
||||
if (fileName.endsWith(".smali")) {
|
||||
add(new JInputSmaliFile(file));
|
||||
} else {
|
||||
add(new JInputFile(file));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
package jadx.gui.treemodel;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JPopupMenu;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.ICodeInfo;
|
||||
import jadx.api.impl.SimpleCodeInfo;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.codearea.AbstractCodeArea;
|
||||
import jadx.gui.ui.codearea.CodeContentPanel;
|
||||
import jadx.gui.ui.panel.ContentPanel;
|
||||
import jadx.gui.ui.tab.TabbedPane;
|
||||
import jadx.gui.utils.Icons;
|
||||
|
||||
public class JInputSmaliFile extends JEditableNode {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JInputSmaliFile.class);
|
||||
|
||||
private final Path filePath;
|
||||
|
||||
public JInputSmaliFile(Path filePath) {
|
||||
this.filePath = Objects.requireNonNull(filePath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JPopupMenu onTreePopupMenu(MainWindow mainWindow) {
|
||||
return JInputFile.buildInputFilePopupMenu(mainWindow, filePath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ContentPanel getContentPanel(TabbedPane tabbedPane) {
|
||||
return new CodeContentPanel(tabbedPane, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSyntaxName() {
|
||||
return AbstractCodeArea.SYNTAX_STYLE_SMALI;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICodeInfo getCodeInfo() {
|
||||
try {
|
||||
return new SimpleCodeInfo(FileUtils.readFile(filePath));
|
||||
} catch (Exception e) {
|
||||
throw new JadxRuntimeException("Failed to read file: " + filePath.toAbsolutePath(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(String newContent) {
|
||||
try {
|
||||
FileUtils.writeFile(filePath, newContent);
|
||||
LOG.debug("File saved: {}", filePath.toAbsolutePath());
|
||||
} catch (Exception e) {
|
||||
throw new JadxRuntimeException("Failed to write file: " + filePath.toAbsolutePath(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JClass getJParent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Icon getIcon() {
|
||||
return Icons.FILE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String makeString() {
|
||||
return filePath.getFileName().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTooltip() {
|
||||
return filePath.normalize().toAbsolutePath().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return filePath.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
return ((JInputSmaliFile) o).filePath.equals(filePath);
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import jadx.core.utils.Utils;
|
||||
import jadx.core.xmlgen.ResContainer;
|
||||
import jadx.gui.jobs.SimpleTask;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.codearea.AbstractCodeArea;
|
||||
import jadx.gui.ui.codearea.BinaryContentPanel;
|
||||
import jadx.gui.ui.codearea.CodeContentPanel;
|
||||
import jadx.gui.ui.panel.ContentPanel;
|
||||
@@ -275,6 +276,7 @@ public class JResource extends JLoadableNode {
|
||||
|
||||
private static final Map<String, String> EXTENSION_TO_FILE_SYNTAX = jadx.core.utils.Utils.newConstStringMap(
|
||||
"java", SyntaxConstants.SYNTAX_STYLE_JAVA,
|
||||
"smali", AbstractCodeArea.SYNTAX_STYLE_SMALI,
|
||||
"js", SyntaxConstants.SYNTAX_STYLE_JAVASCRIPT,
|
||||
"ts", SyntaxConstants.SYNTAX_STYLE_TYPESCRIPT,
|
||||
"json", SyntaxConstants.SYNTAX_STYLE_JSON,
|
||||
|
||||
@@ -6,6 +6,7 @@ import jadx.gui.treemodel.JNode;
|
||||
|
||||
public class TabBlueprint {
|
||||
private final JNode node;
|
||||
private boolean created;
|
||||
private boolean pinned;
|
||||
private boolean bookmarked;
|
||||
private boolean hidden;
|
||||
@@ -19,6 +20,14 @@ public class TabBlueprint {
|
||||
return node;
|
||||
}
|
||||
|
||||
public boolean isCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
public void setCreated(boolean created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public boolean isPinned() {
|
||||
return pinned;
|
||||
}
|
||||
|
||||
@@ -342,9 +342,10 @@ public class TabComponent extends JPanel {
|
||||
}
|
||||
|
||||
public TabBlueprint getBlueprint() {
|
||||
TabBlueprint blueprint = tabsController.getTabByNode(contentPanel.getNode());
|
||||
JNode node = contentPanel.getNode();
|
||||
TabBlueprint blueprint = tabsController.getTabByNode(node);
|
||||
if (blueprint == null) {
|
||||
throw new JadxRuntimeException("TabComponent does not have a corresponding TabBlueprint");
|
||||
throw new JadxRuntimeException("TabComponent does not have a corresponding TabBlueprint, node: " + node);
|
||||
}
|
||||
return blueprint;
|
||||
}
|
||||
|
||||
@@ -407,10 +407,15 @@ public class TabbedPane extends JTabbedPane implements ITabStatesListener {
|
||||
if (blueprint.isHidden()) {
|
||||
return;
|
||||
}
|
||||
ContentPanel newPanel = blueprint.getNode().getContentPanel(this);
|
||||
JNode node = blueprint.getNode();
|
||||
ContentPanel newPanel = node.getContentPanel(this);
|
||||
if (newPanel != null) {
|
||||
if (node != newPanel.getNode()) {
|
||||
throw new JadxRuntimeException("Incorrect node found in content panel");
|
||||
}
|
||||
FocusManager.listen(newPanel);
|
||||
addContentPanel(newPanel);
|
||||
blueprint.setCreated(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -77,6 +77,10 @@ public class TabsController {
|
||||
blueprint = newBlueprint;
|
||||
}
|
||||
setTabHiddenInternal(blueprint, hidden);
|
||||
if (!blueprint.isCreated()) {
|
||||
LOG.warn("No content panel for node: {}", node);
|
||||
closeTabForce(blueprint);
|
||||
}
|
||||
return blueprint;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user