feat: allow to disable installed plugins (#2277)

This commit is contained in:
Skylot
2024-10-26 22:21:26 +01:00
parent 4d8a5d6671
commit cc6a893402
33 changed files with 358 additions and 90 deletions
@@ -33,6 +33,12 @@ public class CommandPlugins implements ICommand {
@Parameter(names = { "--uninstall" }, description = "uninstall plugin with pluginId")
protected String uninstall;
@Parameter(names = { "--disable" }, description = "disable plugin with pluginId")
protected String disable;
@Parameter(names = { "--enable" }, description = "enable plugin with pluginId")
protected String enable;
@Parameter(names = { "-h", "--help" }, description = "print this help", help = true)
protected boolean printHelp = false;
@@ -47,6 +53,10 @@ public class CommandPlugins implements ICommand {
jcw.printUsage(subCommander);
return;
}
if (!subCommander.getUnknownOptions().isEmpty()) {
System.out.println("Error: found unknown options: " + subCommander.getUnknownOptions());
}
if (install != null) {
installPlugin(install);
}
@@ -69,26 +79,54 @@ public class CommandPlugins implements ICommand {
}
}
if (list) {
List<JadxPluginMetadata> installed = JadxPluginsTools.getInstance().getInstalled();
System.out.println("Installed plugins: " + installed.size());
int i = 1;
for (JadxPluginMetadata plugin : installed) {
System.out.println(" " + (i++) + ") "
+ plugin.getPluginId() + " (" + plugin.getVersion() + ") - "
+ plugin.getName() + ": " + plugin.getDescription());
}
printInstalledPlugins();
}
if (available) {
List<JadxPluginMetadata> availableList = JadxPluginsList.getInstance().get();
System.out.println("Available plugins: " + availableList.size());
int i = 1;
for (JadxPluginMetadata plugin : availableList) {
System.out.println(" " + (i++) + ") "
+ plugin.getName() + ": " + plugin.getDescription()
System.out.println(" - " + plugin.getName() + ": " + plugin.getDescription()
+ " (" + plugin.getLocationId() + ")");
}
}
if (disable != null) {
if (JadxPluginsTools.getInstance().changeDisabledStatus(disable, true)) {
System.out.println("Plugin '" + disable + "' disabled.");
} else {
System.out.println("Plugin '" + disable + "' already disabled.");
}
}
if (enable != null) {
if (JadxPluginsTools.getInstance().changeDisabledStatus(enable, false)) {
System.out.println("Plugin '" + enable + "' enabled.");
} else {
System.out.println("Plugin '" + enable + "' already enabled.");
}
}
}
private static void printInstalledPlugins() {
List<JadxPluginMetadata> installed = JadxPluginsTools.getInstance().getInstalled();
System.out.println("Installed plugins: " + installed.size());
for (JadxPluginMetadata plugin : installed) {
StringBuilder sb = new StringBuilder();
sb.append(" - ");
sb.append(plugin.getPluginId());
String version = plugin.getVersion();
if (version != null) {
sb.append(" (").append(version).append(')');
}
if (plugin.isDisabled()) {
sb.append(" (disabled)");
}
sb.append(" - ");
sb.append(plugin.getName());
sb.append(": ");
sb.append(plugin.getDescription());
System.out.println(sb);
}
}
private void installPlugin(String locationId) {
@@ -93,13 +93,14 @@ public final class JadxDecompiler implements Closeable {
private List<ResourceFile> resources;
private final IDecompileScheduler decompileScheduler = new DecompilerScheduler();
private final JadxEventsImpl events = new JadxEventsImpl();
private final ResourcesLoader resourcesLoader = new ResourcesLoader(this);
private final List<ICodeLoader> customCodeLoaders = new ArrayList<>();
private final List<CustomResourcesLoader> customResourcesLoaders = new ArrayList<>();
private final Map<JadxPassType, List<JadxPass>> customPasses = new HashMap<>();
private IJadxEvents events = new JadxEventsImpl();
public JadxDecompiler() {
this(new JadxArgs());
}
@@ -666,6 +667,10 @@ public final class JadxDecompiler implements Closeable {
return events;
}
public void setEventsImpl(IJadxEvents eventsImpl) {
this.events = eventsImpl;
}
public void addCustomCodeLoader(ICodeLoader customCodeLoader) {
customCodeLoaders.add(customCodeLoader);
}
@@ -15,4 +15,15 @@ public interface IJadxEvents {
* For public event types check {@link JadxEvents} class.
*/
<E extends IJadxEvent> void addListener(JadxEventType<E> eventType, Consumer<E> listener);
/**
* Remove listener for specific event.
* Listener should be same or equal object.
*/
<E extends IJadxEvent> void removeListener(JadxEventType<E> eventType, Consumer<E> listener);
/**
* Clear all listeners.
*/
void reset();
}
@@ -6,4 +6,13 @@ public abstract class JadxEventType<T extends IJadxEvent> {
return new JadxEventType<>() {
};
}
public static <E extends IJadxEvent> JadxEventType<E> create(String name) {
return new JadxEventType<>() {
@Override
public String toString() {
return name;
}
};
}
}
@@ -16,17 +16,17 @@ public class JadxEvents {
/**
* Notify about renaming done by user (GUI only).
*/
public static final JadxEventType<NodeRenamedByUser> NODE_RENAMED_BY_USER = create();
public static final JadxEventType<NodeRenamedByUser> NODE_RENAMED_BY_USER = create("NODE_RENAMED_BY_USER");
/**
* Request reload of a current project (GUI only).
*/
public static final JadxEventType<ReloadProject> RELOAD_PROJECT = create();
public static final JadxEventType<ReloadProject> RELOAD_PROJECT = create("RELOAD_PROJECT");
/**
* Request reload of a settings window (GUI only).
* Useful for a reload custom settings group which was set with
* {@link JadxGuiSettings#setCustomSettingsGroup(ISettingsGroup)}.
*/
public static final JadxEventType<ReloadSettingsWindow> RELOAD_SETTINGS_WINDOW = create();
public static final JadxEventType<ReloadSettingsWindow> RELOAD_SETTINGS_WINDOW = create("RELOAD_SETTINGS_WINDOW");
}
@@ -10,7 +10,7 @@ public class Consts {
public static final boolean DEBUG_FINALLY = false;
public static final boolean DEBUG_ATTRIBUTES = false;
public static final boolean DEBUG_RESTRUCTURE = false;
public static final boolean DEBUG_EVENTS = true;
public static final boolean DEBUG_EVENTS = Jadx.isDevVersion();
public static final String CLASS_OBJECT = "java.lang.Object";
public static final String CLASS_STRING = "java.lang.String";
@@ -26,8 +26,20 @@ public class JadxEventsImpl implements IJadxEvents {
@Override
public <E extends IJadxEvent> void addListener(JadxEventType<E> eventType, Consumer<E> listener) {
manager.addListener(eventType, listener);
if (Consts.DEBUG_EVENTS) {
LOG.debug("add listener for: {}, stats: {}", eventType, manager.listenersDebugStats());
}
}
@Override
public <E extends IJadxEvent> void removeListener(JadxEventType<E> eventType, Consumer<E> listener) {
manager.removeListener(eventType, listener);
if (Consts.DEBUG_EVENTS) {
LOG.debug("remove listener for: {}, stats: {}", eventType, manager.listenersDebugStats());
}
}
@Override
public void reset() {
manager.reset();
}
@@ -9,6 +9,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.jetbrains.annotations.NotNull;
@@ -35,6 +36,14 @@ public class JadxEventsManager {
.add((Consumer<IJadxEvent>) listener);
}
public synchronized <E extends IJadxEvent> boolean removeListener(JadxEventType<E> eventType, Consumer<E> listener) {
List<Consumer<IJadxEvent>> eventListeners = listeners.get(eventType);
if (eventListeners != null) {
return eventListeners.remove(listener);
}
return false;
}
public synchronized void send(IJadxEvent event) {
List<Consumer<IJadxEvent>> consumers = listeners.get(event.getType());
if (consumers != null) {
@@ -58,4 +67,12 @@ public class JadxEventsManager {
}
};
}
public String listenersDebugStats() {
return listeners.entrySet()
.stream()
.filter(p -> !p.getValue().isEmpty())
.map(p -> p.getKey() + ":" + p.getValue().size())
.collect(Collectors.joining(", ", "[", "]"));
}
}
@@ -70,6 +70,8 @@ public class JadxWrapper {
decompiler = new JadxDecompiler(jadxArgs);
guiPluginsContext = initGuiPluginsContext(decompiler, mainWindow);
initUsageCache(jadxArgs);
decompiler.setEventsImpl(mainWindow.events());
decompiler.load();
initCodeCache();
}
@@ -159,6 +161,11 @@ public class JadxWrapper {
guiPluginsContext.reset();
}
public void reloadPasses() {
resetGuiPluginsContext();
decompiler.reloadPasses();
}
/**
* Get the complete list of classes
*/
@@ -7,5 +7,5 @@ import static jadx.api.plugins.events.JadxEventType.create;
public class JadxGuiEvents {
public static final JadxEventType<TreeUpdate> TREE_UPDATE = create();
public static final JadxEventType<TreeUpdate> TREE_UPDATE = create("TREE_UPDATE");
}
@@ -48,7 +48,7 @@ public class RenameService {
public static void init(MainWindow mainWindow) {
RenameService renameService = new RenameService(mainWindow);
mainWindow.events().addListener(JadxEvents.NODE_RENAMED_BY_USER, renameService::process);
mainWindow.events().global().addListener(JadxEvents.NODE_RENAMED_BY_USER, renameService::process);
}
private final MainWindow mainWindow;
@@ -0,0 +1,43 @@
package jadx.gui.events.types;
import java.util.function.Consumer;
import jadx.api.plugins.events.IJadxEvent;
import jadx.api.plugins.events.IJadxEvents;
import jadx.api.plugins.events.JadxEventType;
import jadx.core.plugins.events.JadxEventsImpl;
/**
* Special events implementation to operate on both: global UI and project events.
* Project events hold listeners only while a project opened and reset them on close.
*/
public class JadxGuiEventsImpl implements IJadxEvents {
private final IJadxEvents global = new JadxEventsImpl();
private final IJadxEvents project = new JadxEventsImpl();
public IJadxEvents global() {
return global;
}
@Override
public void send(IJadxEvent event) {
global.send(event);
project.send(event);
}
@Override
public <E extends IJadxEvent> void addListener(JadxEventType<E> eventType, Consumer<E> listener) {
project.addListener(eventType, listener);
}
@Override
public <E extends IJadxEvent> void removeListener(JadxEventType<E> eventType, Consumer<E> listener) {
project.removeListener(eventType, listener);
}
@Override
public void reset() {
project.reset();
}
}
@@ -22,7 +22,6 @@ import org.slf4j.LoggerFactory;
import kotlin.script.experimental.api.ScriptDiagnostic;
import kotlin.script.experimental.api.ScriptDiagnostic.Severity;
import jadx.gui.JadxWrapper;
import jadx.gui.logs.LogOptions;
import jadx.gui.settings.JadxSettings;
import jadx.gui.settings.LineNumbersMode;
@@ -137,9 +136,7 @@ public class ScriptContentPanel extends AbstractCodeContentPanel {
MainWindow mainWindow = tabbedPane.getMainWindow();
mainWindow.getBackgroundExecutor().execute(NLS.str("script.run"), () -> {
try {
JadxWrapper wrapper = mainWindow.getWrapper();
wrapper.resetGuiPluginsContext();
wrapper.getDecompiler().reloadPasses();
mainWindow.getWrapper().reloadPasses();
} catch (Exception e) {
scriptLog.error("Passes reload failed", e);
}
@@ -15,6 +15,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
import javax.swing.BorderFactory;
import javax.swing.Box;
@@ -54,6 +55,7 @@ import jadx.api.args.IntegerFormat;
import jadx.api.args.ResourceNameSource;
import jadx.api.args.UseSourceNameAsClassNameAlias;
import jadx.api.plugins.events.JadxEvents;
import jadx.api.plugins.events.types.ReloadSettingsWindow;
import jadx.api.plugins.gui.ISettingsGroup;
import jadx.gui.settings.JadxSettings;
import jadx.gui.settings.JadxSettingsAdapter;
@@ -85,6 +87,7 @@ public class JadxSettingsWindow extends JDialog {
private final transient String startSettings;
private final transient String startSettingsHash;
private final transient LangLocale prevLang;
private final transient Consumer<ReloadSettingsWindow> reloadListener;
private transient boolean needReload = false;
private transient SettingsTree tree;
@@ -107,8 +110,8 @@ public class JadxSettingsWindow extends JDialog {
if (!mainWindow.getSettings().loadWindowPos(this)) {
setSize(700, 800);
}
mainWindow.events().addListener(JadxEvents.RELOAD_SETTINGS_WINDOW, r -> UiUtils.uiRun(this::reloadUI));
mainWindow.events().addListener(JadxEvents.RELOAD_PROJECT, r -> UiUtils.uiRun(this::reloadUI));
reloadListener = ev -> UiUtils.uiRun(this::reloadUI);
mainWindow.events().global().addListener(JadxEvents.RELOAD_SETTINGS_WINDOW, reloadListener);
}
private void reloadUI() {
@@ -773,6 +776,7 @@ public class JadxSettingsWindow extends JDialog {
@Override
public void dispose() {
mainWindow.events().global().removeListener(JadxEvents.RELOAD_SETTINGS_WINDOW, reloadListener);
settings.saveWindowPos(this);
super.dispose();
}
@@ -28,6 +28,10 @@ abstract class BasePluginListNode {
return null;
}
public boolean isDisabled() {
return false;
}
public PluginAction getAction() {
return PluginAction.NONE;
}
@@ -2,21 +2,18 @@ package jadx.gui.settings.ui.plugins;
import org.jetbrains.annotations.Nullable;
import jadx.core.plugins.PluginContext;
import jadx.plugins.tools.data.JadxPluginMetadata;
public class InstalledPluginNode extends BasePluginListNode {
private final PluginContext plugin;
private final JadxPluginMetadata metadata;
public InstalledPluginNode(PluginContext plugin, @Nullable JadxPluginMetadata metadata) {
this.plugin = plugin;
public InstalledPluginNode(JadxPluginMetadata metadata) {
this.metadata = metadata;
}
@Override
public @Nullable String getTitle() {
return plugin.getPluginInfo().getName();
return metadata.getName();
}
@Override
@@ -26,37 +23,36 @@ public class InstalledPluginNode extends BasePluginListNode {
@Override
public String getPluginId() {
return plugin.getPluginId();
return metadata.getPluginId();
}
@Override
public String getDescription() {
return plugin.getPluginInfo().getDescription();
return metadata.getDescription();
}
@Override
public String getHomepage() {
return plugin.getPluginInfo().getHomepage();
return metadata.getHomepage();
}
@Override
public PluginAction getAction() {
if (metadata != null) {
return PluginAction.UNINSTALL;
}
return PluginAction.NONE;
return PluginAction.UNINSTALL;
}
@Override
public @Nullable String getVersion() {
if (metadata != null) {
return metadata.getVersion();
}
return null;
return metadata.getVersion();
}
@Override
public boolean isDisabled() {
return metadata.isDisabled();
}
@Override
public String toString() {
return plugin.getPluginInfo().getName();
return metadata.getName();
}
}
@@ -0,0 +1,43 @@
package jadx.gui.settings.ui.plugins;
import org.jetbrains.annotations.Nullable;
import jadx.core.plugins.PluginContext;
public class LoadedPluginNode extends BasePluginListNode {
private final PluginContext plugin;
public LoadedPluginNode(PluginContext plugin) {
this.plugin = plugin;
}
@Override
public @Nullable String getTitle() {
return plugin.getPluginInfo().getName();
}
@Override
public boolean hasDetails() {
return true;
}
@Override
public String getPluginId() {
return plugin.getPluginId();
}
@Override
public String getDescription() {
return plugin.getPluginInfo().getDescription();
}
@Override
public String getHomepage() {
return plugin.getPluginInfo().getHomepage();
}
@Override
public String toString() {
return plugin.getPluginInfo().getName();
}
}
@@ -53,9 +53,9 @@ public class PluginSettings {
}
public ISettingsGroup build() {
List<PluginContext> installedPlugins = new CollectPlugins(mainWindow).build();
ISettingsGroup pluginsGroup = new PluginSettingsGroup(this, mainWindow, installedPlugins);
for (PluginContext context : installedPlugins) {
List<PluginContext> collectedPlugins = new CollectPlugins(mainWindow).build();
ISettingsGroup pluginsGroup = new PluginSettingsGroup(this, mainWindow, collectedPlugins);
for (PluginContext context : collectedPlugins) {
ISettingsGroup pluginGroup = addPluginGroup(context);
if (pluginGroup != null) {
pluginsGroup.getSubGroups().add(pluginGroup);
@@ -98,6 +98,13 @@ public class PluginSettings {
});
}
public void changeDisableStatus(String pluginId, boolean disabled) {
mainWindow.getBackgroundExecutor().execute(
NLS.str("preferences.plugins.task.status"),
() -> JadxPluginsTools.getInstance().changeDisabledStatus(pluginId, disabled),
s -> requestReload());
}
void updateAll() {
mainWindow.getBackgroundExecutor().execute(NLS.str("preferences.plugins.task.updating"), () -> {
List<JadxPluginUpdate> updates = JadxPluginsTools.getInstance().updateAll();
@@ -113,7 +120,7 @@ public class PluginSettings {
private ISettingsGroup addPluginGroup(PluginContext context) {
JadxGuiContext guiContext = context.getGuiContext();
if (guiContext instanceof GuiPluginContext) {
GuiPluginContext pluginGuiContext = ((GuiPluginContext) guiContext);
GuiPluginContext pluginGuiContext = (GuiPluginContext) guiContext;
ISettingsGroup customSettingsGroup = pluginGuiContext.getCustomSettingsGroup();
if (customSettingsGroup != null) {
return customSettingsGroup;
@@ -7,9 +7,8 @@ import java.awt.Font;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@@ -52,15 +51,15 @@ class PluginSettingsGroup implements ISettingsGroup {
private final MainWindow mainWindow;
private final String title;
private final List<ISettingsGroup> subGroups = new ArrayList<>();
private final List<PluginContext> installedPlugins;
private final List<PluginContext> collectedPlugins;
private JPanel detailsPanel;
public PluginSettingsGroup(PluginSettings pluginSettings, MainWindow mainWindow, List<PluginContext> installedPlugins) {
public PluginSettingsGroup(PluginSettings pluginSettings, MainWindow mainWindow, List<PluginContext> collectedPlugins) {
this.pluginsSettings = pluginSettings;
this.mainWindow = mainWindow;
this.title = NLS.str("preferences.plugins");
this.installedPlugins = installedPlugins;
this.collectedPlugins = collectedPlugins;
}
@Override
@@ -124,17 +123,21 @@ class PluginSettingsGroup implements ISettingsGroup {
private void applyData(DefaultListModel<BasePluginListNode> listModel) {
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 : installedPlugins) {
nodes.add(new InstalledPluginNode(plugin, installedMap.get(plugin.getPluginId())));
List<BasePluginListNode> nodes = new ArrayList<>(installed.size() + collectedPlugins.size());
Set<String> installedSet = new HashSet<>(installed.size());
for (JadxPluginMetadata pluginMetadata : installed) {
installedSet.add(pluginMetadata.getPluginId());
nodes.add(new InstalledPluginNode(pluginMetadata));
}
for (PluginContext plugin : collectedPlugins) {
if (!installedSet.contains(plugin.getPluginId())) {
nodes.add(new LoadedPluginNode(plugin));
}
}
nodes.sort(Comparator.comparing(BasePluginListNode::getTitle));
fillListModel(listModel, nodes, Collections.emptyList());
loadAvailablePlugins(listModel, nodes, installedPlugins);
loadAvailablePlugins(listModel, nodes, installedSet);
}
private static void fillListModel(DefaultListModel<BasePluginListNode> listModel,
@@ -149,17 +152,14 @@ class PluginSettingsGroup implements ISettingsGroup {
}
private void loadAvailablePlugins(DefaultListModel<BasePluginListNode> listModel,
List<BasePluginListNode> nodes, List<PluginContext> installedPlugins) {
List<BasePluginListNode> nodes, Set<String> installedSet) {
mainWindow.getBackgroundExecutor().execute(
NLS.str("preferences.plugins.task.downloading_list"),
() -> {
try {
JadxPluginsList.getInstance().get(availablePlugins -> {
Set<String> installed = installedPlugins.stream()
.map(PluginContext::getPluginId)
.collect(Collectors.toSet());
List<AvailablePluginNode> availableNodes = availablePlugins.stream()
.filter(availablePlugin -> !installed.contains(availablePlugin.getPluginId()))
.filter(availablePlugin -> !installedSet.contains(availablePlugin.getPluginId()))
.map(AvailablePluginNode::new)
.collect(Collectors.toList());
UiUtils.uiRunAndWait(() -> fillListModel(listModel, nodes, availableNodes));
@@ -200,6 +200,17 @@ class PluginSettingsGroup implements ISettingsGroup {
if (actionBtn != null) {
top.add(actionBtn);
}
if (node.getAction() == PluginAction.UNINSTALL) {
// TODO: allow disable bundled plugins
boolean disabled = node.isDisabled();
String statusChangeLabel = disabled
? NLS.str("preferences.plugins.enable_btn")
: NLS.str("preferences.plugins.disable_btn");
JButton statusBtn = new JButton(statusChangeLabel);
statusBtn.addActionListener(ev -> pluginsSettings.changeDisableStatus(node.getPluginId(), !disabled));
top.add(Box.createHorizontalStrut(10));
top.add(statusBtn);
}
JPanel center = new JPanel();
center.setLayout(new BoxLayout(center, BoxLayout.PAGE_AXIS));
@@ -271,14 +282,19 @@ class PluginSettingsGroup implements ISettingsGroup {
@Override
public Component getListCellRendererComponent(JList<? extends BasePluginListNode> list,
BasePluginListNode value, int index, boolean isSelected, boolean cellHasFocus) {
if (!value.hasDetails()) {
titleLbl.setText(value.getTitle());
BasePluginListNode plugin, int index, boolean isSelected, boolean cellHasFocus) {
if (!plugin.hasDetails()) {
titleLbl.setText(plugin.getTitle());
return titleLbl;
}
nameLbl.setText(value.getTitle());
nameLbl.setToolTipText(value.getLocationId());
versionLbl.setText(Utils.getOrElse(value.getVersion(), ""));
nameLbl.setText(plugin.getTitle());
nameLbl.setToolTipText(plugin.getLocationId());
versionLbl.setText(Utils.getOrElse(plugin.getVersion(), ""));
boolean enabled = !plugin.isDisabled();
nameLbl.setEnabled(enabled);
versionLbl.setEnabled(enabled);
if (isSelected) {
panel.setBackground(list.getSelectionBackground());
nameLbl.setBackground(list.getSelectionBackground());
@@ -77,17 +77,15 @@ import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Level;
import jadx.api.JadxArgs;
import jadx.api.JadxDecompiler;
import jadx.api.JavaClass;
import jadx.api.JavaNode;
import jadx.api.ResourceFile;
import jadx.api.plugins.events.IJadxEvents;
import jadx.api.plugins.events.JadxEvents;
import jadx.api.plugins.events.types.ReloadProject;
import jadx.api.plugins.events.types.ReloadSettingsWindow;
import jadx.api.plugins.utils.CommonFileUtils;
import jadx.core.Jadx;
import jadx.core.export.TemplateFile;
import jadx.core.plugins.events.JadxEventsImpl;
import jadx.core.utils.ListUtils;
import jadx.core.utils.StringUtils;
import jadx.core.utils.android.AndroidManifestParser;
@@ -99,6 +97,7 @@ import jadx.gui.JadxWrapper;
import jadx.gui.cache.manager.CacheManager;
import jadx.gui.device.debugger.BreakpointManager;
import jadx.gui.events.services.RenameService;
import jadx.gui.events.types.JadxGuiEventsImpl;
import jadx.gui.jobs.BackgroundExecutor;
import jadx.gui.jobs.DecompileTask;
import jadx.gui.jobs.ExportTask;
@@ -195,6 +194,7 @@ public class MainWindow extends JFrame {
private final transient CacheObject cacheObject;
private final transient CacheManager cacheManager;
private final transient BackgroundExecutor backgroundExecutor;
private final transient JadxGuiEventsImpl events = new JadxGuiEventsImpl();
private final TabsController tabsController;
private final NavigationController navController;
@@ -271,6 +271,7 @@ public class MainWindow extends JFrame {
UiUtils.setWindowIcons(this);
this.shortcutsController.registerMouseEventListener(this);
loadSettings();
initEvents();
update();
checkForUpdate();
@@ -500,9 +501,10 @@ public class MainWindow extends JFrame {
synchronized (ReloadProject.EVENT) {
saveAll();
closeAll();
loadFiles(UiUtils.EMPTY_RUNNABLE);
menuBar.reloadShortcuts();
loadFiles(() -> {
menuBar.reloadShortcuts();
events().send(ReloadSettingsWindow.INSTANCE);
});
}
}
@@ -525,6 +527,7 @@ public class MainWindow extends JFrame {
private void loadFiles(Runnable onFinish) {
if (project.getFilePaths().isEmpty()) {
tabsController.selectTab(new StartPageNode());
onFinish.run();
return;
}
AtomicReference<Exception> wrapperException = new AtomicReference<>();
@@ -605,7 +608,6 @@ public class MainWindow extends JFrame {
initTree();
updateLiveReload(project.isEnableLiveReload());
BreakpointManager.init(project.getFilePaths().get(0).toAbsolutePath().getParent());
initEvents();
List<EditorViewState> openTabs = project.getOpenTabs(this);
backgroundExecutor.execute(NLS.str("progress.load"),
@@ -619,13 +621,12 @@ public class MainWindow extends JFrame {
}
public void passesReloaded() {
initEvents(); // TODO: events reset on reload passes on script run
tabbedPane.reloadInactiveTabs();
reloadTree();
}
private void initEvents() {
events().addListener(JadxEvents.RELOAD_PROJECT, ev -> UiUtils.uiRun(this::reopen));
events().global().addListener(JadxEvents.RELOAD_PROJECT, ev -> UiUtils.uiRun(this::reopen));
RenameService.init(this);
}
@@ -1753,14 +1754,7 @@ public class MainWindow extends JFrame {
return cacheManager;
}
/**
* Events instance if decompiler not yet available
*/
private final IJadxEvents fallbackEvents = new JadxEventsImpl();
public IJadxEvents events() {
return wrapper.getCurrentDecompiler()
.map(JadxDecompiler::events)
.orElse(fallbackEvents);
public JadxGuiEventsImpl events() {
return events;
}
}
@@ -259,6 +259,8 @@ preferences.tab_dnd_appearance=Dragging tab appearance
#preferences.plugins.install=Install plugin
#preferences.plugins.install_btn=Install
#preferences.plugins.uninstall_btn=Uninstall
#preferences.plugins.disable_btn=Disable
#preferences.plugins.enable_btn=Enable
#preferences.plugins.location_id_label=Location id:
#preferences.plugins.plugin_jar=Select Plugin jar
#preferences.plugins.plugin_jar_label=or
@@ -268,6 +270,7 @@ preferences.tab_dnd_appearance=Dragging tab appearance
#preferences.plugins.task.uninstalling=Uninstalling plugin
#preferences.plugins.task.updating=Updating plugins
#preferences.plugins.task.downloading_list=Downloading plugins list
#preferences.plugins.task.status=Changing plugin status
#preferences.cache=Cache
#preferences.cache.location=Cache location
@@ -259,6 +259,8 @@ preferences.tab_dnd_appearance=Dragging tab appearance
preferences.plugins.install=Install plugin
preferences.plugins.install_btn=Install
preferences.plugins.uninstall_btn=Uninstall
preferences.plugins.disable_btn=Disable
preferences.plugins.enable_btn=Enable
preferences.plugins.location_id_label=Location id:
preferences.plugins.plugin_jar=Select Plugin jar
preferences.plugins.plugin_jar_label=or
@@ -268,6 +270,7 @@ preferences.plugins.task.installing=Installing plugin
preferences.plugins.task.uninstalling=Uninstalling plugin
preferences.plugins.task.updating=Updating plugins
preferences.plugins.task.downloading_list=Downloading plugins list
preferences.plugins.task.status=Changing plugin status
preferences.cache=Cache
preferences.cache.location=Cache location
@@ -259,6 +259,8 @@ preferences.rename_use_source_name_as_class_name_alias=Usar el nombre del source
#preferences.plugins.install=Install plugin
#preferences.plugins.install_btn=Install
#preferences.plugins.uninstall_btn=Uninstall
#preferences.plugins.disable_btn=Disable
#preferences.plugins.enable_btn=Enable
#preferences.plugins.location_id_label=Location id:
#preferences.plugins.plugin_jar=Select Plugin jar
#preferences.plugins.plugin_jar_label=or
@@ -268,6 +270,7 @@ preferences.rename_use_source_name_as_class_name_alias=Usar el nombre del source
#preferences.plugins.task.uninstalling=Uninstalling plugin
#preferences.plugins.task.updating=Updating plugins
#preferences.plugins.task.downloading_list=Downloading plugins list
#preferences.plugins.task.status=Changing plugin status
#preferences.cache=Cache
#preferences.cache.location=Cache location
@@ -259,6 +259,8 @@ preferences.tab_dnd_appearance=Dragging tab appearance
preferences.plugins.install=Instal plugin
preferences.plugins.install_btn=Instal
preferences.plugins.uninstall_btn=Uninstal
#preferences.plugins.disable_btn=Disable
#preferences.plugins.enable_btn=Enable
preferences.plugins.location_id_label=Lokasi id:
preferences.plugins.plugin_jar=Pilih berkas jar Plugin
preferences.plugins.plugin_jar_label=atau
@@ -268,6 +270,7 @@ preferences.plugins.task.installing=Menginstal plugin
preferences.plugins.task.uninstalling=Menguninstal plugin
preferences.plugins.task.updating=Mengperbarui plugin
preferences.plugins.task.downloading_list=Mengunduh daftar plugin
#preferences.plugins.task.status=Changing plugin status
preferences.cache=Cache
preferences.cache.location=Lokasi cache
@@ -259,6 +259,8 @@ preferences.tab_dnd_appearance=Dragging tab appearance
#preferences.plugins.install=Install plugin
#preferences.plugins.install_btn=Install
#preferences.plugins.uninstall_btn=Uninstall
#preferences.plugins.disable_btn=Disable
#preferences.plugins.enable_btn=Enable
#preferences.plugins.location_id_label=Location id:
#preferences.plugins.plugin_jar=Select Plugin jar
#preferences.plugins.plugin_jar_label=or
@@ -268,6 +270,7 @@ preferences.tab_dnd_appearance=Dragging tab appearance
#preferences.plugins.task.uninstalling=Uninstalling plugin
#preferences.plugins.task.updating=Updating plugins
#preferences.plugins.task.downloading_list=Downloading plugins list
#preferences.plugins.task.status=Changing plugin status
#preferences.cache=Cache
#preferences.cache.location=Cache location
@@ -259,6 +259,8 @@ preferences.tab_dnd_appearance=Dragging tab appearance
#preferences.plugins.install=Install plugin
#preferences.plugins.install_btn=Install
#preferences.plugins.uninstall_btn=Uninstall
#preferences.plugins.disable_btn=Disable
#preferences.plugins.enable_btn=Enable
#preferences.plugins.location_id_label=Location id:
#preferences.plugins.plugin_jar=Select Plugin jar
#preferences.plugins.plugin_jar_label=or
@@ -268,6 +270,7 @@ preferences.tab_dnd_appearance=Dragging tab appearance
#preferences.plugins.task.uninstalling=Uninstalling plugin
#preferences.plugins.task.updating=Updating plugins
#preferences.plugins.task.downloading_list=Downloading plugins list
#preferences.plugins.task.status=Changing plugin status
#preferences.cache=Cache
#preferences.cache.location=Cache location
@@ -259,6 +259,8 @@ preferences.tab_dnd_appearance=Dragging tab appearance
preferences.plugins.install=Установить плагин
preferences.plugins.install_btn=Установить
preferences.plugins.uninstall_btn=Удалить
#preferences.plugins.disable_btn=Disable
#preferences.plugins.enable_btn=Enable
preferences.plugins.location_id_label=ID источика:
preferences.plugins.plugin_jar=Выберите jar-файл плагина
preferences.plugins.plugin_jar_label=или
@@ -268,6 +270,7 @@ preferences.plugins.task.installing=Установка плагина
preferences.plugins.task.uninstalling=Удаление плагина
preferences.plugins.task.updating=Обновление плагинов
preferences.plugins.task.downloading_list=Загрузка списка плагинов
#preferences.plugins.task.status=Changing plugin status
preferences.cache=Кэш
preferences.cache.location=Директория кэша
@@ -259,6 +259,8 @@ preferences.tab_dnd_appearance=拖拽选项卡外观
preferences.plugins.install=安装插件
preferences.plugins.install_btn=安装
preferences.plugins.uninstall_btn=卸载
#preferences.plugins.disable_btn=Disable
#preferences.plugins.enable_btn=Enable
preferences.plugins.location_id_label=位置ID
preferences.plugins.plugin_jar=选择插件 jar
preferences.plugins.plugin_jar_label=
@@ -268,6 +270,7 @@ preferences.plugins.task.installing=安装插件中
preferences.plugins.task.uninstalling=卸载插件中
preferences.plugins.task.updating=更新插件中
preferences.plugins.task.downloading_list=正在下载插件列表
#preferences.plugins.task.status=Changing plugin status
preferences.cache=缓存
preferences.cache.location=缓存位置
@@ -259,6 +259,8 @@ preferences.tab_dnd_appearance=Dragging tab appearance
preferences.plugins.install=安裝外掛程式
preferences.plugins.install_btn=安裝
preferences.plugins.uninstall_btn=解除安裝
#preferences.plugins.disable_btn=Disable
#preferences.plugins.enable_btn=Enable
preferences.plugins.location_id_label=位置 id
preferences.plugins.plugin_jar=選擇外掛程式 jar
preferences.plugins.plugin_jar_label=
@@ -268,6 +270,7 @@ preferences.plugins.task.installing=正在安裝外掛程式
preferences.plugins.task.uninstalling=正在解除安裝外掛程式
preferences.plugins.task.updating=正在更新外掛程式
preferences.plugins.task.downloading_list=正在下載外掛程式列表
#preferences.plugins.task.status=Changing plugin status
preferences.cache=快取
preferences.cache.location=快取位置
@@ -72,7 +72,7 @@ public class JadxExternalPluginsLoader implements JadxPluginLoader {
}
private void loadInstalledPlugins(Map<Class<? extends JadxPlugin>, JadxPlugin> map, ClassLoader classLoader) {
List<Path> jars = JadxPluginsTools.getInstance().getAllPluginJars();
List<Path> jars = JadxPluginsTools.getInstance().getEnabledPluginJars();
for (Path jar : jars) {
classLoaders.add(loadFromJar(map, classLoader, jar));
}
@@ -28,9 +28,6 @@ import jadx.plugins.tools.utils.PluginUtils;
import static jadx.plugins.tools.utils.PluginFiles.PLUGINS_LIST_CACHE;
/**
* TODO: implement list caching (on disk) with check for new release
*/
public class JadxPluginsList {
private static final JadxPluginsList INSTANCE = new JadxPluginsList();
@@ -124,6 +124,38 @@ public class JadxPluginsTools {
return list;
}
public List<Path> getEnabledPluginJars() {
List<Path> list = new ArrayList<>();
for (JadxPluginMetadata pluginMetadata : loadPluginsJson().getInstalled()) {
if (pluginMetadata.isDisabled()) {
continue;
}
list.add(INSTALLED_DIR.resolve(pluginMetadata.getJar()));
}
collectFromDir(list, DROPINS_DIR);
return list;
}
/**
* Disable or enable plugin
*
* @return true if disabled status was changed
*/
public boolean changeDisabledStatus(String pluginId, boolean disabled) {
JadxInstalledPlugins data = loadPluginsJson();
JadxPluginMetadata plugin = data.getInstalled().stream()
.filter(p -> p.getPluginId().equals(pluginId))
.findFirst()
.orElseThrow(() -> new RuntimeException("Plugin not found: " + pluginId));
if (plugin.isDisabled() == disabled) {
return false;
}
plugin.setDisabled(disabled);
data.setUpdated(System.currentTimeMillis());
savePluginsJson(data);
return true;
}
private @Nullable JadxPluginMetadata update(JadxPluginMetadata plugin) {
IJadxPluginResolver resolver = ResolversRegistry.getById(plugin.getResolverId());
if (!resolver.isUpdateSupported()) {
@@ -12,6 +12,7 @@ public class JadxPluginMetadata implements Comparable<JadxPluginMetadata> {
private String locationId;
private String resolverId;
private String jar;
private boolean disabled;
public String getPluginId() {
return pluginId;
@@ -77,6 +78,14 @@ public class JadxPluginMetadata implements Comparable<JadxPluginMetadata> {
this.jar = jar;
}
public boolean isDisabled() {
return disabled;
}
public void setDisabled(boolean disabled) {
this.disabled = disabled;
}
@Override
public boolean equals(Object other) {
if (this == other) {