feat(gui): load available plugins from jadx-plugins-list
This commit is contained in:
@@ -4,20 +4,26 @@ public class JadxPluginInfo {
|
||||
private final String pluginId;
|
||||
private final String name;
|
||||
private final String description;
|
||||
private final String homepage;
|
||||
|
||||
/**
|
||||
* Conflicting plugins should have same 'provides' property, only one will be loaded
|
||||
* Conflicting plugins should have the same 'provides' property; only one will be loaded
|
||||
*/
|
||||
private final String provides;
|
||||
|
||||
public JadxPluginInfo(String id, String name, String description) {
|
||||
this(id, name, description, id);
|
||||
this(id, name, description, "", id);
|
||||
}
|
||||
|
||||
public JadxPluginInfo(String pluginId, String name, String description, String provides) {
|
||||
this(pluginId, name, description, "", provides);
|
||||
}
|
||||
|
||||
public JadxPluginInfo(String pluginId, String name, String description, String homepage, String provides) {
|
||||
this.pluginId = pluginId;
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.homepage = homepage;
|
||||
this.provides = provides;
|
||||
}
|
||||
|
||||
@@ -33,6 +39,10 @@ public class JadxPluginInfo {
|
||||
return description;
|
||||
}
|
||||
|
||||
public String getHomepage() {
|
||||
return homepage;
|
||||
}
|
||||
|
||||
public String getProvides() {
|
||||
return provides;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package jadx.api.plugins;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class JadxPluginInfoBuilder {
|
||||
private String pluginId;
|
||||
private String name;
|
||||
private String description;
|
||||
private String homepage = "";
|
||||
private @Nullable String provides;
|
||||
|
||||
public JadxPluginInfoBuilder() {
|
||||
}
|
||||
|
||||
public JadxPluginInfoBuilder pluginId(String pluginId) {
|
||||
this.pluginId = Objects.requireNonNull(pluginId);
|
||||
return this;
|
||||
}
|
||||
|
||||
public JadxPluginInfoBuilder name(String name) {
|
||||
this.name = Objects.requireNonNull(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
public JadxPluginInfoBuilder description(String description) {
|
||||
this.description = Objects.requireNonNull(description);
|
||||
return this;
|
||||
}
|
||||
|
||||
public JadxPluginInfoBuilder homepage(String homepage) {
|
||||
this.homepage = homepage;
|
||||
return this;
|
||||
}
|
||||
|
||||
public JadxPluginInfoBuilder provides(String provides) {
|
||||
this.provides = provides;
|
||||
return this;
|
||||
}
|
||||
|
||||
public JadxPluginInfo build() {
|
||||
Objects.requireNonNull(pluginId, "PluginId is required");
|
||||
Objects.requireNonNull(name, "Name is required");
|
||||
Objects.requireNonNull(description, "Description is required");
|
||||
if (provides == null) {
|
||||
provides = pluginId;
|
||||
}
|
||||
return new JadxPluginInfo(pluginId, name, description, homepage, provides);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package jadx.api.plugins.events;
|
||||
|
||||
import jadx.api.plugins.events.types.NodeRenamedByUser;
|
||||
import jadx.api.plugins.events.types.ReloadProject;
|
||||
import jadx.api.plugins.events.types.ReloadSettingsWindow;
|
||||
import jadx.api.plugins.gui.ISettingsGroup;
|
||||
import jadx.api.plugins.gui.JadxGuiSettings;
|
||||
@@ -13,13 +14,18 @@ import static jadx.api.plugins.events.JadxEventType.create;
|
||||
public class JadxEvents {
|
||||
|
||||
/**
|
||||
* Notify about renames done by user (GUI only).
|
||||
* Notify about renaming 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
|
||||
* Request reload of a current project (GUI only).
|
||||
*/
|
||||
public static final JadxEventType<ReloadProject> RELOAD_PROJECT = create();
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
@@ -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 ReloadProject implements IJadxEvent {
|
||||
|
||||
public static final ReloadProject INSTANCE = new ReloadProject();
|
||||
|
||||
private ReloadProject() {
|
||||
// singleton
|
||||
}
|
||||
|
||||
@Override
|
||||
public JadxEventType<ReloadProject> getType() {
|
||||
return JadxEvents.RELOAD_PROJECT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RELOAD_PROJECT";
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import jadx.api.plugins.gui.ISettingsGroup;
|
||||
import jadx.api.plugins.gui.JadxGuiSettings;
|
||||
import jadx.api.plugins.options.OptionDescription;
|
||||
import jadx.gui.settings.ui.SubSettingsGroup;
|
||||
import jadx.gui.settings.ui.plugins.PluginsSettings;
|
||||
import jadx.gui.settings.ui.plugins.PluginSettings;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
|
||||
public class GuiSettingsContext implements JadxGuiSettings {
|
||||
@@ -24,7 +24,7 @@ public class GuiSettingsContext implements JadxGuiSettings {
|
||||
@Override
|
||||
public ISettingsGroup buildSettingsGroupForOptions(String title, List<OptionDescription> options) {
|
||||
MainWindow mainWindow = guiPluginContext.getCommonContext().getMainWindow();
|
||||
PluginsSettings pluginsSettings = new PluginsSettings(mainWindow, mainWindow.getSettings());
|
||||
PluginSettings pluginsSettings = new PluginSettings(mainWindow, mainWindow.getSettings());
|
||||
SubSettingsGroup settingsGroup = new SubSettingsGroup(title);
|
||||
pluginsSettings.addOptions(settingsGroup, options);
|
||||
return settingsGroup;
|
||||
|
||||
@@ -203,7 +203,7 @@ public class QuarkManager {
|
||||
}
|
||||
|
||||
private void runCommand(List<String> cmd) throws Exception {
|
||||
UiUtils.uiRun(() -> mainWindow.showLogViewer(LogOptions.forLevel(Level.INFO)));
|
||||
mainWindow.showLogViewer(LogOptions.forLevel(Level.INFO));
|
||||
LOG.info("Running command: {}", String.join(" ", cmd));
|
||||
ProcessBuilder builder = new ProcessBuilder(cmd);
|
||||
builder.redirectErrorStream(true);
|
||||
|
||||
@@ -37,7 +37,6 @@ 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;
|
||||
@@ -61,7 +60,7 @@ import jadx.gui.settings.JadxSettings;
|
||||
import jadx.gui.settings.JadxSettingsAdapter;
|
||||
import jadx.gui.settings.LineNumbersMode;
|
||||
import jadx.gui.settings.ui.cache.CacheSettingsGroup;
|
||||
import jadx.gui.settings.ui.plugins.PluginsSettings;
|
||||
import jadx.gui.settings.ui.plugins.PluginSettings;
|
||||
import jadx.gui.settings.ui.shortcut.ShortcutsSettingsGroup;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.codearea.EditorTheme;
|
||||
@@ -85,7 +84,7 @@ public class JadxSettingsWindow extends JDialog {
|
||||
private final transient LangLocale prevLang;
|
||||
|
||||
private transient boolean needReload = false;
|
||||
private SettingsTree tree;
|
||||
private transient SettingsTree tree;
|
||||
|
||||
public JadxSettingsWindow(MainWindow mainWindow, JadxSettings settings) {
|
||||
this.mainWindow = mainWindow;
|
||||
@@ -95,7 +94,6 @@ 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);
|
||||
@@ -106,16 +104,19 @@ 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));
|
||||
}
|
||||
|
||||
private void reloadUI() {
|
||||
TreePath selectionPath = tree.getSelectionPath();
|
||||
mainWindow.getSettings().saveWindowPos(this);
|
||||
int[] selection = tree.getSelectionRows();
|
||||
getContentPane().removeAll();
|
||||
initUI();
|
||||
pack();
|
||||
mainWindow.getSettings().loadWindowPos(this);
|
||||
tree.setSelectionPath(selectionPath);
|
||||
// wait for other events to process
|
||||
UiUtils.uiRun(() -> {
|
||||
tree.setSelectionRows(selection);
|
||||
SwingUtilities.updateComponentTreeUI(this);
|
||||
});
|
||||
}
|
||||
|
||||
private void initUI() {
|
||||
@@ -130,7 +131,7 @@ public class JadxSettingsWindow extends JDialog {
|
||||
groups.add(new ShortcutsSettingsGroup(this, settings));
|
||||
groups.add(makeSearchResGroup());
|
||||
groups.add(makeProjectGroup());
|
||||
groups.add(new PluginsSettings(mainWindow, settings).build());
|
||||
groups.add(new PluginSettings(mainWindow, settings).build());
|
||||
groups.add(makeOtherGroup());
|
||||
|
||||
tree = new SettingsTree();
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package jadx.gui.settings.ui.plugins;
|
||||
|
||||
import jadx.plugins.tools.data.JadxPluginMetadata;
|
||||
|
||||
public class AvailablePluginNode extends BasePluginListNode {
|
||||
|
||||
private final JadxPluginMetadata metadata;
|
||||
|
||||
public AvailablePluginNode(JadxPluginMetadata metadata) {
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return metadata.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasDetails() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPluginId() {
|
||||
return metadata.getPluginId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return metadata.getDescription();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHomepage() {
|
||||
return metadata.getHomepage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLocationId() {
|
||||
return metadata.getLocationId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginAction getAction() {
|
||||
return PluginAction.INSTALL;
|
||||
}
|
||||
}
|
||||
@@ -2,19 +2,33 @@ package jadx.gui.settings.ui.plugins;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.api.plugins.JadxPluginInfo;
|
||||
|
||||
abstract class BasePluginListNode {
|
||||
|
||||
public @Nullable String getTitle() {
|
||||
public abstract String getTitle();
|
||||
|
||||
public abstract boolean hasDetails();
|
||||
|
||||
public String getPluginId() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public JadxPluginInfo getPluginInfo() {
|
||||
public String getDescription() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getHomepage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public @Nullable String getLocationId() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public @Nullable String getVersion() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public PluginAction getAction() {
|
||||
return PluginAction.NONE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,29 +22,25 @@ 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 final PluginSettings pluginsSettings;
|
||||
private JTextField locationFld;
|
||||
|
||||
public InstallPluginDialog(MainWindow mainWindow) {
|
||||
public InstallPluginDialog(MainWindow mainWindow, PluginSettings pluginsSettings) {
|
||||
super(mainWindow, NLS.str("preferences.plugins.install"));
|
||||
this.mainWindow = mainWindow;
|
||||
this.pluginsSettings = pluginsSettings;
|
||||
init();
|
||||
}
|
||||
|
||||
@@ -121,20 +117,7 @@ public class InstallPluginDialog extends JDialog {
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
pluginsSettings.install(locationFld.getText());
|
||||
dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
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;
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
@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 PluginAction getAction() {
|
||||
if (metadata != null) {
|
||||
return PluginAction.UNINSTALL;
|
||||
}
|
||||
return PluginAction.NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getVersion() {
|
||||
if (metadata != null) {
|
||||
return metadata.getVersion();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return plugin.getPluginInfo().getName();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package jadx.gui.settings.ui.plugins;
|
||||
|
||||
public enum PluginAction {
|
||||
NONE,
|
||||
INSTALL,
|
||||
UNINSTALL
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
+32
-13
@@ -17,7 +17,9 @@ import javax.swing.JTextField;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.plugins.events.types.ReloadSettingsWindow;
|
||||
import ch.qos.logback.classic.Level;
|
||||
|
||||
import jadx.api.plugins.events.types.ReloadProject;
|
||||
import jadx.api.plugins.gui.ISettingsGroup;
|
||||
import jadx.api.plugins.gui.JadxGuiContext;
|
||||
import jadx.api.plugins.options.JadxPluginOptions;
|
||||
@@ -26,33 +28,34 @@ 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.logs.LogOptions;
|
||||
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.UiUtils;
|
||||
import jadx.gui.utils.plugins.CollectPlugins;
|
||||
import jadx.gui.utils.ui.DocumentUpdateListener;
|
||||
import jadx.plugins.tools.JadxPluginsTools;
|
||||
import jadx.plugins.tools.data.JadxPluginMetadata;
|
||||
import jadx.plugins.tools.data.JadxPluginUpdate;
|
||||
|
||||
public class PluginsSettings {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(PluginsSettings.class);
|
||||
public class PluginSettings {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(PluginSettings.class);
|
||||
|
||||
private final MainWindow mainWindow;
|
||||
private final JadxSettings settings;
|
||||
|
||||
public PluginsSettings(MainWindow mainWindow, JadxSettings settings) {
|
||||
public PluginSettings(MainWindow mainWindow, JadxSettings settings) {
|
||||
this.mainWindow = mainWindow;
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
public ISettingsGroup build() {
|
||||
List<PluginContext> list = new CollectPlugins(mainWindow).build();
|
||||
ISettingsGroup pluginsGroup = new PluginsSettingsGroup(this, list);
|
||||
for (PluginContext context : list) {
|
||||
List<PluginContext> installedPlugins = new CollectPlugins(mainWindow).build();
|
||||
ISettingsGroup pluginsGroup = new PluginSettingsGroup(this, mainWindow, installedPlugins);
|
||||
for (PluginContext context : installedPlugins) {
|
||||
ISettingsGroup pluginGroup = addPluginGroup(context);
|
||||
if (pluginGroup != null) {
|
||||
pluginsGroup.getSubGroups().add(pluginGroup);
|
||||
@@ -62,7 +65,25 @@ public class PluginsSettings {
|
||||
}
|
||||
|
||||
public void addPlugin() {
|
||||
new InstallPluginDialog(mainWindow).setVisible(true);
|
||||
new InstallPluginDialog(mainWindow, this).setVisible(true);
|
||||
}
|
||||
|
||||
private void requestReload() {
|
||||
mainWindow.events().send(ReloadProject.INSTANCE);
|
||||
}
|
||||
|
||||
public void install(String locationId) {
|
||||
mainWindow.getBackgroundExecutor().execute(NLS.str("preferences.plugins.task.installing"),
|
||||
() -> {
|
||||
try {
|
||||
JadxPluginMetadata metadata = JadxPluginsTools.getInstance().install(locationId);
|
||||
LOG.info("Plugin installed: {}", metadata);
|
||||
requestReload();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Install failed", e);
|
||||
mainWindow.showLogViewer(LogOptions.forLevel(Level.ERROR));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void uninstall(String pluginId) {
|
||||
@@ -70,8 +91,7 @@ public class PluginsSettings {
|
||||
boolean success = JadxPluginsTools.getInstance().uninstall(pluginId);
|
||||
if (success) {
|
||||
LOG.info("Uninstall complete");
|
||||
mainWindow.events().send(ReloadSettingsWindow.INSTANCE);
|
||||
UiUtils.uiRun(mainWindow::reopen);
|
||||
requestReload();
|
||||
} else {
|
||||
LOG.warn("Uninstall failed");
|
||||
}
|
||||
@@ -83,8 +103,7 @@ public class PluginsSettings {
|
||||
List<JadxPluginUpdate> updates = JadxPluginsTools.getInstance().updateAll();
|
||||
if (!updates.isEmpty()) {
|
||||
LOG.info("Updates: {}\n ", Utils.listToString(updates, "\n "));
|
||||
mainWindow.events().send(ReloadSettingsWindow.INSTANCE);
|
||||
UiUtils.uiRun(mainWindow::reopen);
|
||||
requestReload();
|
||||
} else {
|
||||
LOG.info("No updates found");
|
||||
}
|
||||
+92
-37
@@ -9,6 +9,8 @@ import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.Box;
|
||||
@@ -26,27 +28,36 @@ import javax.swing.ListCellRenderer;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.SwingConstants;
|
||||
|
||||
import jadx.api.plugins.JadxPluginInfo;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.plugins.gui.ISettingsGroup;
|
||||
import jadx.core.plugins.PluginContext;
|
||||
import jadx.core.utils.StringUtils;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.plugins.tools.JadxPluginsList;
|
||||
import jadx.plugins.tools.JadxPluginsTools;
|
||||
import jadx.plugins.tools.data.JadxPluginMetadata;
|
||||
|
||||
class PluginsSettingsGroup implements ISettingsGroup {
|
||||
private final PluginsSettings pluginsSettings;
|
||||
class PluginSettingsGroup implements ISettingsGroup {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(PluginSettingsGroup.class);
|
||||
|
||||
private final PluginSettings pluginsSettings;
|
||||
private final MainWindow mainWindow;
|
||||
private final String title;
|
||||
private final List<ISettingsGroup> subGroups = new ArrayList<>();
|
||||
private final List<PluginContext> pluginsList;
|
||||
private final List<PluginContext> installedPlugins;
|
||||
|
||||
private PluginListNode selectedPlugin;
|
||||
private JPanel detailsPanel;
|
||||
|
||||
public PluginsSettingsGroup(PluginsSettings pluginsSettings, List<PluginContext> pluginsList) {
|
||||
this.pluginsSettings = pluginsSettings;
|
||||
public PluginSettingsGroup(PluginSettings pluginSettings, MainWindow mainWindow, List<PluginContext> installedPlugins) {
|
||||
this.pluginsSettings = pluginSettings;
|
||||
this.mainWindow = mainWindow;
|
||||
this.title = NLS.str("preferences.plugins");
|
||||
this.pluginsList = pluginsList;
|
||||
this.installedPlugins = installedPlugins;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -83,25 +94,27 @@ class PluginsSettingsGroup implements ISettingsGroup {
|
||||
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())));
|
||||
for (PluginContext plugin : installedPlugins) {
|
||||
nodes.add(new InstalledPluginNode(plugin, installedMap.get(plugin.getPluginId())));
|
||||
}
|
||||
nodes.sort(Comparator.comparing(n -> n.getPluginInfo().getName()));
|
||||
nodes.sort(Comparator.comparing(BasePluginListNode::getTitle));
|
||||
|
||||
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"));
|
||||
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()));
|
||||
JList<BasePluginListNode> pluginList = new JList<>(listModel);
|
||||
pluginList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
pluginList.setCellRenderer(new PluginsListCellRenderer());
|
||||
pluginList.addListSelectionListener(ev -> onSelection(pluginList.getSelectedValue()));
|
||||
|
||||
JScrollPane scrollPane = new JScrollPane(pluginsList);
|
||||
loadAvailablePlugins(listModel, installedPlugins);
|
||||
|
||||
JScrollPane scrollPane = new JScrollPane(pluginList);
|
||||
scrollPane.setMinimumSize(new Dimension(80, 120));
|
||||
|
||||
detailsPanel = new JPanel(new BorderLayout(5, 5));
|
||||
detailsPanel.setBorder(BorderFactory.createCompoundBorder(
|
||||
@@ -113,7 +126,6 @@ class PluginsSettingsGroup implements ISettingsGroup {
|
||||
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));
|
||||
@@ -123,22 +135,45 @@ class PluginsSettingsGroup implements ISettingsGroup {
|
||||
return mainPanel;
|
||||
}
|
||||
|
||||
private void loadAvailablePlugins(DefaultListModel<BasePluginListNode> listModel, List<PluginContext> installedPlugins) {
|
||||
List<AvailablePluginNode> list = new ArrayList<>();
|
||||
mainWindow.getBackgroundExecutor().execute(
|
||||
NLS.str("preferences.plugins.task.downloading_list"),
|
||||
() -> {
|
||||
List<JadxPluginMetadata> availablePlugins;
|
||||
try {
|
||||
availablePlugins = JadxPluginsList.getInstance().fetch();
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Failed to load available plugins list", e);
|
||||
return;
|
||||
}
|
||||
Set<String> installed = installedPlugins.stream().map(PluginContext::getPluginId).collect(Collectors.toSet());
|
||||
for (JadxPluginMetadata availablePlugin : availablePlugins) {
|
||||
if (!installed.contains(availablePlugin.getPluginId())) {
|
||||
list.add(new AvailablePluginNode(availablePlugin));
|
||||
}
|
||||
}
|
||||
},
|
||||
status -> listModel.addAll(list));
|
||||
}
|
||||
|
||||
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());
|
||||
if (node.hasDetails()) {
|
||||
JLabel nameLbl = new JLabel(node.getTitle());
|
||||
Font baseFont = nameLbl.getFont();
|
||||
nameLbl.setFont(baseFont.deriveFont(Font.BOLD, baseFont.getSize2D() + 2));
|
||||
|
||||
String desc;
|
||||
String homepage = node.getHomepage();
|
||||
if (StringUtils.notBlank(homepage)) {
|
||||
desc = node.getDescription() + "\n\nHomepage: " + homepage;
|
||||
} else {
|
||||
desc = node.getDescription();
|
||||
}
|
||||
|
||||
JTextPane descArea = new JTextPane();
|
||||
descArea.setText(pluginInfo.getDescription());
|
||||
descArea.setText(desc);
|
||||
descArea.setFont(baseFont.deriveFont(baseFont.getSize2D() + 1));
|
||||
descArea.setEditable(false);
|
||||
descArea.setBorder(BorderFactory.createEmptyBorder());
|
||||
@@ -149,21 +184,41 @@ class PluginsSettingsGroup implements ISettingsGroup {
|
||||
top.setBorder(BorderFactory.createEmptyBorder(10, 2, 10, 2));
|
||||
top.add(nameLbl);
|
||||
top.add(Box.createHorizontalGlue());
|
||||
top.add(uninstallBtn);
|
||||
|
||||
JButton actionBtn = makeActionButton(node);
|
||||
if (actionBtn != null) {
|
||||
top.add(actionBtn);
|
||||
}
|
||||
detailsPanel.add(top, BorderLayout.PAGE_START);
|
||||
detailsPanel.add(descArea, BorderLayout.CENTER);
|
||||
}
|
||||
detailsPanel.updateUI();
|
||||
}
|
||||
|
||||
private @Nullable JButton makeActionButton(BasePluginListNode node) {
|
||||
switch (node.getAction()) {
|
||||
case NONE:
|
||||
return null;
|
||||
case INSTALL: {
|
||||
JButton installBtn = new JButton(NLS.str("preferences.plugins.install_btn"));
|
||||
installBtn.addActionListener(ev -> pluginsSettings.install(node.getLocationId()));
|
||||
return installBtn;
|
||||
}
|
||||
case UNINSTALL: {
|
||||
JButton uninstallBtn = new JButton(NLS.str("preferences.plugins.uninstall_btn"));
|
||||
uninstallBtn.addActionListener(ev -> pluginsSettings.uninstall(node.getPluginId()));
|
||||
return uninstallBtn;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
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) {
|
||||
public PluginsListCellRenderer() {
|
||||
panel = new JPanel();
|
||||
panel.setOpaque(true);
|
||||
panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS));
|
||||
@@ -176,6 +231,7 @@ class PluginsSettingsGroup implements ISettingsGroup {
|
||||
versionLbl.setOpaque(true);
|
||||
|
||||
panel.add(nameLbl);
|
||||
panel.add(Box.createHorizontalStrut(20));
|
||||
panel.add(Box.createHorizontalGlue());
|
||||
panel.add(versionLbl);
|
||||
|
||||
@@ -187,13 +243,12 @@ class PluginsSettingsGroup implements ISettingsGroup {
|
||||
@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);
|
||||
if (!value.hasDetails()) {
|
||||
titleLbl.setText(value.getTitle());
|
||||
return titleLbl;
|
||||
}
|
||||
nameLbl.setText(value.getPluginInfo().getName());
|
||||
nameLbl.setToolTipText(value.getPluginInfo().getDescription());
|
||||
nameLbl.setText(value.getTitle());
|
||||
nameLbl.setToolTipText(value.getLocationId());
|
||||
versionLbl.setText(Utils.getOrElse(value.getVersion(), ""));
|
||||
if (isSelected) {
|
||||
panel.setBackground(list.getSelectionBackground());
|
||||
@@ -11,4 +11,9 @@ public class TitleNode extends BasePluginListNode {
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasDetails() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +81,8 @@ 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.utils.CommonFileUtils;
|
||||
import jadx.core.Jadx;
|
||||
import jadx.core.export.TemplateFile;
|
||||
@@ -107,7 +109,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.settings.ui.plugins.PluginSettings;
|
||||
import jadx.gui.treemodel.ApkSignature;
|
||||
import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JLoadableNode;
|
||||
@@ -472,12 +474,14 @@ public class MainWindow extends JFrame {
|
||||
return loadedFile.resolveSibling(fileName);
|
||||
}
|
||||
|
||||
public synchronized void reopen() {
|
||||
saveAll();
|
||||
closeAll();
|
||||
loadFiles(EMPTY_RUNNABLE);
|
||||
public void reopen() {
|
||||
synchronized (ReloadProject.INSTANCE) {
|
||||
saveAll();
|
||||
closeAll();
|
||||
loadFiles(EMPTY_RUNNABLE);
|
||||
|
||||
menuBar.reloadShortcuts();
|
||||
menuBar.reloadShortcuts();
|
||||
}
|
||||
}
|
||||
|
||||
private void openProject(Path path, Runnable onFinish) {
|
||||
@@ -576,6 +580,7 @@ public class MainWindow extends JFrame {
|
||||
initTree();
|
||||
updateLiveReload(project.isEnableLiveReload());
|
||||
BreakpointManager.init(project.getFilePaths().get(0).toAbsolutePath().getParent());
|
||||
events().addListener(JadxEvents.RELOAD_PROJECT, ev -> UiUtils.uiRun(this::reopen));
|
||||
|
||||
List<EditorViewState> openTabs = project.getOpenTabs(this);
|
||||
backgroundExecutor.execute(NLS.str("progress.load"),
|
||||
@@ -1519,11 +1524,13 @@ public class MainWindow extends JFrame {
|
||||
}
|
||||
|
||||
public void showLogViewer(LogOptions logOptions) {
|
||||
if (settings.isDockLogViewer()) {
|
||||
showDockedLog(logOptions);
|
||||
} else {
|
||||
LogViewerDialog.open(this, logOptions);
|
||||
}
|
||||
UiUtils.uiRun(() -> {
|
||||
if (settings.isDockLogViewer()) {
|
||||
showDockedLog(logOptions);
|
||||
} else {
|
||||
LogViewerDialog.open(this, logOptions);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showDockedLog(LogOptions logOptions) {
|
||||
@@ -1555,7 +1562,7 @@ public class MainWindow extends JFrame {
|
||||
|
||||
public void resetPluginsMenu() {
|
||||
pluginsMenu.removeAll();
|
||||
pluginsMenu.add(new ActionHandler(() -> new InstallPluginDialog(this).setVisible(true))
|
||||
pluginsMenu.add(new ActionHandler(() -> new PluginSettings(this, settings).addPlugin())
|
||||
.withNameAndDesc(NLS.str("preferences.plugins.install")));
|
||||
}
|
||||
|
||||
|
||||
@@ -232,6 +232,7 @@ preferences.res_skip_file=Dateien überspringen (MB)
|
||||
|
||||
#preferences.plugins.install=Install plugin
|
||||
#preferences.plugins.install_btn=Install
|
||||
#preferences.plugins.uninstall_btn=Uninstall
|
||||
#preferences.plugins.location_id_label=Location id:
|
||||
#preferences.plugins.plugin_jar=Select Plugin jar
|
||||
#preferences.plugins.plugin_jar_label=or
|
||||
@@ -240,6 +241,7 @@ preferences.res_skip_file=Dateien überspringen (MB)
|
||||
#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.cache=Cache
|
||||
#preferences.cache.location=Cache location
|
||||
|
||||
@@ -232,6 +232,7 @@ preferences.res_skip_file=Skip resources files if larger (MB) (0 - disable)
|
||||
|
||||
preferences.plugins.install=Install plugin
|
||||
preferences.plugins.install_btn=Install
|
||||
preferences.plugins.uninstall_btn=Uninstall
|
||||
preferences.plugins.location_id_label=Location id:
|
||||
preferences.plugins.plugin_jar=Select Plugin jar
|
||||
preferences.plugins.plugin_jar_label=or
|
||||
@@ -240,6 +241,7 @@ preferences.plugins.details=Plugin details
|
||||
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.cache=Cache
|
||||
preferences.cache.location=Cache location
|
||||
|
||||
@@ -232,6 +232,7 @@ preferences.reset_title=Reestablecer preferencias
|
||||
|
||||
#preferences.plugins.install=Install plugin
|
||||
#preferences.plugins.install_btn=Install
|
||||
#preferences.plugins.uninstall_btn=Uninstall
|
||||
#preferences.plugins.location_id_label=Location id:
|
||||
#preferences.plugins.plugin_jar=Select Plugin jar
|
||||
#preferences.plugins.plugin_jar_label=or
|
||||
@@ -240,6 +241,7 @@ preferences.reset_title=Reestablecer preferencias
|
||||
#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.cache=Cache
|
||||
#preferences.cache.location=Cache location
|
||||
|
||||
@@ -232,6 +232,7 @@ preferences.res_skip_file=이 옵션보다 큰 파일 건너 뛰기 (MB)
|
||||
|
||||
#preferences.plugins.install=Install plugin
|
||||
#preferences.plugins.install_btn=Install
|
||||
#preferences.plugins.uninstall_btn=Uninstall
|
||||
#preferences.plugins.location_id_label=Location id:
|
||||
#preferences.plugins.plugin_jar=Select Plugin jar
|
||||
#preferences.plugins.plugin_jar_label=or
|
||||
@@ -240,6 +241,7 @@ preferences.res_skip_file=이 옵션보다 큰 파일 건너 뛰기 (MB)
|
||||
#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.cache=Cache
|
||||
#preferences.cache.location=Cache location
|
||||
|
||||
@@ -232,6 +232,7 @@ preferences.res_skip_file=Pular arquivos excedidos
|
||||
|
||||
#preferences.plugins.install=Install plugin
|
||||
#preferences.plugins.install_btn=Install
|
||||
#preferences.plugins.uninstall_btn=Uninstall
|
||||
#preferences.plugins.location_id_label=Location id:
|
||||
#preferences.plugins.plugin_jar=Select Plugin jar
|
||||
#preferences.plugins.plugin_jar_label=or
|
||||
@@ -240,6 +241,7 @@ preferences.res_skip_file=Pular arquivos excedidos
|
||||
#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.cache=Cache
|
||||
#preferences.cache.location=Cache location
|
||||
|
||||
@@ -232,6 +232,7 @@ preferences.res_skip_file=Пропускать ресурсы больше че
|
||||
|
||||
#preferences.plugins.install=Install plugin
|
||||
#preferences.plugins.install_btn=Install
|
||||
#preferences.plugins.uninstall_btn=Uninstall
|
||||
#preferences.plugins.location_id_label=Location id:
|
||||
#preferences.plugins.plugin_jar=Select Plugin jar
|
||||
#preferences.plugins.plugin_jar_label=or
|
||||
@@ -240,6 +241,7 @@ preferences.res_skip_file=Пропускать ресурсы больше че
|
||||
#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.cache=Cache
|
||||
#preferences.cache.location=Cache location
|
||||
|
||||
@@ -232,6 +232,7 @@ preferences.res_skip_file=跳过文件大小(MB)
|
||||
|
||||
preferences.plugins.install=安装插件
|
||||
preferences.plugins.install_btn=安装
|
||||
#preferences.plugins.uninstall_btn=Uninstall
|
||||
preferences.plugins.location_id_label=位置ID:
|
||||
preferences.plugins.plugin_jar=选择插件 jar
|
||||
preferences.plugins.plugin_jar_label=或
|
||||
@@ -240,6 +241,7 @@ preferences.plugins.details=插件详情
|
||||
preferences.plugins.task.installing=安装插件中
|
||||
preferences.plugins.task.uninstalling=卸载插件中
|
||||
preferences.plugins.task.updating=更新插件中
|
||||
#preferences.plugins.task.downloading_list=Downloading plugins list
|
||||
|
||||
preferences.cache=缓存
|
||||
preferences.cache.location=缓存位置
|
||||
|
||||
@@ -232,6 +232,7 @@ preferences.res_skip_file=略過大於此值的檔案 (MB)
|
||||
|
||||
preferences.plugins.install=安裝外掛程式
|
||||
preferences.plugins.install_btn=安裝
|
||||
#preferences.plugins.uninstall_btn=Uninstall
|
||||
preferences.plugins.location_id_label=位置 id:
|
||||
preferences.plugins.plugin_jar=選擇外掛程式 jar
|
||||
preferences.plugins.plugin_jar_label=或
|
||||
@@ -240,6 +241,7 @@ preferences.plugins.update_all=全部更新
|
||||
preferences.plugins.task.installing=正在安裝外掛程式
|
||||
preferences.plugins.task.uninstalling=正在解除安裝外掛程式
|
||||
preferences.plugins.task.updating=正在更新外掛程式
|
||||
#preferences.plugins.task.downloading_list=Downloading plugins list
|
||||
|
||||
#preferences.cache=Cache
|
||||
#preferences.cache.location=Cache location
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package jadx.plugins.tools;
|
||||
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import jadx.api.plugins.utils.ZipSecurity;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
import jadx.plugins.tools.data.JadxPluginMetadata;
|
||||
import jadx.plugins.tools.resolvers.github.GithubTools;
|
||||
import jadx.plugins.tools.resolvers.github.LocationInfo;
|
||||
import jadx.plugins.tools.resolvers.github.data.Asset;
|
||||
import jadx.plugins.tools.resolvers.github.data.Release;
|
||||
import jadx.plugins.tools.utils.PluginUtils;
|
||||
|
||||
/**
|
||||
* TODO: implement list caching (on disk) with check for new release
|
||||
*/
|
||||
public class JadxPluginsList {
|
||||
private static final JadxPluginsList INSTANCE = new JadxPluginsList();
|
||||
|
||||
private static final Type LIST_TYPE = new TypeToken<List<JadxPluginMetadata>>() {
|
||||
}.getType();
|
||||
|
||||
public static JadxPluginsList getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private @Nullable List<JadxPluginMetadata> cache;
|
||||
|
||||
private JadxPluginsList() {
|
||||
}
|
||||
|
||||
public synchronized List<JadxPluginMetadata> fetch() {
|
||||
if (cache != null) {
|
||||
return cache;
|
||||
}
|
||||
LocationInfo latest = new LocationInfo("jadx-decompiler", "jadx-plugins-list", "list", null);
|
||||
Release release = GithubTools.fetchRelease(latest);
|
||||
List<Asset> assets = release.getAssets();
|
||||
if (assets.isEmpty()) {
|
||||
throw new RuntimeException("Release don't have assets");
|
||||
}
|
||||
Asset listAsset = assets.get(0);
|
||||
Path tmpListFile = FileUtils.createTempFile("list.zip");
|
||||
PluginUtils.downloadFile(listAsset.getDownloadUrl(), tmpListFile);
|
||||
|
||||
List<JadxPluginMetadata> entries = loadListBundle(tmpListFile);
|
||||
cache = entries;
|
||||
return entries;
|
||||
}
|
||||
|
||||
private static List<JadxPluginMetadata> loadListBundle(Path tmpListFile) {
|
||||
Gson gson = new Gson();
|
||||
List<JadxPluginMetadata> entries = new ArrayList<>();
|
||||
ZipSecurity.readZipEntries(tmpListFile.toFile(), (entry, in) -> {
|
||||
if (entry.getName().endsWith(".json")) {
|
||||
try (Reader reader = new InputStreamReader(in)) {
|
||||
entries.addAll(gson.fromJson(reader, LIST_TYPE));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to read plugins list entry: " + entry.getName());
|
||||
}
|
||||
}
|
||||
});
|
||||
return entries;
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public synchronized List<JadxPluginMetadata> fetchFromLocalBundle(Path bundleFile) {
|
||||
if (cache != null) {
|
||||
return cache;
|
||||
}
|
||||
List<JadxPluginMetadata> entries = loadListBundle(bundleFile);
|
||||
cache = entries;
|
||||
return entries;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,8 @@
|
||||
package jadx.plugins.tools;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@@ -12,6 +10,7 @@ import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -20,8 +19,6 @@ import org.jetbrains.annotations.Nullable;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import dev.dirs.ProjectDirectories;
|
||||
|
||||
import jadx.api.plugins.JadxPlugin;
|
||||
import jadx.api.plugins.JadxPluginInfo;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
@@ -30,8 +27,11 @@ import jadx.plugins.tools.data.JadxPluginMetadata;
|
||||
import jadx.plugins.tools.data.JadxPluginUpdate;
|
||||
import jadx.plugins.tools.resolvers.IJadxPluginResolver;
|
||||
import jadx.plugins.tools.resolvers.ResolversRegistry;
|
||||
import jadx.plugins.tools.utils.PluginUtils;
|
||||
|
||||
import static jadx.core.utils.files.FileUtils.makeDirs;
|
||||
import static jadx.plugins.tools.utils.PluginFiles.DROPINS_DIR;
|
||||
import static jadx.plugins.tools.utils.PluginFiles.INSTALLED_DIR;
|
||||
import static jadx.plugins.tools.utils.PluginFiles.PLUGINS_JSON;
|
||||
|
||||
public class JadxPluginsTools {
|
||||
private static final JadxPluginsTools INSTANCE = new JadxPluginsTools();
|
||||
@@ -40,25 +40,19 @@ public class JadxPluginsTools {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private final Path pluginsJson;
|
||||
private final Path dropins;
|
||||
private final Path installed;
|
||||
|
||||
private JadxPluginsTools() {
|
||||
ProjectDirectories jadxDirs = ProjectDirectories.from("io.github", "skylot", "jadx");
|
||||
Path plugins = Paths.get(jadxDirs.configDir, "plugins");
|
||||
makeDirs(plugins);
|
||||
pluginsJson = plugins.resolve("plugins.json");
|
||||
dropins = plugins.resolve("dropins");
|
||||
makeDirs(dropins);
|
||||
installed = plugins.resolve("installed");
|
||||
makeDirs(installed);
|
||||
}
|
||||
|
||||
public JadxPluginMetadata install(String locationId) {
|
||||
JadxPluginMetadata pluginMetadata = resolveMetadata(locationId);
|
||||
install(pluginMetadata);
|
||||
return pluginMetadata;
|
||||
}
|
||||
|
||||
public JadxPluginMetadata resolveMetadata(String locationId) {
|
||||
JadxPluginMetadata pluginMetadata = ResolversRegistry.resolve(locationId)
|
||||
.orElseThrow(() -> new RuntimeException("Failed to resolve locationId: " + locationId));
|
||||
install(pluginMetadata);
|
||||
fillMetadata(pluginMetadata);
|
||||
return pluginMetadata;
|
||||
}
|
||||
|
||||
@@ -117,14 +111,6 @@ public class JadxPluginsTools {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void deletePluginJar(JadxPluginMetadata plugin) {
|
||||
try {
|
||||
Files.deleteIfExists(installed.resolve(plugin.getJar()));
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
public List<JadxPluginMetadata> getInstalled() {
|
||||
return loadPluginsJson().getInstalled();
|
||||
}
|
||||
@@ -132,9 +118,9 @@ public class JadxPluginsTools {
|
||||
public List<Path> getAllPluginJars() {
|
||||
List<Path> list = new ArrayList<>();
|
||||
for (JadxPluginMetadata pluginMetadata : loadPluginsJson().getInstalled()) {
|
||||
list.add(installed.resolve(pluginMetadata.getJar()));
|
||||
list.add(INSTALLED_DIR.resolve(pluginMetadata.getJar()));
|
||||
}
|
||||
collectFromDir(list, dropins);
|
||||
collectFromDir(list, DROPINS_DIR);
|
||||
return list;
|
||||
}
|
||||
|
||||
@@ -148,28 +134,20 @@ public class JadxPluginsTools {
|
||||
return null;
|
||||
}
|
||||
JadxPluginMetadata update = updateOpt.get();
|
||||
if (update.getVersion().equals(plugin.getVersion())) {
|
||||
if (Objects.equals(update.getVersion(), plugin.getVersion())) {
|
||||
return null;
|
||||
}
|
||||
fillMetadata(update);
|
||||
install(update);
|
||||
return update;
|
||||
}
|
||||
|
||||
private void install(JadxPluginMetadata metadata) {
|
||||
Path tmpJar;
|
||||
if (needDownload(metadata.getJar())) {
|
||||
tmpJar = FileUtils.createTempFile("plugin.jar");
|
||||
downloadJar(metadata.getJar(), tmpJar);
|
||||
} else {
|
||||
tmpJar = Paths.get(metadata.getJar());
|
||||
}
|
||||
fillPluginInfoFromJar(metadata, tmpJar);
|
||||
|
||||
public void install(JadxPluginMetadata metadata) {
|
||||
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());
|
||||
Path pluginJar = INSTALLED_DIR.resolve(fileName);
|
||||
copyJar(Paths.get(metadata.getJar()), pluginJar);
|
||||
metadata.setJar(INSTALLED_DIR.relativize(pluginJar).toString());
|
||||
|
||||
JadxInstalledPlugins plugins = loadPluginsJson();
|
||||
// remove previous version jar
|
||||
@@ -182,28 +160,33 @@ public class JadxPluginsTools {
|
||||
savePluginsJson(plugins);
|
||||
}
|
||||
|
||||
private void fillPluginInfoFromJar(JadxPluginMetadata metadata, Path jar) {
|
||||
private void fillMetadata(JadxPluginMetadata metadata) {
|
||||
Path tmpJar;
|
||||
if (needDownload(metadata.getJar())) {
|
||||
tmpJar = FileUtils.createTempFile("plugin.jar");
|
||||
PluginUtils.downloadFile(metadata.getJar(), tmpJar);
|
||||
metadata.setJar(tmpJar.toAbsolutePath().toString());
|
||||
} else {
|
||||
tmpJar = Paths.get(metadata.getJar());
|
||||
}
|
||||
fillMetadataFromJar(metadata, tmpJar);
|
||||
}
|
||||
|
||||
private void fillMetadataFromJar(JadxPluginMetadata metadata, Path jar) {
|
||||
try (JadxExternalPluginsLoader loader = new JadxExternalPluginsLoader()) {
|
||||
JadxPlugin jadxPlugin = loader.loadFromJar(jar);
|
||||
JadxPluginInfo pluginInfo = jadxPlugin.getPluginInfo();
|
||||
metadata.setPluginId(pluginInfo.getPluginId());
|
||||
metadata.setName(pluginInfo.getName());
|
||||
metadata.setDescription(pluginInfo.getDescription());
|
||||
metadata.setHomepage(pluginInfo.getHomepage());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean needDownload(String jar) {
|
||||
private static boolean needDownload(String jar) {
|
||||
return jar.startsWith("https://") || jar.startsWith("http://");
|
||||
}
|
||||
|
||||
private void downloadJar(String sourceJar, Path destPath) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private void copyJar(Path sourceJar, Path destJar) {
|
||||
try {
|
||||
Files.copy(sourceJar, destJar, StandardCopyOption.REPLACE_EXISTING);
|
||||
@@ -212,35 +195,45 @@ public class JadxPluginsTools {
|
||||
}
|
||||
}
|
||||
|
||||
private void deletePluginJar(JadxPluginMetadata plugin) {
|
||||
try {
|
||||
Files.deleteIfExists(INSTALLED_DIR.resolve(plugin.getJar()));
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
private static Gson buildGson() {
|
||||
return new GsonBuilder().setPrettyPrinting().create();
|
||||
return new GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.create();
|
||||
}
|
||||
|
||||
private JadxInstalledPlugins loadPluginsJson() {
|
||||
if (!Files.isRegularFile(pluginsJson)) {
|
||||
if (!Files.isRegularFile(PLUGINS_JSON)) {
|
||||
return new JadxInstalledPlugins();
|
||||
}
|
||||
try (Reader reader = Files.newBufferedReader(pluginsJson, StandardCharsets.UTF_8)) {
|
||||
try (Reader reader = Files.newBufferedReader(PLUGINS_JSON, StandardCharsets.UTF_8)) {
|
||||
return buildGson().fromJson(reader, JadxInstalledPlugins.class);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to read file: " + pluginsJson);
|
||||
throw new RuntimeException("Failed to read file: " + PLUGINS_JSON);
|
||||
}
|
||||
}
|
||||
|
||||
private void savePluginsJson(JadxInstalledPlugins data) {
|
||||
if (data.getInstalled().isEmpty()) {
|
||||
try {
|
||||
Files.deleteIfExists(pluginsJson);
|
||||
Files.deleteIfExists(PLUGINS_JSON);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to remove file: " + pluginsJson, e);
|
||||
throw new RuntimeException("Failed to remove file: " + PLUGINS_JSON, e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
data.getInstalled().sort(null);
|
||||
try (Writer writer = Files.newBufferedWriter(pluginsJson, StandardCharsets.UTF_8)) {
|
||||
try (Writer writer = Files.newBufferedWriter(PLUGINS_JSON, StandardCharsets.UTF_8)) {
|
||||
buildGson().toJson(data, writer);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Error saving file: " + pluginsJson, e);
|
||||
throw new RuntimeException("Error saving file: " + PLUGINS_JSON, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ public class JadxPluginMetadata implements Comparable<JadxPluginMetadata> {
|
||||
private String pluginId;
|
||||
private String name;
|
||||
private String description;
|
||||
private String homepage;
|
||||
private @Nullable String version;
|
||||
private String locationId;
|
||||
private String resolverId;
|
||||
@@ -44,6 +45,14 @@ public class JadxPluginMetadata implements Comparable<JadxPluginMetadata> {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getHomepage() {
|
||||
return homepage;
|
||||
}
|
||||
|
||||
public void setHomepage(String homepage) {
|
||||
this.homepage = homepage;
|
||||
}
|
||||
|
||||
public String getLocationId() {
|
||||
return locationId;
|
||||
}
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ import java.util.Optional;
|
||||
import jadx.plugins.tools.data.JadxPluginMetadata;
|
||||
import jadx.plugins.tools.resolvers.IJadxPluginResolver;
|
||||
|
||||
import static jadx.plugins.tools.utils.PluginsUtils.removePrefix;
|
||||
import static jadx.plugins.tools.utils.PluginUtils.removePrefix;
|
||||
|
||||
public class LocalFileResolver implements IJadxPluginResolver {
|
||||
@Override
|
||||
|
||||
+3
-56
@@ -1,19 +1,8 @@
|
||||
package jadx.plugins.tools.resolvers.github;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.lang.reflect.Type;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import jadx.core.utils.ListUtils;
|
||||
import jadx.plugins.tools.data.JadxPluginMetadata;
|
||||
@@ -21,24 +10,18 @@ import jadx.plugins.tools.resolvers.IJadxPluginResolver;
|
||||
import jadx.plugins.tools.resolvers.github.data.Asset;
|
||||
import jadx.plugins.tools.resolvers.github.data.Release;
|
||||
|
||||
import static jadx.plugins.tools.utils.PluginsUtils.removePrefix;
|
||||
import static jadx.plugins.tools.utils.PluginUtils.removePrefix;
|
||||
|
||||
public class GithubReleaseResolver implements IJadxPluginResolver {
|
||||
private static final String GITHUB_API_URL = "https://api.github.com/";
|
||||
private static final Pattern VERSION_PATTERN = Pattern.compile("v?\\d+\\.\\d+(\\.\\d+)?");
|
||||
|
||||
private static final Type RELEASE_TYPE = new TypeToken<Release>() {
|
||||
}.getType();
|
||||
private static final Type RELEASE_LIST_TYPE = new TypeToken<List<Release>>() {
|
||||
}.getType();
|
||||
|
||||
@Override
|
||||
public Optional<JadxPluginMetadata> resolve(String locationId) {
|
||||
LocationInfo info = parseLocation(locationId);
|
||||
if (info == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
Release release = fetchRelease(info);
|
||||
Release release = GithubTools.fetchRelease(info);
|
||||
List<Asset> assets = release.getAssets();
|
||||
String releaseVersion = removePrefix(release.getName(), "v");
|
||||
Asset asset = searchPluginAsset(assets, info.getArtifactPrefix(), releaseVersion);
|
||||
@@ -83,7 +66,7 @@ public class GithubReleaseResolver implements IJadxPluginResolver {
|
||||
if (exactAsset != null) {
|
||||
return exactAsset;
|
||||
}
|
||||
// search without version
|
||||
// search without version filter
|
||||
Asset foundAsset = ListUtils.filterOnlyOne(assets, a -> {
|
||||
String assetFileName = a.getName();
|
||||
return assetFileName.startsWith(artifactPrefix) && assetFileName.endsWith(".jar");
|
||||
@@ -102,42 +85,6 @@ public class GithubReleaseResolver implements IJadxPluginResolver {
|
||||
return baseLocation + ':' + info.getArtifactPrefix();
|
||||
}
|
||||
|
||||
private static Release fetchRelease(LocationInfo info) {
|
||||
String projectUrl = GITHUB_API_URL + "repos/" + info.getOwner() + "/" + info.getProject();
|
||||
String version = info.getVersion();
|
||||
if (version == null) {
|
||||
// get latest version
|
||||
return get(projectUrl + "/releases/latest", RELEASE_TYPE);
|
||||
}
|
||||
// search version among all releases (by name)
|
||||
List<Release> releases = get(projectUrl + "/releases", RELEASE_LIST_TYPE);
|
||||
return releases.stream()
|
||||
.filter(r -> r.getName().equals(version))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new RuntimeException("Release with version: " + version + " not found."
|
||||
+ " Available versions: " + releases.stream().map(Release::getName).collect(Collectors.joining(", "))));
|
||||
}
|
||||
|
||||
private static <T> T get(String url, Type type) {
|
||||
HttpURLConnection con;
|
||||
try {
|
||||
con = (HttpURLConnection) URI.create(url).toURL().openConnection();
|
||||
con.setRequestMethod("GET");
|
||||
int code = con.getResponseCode();
|
||||
if (code != 200) {
|
||||
// TODO: support redirects?
|
||||
throw new RuntimeException("Request failed, response: " + code + ", url: " + url);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Request failed, url: " + url, e);
|
||||
}
|
||||
try (Reader reader = new InputStreamReader(con.getInputStream(), StandardCharsets.UTF_8)) {
|
||||
return new Gson().fromJson(reader, type);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to parse response, url: " + url, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String id() {
|
||||
return "github-release";
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package jadx.plugins.tools.resolvers.github;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.lang.reflect.Type;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import jadx.plugins.tools.resolvers.github.data.Release;
|
||||
|
||||
public class GithubTools {
|
||||
private static final String GITHUB_API_URL = "https://api.github.com/";
|
||||
|
||||
private static final Type RELEASE_TYPE = new TypeToken<Release>() {
|
||||
}.getType();
|
||||
private static final Type RELEASE_LIST_TYPE = new TypeToken<List<Release>>() {
|
||||
}.getType();
|
||||
|
||||
public static Release fetchRelease(LocationInfo info) {
|
||||
String projectUrl = GITHUB_API_URL + "repos/" + info.getOwner() + "/" + info.getProject();
|
||||
String version = info.getVersion();
|
||||
if (version == null) {
|
||||
// get latest version
|
||||
return get(projectUrl + "/releases/latest", RELEASE_TYPE);
|
||||
}
|
||||
// search version among all releases (by name)
|
||||
List<Release> releases = get(projectUrl + "/releases", RELEASE_LIST_TYPE);
|
||||
return releases.stream()
|
||||
.filter(r -> r.getName().equals(version))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new RuntimeException("Release with version: " + version + " not found."
|
||||
+ " Available versions: " + releases.stream().map(Release::getName).collect(Collectors.joining(", "))));
|
||||
}
|
||||
|
||||
private static <T> T get(String url, Type type) {
|
||||
HttpURLConnection con;
|
||||
try {
|
||||
con = (HttpURLConnection) URI.create(url).toURL().openConnection();
|
||||
con.setRequestMethod("GET");
|
||||
int code = con.getResponseCode();
|
||||
if (code != 200) {
|
||||
// TODO: support redirects?
|
||||
throw new RuntimeException("Request failed, response: " + code + ", url: " + url);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Request failed, url: " + url, e);
|
||||
}
|
||||
try (Reader reader = new InputStreamReader(con.getInputStream(), StandardCharsets.UTF_8)) {
|
||||
return new Gson().fromJson(reader, type);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to parse response, url: " + url, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -2,7 +2,7 @@ package jadx.plugins.tools.resolvers.github;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
class LocationInfo {
|
||||
public class LocationInfo {
|
||||
private final String owner;
|
||||
private final String project;
|
||||
private final String artifactPrefix;
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package jadx.plugins.tools.utils;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import dev.dirs.ProjectDirectories;
|
||||
|
||||
import static jadx.core.utils.files.FileUtils.makeDirs;
|
||||
|
||||
public class PluginFiles {
|
||||
private static final ProjectDirectories DIRS = ProjectDirectories.from("io.github", "skylot", "jadx");
|
||||
|
||||
public static final Path PLUGINS_DIR = Paths.get(DIRS.configDir, "plugins");
|
||||
public static final Path PLUGINS_JSON = PLUGINS_DIR.resolve("plugins.json");
|
||||
public static final Path INSTALLED_DIR = PLUGINS_DIR.resolve("installed");
|
||||
public static final Path DROPINS_DIR = PLUGINS_DIR.resolve("dropins");
|
||||
|
||||
public static final Path CACHE_DIR = Paths.get(DIRS.cacheDir);
|
||||
|
||||
static {
|
||||
makeDirs(INSTALLED_DIR);
|
||||
makeDirs(DROPINS_DIR);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package jadx.plugins.tools.utils;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
|
||||
public class PluginUtils {
|
||||
|
||||
public static String removePrefix(String str, String prefix) {
|
||||
if (str.startsWith(prefix)) {
|
||||
return str.substring(prefix.length());
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
public static void downloadFile(String fileUrl, Path destPath) {
|
||||
try (InputStream in = URI.create(fileUrl).toURL().openStream()) {
|
||||
Files.copy(in, destPath, StandardCopyOption.REPLACE_EXISTING);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to download file: " + fileUrl, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package jadx.plugins.tools.utils;
|
||||
|
||||
public class PluginsUtils {
|
||||
|
||||
public static String removePrefix(String str, String prefix) {
|
||||
if (str.startsWith(prefix)) {
|
||||
return str.substring(prefix.length());
|
||||
}
|
||||
return str;
|
||||
}
|
||||
}
|
||||
+1
-6
@@ -18,14 +18,9 @@ import jadx.plugins.input.java.utils.JavaClassParseException;
|
||||
|
||||
public class JavaInputPlugin implements JadxPlugin {
|
||||
|
||||
public static final JadxPluginInfo PLUGIN_INFO = new JadxPluginInfo(
|
||||
"java-input",
|
||||
"JavaInput",
|
||||
"Load .class and .jar files");
|
||||
|
||||
@Override
|
||||
public JadxPluginInfo getPluginInfo() {
|
||||
return PLUGIN_INFO;
|
||||
return new JadxPluginInfo("java-input", "Java Input", "Load .class and .jar files");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-4
@@ -15,10 +15,7 @@ public class RaungInputPlugin implements JadxPlugin, JadxCodeInput {
|
||||
|
||||
@Override
|
||||
public JadxPluginInfo getPluginInfo() {
|
||||
return new JadxPluginInfo(
|
||||
"raung-input",
|
||||
"RaungInput",
|
||||
"Load .raung files");
|
||||
return new JadxPluginInfo("raung-input", "Raung Input", "Load .raung files");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ public class SmaliInputPlugin implements JadxPlugin, JadxCodeInput {
|
||||
|
||||
@Override
|
||||
public JadxPluginInfo getPluginInfo() {
|
||||
return new JadxPluginInfo("smali-input", "SmaliInput", "Load .smali files");
|
||||
return new JadxPluginInfo("smali-input", "Smali Input", "Load .smali files");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user