feat(gui): manage plugins in preferences window
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
package jadx.api.plugins.events;
|
||||
|
||||
import jadx.api.plugins.events.types.NodeRenamedByUser;
|
||||
import jadx.api.plugins.events.types.ReloadSettingsWindow;
|
||||
import jadx.api.plugins.gui.ISettingsGroup;
|
||||
import jadx.api.plugins.gui.JadxGuiSettings;
|
||||
|
||||
import static jadx.api.plugins.events.JadxEventType.create;
|
||||
|
||||
@@ -13,4 +16,11 @@ public class JadxEvents {
|
||||
* Notify about renames done by user (GUI only).
|
||||
*/
|
||||
public static final JadxEventType<NodeRenamedByUser> NODE_RENAMED_BY_USER = create();
|
||||
|
||||
/**
|
||||
* Request reload of settings window (GUI only).
|
||||
* Useful for reload custom settings group set with
|
||||
* {@link JadxGuiSettings#setCustomSettingsGroup(ISettingsGroup)}.
|
||||
*/
|
||||
public static final JadxEventType<ReloadSettingsWindow> RELOAD_SETTINGS_WINDOW = create();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package jadx.api.plugins.events.types;
|
||||
|
||||
import jadx.api.plugins.events.IJadxEvent;
|
||||
import jadx.api.plugins.events.JadxEventType;
|
||||
import jadx.api.plugins.events.JadxEvents;
|
||||
|
||||
public class ReloadSettingsWindow implements IJadxEvent {
|
||||
|
||||
public static final ReloadSettingsWindow INSTANCE = new ReloadSettingsWindow();
|
||||
|
||||
private ReloadSettingsWindow() {
|
||||
// singleton
|
||||
}
|
||||
|
||||
@Override
|
||||
public JadxEventType<ReloadSettingsWindow> getType() {
|
||||
return JadxEvents.RELOAD_SETTINGS_WINDOW;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RELOAD_SETTINGS_WINDOW";
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ public interface JadxGuiSettings {
|
||||
/**
|
||||
* Set plugin custom settings page
|
||||
*/
|
||||
void setCustomSettings(ISettingsGroup group);
|
||||
void setCustomSettingsGroup(ISettingsGroup group);
|
||||
|
||||
/**
|
||||
* Helper method to build options group only for provided option list
|
||||
|
||||
@@ -7,5 +7,5 @@ public enum TaskStatus {
|
||||
CANCEL_BY_USER,
|
||||
CANCEL_BY_TIMEOUT,
|
||||
CANCEL_BY_MEMORY,
|
||||
ERROR;
|
||||
ERROR
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.swing.JMenu;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -41,9 +39,7 @@ public class CommonGuiPluginsContext {
|
||||
|
||||
public void reset() {
|
||||
codePopupActionList.clear();
|
||||
JMenu pluginsMenu = mainWindow.getPluginsMenu();
|
||||
pluginsMenu.removeAll();
|
||||
pluginsMenu.setVisible(false);
|
||||
mainWindow.resetPluginsMenu();
|
||||
}
|
||||
|
||||
public MainWindow getMainWindow() {
|
||||
@@ -63,9 +59,7 @@ public class CommonGuiPluginsContext {
|
||||
}
|
||||
});
|
||||
item.setNameAndDesc(name);
|
||||
JMenu pluginsMenu = mainWindow.getPluginsMenu();
|
||||
pluginsMenu.add(item);
|
||||
pluginsMenu.setVisible(true);
|
||||
mainWindow.addToPluginsMenu(item);
|
||||
}
|
||||
|
||||
public void appendPopupMenus(CodeArea codeArea, JNodePopupBuilder popup) {
|
||||
|
||||
@@ -5,8 +5,8 @@ import java.util.List;
|
||||
import jadx.api.plugins.gui.ISettingsGroup;
|
||||
import jadx.api.plugins.gui.JadxGuiSettings;
|
||||
import jadx.api.plugins.options.OptionDescription;
|
||||
import jadx.gui.settings.ui.PluginsSettings;
|
||||
import jadx.gui.settings.ui.SubSettingsGroup;
|
||||
import jadx.gui.settings.ui.plugins.PluginsSettings;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
|
||||
public class GuiSettingsContext implements JadxGuiSettings {
|
||||
@@ -17,7 +17,7 @@ public class GuiSettingsContext implements JadxGuiSettings {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCustomSettings(ISettingsGroup group) {
|
||||
public void setCustomSettingsGroup(ISettingsGroup group) {
|
||||
guiPluginContext.setCustomSettings(group);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ import javax.swing.ScrollPaneConstants;
|
||||
import javax.swing.SpinnerNumberModel;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.WindowConstants;
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -54,12 +55,14 @@ import jadx.api.JadxDecompiler;
|
||||
import jadx.api.args.GeneratedRenamesMappingFileMode;
|
||||
import jadx.api.args.IntegerFormat;
|
||||
import jadx.api.args.ResourceNameSource;
|
||||
import jadx.api.plugins.events.JadxEvents;
|
||||
import jadx.api.plugins.gui.ISettingsGroup;
|
||||
import jadx.gui.cache.code.CodeCacheMode;
|
||||
import jadx.gui.cache.usage.UsageCacheMode;
|
||||
import jadx.gui.settings.JadxSettings;
|
||||
import jadx.gui.settings.JadxSettingsAdapter;
|
||||
import jadx.gui.settings.LineNumbersMode;
|
||||
import jadx.gui.settings.ui.plugins.PluginsSettings;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.codearea.EditorTheme;
|
||||
import jadx.gui.utils.FontUtils;
|
||||
@@ -82,6 +85,7 @@ public class JadxSettingsWindow extends JDialog {
|
||||
private final transient LangLocale prevLang;
|
||||
|
||||
private transient boolean needReload = false;
|
||||
private SettingsTree tree;
|
||||
|
||||
public JadxSettingsWindow(MainWindow mainWindow, JadxSettings settings) {
|
||||
this.mainWindow = mainWindow;
|
||||
@@ -91,6 +95,7 @@ public class JadxSettingsWindow extends JDialog {
|
||||
this.prevLang = settings.getLangLocale();
|
||||
|
||||
initUI();
|
||||
mainWindow.events().addListener(JadxEvents.RELOAD_SETTINGS_WINDOW, r -> UiUtils.uiRun(this::reloadUI));
|
||||
|
||||
setTitle(NLS.str("preferences.title"));
|
||||
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
|
||||
@@ -103,10 +108,18 @@ public class JadxSettingsWindow extends JDialog {
|
||||
}
|
||||
}
|
||||
|
||||
private void reloadUI() {
|
||||
TreePath selectionPath = tree.getSelectionPath();
|
||||
mainWindow.getSettings().saveWindowPos(this);
|
||||
getContentPane().removeAll();
|
||||
initUI();
|
||||
pack();
|
||||
mainWindow.getSettings().loadWindowPos(this);
|
||||
tree.setSelectionPath(selectionPath);
|
||||
}
|
||||
|
||||
private void initUI() {
|
||||
JPanel groupPanel = new JPanel();
|
||||
groupPanel.setLayout(new BoxLayout(groupPanel, BoxLayout.LINE_AXIS));
|
||||
groupPanel.setBorder(BorderFactory.createEmptyBorder(10, 3, 3, 10));
|
||||
JPanel wrapGroupPanel = new JPanel(new BorderLayout(10, 10));
|
||||
|
||||
List<ISettingsGroup> groups = new ArrayList<>();
|
||||
groups.add(makeDecompilationGroup());
|
||||
@@ -118,18 +131,15 @@ public class JadxSettingsWindow extends JDialog {
|
||||
groups.add(new PluginsSettings(mainWindow, settings).build());
|
||||
groups.add(makeOtherGroup());
|
||||
|
||||
SettingsTree tree = new SettingsTree();
|
||||
tree.init(groupPanel, groups);
|
||||
tree = new SettingsTree();
|
||||
tree.init(wrapGroupPanel, groups);
|
||||
JScrollPane leftPane = new JScrollPane(tree);
|
||||
leftPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 3, 3));
|
||||
|
||||
JPanel wrapGroupPanel = new JPanel(new BorderLayout());
|
||||
wrapGroupPanel.add(groupPanel, BorderLayout.PAGE_START);
|
||||
|
||||
JScrollPane rightPane = new JScrollPane(wrapGroupPanel);
|
||||
rightPane.getVerticalScrollBar().setUnitIncrement(16);
|
||||
rightPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
||||
rightPane.setBorder(BorderFactory.createEmptyBorder());
|
||||
rightPane.setBorder(BorderFactory.createEmptyBorder(10, 3, 3, 10));
|
||||
|
||||
JSplitPane splitPane = new JSplitPane();
|
||||
splitPane.setResizeWeight(0.2);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package jadx.gui.settings.ui;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.Insets;
|
||||
@@ -18,16 +19,21 @@ public class SettingsGroup implements ISettingsGroup {
|
||||
|
||||
private final String title;
|
||||
private final JPanel panel;
|
||||
private final JPanel gridPanel;
|
||||
private final GridBagConstraints c;
|
||||
private int row;
|
||||
|
||||
public SettingsGroup(String title) {
|
||||
this.title = title;
|
||||
panel = new JPanel(new GridBagLayout());
|
||||
panel.setBorder(BorderFactory.createTitledBorder(title));
|
||||
gridPanel = new JPanel(new GridBagLayout());
|
||||
c = new GridBagConstraints();
|
||||
c.insets = new Insets(5, 5, 5, 5);
|
||||
c.weighty = 1.0;
|
||||
|
||||
panel = new JPanel();
|
||||
panel.setLayout(new BorderLayout(5, 5));
|
||||
panel.setBorder(BorderFactory.createTitledBorder(title));
|
||||
panel.add(gridPanel, BorderLayout.PAGE_START);
|
||||
}
|
||||
|
||||
public JLabel addRow(String label, JComponent comp) {
|
||||
@@ -36,15 +42,15 @@ public class SettingsGroup implements ISettingsGroup {
|
||||
|
||||
public JLabel addRow(String label, String tooltip, JComponent comp) {
|
||||
c.gridy = row++;
|
||||
JLabel jLabel = new JLabel(label);
|
||||
jLabel.setLabelFor(comp);
|
||||
jLabel.setHorizontalAlignment(SwingConstants.LEFT);
|
||||
JLabel rowLbl = new JLabel(label);
|
||||
rowLbl.setLabelFor(comp);
|
||||
rowLbl.setHorizontalAlignment(SwingConstants.LEFT);
|
||||
c.gridx = 0;
|
||||
c.gridwidth = 1;
|
||||
c.anchor = GridBagConstraints.LINE_START;
|
||||
c.weightx = 0.8;
|
||||
c.fill = GridBagConstraints.NONE;
|
||||
panel.add(jLabel, c);
|
||||
gridPanel.add(rowLbl, c);
|
||||
c.gridx = 1;
|
||||
c.gridwidth = GridBagConstraints.REMAINDER;
|
||||
c.anchor = GridBagConstraints.CENTER;
|
||||
@@ -52,18 +58,16 @@ public class SettingsGroup implements ISettingsGroup {
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
|
||||
if (tooltip != null) {
|
||||
jLabel.setToolTipText(tooltip);
|
||||
rowLbl.setToolTipText(tooltip);
|
||||
comp.setToolTipText(tooltip);
|
||||
}
|
||||
|
||||
panel.add(comp, c);
|
||||
|
||||
comp.addPropertyChangeListener("enabled", evt -> jLabel.setEnabled((boolean) evt.getNewValue()));
|
||||
return jLabel;
|
||||
gridPanel.add(comp, c);
|
||||
comp.addPropertyChangeListener("enabled", evt -> rowLbl.setEnabled((boolean) evt.getNewValue()));
|
||||
return rowLbl;
|
||||
}
|
||||
|
||||
public void end() {
|
||||
panel.add(Box.createVerticalGlue());
|
||||
gridPanel.add(Box.createVerticalGlue());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package jadx.gui.settings.ui.plugins;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.api.plugins.JadxPluginInfo;
|
||||
|
||||
abstract class BasePluginListNode {
|
||||
|
||||
public @Nullable String getTitle() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public JadxPluginInfo getPluginInfo() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public @Nullable String getVersion() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package jadx.gui.settings.ui.plugins;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dimension;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.WindowConstants;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.formdev.flatlaf.FlatClientProperties;
|
||||
|
||||
import ch.qos.logback.classic.Level;
|
||||
|
||||
import jadx.api.plugins.events.types.ReloadSettingsWindow;
|
||||
import jadx.gui.logs.LogOptions;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.filedialog.FileDialogWrapper;
|
||||
import jadx.gui.ui.filedialog.FileOpenMode;
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.TextStandardActions;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
import jadx.plugins.tools.JadxPluginsTools;
|
||||
import jadx.plugins.tools.data.JadxPluginMetadata;
|
||||
|
||||
public class InstallPluginDialog extends JDialog {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(InstallPluginDialog.class);
|
||||
private static final long serialVersionUID = 5304314264730563853L;
|
||||
|
||||
private final MainWindow mainWindow;
|
||||
private JTextField locationFld;
|
||||
|
||||
public InstallPluginDialog(MainWindow mainWindow) {
|
||||
super(mainWindow, NLS.str("preferences.plugins.install"));
|
||||
this.mainWindow = mainWindow;
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
locationFld = new JTextField();
|
||||
locationFld.setAlignmentX(LEFT_ALIGNMENT);
|
||||
locationFld.setColumns(50);
|
||||
TextStandardActions.attach(locationFld);
|
||||
locationFld.putClientProperty(FlatClientProperties.TEXT_FIELD_SHOW_CLEAR_BUTTON, true);
|
||||
|
||||
JLabel locationLbl = new JLabel(NLS.str("preferences.plugins.location_id_label"));
|
||||
locationLbl.setLabelFor(locationFld);
|
||||
|
||||
JPanel locationPanel = new JPanel();
|
||||
locationPanel.setLayout(new BoxLayout(locationPanel, BoxLayout.LINE_AXIS));
|
||||
locationPanel.add(locationLbl);
|
||||
locationPanel.add(Box.createRigidArea(new Dimension(5, 0)));
|
||||
locationPanel.add(locationFld);
|
||||
|
||||
JButton fileBtn = new JButton(NLS.str("preferences.plugins.plugin_jar"));
|
||||
fileBtn.addActionListener(ev -> openPluginJar());
|
||||
JLabel fileLbl = new JLabel(NLS.str("preferences.plugins.plugin_jar_label"));
|
||||
fileLbl.setLabelFor(fileBtn);
|
||||
|
||||
JPanel filePanel = new JPanel();
|
||||
filePanel.setLayout(new BoxLayout(filePanel, BoxLayout.LINE_AXIS));
|
||||
filePanel.add(fileLbl);
|
||||
filePanel.add(Box.createRigidArea(new Dimension(5, 0)));
|
||||
filePanel.add(fileBtn);
|
||||
|
||||
JPanel mainPanel = new JPanel();
|
||||
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
|
||||
mainPanel.add(locationPanel);
|
||||
mainPanel.add(Box.createRigidArea(new Dimension(0, 5)));
|
||||
mainPanel.add(filePanel);
|
||||
|
||||
JButton installBtn = new JButton(NLS.str("preferences.plugins.install_btn"));
|
||||
installBtn.addActionListener(ev -> install());
|
||||
JButton cancelBtn = new JButton(NLS.str("preferences.cancel"));
|
||||
cancelBtn.addActionListener(ev -> dispose());
|
||||
|
||||
JPanel buttonPane = new JPanel();
|
||||
buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));
|
||||
// TODO: add operation progress
|
||||
buttonPane.add(Box.createHorizontalGlue());
|
||||
buttonPane.add(installBtn);
|
||||
buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
|
||||
buttonPane.add(cancelBtn);
|
||||
getRootPane().setDefaultButton(installBtn);
|
||||
|
||||
JPanel contentPanel = new JPanel();
|
||||
contentPanel.setLayout(new BorderLayout(5, 5));
|
||||
contentPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||
contentPanel.add(mainPanel, BorderLayout.PAGE_START);
|
||||
contentPanel.add(buttonPane, BorderLayout.PAGE_END);
|
||||
getContentPane().add(contentPanel);
|
||||
|
||||
pack();
|
||||
setLocationRelativeTo(null);
|
||||
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
|
||||
setModalityType(ModalityType.APPLICATION_MODAL);
|
||||
UiUtils.addEscapeShortCutToDispose(this);
|
||||
}
|
||||
|
||||
private void openPluginJar() {
|
||||
FileDialogWrapper fd = new FileDialogWrapper(mainWindow, FileOpenMode.CUSTOM_OPEN);
|
||||
fd.setTitle(NLS.str("preferences.plugins.plugin_jar"));
|
||||
fd.setFileExtList(Collections.singletonList("jar"));
|
||||
fd.setSelectionMode(JFileChooser.FILES_ONLY);
|
||||
List<Path> files = fd.show();
|
||||
if (files.size() == 1) {
|
||||
locationFld.setText("file:" + files.get(0).toAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
private void install() {
|
||||
mainWindow.getBackgroundExecutor().execute(NLS.str("preferences.plugins.task.installing"),
|
||||
() -> {
|
||||
try {
|
||||
JadxPluginMetadata metadata = JadxPluginsTools.getInstance().install(locationFld.getText());
|
||||
LOG.info("Plugin installed: {}", metadata);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Install failed", e);
|
||||
mainWindow.showLogViewer(LogOptions.forLevel(Level.ERROR));
|
||||
}
|
||||
},
|
||||
status -> {
|
||||
mainWindow.events().send(ReloadSettingsWindow.INSTANCE);
|
||||
UiUtils.uiRun(mainWindow::reopen);
|
||||
});
|
||||
dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package jadx.gui.settings.ui.plugins;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.api.plugins.JadxPluginInfo;
|
||||
import jadx.core.plugins.PluginContext;
|
||||
import jadx.plugins.tools.data.JadxPluginMetadata;
|
||||
|
||||
public class PluginListNode extends BasePluginListNode {
|
||||
private final PluginContext plugin;
|
||||
private final JadxPluginMetadata metadata;
|
||||
|
||||
public PluginListNode(PluginContext plugin, @Nullable JadxPluginMetadata metadata) {
|
||||
this.plugin = plugin;
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JadxPluginInfo getPluginInfo() {
|
||||
return plugin.getPluginInfo();
|
||||
}
|
||||
|
||||
public @Nullable JadxPluginMetadata getMetadata() {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getVersion() {
|
||||
if (metadata != null) {
|
||||
return metadata.getVersion();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return plugin.getPluginInfo().getName();
|
||||
}
|
||||
}
|
||||
+41
-12
@@ -1,4 +1,4 @@
|
||||
package jadx.gui.settings.ui;
|
||||
package jadx.gui.settings.ui.plugins;
|
||||
|
||||
import java.awt.event.ItemEvent;
|
||||
import java.util.List;
|
||||
@@ -11,13 +11,13 @@ import javax.swing.JCheckBox;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JSpinner;
|
||||
import javax.swing.JTextField;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.plugins.events.types.ReloadSettingsWindow;
|
||||
import jadx.api.plugins.gui.ISettingsGroup;
|
||||
import jadx.api.plugins.gui.JadxGuiContext;
|
||||
import jadx.api.plugins.options.JadxPluginOptions;
|
||||
@@ -25,13 +25,18 @@ import jadx.api.plugins.options.OptionDescription;
|
||||
import jadx.api.plugins.options.OptionFlag;
|
||||
import jadx.api.plugins.options.OptionType;
|
||||
import jadx.core.plugins.PluginContext;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.gui.plugins.context.GuiPluginContext;
|
||||
import jadx.gui.settings.JadxProject;
|
||||
import jadx.gui.settings.JadxSettings;
|
||||
import jadx.gui.settings.ui.SettingsGroup;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.plugins.CollectPluginOptions;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
import jadx.gui.utils.plugins.CollectPlugins;
|
||||
import jadx.gui.utils.ui.DocumentUpdateListener;
|
||||
import jadx.plugins.tools.JadxPluginsTools;
|
||||
import jadx.plugins.tools.data.JadxPluginUpdate;
|
||||
|
||||
public class PluginsSettings {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(PluginsSettings.class);
|
||||
@@ -44,12 +49,11 @@ public class PluginsSettings {
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
public SettingsGroup build() {
|
||||
SettingsGroup pluginsGroup = new SubSettingsGroup(NLS.str("preferences.plugins"));
|
||||
fillMainSettings(pluginsGroup);
|
||||
List<PluginContext> list = new CollectPluginOptions(mainWindow).build();
|
||||
public ISettingsGroup build() {
|
||||
List<PluginContext> list = new CollectPlugins(mainWindow).build();
|
||||
ISettingsGroup pluginsGroup = new PluginsSettingsGroup(this, list);
|
||||
for (PluginContext context : list) {
|
||||
ISettingsGroup pluginGroup = buildPluginGroup(context);
|
||||
ISettingsGroup pluginGroup = addPluginGroup(context);
|
||||
if (pluginGroup != null) {
|
||||
pluginsGroup.getSubGroups().add(pluginGroup);
|
||||
}
|
||||
@@ -57,12 +61,37 @@ public class PluginsSettings {
|
||||
return pluginsGroup;
|
||||
}
|
||||
|
||||
private void fillMainSettings(SettingsGroup settingsGroup) {
|
||||
JPanel panel = settingsGroup.getPanel();
|
||||
panel.add(new JPanel());
|
||||
public void addPlugin() {
|
||||
new InstallPluginDialog(mainWindow).setVisible(true);
|
||||
}
|
||||
|
||||
private ISettingsGroup buildPluginGroup(PluginContext context) {
|
||||
public void uninstall(String pluginId) {
|
||||
mainWindow.getBackgroundExecutor().execute(NLS.str("preferences.plugins.task.uninstalling"), () -> {
|
||||
boolean success = JadxPluginsTools.getInstance().uninstall(pluginId);
|
||||
if (success) {
|
||||
LOG.debug("Uninstall complete");
|
||||
mainWindow.events().send(ReloadSettingsWindow.INSTANCE);
|
||||
UiUtils.uiRun(mainWindow::reopen);
|
||||
} else {
|
||||
LOG.debug("Uninstall failed");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void updateAll() {
|
||||
mainWindow.getBackgroundExecutor().execute(NLS.str("preferences.plugins.task.updating"), () -> {
|
||||
List<JadxPluginUpdate> updates = JadxPluginsTools.getInstance().updateAll();
|
||||
if (!updates.isEmpty()) {
|
||||
LOG.debug("Updates: {}\n ", Utils.listToString(updates, "\n "));
|
||||
mainWindow.events().send(ReloadSettingsWindow.INSTANCE);
|
||||
UiUtils.uiRun(mainWindow::reopen);
|
||||
} else {
|
||||
LOG.debug("No updates found");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private ISettingsGroup addPluginGroup(PluginContext context) {
|
||||
JadxGuiContext guiContext = context.getGuiContext();
|
||||
if (guiContext instanceof GuiPluginContext) {
|
||||
GuiPluginContext pluginGuiContext = ((GuiPluginContext) guiContext);
|
||||
@@ -0,0 +1,211 @@
|
||||
package jadx.gui.settings.ui.plugins;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.DefaultListModel;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JSplitPane;
|
||||
import javax.swing.JTextPane;
|
||||
import javax.swing.ListCellRenderer;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.SwingConstants;
|
||||
|
||||
import jadx.api.plugins.JadxPluginInfo;
|
||||
import jadx.api.plugins.gui.ISettingsGroup;
|
||||
import jadx.core.plugins.PluginContext;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.plugins.tools.JadxPluginsTools;
|
||||
import jadx.plugins.tools.data.JadxPluginMetadata;
|
||||
|
||||
class PluginsSettingsGroup implements ISettingsGroup {
|
||||
private final PluginsSettings pluginsSettings;
|
||||
private final String title;
|
||||
private final List<ISettingsGroup> subGroups = new ArrayList<>();
|
||||
private final List<PluginContext> pluginsList;
|
||||
|
||||
private PluginListNode selectedPlugin;
|
||||
private JPanel detailsPanel;
|
||||
|
||||
public PluginsSettingsGroup(PluginsSettings pluginsSettings, List<PluginContext> pluginsList) {
|
||||
this.pluginsSettings = pluginsSettings;
|
||||
this.title = NLS.str("preferences.plugins");
|
||||
this.pluginsList = pluginsList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ISettingsGroup> getSubGroups() {
|
||||
return subGroups;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent buildComponent() {
|
||||
// lazy load main page
|
||||
return buildMainSettingsPage();
|
||||
}
|
||||
|
||||
private JPanel buildMainSettingsPage() {
|
||||
JButton installPluginBtn = new JButton(NLS.str("preferences.plugins.install"));
|
||||
installPluginBtn.addActionListener(ev -> pluginsSettings.addPlugin());
|
||||
|
||||
JButton updateAllBtn = new JButton(NLS.str("preferences.plugins.update_all"));
|
||||
updateAllBtn.addActionListener(ev -> pluginsSettings.updateAll());
|
||||
|
||||
JPanel actionsPanel = new JPanel();
|
||||
actionsPanel.setLayout(new BoxLayout(actionsPanel, BoxLayout.LINE_AXIS));
|
||||
actionsPanel.add(installPluginBtn);
|
||||
actionsPanel.add(Box.createRigidArea(new Dimension(5, 0)));
|
||||
actionsPanel.add(updateAllBtn);
|
||||
|
||||
List<JadxPluginMetadata> installed = JadxPluginsTools.getInstance().getInstalled();
|
||||
Map<String, JadxPluginMetadata> installedMap = new HashMap<>(installed.size());
|
||||
installed.forEach(p -> installedMap.put(p.getPluginId(), p));
|
||||
|
||||
List<BasePluginListNode> nodes = new ArrayList<>(installed.size() + 3);
|
||||
for (PluginContext plugin : pluginsList) {
|
||||
nodes.add(new PluginListNode(plugin, installedMap.get(plugin.getPluginId())));
|
||||
}
|
||||
nodes.sort(Comparator.comparing(n -> n.getPluginInfo().getName()));
|
||||
|
||||
DefaultListModel<BasePluginListNode> listModel = new DefaultListModel<>();
|
||||
listModel.addElement(new TitleNode("Installed"));
|
||||
nodes.stream().filter(n -> n.getVersion() != null).forEach(listModel::addElement);
|
||||
listModel.addElement(new TitleNode("Bundled"));
|
||||
nodes.stream().filter(n -> n.getVersion() == null).forEach(listModel::addElement);
|
||||
// TODO: load external plugins list
|
||||
// listModel.addElement(new TitleNode("Available"));
|
||||
|
||||
JList<BasePluginListNode> pluginsList = new JList<>(listModel);
|
||||
pluginsList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
pluginsList.setCellRenderer(new PluginsListCellRenderer(pluginsList));
|
||||
pluginsList.addListSelectionListener(ev -> onSelection(pluginsList.getSelectedValue()));
|
||||
|
||||
JScrollPane scrollPane = new JScrollPane(pluginsList);
|
||||
|
||||
detailsPanel = new JPanel(new BorderLayout(5, 5));
|
||||
detailsPanel.setBorder(BorderFactory.createTitledBorder("Plugin details"));
|
||||
detailsPanel.setLayout(new BoxLayout(detailsPanel, BoxLayout.PAGE_AXIS));
|
||||
detailsPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||
|
||||
JSplitPane splitPanel = new JSplitPane();
|
||||
splitPanel.setBorder(BorderFactory.createEmptyBorder(10, 2, 2, 2));
|
||||
splitPanel.setLeftComponent(scrollPane);
|
||||
splitPanel.setRightComponent(detailsPanel);
|
||||
splitPanel.setDividerLocation(0.4);
|
||||
|
||||
JPanel mainPanel = new JPanel();
|
||||
mainPanel.setLayout(new BorderLayout(5, 5));
|
||||
mainPanel.setBorder(BorderFactory.createTitledBorder(title));
|
||||
mainPanel.add(actionsPanel, BorderLayout.PAGE_START);
|
||||
mainPanel.add(splitPanel, BorderLayout.CENTER);
|
||||
return mainPanel;
|
||||
}
|
||||
|
||||
private void onSelection(BasePluginListNode node) {
|
||||
detailsPanel.removeAll();
|
||||
JadxPluginInfo pluginInfo = node.getPluginInfo();
|
||||
if (pluginInfo != null) {
|
||||
JButton uninstallBtn = new JButton("Uninstall");
|
||||
if (node.getVersion() != null) {
|
||||
uninstallBtn.addActionListener(ev -> pluginsSettings.uninstall(pluginInfo.getPluginId()));
|
||||
} else {
|
||||
uninstallBtn.setEnabled(false);
|
||||
}
|
||||
JLabel nameLbl = new JLabel(pluginInfo.getName());
|
||||
Font baseFont = nameLbl.getFont();
|
||||
nameLbl.setFont(baseFont.deriveFont(Font.BOLD, baseFont.getSize2D() + 2));
|
||||
|
||||
JTextPane descArea = new JTextPane();
|
||||
descArea.setText(pluginInfo.getDescription());
|
||||
descArea.setFont(baseFont.deriveFont(baseFont.getSize2D() + 1));
|
||||
descArea.setEditable(false);
|
||||
descArea.setBorder(BorderFactory.createEmptyBorder());
|
||||
descArea.setOpaque(true);
|
||||
|
||||
JPanel top = new JPanel();
|
||||
top.setLayout(new BoxLayout(top, BoxLayout.LINE_AXIS));
|
||||
top.setBorder(BorderFactory.createEmptyBorder(10, 2, 10, 2));
|
||||
top.add(nameLbl);
|
||||
top.add(Box.createHorizontalGlue());
|
||||
top.add(uninstallBtn);
|
||||
|
||||
detailsPanel.add(top, BorderLayout.PAGE_START);
|
||||
detailsPanel.add(descArea, BorderLayout.CENTER);
|
||||
}
|
||||
detailsPanel.updateUI();
|
||||
}
|
||||
|
||||
private static class PluginsListCellRenderer implements ListCellRenderer<BasePluginListNode> {
|
||||
private final JPanel panel;
|
||||
private final JLabel nameLbl;
|
||||
private final JLabel versionLbl;
|
||||
private final JLabel titleLbl;
|
||||
|
||||
public PluginsListCellRenderer(JList<BasePluginListNode> pluginsList) {
|
||||
panel = new JPanel();
|
||||
panel.setOpaque(true);
|
||||
panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS));
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(2, 10, 2, 10));
|
||||
|
||||
nameLbl = new JLabel("");
|
||||
nameLbl.setFont(nameLbl.getFont().deriveFont(Font.BOLD));
|
||||
nameLbl.setOpaque(true);
|
||||
versionLbl = new JLabel("");
|
||||
versionLbl.setOpaque(true);
|
||||
|
||||
panel.add(nameLbl);
|
||||
panel.add(Box.createHorizontalGlue());
|
||||
panel.add(versionLbl);
|
||||
|
||||
titleLbl = new JLabel();
|
||||
titleLbl.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
titleLbl.setEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getListCellRendererComponent(JList<? extends BasePluginListNode> list,
|
||||
BasePluginListNode value, int index, boolean isSelected, boolean cellHasFocus) {
|
||||
String title = value.getTitle();
|
||||
if (title != null) {
|
||||
titleLbl.setText(title);
|
||||
return titleLbl;
|
||||
}
|
||||
nameLbl.setText(value.getPluginInfo().getName());
|
||||
nameLbl.setToolTipText(value.getPluginInfo().getDescription());
|
||||
versionLbl.setText(Utils.getOrElse(value.getVersion(), ""));
|
||||
if (isSelected) {
|
||||
panel.setBackground(list.getSelectionBackground());
|
||||
nameLbl.setBackground(list.getSelectionBackground());
|
||||
nameLbl.setForeground(list.getSelectionForeground());
|
||||
versionLbl.setBackground(list.getSelectionBackground());
|
||||
} else {
|
||||
panel.setBackground(list.getBackground());
|
||||
nameLbl.setBackground(list.getBackground());
|
||||
nameLbl.setForeground(list.getForeground());
|
||||
versionLbl.setBackground(list.getBackground());
|
||||
}
|
||||
return panel;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package jadx.gui.settings.ui.plugins;
|
||||
|
||||
public class TitleNode extends BasePluginListNode {
|
||||
private final String title;
|
||||
|
||||
public TitleNode(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
}
|
||||
@@ -104,6 +104,7 @@ import jadx.gui.plugins.quark.QuarkDialog;
|
||||
import jadx.gui.settings.JadxProject;
|
||||
import jadx.gui.settings.JadxSettings;
|
||||
import jadx.gui.settings.ui.JadxSettingsWindow;
|
||||
import jadx.gui.settings.ui.plugins.InstallPluginDialog;
|
||||
import jadx.gui.treemodel.ApkSignature;
|
||||
import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JLoadableNode;
|
||||
@@ -482,6 +483,7 @@ public class MainWindow extends JFrame {
|
||||
|
||||
private void loadFiles(Runnable onFinish) {
|
||||
if (project.getFilePaths().isEmpty()) {
|
||||
tabbedPane.showNode(new StartPageNode());
|
||||
return;
|
||||
}
|
||||
AtomicReference<Exception> wrapperException = new AtomicReference<>();
|
||||
@@ -1123,7 +1125,7 @@ public class MainWindow extends JFrame {
|
||||
|
||||
pluginsMenu = new JMenu(NLS.str("menu.plugins"));
|
||||
pluginsMenu.setMnemonic(KeyEvent.VK_P);
|
||||
pluginsMenu.setVisible(false);
|
||||
resetPluginsMenu();
|
||||
|
||||
JMenu tools = new JMenu(NLS.str("menu.tools"));
|
||||
tools.setMnemonic(KeyEvent.VK_T);
|
||||
@@ -1639,6 +1641,19 @@ public class MainWindow extends JFrame {
|
||||
return pluginsMenu;
|
||||
}
|
||||
|
||||
public void resetPluginsMenu() {
|
||||
pluginsMenu.removeAll();
|
||||
pluginsMenu.add(new ActionHandler(() -> new InstallPluginDialog(this).setVisible(true))
|
||||
.withNameAndDesc(NLS.str("preferences.plugins.install")));
|
||||
}
|
||||
|
||||
public void addToPluginsMenu(Action item) {
|
||||
if (pluginsMenu.getMenuComponentCount() == 1) {
|
||||
pluginsMenu.addSeparator();
|
||||
}
|
||||
pluginsMenu.add(item);
|
||||
}
|
||||
|
||||
public RenameMappingsGui getRenameMappings() {
|
||||
return renameMappings;
|
||||
}
|
||||
|
||||
@@ -40,8 +40,8 @@ public class LafManager {
|
||||
public static void updateLaf(JadxSettings settings) {
|
||||
if (setupLaf(getThemeClass(settings))) {
|
||||
// update all components
|
||||
FlatLaf.updateUI();
|
||||
FlatAnimatedLafChange.hideSnapshotWithAnimation();
|
||||
FlatLaf.updateUI();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-8
@@ -1,9 +1,9 @@
|
||||
package jadx.gui.utils.plugins;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.api.JadxDecompiler;
|
||||
@@ -15,15 +15,15 @@ import jadx.gui.ui.MainWindow;
|
||||
import jadx.plugins.tools.JadxExternalPluginsLoader;
|
||||
|
||||
/**
|
||||
* Collect options from all plugins.
|
||||
* Collect all plugins.
|
||||
* Init not yet loaded plugins in new temporary context.
|
||||
* Support case if decompiler in wrapper not initialized yet.
|
||||
*/
|
||||
public class CollectPluginOptions {
|
||||
public class CollectPlugins {
|
||||
|
||||
private final MainWindow mainWindow;
|
||||
|
||||
public CollectPluginOptions(MainWindow mainWindow) {
|
||||
public CollectPlugins(MainWindow mainWindow) {
|
||||
this.mainWindow = mainWindow;
|
||||
}
|
||||
|
||||
@@ -50,9 +50,6 @@ public class CollectPluginOptions {
|
||||
pluginManager.init(missingPlugins);
|
||||
allPlugins.addAll(missingPlugins);
|
||||
}
|
||||
return allPlugins.stream()
|
||||
.filter(context -> context.getOptions() != null)
|
||||
.sorted()
|
||||
.collect(Collectors.toList());
|
||||
return new ArrayList<>(allPlugins);
|
||||
}
|
||||
}
|
||||
@@ -222,6 +222,16 @@ preferences.search_group_title=Ressourcen durchsuchen
|
||||
preferences.res_file_ext=Dateierweiterungen (z.B. .xml|.html), * bedeutet alle
|
||||
preferences.res_skip_file=Dateien überspringen (MB)
|
||||
|
||||
#preferences.plugins.install=Install plugin
|
||||
#preferences.plugins.install_btn=Install
|
||||
#preferences.plugins.location_id_label=Location id:
|
||||
#preferences.plugins.plugin_jar=Select Plugin jar
|
||||
#preferences.plugins.plugin_jar_label=or
|
||||
#preferences.plugins.update_all=Update All
|
||||
#preferences.plugins.task.installing=Installing plugin
|
||||
#preferences.plugins.task.uninstalling=Uninstalling plugin
|
||||
#preferences.plugins.task.updating=Updating plugins
|
||||
|
||||
msg.open_file=Bitte Datei öffnen
|
||||
msg.saving_sources=Quelltexte speichern
|
||||
msg.language_changed_title=Sprache speichern
|
||||
|
||||
@@ -222,6 +222,16 @@ preferences.search_results_per_page=Results per page (0 - no limit)
|
||||
preferences.res_file_ext=Resource files extensions ('xml|html', * for all)
|
||||
preferences.res_skip_file=Skip resources files if larger (MB) (0 - disable)
|
||||
|
||||
preferences.plugins.install=Install plugin
|
||||
preferences.plugins.install_btn=Install
|
||||
preferences.plugins.location_id_label=Location id:
|
||||
preferences.plugins.plugin_jar=Select Plugin jar
|
||||
preferences.plugins.plugin_jar_label=or
|
||||
preferences.plugins.update_all=Update All
|
||||
preferences.plugins.task.installing=Installing plugin
|
||||
preferences.plugins.task.uninstalling=Uninstalling plugin
|
||||
preferences.plugins.task.updating=Updating plugins
|
||||
|
||||
msg.open_file=Please open file
|
||||
msg.saving_sources=Saving sources
|
||||
msg.language_changed_title=Language changed
|
||||
|
||||
@@ -222,6 +222,16 @@ preferences.reset_title=Reestablecer preferencias
|
||||
#preferences.res_file_ext=Resource files extensions ('xml|html', * for all)
|
||||
#preferences.res_skip_file=Skip resources files if larger (MB)
|
||||
|
||||
#preferences.plugins.install=Install plugin
|
||||
#preferences.plugins.install_btn=Install
|
||||
#preferences.plugins.location_id_label=Location id:
|
||||
#preferences.plugins.plugin_jar=Select Plugin jar
|
||||
#preferences.plugins.plugin_jar_label=or
|
||||
#preferences.plugins.update_all=Update All
|
||||
#preferences.plugins.task.installing=Installing plugin
|
||||
#preferences.plugins.task.uninstalling=Uninstalling plugin
|
||||
#preferences.plugins.task.updating=Updating plugins
|
||||
|
||||
msg.open_file=Por favor, abra un archivo
|
||||
msg.saving_sources=Guardando fuente
|
||||
msg.language_changed_title=Idioma cambiado
|
||||
|
||||
@@ -222,6 +222,16 @@ preferences.search_group_title=리소스 검색
|
||||
preferences.res_file_ext=파일 확장자 (예: .xml|.html) (* 은 전체를 의미)
|
||||
preferences.res_skip_file=이 옵션보다 큰 파일 건너 뛰기 (MB)
|
||||
|
||||
#preferences.plugins.install=Install plugin
|
||||
#preferences.plugins.install_btn=Install
|
||||
#preferences.plugins.location_id_label=Location id:
|
||||
#preferences.plugins.plugin_jar=Select Plugin jar
|
||||
#preferences.plugins.plugin_jar_label=or
|
||||
#preferences.plugins.update_all=Update All
|
||||
#preferences.plugins.task.installing=Installing plugin
|
||||
#preferences.plugins.task.uninstalling=Uninstalling plugin
|
||||
#preferences.plugins.task.updating=Updating plugins
|
||||
|
||||
msg.open_file=파일을 여십시오
|
||||
msg.saving_sources=소스 저장 중
|
||||
msg.language_changed_title=언어 변경됨
|
||||
|
||||
@@ -222,6 +222,16 @@ preferences.search_group_title=Buscar recursos
|
||||
preferences.res_file_ext=Extensões de arquivos (ex: .xml|.html), * significa todas
|
||||
preferences.res_skip_file=Pular arquivos excedidos
|
||||
|
||||
#preferences.plugins.install=Install plugin
|
||||
#preferences.plugins.install_btn=Install
|
||||
#preferences.plugins.location_id_label=Location id:
|
||||
#preferences.plugins.plugin_jar=Select Plugin jar
|
||||
#preferences.plugins.plugin_jar_label=or
|
||||
#preferences.plugins.update_all=Update All
|
||||
#preferences.plugins.task.installing=Installing plugin
|
||||
#preferences.plugins.task.uninstalling=Uninstalling plugin
|
||||
#preferences.plugins.task.updating=Updating plugins
|
||||
|
||||
msg.open_file=Abra um arquivo
|
||||
msg.saving_sources=Salvando recursos
|
||||
msg.language_changed_title=Idioma alterado
|
||||
|
||||
@@ -222,6 +222,16 @@ preferences.search_results_per_page=Результатов на страницу
|
||||
preferences.res_file_ext=Расширения файлов ресурсов ('xml|html', * для всех)
|
||||
preferences.res_skip_file=Пропускать ресурсы больше чем (в МБ)
|
||||
|
||||
#preferences.plugins.install=Install plugin
|
||||
#preferences.plugins.install_btn=Install
|
||||
#preferences.plugins.location_id_label=Location id:
|
||||
#preferences.plugins.plugin_jar=Select Plugin jar
|
||||
#preferences.plugins.plugin_jar_label=or
|
||||
#preferences.plugins.update_all=Update All
|
||||
#preferences.plugins.task.installing=Installing plugin
|
||||
#preferences.plugins.task.uninstalling=Uninstalling plugin
|
||||
#preferences.plugins.task.updating=Updating plugins
|
||||
|
||||
msg.open_file=Пожалуйста, откройте файл
|
||||
msg.saving_sources=Сохранение ресурсов
|
||||
msg.language_changed_title=Язык изменен
|
||||
|
||||
@@ -222,6 +222,16 @@ preferences.search_results_per_page=每页结果数(0 - 无限制)
|
||||
preferences.res_file_ext=文件扩展名(e.g. .xml|.html),* 表示所有
|
||||
preferences.res_skip_file=跳过文件大小(MB)
|
||||
|
||||
#preferences.plugins.install=Install plugin
|
||||
#preferences.plugins.install_btn=Install
|
||||
#preferences.plugins.location_id_label=Location id:
|
||||
#preferences.plugins.plugin_jar=Select Plugin jar
|
||||
#preferences.plugins.plugin_jar_label=or
|
||||
#preferences.plugins.update_all=Update All
|
||||
#preferences.plugins.task.installing=Installing plugin
|
||||
#preferences.plugins.task.uninstalling=Uninstalling plugin
|
||||
#preferences.plugins.task.updating=Updating plugins
|
||||
|
||||
msg.open_file=请打开文件
|
||||
msg.saving_sources=正在导出源代码
|
||||
msg.language_changed_title=语言已更改
|
||||
|
||||
@@ -222,6 +222,16 @@ preferences.search_results_per_page=每頁的搜尋結果數 (0 - 無限制)
|
||||
preferences.res_file_ext=副檔名 (e.g. .xml|.html), * 表示全部
|
||||
preferences.res_skip_file=略過大於此值的檔案 (MB)
|
||||
|
||||
#preferences.plugins.install=Install plugin
|
||||
#preferences.plugins.install_btn=Install
|
||||
#preferences.plugins.location_id_label=Location id:
|
||||
#preferences.plugins.plugin_jar=Select Plugin jar
|
||||
#preferences.plugins.plugin_jar_label=or
|
||||
#preferences.plugins.update_all=Update All
|
||||
#preferences.plugins.task.installing=Installing plugin
|
||||
#preferences.plugins.task.uninstalling=Uninstalling plugin
|
||||
#preferences.plugins.task.updating=Updating plugins
|
||||
|
||||
msg.open_file=請開啟檔案
|
||||
msg.saving_sources=正在儲存原始碼
|
||||
msg.language_changed_title=已更改語言
|
||||
|
||||
@@ -4,7 +4,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.net.URL;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@@ -46,7 +46,7 @@ public class JadxPluginsTools {
|
||||
|
||||
private JadxPluginsTools() {
|
||||
ProjectDirectories jadxDirs = ProjectDirectories.from("io.github", "skylot", "jadx");
|
||||
Path plugins = Paths.get(jadxDirs.configDir, "plugins"); // TODO: use dataDir?
|
||||
Path plugins = Paths.get(jadxDirs.configDir, "plugins");
|
||||
makeDirs(plugins);
|
||||
pluginsJson = plugins.resolve("plugins.json");
|
||||
dropins = plugins.resolve("dropins");
|
||||
@@ -165,7 +165,9 @@ public class JadxPluginsTools {
|
||||
}
|
||||
fillPluginInfoFromJar(metadata, tmpJar);
|
||||
|
||||
Path pluginJar = installed.resolve(metadata.getPluginId() + '-' + metadata.getVersion() + ".jar");
|
||||
String version = metadata.getVersion();
|
||||
String fileName = metadata.getPluginId() + (version != null ? '-' + version : "") + ".jar";
|
||||
Path pluginJar = installed.resolve(fileName);
|
||||
copyJar(tmpJar, pluginJar);
|
||||
metadata.setJar(installed.relativize(pluginJar).toString());
|
||||
|
||||
@@ -195,7 +197,7 @@ public class JadxPluginsTools {
|
||||
}
|
||||
|
||||
private void downloadJar(String sourceJar, Path destPath) {
|
||||
try (InputStream in = new URL(sourceJar).openStream()) {
|
||||
try (InputStream in = URI.create(sourceJar).toURL().openStream()) {
|
||||
Files.copy(in, destPath, StandardCopyOption.REPLACE_EXISTING);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to download jar: " + sourceJar, e);
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package jadx.plugins.tools.data;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class JadxPluginMetadata implements Comparable<JadxPluginMetadata> {
|
||||
private String pluginId;
|
||||
private String name;
|
||||
private String description;
|
||||
private String version;
|
||||
private @Nullable String version;
|
||||
private String locationId;
|
||||
private String resolverId;
|
||||
private String jar;
|
||||
@@ -27,7 +28,7 @@ public class JadxPluginMetadata implements Comparable<JadxPluginMetadata> {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
public @Nullable String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@@ -93,7 +94,7 @@ public class JadxPluginMetadata implements Comparable<JadxPluginMetadata> {
|
||||
return "JadxPluginMetadata{"
|
||||
+ "id=" + pluginId
|
||||
+ ", name=" + name
|
||||
+ ", version=" + version
|
||||
+ ", version=" + (version != null ? version : "?")
|
||||
+ ", locationId=" + locationId
|
||||
+ ", jar=" + jar
|
||||
+ '}';
|
||||
|
||||
@@ -10,10 +10,9 @@ Examples: `github:skylot:jadx`, `github:skylot:jadx:sample-plugin` or `github:sk
|
||||
|
||||
`<version>` - exact version to install (optional), should be equal to release name
|
||||
|
||||
Artifact should have a name: `<artifact name prefix>-<release-version-name>.jar`.
|
||||
Artifact name pattern: `<artifact name prefix>[-<release-version-name>].jar`.
|
||||
|
||||
Default value for `<artifact name prefix>` is a repo name,
|
||||
`release-version-name` should have a `x.x.x` format.
|
||||
Default value for `<artifact name prefix>` is a repo name, `-<release-version-name>` is optional.
|
||||
|
||||
---
|
||||
|
||||
|
||||
+8
-1
@@ -64,7 +64,7 @@ public class GithubReleaseResolver implements IJadxPluginResolver {
|
||||
Asset asset = release.getAssets().stream()
|
||||
.filter(a -> a.getName().equals(artifactName))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new RuntimeException("Release artifact with name '" + artifactName + "' not found"));
|
||||
.orElse(searchIgnoringVersion(release, artifactPrefix));
|
||||
|
||||
JadxPluginMetadata metadata = new JadxPluginMetadata();
|
||||
metadata.setResolverId(id());
|
||||
@@ -74,6 +74,13 @@ public class GithubReleaseResolver implements IJadxPluginResolver {
|
||||
return Optional.of(metadata);
|
||||
}
|
||||
|
||||
private @NotNull Asset searchIgnoringVersion(Release release, String artifactPrefix) {
|
||||
return release.getAssets().stream()
|
||||
.filter(a -> a.getName().startsWith(artifactPrefix) && a.getName().startsWith(".jar"))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new RuntimeException("Release artifact with prefix '" + artifactPrefix + "' not found"));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String buildLocationId(String owner, String project, String artifactPrefix) {
|
||||
if (project.equals(artifactPrefix)) {
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ public class RenameMappingsOptions extends BaseOptionsParser {
|
||||
public List<OptionDescription> getOptionsDescriptions() {
|
||||
return Arrays.asList(
|
||||
new JadxOptionDescription(FORMAT_OPT, "mapping format", "auto", getMappingFormats())
|
||||
.withFlag(OptionFlag.PER_PROJECT),
|
||||
.withFlags(OptionFlag.PER_PROJECT, OptionFlag.DISABLE_IN_GUI),
|
||||
JadxOptionDescription.booleanOption(INVERT_OPT, "invert mapping", false)
|
||||
.withFlag(OptionFlag.PER_PROJECT));
|
||||
}
|
||||
|
||||
@@ -4,7 +4,14 @@ val jadx = getJadxInstance()
|
||||
jadx.args.isDeobfuscationOn = false
|
||||
jadx.args.renameFlags = emptySet()
|
||||
|
||||
val regex = """[Oo0]+""".toRegex()
|
||||
val regexOpt = jadx.options.registerString(
|
||||
"regex",
|
||||
"Apply rename for names matches regex",
|
||||
values = listOf(),
|
||||
defaultValue = "[Oo0]+",
|
||||
)
|
||||
|
||||
val regex = regexOpt.value.toRegex()
|
||||
var n = 0
|
||||
jadx.rename.all { name, node ->
|
||||
when {
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ object JadxScriptOptionsUI {
|
||||
.groupBy { it.script }
|
||||
.map { (script, options) -> settings.buildSettingsGroupForOptions(script, options) }
|
||||
.toList()
|
||||
settings.setCustomSettings(EmptyRootGroup("Scripts", subGroups))
|
||||
settings.setCustomSettingsGroup(EmptyRootGroup("Scripts", subGroups))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-3
@@ -1,6 +1,5 @@
|
||||
package jadx.plugins.script
|
||||
|
||||
import jadx.api.JadxDecompiler
|
||||
import jadx.api.plugins.JadxPluginContext
|
||||
import jadx.plugins.script.runtime.JadxScriptData
|
||||
import jadx.plugins.script.runtime.JadxScriptTemplate
|
||||
@@ -18,13 +17,15 @@ import kotlin.script.experimental.jvmhost.BasicJvmScriptingHost
|
||||
import kotlin.script.experimental.jvmhost.createJvmCompilationConfigurationFromTemplate
|
||||
import kotlin.script.experimental.jvmhost.createJvmEvaluationConfigurationFromTemplate
|
||||
import kotlin.system.measureTimeMillis
|
||||
import kotlin.time.DurationUnit
|
||||
import kotlin.time.toDuration
|
||||
|
||||
class ScriptEval {
|
||||
|
||||
private val scriptingHost = BasicJvmScriptingHost()
|
||||
|
||||
fun process(init: JadxPluginContext, scriptOptions: JadxScriptAllOptions): List<JadxScriptData> {
|
||||
val jadx = init.decompiler as JadxDecompiler
|
||||
val jadx = init.decompiler
|
||||
val scripts = jadx.args.inputFiles.filter { f -> f.name.endsWith(".jadx.kts") }
|
||||
if (scripts.isEmpty()) {
|
||||
return emptyList()
|
||||
@@ -60,7 +61,7 @@ class ScriptEval {
|
||||
val result = scriptingHost.eval(scriptData.scriptFile.toScriptSource(), compilationConf, evalConf)
|
||||
processEvalResult(result, scriptData)
|
||||
}
|
||||
scriptData.log.debug { "Script '${scriptData.scriptName}' executed in $execTime ms" }
|
||||
scriptData.log.debug { "Script '${scriptData.scriptName}' executed in ${execTime.toDuration(DurationUnit.MILLISECONDS)}" }
|
||||
}
|
||||
|
||||
private fun processEvalResult(res: ResultWithDiagnostics<EvaluationResult>, scriptData: JadxScriptData) {
|
||||
|
||||
Reference in New Issue
Block a user