From 36da79feb81089b9fba43eec72eb73da99686a97 Mon Sep 17 00:00:00 2001 From: Skylot Date: Mon, 22 Jul 2013 22:43:00 +0400 Subject: [PATCH] gui: add icons for packages tree, add hierarchical mode --- .../src/main/java/jadx/gui/MainWindow.java | 141 ++++++++++++------ jadx-gui/src/main/java/jadx/gui/NLS.java | 21 +++ .../main/java/jadx/gui/treemodel/JClass.java | 36 ++++- .../main/java/jadx/gui/treemodel/JNode.java | 3 + .../java/jadx/gui/treemodel/JPackage.java | 56 ++++++- .../main/java/jadx/gui/treemodel/JRoot.java | 94 +++++++++++- .../resources/i18n/Messages_en_US.properties | 2 + .../main/resources/icons-16/abstract_co.png | Bin 0 -> 187 bytes .../main/resources/icons-16/constr_ovr.png | Bin 0 -> 180 bytes .../icons-16/empty_logical_package_obj.png | Bin 0 -> 352 bytes .../resources/icons-16/empty_pack_obj.png | Bin 0 -> 325 bytes .../resources/icons-16/field_private_obj.png | Bin 0 -> 177 bytes .../icons-16/field_protected_obj.png | Bin 0 -> 222 bytes .../resources/icons-16/field_public_obj.png | Bin 0 -> 221 bytes .../src/main/resources/icons-16/final_co.png | Bin 0 -> 159 bytes .../src/main/resources/icons-16/native_co.png | Bin 0 -> 177 bytes .../src/main/resources/icons-16/run_co.png | Bin 0 -> 180 bytes .../src/main/resources/icons-16/static_co.png | Bin 0 -> 174 bytes .../src/main/resources/icons-16/synch_co.png | Bin 0 -> 254 bytes .../main/resources/icons-16/transient_co.png | Bin 0 -> 178 bytes .../main/resources/icons-16/volatile_co.png | Bin 0 -> 178 bytes 21 files changed, 300 insertions(+), 53 deletions(-) create mode 100644 jadx-gui/src/main/java/jadx/gui/NLS.java create mode 100644 jadx-gui/src/main/resources/i18n/Messages_en_US.properties create mode 100644 jadx-gui/src/main/resources/icons-16/abstract_co.png create mode 100644 jadx-gui/src/main/resources/icons-16/constr_ovr.png create mode 100644 jadx-gui/src/main/resources/icons-16/empty_logical_package_obj.png create mode 100644 jadx-gui/src/main/resources/icons-16/empty_pack_obj.png create mode 100644 jadx-gui/src/main/resources/icons-16/field_private_obj.png create mode 100644 jadx-gui/src/main/resources/icons-16/field_protected_obj.png create mode 100644 jadx-gui/src/main/resources/icons-16/field_public_obj.png create mode 100644 jadx-gui/src/main/resources/icons-16/final_co.png create mode 100644 jadx-gui/src/main/resources/icons-16/native_co.png create mode 100644 jadx-gui/src/main/resources/icons-16/run_co.png create mode 100644 jadx-gui/src/main/resources/icons-16/static_co.png create mode 100644 jadx-gui/src/main/resources/icons-16/synch_co.png create mode 100644 jadx-gui/src/main/resources/icons-16/transient_co.png create mode 100644 jadx-gui/src/main/resources/icons-16/volatile_co.png diff --git a/jadx-gui/src/main/java/jadx/gui/MainWindow.java b/jadx-gui/src/main/java/jadx/gui/MainWindow.java index dd16bf19f..447bfdece 100644 --- a/jadx-gui/src/main/java/jadx/gui/MainWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/MainWindow.java @@ -6,6 +6,8 @@ import jadx.gui.treemodel.JClass; import jadx.gui.treemodel.JNode; import jadx.gui.treemodel.JRoot; +import javax.swing.ImageIcon; +import javax.swing.JButton; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JMenu; @@ -14,6 +16,8 @@ import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; +import javax.swing.JToggleButton; +import javax.swing.JToolBar; import javax.swing.JTree; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; @@ -22,6 +26,7 @@ import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; import java.awt.BorderLayout; import java.awt.Color; @@ -40,8 +45,12 @@ import org.slf4j.LoggerFactory; public class MainWindow extends JFrame { private static final Logger LOG = LoggerFactory.getLogger(MainWindow.class); - public static final String DEFAULT_TITLE = "jadx-gui"; - public static final Color BACKGROUND = new Color(0xf7f7f7); + private static final String DEFAULT_TITLE = "jadx-gui"; + private static final Color BACKGROUND = new Color(0xf7f7f7); + + private static final ImageIcon ICON_OPEN = Utils.openIcon("folder"); + private static final ImageIcon ICON_CLOSE = Utils.openIcon("cross"); + private static final ImageIcon ICON_FLAT_PKG = Utils.openIcon("empty_logical_package_obj"); private final JadxWrapper wrapper; private JPanel mainPanel; @@ -53,46 +62,7 @@ public class MainWindow extends JFrame { this.wrapper = new JadxWrapper(jadxArgs); initUI(); - initMenu(); - } - - private void initMenu() { - JMenuBar menuBar = new JMenuBar(); - - JMenu file = new JMenu("File"); - file.setMnemonic(KeyEvent.VK_F); - - JMenuItem exit = new JMenuItem("Exit", Utils.openIcon("cross")); - exit.setMnemonic(KeyEvent.VK_E); - exit.setToolTipText("Exit application"); - exit.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent event) { - System.exit(0); - } - }); - - JMenuItem open = new JMenuItem("Open", Utils.openIcon("folder")); - open.setMnemonic(KeyEvent.VK_E); - open.setToolTipText("Open file"); - open.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent event) { - JFileChooser fileChooser = new JFileChooser(); - FileFilter filter = new FileNameExtensionFilter("dex files", "dex", "apk", "jar"); - fileChooser.addChoosableFileFilter(filter); - int ret = fileChooser.showDialog(mainPanel, "Open file"); - if (ret == JFileChooser.APPROVE_OPTION) { - File file = fileChooser.getSelectedFile(); - openFile(file); - } - } - }); - - file.add(open); - file.addSeparator(); - file.add(exit); - - menuBar.add(file); - setJMenuBar(menuBar); + initMenuAndToolbar(); } public void openFile(File file) { @@ -106,6 +76,80 @@ public class MainWindow extends JFrame { treeModel.setRoot(treeRoot); treeModel.reload(); tree.expandRow(0); +// expandTree(); + } + + private void toggleFlattenPackage() { + Object root = treeModel.getRoot(); + if (root instanceof JRoot) { + JRoot treeRoot = (JRoot) root; + treeRoot.setFlatPackages(!treeRoot.isFlatPackages()); + treeModel.reload(); + tree.expandRow(0); + } + } + + private void expandTree() { + DefaultMutableTreeNode currentNode = ((DefaultMutableTreeNode) tree.getModel().getRoot()).getNextNode(); + do { + if (currentNode.getLevel() == 1) { + tree.expandPath(new TreePath(currentNode.getPath())); + } + currentNode = currentNode.getNextNode(); + } + while (currentNode != null); + } + + private void initMenuAndToolbar() { + JMenuBar menuBar = new JMenuBar(); + + JMenu file = new JMenu("File"); + file.setMnemonic(KeyEvent.VK_F); + + JMenuItem exit = new JMenuItem("Exit", ICON_CLOSE); + exit.setMnemonic(KeyEvent.VK_E); + exit.setToolTipText("Exit application"); + exit.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent event) { + System.exit(0); + } + }); + + JMenuItem open = new JMenuItem("Open", ICON_OPEN); + open.setMnemonic(KeyEvent.VK_E); + open.setToolTipText("Open file"); + open.addActionListener(new OpenListener()); + + file.add(open); + file.addSeparator(); + file.add(exit); + + menuBar.add(file); + setJMenuBar(menuBar); + + JToolBar toolbar = new JToolBar(); + toolbar.setFloatable(false); + + JButton openButton = new JButton(ICON_OPEN); + openButton.addActionListener(new OpenListener()); + openButton.setToolTipText(NLS.str("file.open")); + + toolbar.add(openButton); + toolbar.addSeparator(); + + JToggleButton flatPkgButton = new JToggleButton(ICON_FLAT_PKG); + flatPkgButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + toggleFlattenPackage(); + } + }); + flatPkgButton.setToolTipText(NLS.str("tree.flatten")); + toolbar.add(flatPkgButton); + + toolbar.addSeparator(); + + add(toolbar, BorderLayout.NORTH); } private void initUI() { @@ -166,4 +210,17 @@ public class MainWindow extends JFrame { pack(); setLocationRelativeTo(null); } + + private class OpenListener implements ActionListener { + public void actionPerformed(ActionEvent event) { + JFileChooser fileChooser = new JFileChooser(); + FileFilter filter = new FileNameExtensionFilter("dex files", "dex", "apk", "jar"); + fileChooser.addChoosableFileFilter(filter); + int ret = fileChooser.showDialog(mainPanel, "Open file"); + if (ret == JFileChooser.APPROVE_OPTION) { + File file = fileChooser.getSelectedFile(); + openFile(file); + } + } + } } diff --git a/jadx-gui/src/main/java/jadx/gui/NLS.java b/jadx-gui/src/main/java/jadx/gui/NLS.java new file mode 100644 index 000000000..c3bf3e2dc --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/NLS.java @@ -0,0 +1,21 @@ +package jadx.gui; + +import java.util.Locale; +import java.util.ResourceBundle; + +public class NLS { + + private static ResourceBundle messages; + + static { + load(new Locale("en", "US")); + } + + public static void load(Locale locale) { + messages = ResourceBundle.getBundle("i18n/Messages", locale); + } + + public static String str(String key) { + return messages.getString(key); + } +} diff --git a/jadx-gui/src/main/java/jadx/gui/treemodel/JClass.java b/jadx-gui/src/main/java/jadx/gui/treemodel/JClass.java index 252b15b40..fabf948e2 100644 --- a/jadx-gui/src/main/java/jadx/gui/treemodel/JClass.java +++ b/jadx-gui/src/main/java/jadx/gui/treemodel/JClass.java @@ -1,26 +1,60 @@ package jadx.gui.treemodel; import jadx.api.JavaClass; +import jadx.core.dex.info.AccessInfo; import jadx.gui.Utils; import javax.swing.Icon; +import javax.swing.ImageIcon; import javax.swing.tree.DefaultMutableTreeNode; public class JClass extends DefaultMutableTreeNode implements JNode { + private static final ImageIcon ICON_CLASS = Utils.openIcon("class_obj"); + private static final ImageIcon ICON_CLASS_DEFAULT = Utils.openIcon("class_default_obj"); + private static final ImageIcon ICON_CLASS_PRIVATE = Utils.openIcon("innerclass_private_obj"); + private static final ImageIcon ICON_CLASS_PROTECTED = Utils.openIcon("innerclass_protected_obj"); + private static final ImageIcon ICON_INTERFACE = Utils.openIcon("int_obj"); + private static final ImageIcon ICON_ENUM = Utils.openIcon("enum_obj"); + private static final ImageIcon ICON_ANNOTATION = Utils.openIcon("annotation_obj"); + private final JavaClass cls; public JClass(JavaClass cls) { this.cls = cls; + updateChilds(); } public JavaClass getCls() { return cls; } + @Override + public void updateChilds() { +// for (JavaClass javaClass : cls.getInnerClasses()) { +// add(new JClass(javaClass)); +// } + } + @Override public Icon getIcon() { - return Utils.openIcon("class_obj"); + AccessInfo accessInfo = cls.getAccessInfo(); + + if (accessInfo.isEnum()) { + return ICON_ENUM; + } else if (accessInfo.isAnnotation()) { + return ICON_ANNOTATION; + } else if (accessInfo.isInterface()) { + return ICON_INTERFACE; + } else if (accessInfo.isProtected()) { + return ICON_CLASS_PROTECTED; + } else if (accessInfo.isPrivate()) { + return ICON_CLASS_PRIVATE; + } else if (accessInfo.isPublic()) { + return ICON_CLASS; + } else { + return ICON_CLASS_DEFAULT; + } } @Override diff --git a/jadx-gui/src/main/java/jadx/gui/treemodel/JNode.java b/jadx-gui/src/main/java/jadx/gui/treemodel/JNode.java index b05409ed3..45a12aa87 100644 --- a/jadx-gui/src/main/java/jadx/gui/treemodel/JNode.java +++ b/jadx-gui/src/main/java/jadx/gui/treemodel/JNode.java @@ -3,5 +3,8 @@ package jadx.gui.treemodel; import javax.swing.Icon; public interface JNode { + + void updateChilds(); + Icon getIcon(); } diff --git a/jadx-gui/src/main/java/jadx/gui/treemodel/JPackage.java b/jadx-gui/src/main/java/jadx/gui/treemodel/JPackage.java index 029c947e2..158f82854 100644 --- a/jadx-gui/src/main/java/jadx/gui/treemodel/JPackage.java +++ b/jadx-gui/src/main/java/jadx/gui/treemodel/JPackage.java @@ -7,22 +7,57 @@ import jadx.gui.Utils; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.tree.DefaultMutableTreeNode; +import java.util.ArrayList; +import java.util.List; -public class JPackage extends DefaultMutableTreeNode implements JNode { +public class JPackage extends DefaultMutableTreeNode implements JNode, Comparable { private static final ImageIcon PACKAGE_ICON = Utils.openIcon("package_obj"); - private final JavaPackage pkg; + private String name; + private List classes; + private List innerPackages = new ArrayList(1); public JPackage(JavaPackage pkg) { - this.pkg = pkg; + this.name = pkg.getName(); + List javaClasses = pkg.getClasses(); + this.classes = new ArrayList(javaClasses.size()); + for (JavaClass javaClass : javaClasses) { + classes.add(new JClass(javaClass)); + } + updateChilds(); + } - for (JavaClass javaClass : pkg.getClasses()) { - add(new JClass(javaClass)); + public JPackage(String name) { + this.name = name; + this.classes = new ArrayList(1); + } + + @Override + public void updateChilds() { + removeAllChildren(); + for (JPackage pkg : innerPackages) { + pkg.updateChilds(); + add(pkg); + } + for (JClass cls : classes) { + add(cls); } } - public JavaPackage getPkg() { - return pkg; + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getInnerPackages() { + return innerPackages; + } + + public List getClasses() { + return classes; } @Override @@ -30,8 +65,13 @@ public class JPackage extends DefaultMutableTreeNode implements JNode { return PACKAGE_ICON; } + @Override + public int compareTo(JPackage o) { + return name.compareTo(o.name); + } + @Override public String toString() { - return pkg.getName(); + return name; } } diff --git a/jadx-gui/src/main/java/jadx/gui/treemodel/JRoot.java b/jadx-gui/src/main/java/jadx/gui/treemodel/JRoot.java index dfc755e91..f00cabb72 100644 --- a/jadx-gui/src/main/java/jadx/gui/treemodel/JRoot.java +++ b/jadx-gui/src/main/java/jadx/gui/treemodel/JRoot.java @@ -8,6 +8,14 @@ import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.tree.DefaultMutableTreeNode; import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; public class JRoot extends DefaultMutableTreeNode implements JNode { @@ -15,11 +23,93 @@ public class JRoot extends DefaultMutableTreeNode implements JNode { private final JadxWrapper wrapper; + private boolean flatPackages = false; + public JRoot(JadxWrapper wrapper) { this.wrapper = wrapper; + updateChilds(); + } - for (JavaPackage pkg : wrapper.getPackages()) { - add(new JPackage(pkg)); + @Override + public void updateChilds() { + removeAllChildren(); + if (flatPackages) { + for (JavaPackage pkg : wrapper.getPackages()) { + add(new JPackage(pkg)); + } + } else { + // build packages hierarchy + Map pkgMap = new HashMap(); + for (JavaPackage pkg : wrapper.getPackages()) { + addPackage(pkgMap, new JPackage(pkg)); + } + // merge packages without classes + for (JPackage pkg : pkgMap.values()) { + if (pkg.getInnerPackages().size() == 1 && pkg.getClasses().isEmpty()) { + JPackage innerPkg = pkg.getInnerPackages().get(0); + pkg.getInnerPackages().clear(); + pkg.getInnerPackages().addAll(innerPkg.getInnerPackages()); + pkg.getClasses().addAll(innerPkg.getClasses()); + pkg.setName(pkg.getName() + "." + innerPkg.getName()); + innerPkg.getInnerPackages().clear(); + innerPkg.getClasses().clear(); + } + } + // remove empty packages + for (Iterator> it = pkgMap.entrySet().iterator(); it.hasNext(); ) { + JPackage pkg = it.next().getValue(); + if (pkg.getInnerPackages().isEmpty() && pkg.getClasses().isEmpty()) { + it.remove(); + } + } + // find root packages + Set inners = new HashSet(); + for (JPackage pkg : pkgMap.values()) { + inners.addAll(pkg.getInnerPackages()); + } + List rootPkgs = new ArrayList(); + for (JPackage pkg : pkgMap.values()) { + if (!inners.contains(pkg)) { + rootPkgs.add(pkg); + } + } + Collections.sort(rootPkgs); + for (JPackage jPackage : rootPkgs) { + jPackage.updateChilds(); + add(jPackage); + } + } + } + + private void addPackage(Map pkgs, JPackage pkg) { + String pkgName = pkg.getName(); + JPackage replaced = pkgs.put(pkgName, pkg); + if (replaced != null) { + pkg.getInnerPackages().addAll(replaced.getInnerPackages()); + pkg.getClasses().addAll(replaced.getClasses()); + } + int dot = pkgName.lastIndexOf('.'); + if (dot > 0) { + String prevPart = pkgName.substring(0, dot); + String shortName = pkgName.substring(dot + 1); + pkg.setName(shortName); + JPackage prevPkg = pkgs.get(prevPart); + if (prevPkg == null) { + prevPkg = new JPackage(prevPart); + addPackage(pkgs, prevPkg); + } + prevPkg.getInnerPackages().add(pkg); + } + } + + public boolean isFlatPackages() { + return flatPackages; + } + + public void setFlatPackages(boolean flatPackages) { + if (this.flatPackages != flatPackages) { + this.flatPackages = flatPackages; + updateChilds(); } } diff --git a/jadx-gui/src/main/resources/i18n/Messages_en_US.properties b/jadx-gui/src/main/resources/i18n/Messages_en_US.properties new file mode 100644 index 000000000..2f6124650 --- /dev/null +++ b/jadx-gui/src/main/resources/i18n/Messages_en_US.properties @@ -0,0 +1,2 @@ +file.open=Open file +tree.flatten=Flatten packages diff --git a/jadx-gui/src/main/resources/icons-16/abstract_co.png b/jadx-gui/src/main/resources/icons-16/abstract_co.png new file mode 100644 index 0000000000000000000000000000000000000000..658b8fde5f54835615deda6e1c66dc50c1e5142d GIT binary patch literal 187 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)H!VDyjYyB_%0HK04TZfHCsE7VlN5u3x*0xc{95Kg&32(-Cfwl5<6Z4Ih+L^k;OpT1B~5HX4?T7 znw~C>Asp9}J-9i%6Ffo#ScTTC2~g^2)ND{xFi~hwG%zt}P&6?yVPIH(lw(Tn@pw6) OCI(MeKbLh*2~7Zr<2UdC literal 0 HcmV?d00001 diff --git a/jadx-gui/src/main/resources/icons-16/constr_ovr.png b/jadx-gui/src/main/resources/icons-16/constr_ovr.png new file mode 100644 index 0000000000000000000000000000000000000000..65964a92e80022db7a4b51b90f2209825a651123 GIT binary patch literal 180 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)H!VDyjYyB_zLiQ$MF*A~uJMrEL422WQ% Jmvv4FO#truIXVCU literal 0 HcmV?d00001 diff --git a/jadx-gui/src/main/resources/icons-16/empty_logical_package_obj.png b/jadx-gui/src/main/resources/icons-16/empty_logical_package_obj.png new file mode 100644 index 0000000000000000000000000000000000000000..acfb2238a7a230d59e94955bb4d6d38af8fdc5b8 GIT binary patch literal 352 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}LV!<*YgA;Ezkfh{d_r7Y{P&-K zAHV+LAK;&nk)EEO77`pB9v1fh|Nl#uE}cGo`sB%zEiEm@#l@MKnQ?J(VPRo`fq|Z$ zoC5%bl?k@7IyaIcH9L@rd$YLPv0mg18 zv+aP4GEW!B5Q)pN=NtK&6?j}P`u&nwJi8(K?|=W;)g4FFRGzfV4!D))d_C0t#Mc=$ zPOKZ|3*Qr{blTCp=|p?$V(0hM1=N-`aXjWz%P literal 0 HcmV?d00001 diff --git a/jadx-gui/src/main/resources/icons-16/empty_pack_obj.png b/jadx-gui/src/main/resources/icons-16/empty_pack_obj.png new file mode 100644 index 0000000000000000000000000000000000000000..a2d43103f01ab349950bada7aaedd538796ae06c GIT binary patch literal 325 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}LV!<*YfN-ZTwMI)*FS=SgZ%^i zGcwZC(o!QMBmV#Y-_p`jTwI))nVFuRo|2N1l#~<}78V*B8WFEhn05pRjxO!px zE}$Zjk|4iepcZt%;P3xj5Gc=>ZI?0ovtT%mve!(T*mv%n*=7)X17vD?XPJ0L^R)5S4_ z<9c#IBNJPYBoB{{0NY~4DF<_SgbkA%gPpgWWY~OCZp*aa`;>u-89ZJ6T-G@yGywop C!88^C literal 0 HcmV?d00001 diff --git a/jadx-gui/src/main/resources/icons-16/field_protected_obj.png b/jadx-gui/src/main/resources/icons-16/field_protected_obj.png new file mode 100644 index 0000000000000000000000000000000000000000..dc80489bc455b63fa58d6373c3a5f37a72cf49a0 GIT binary patch literal 222 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFP2=EDU-8V;L_YC#jGu79%%Wv*g z+SIGGyiR6SqwK0CxpnOd|L#rucRuGo7yxwu!MB5XSwM=fB*-rqA`T@PTo2qi0hD1( z@^*LOm)ON{6UgB#@Q5r1(jH*!b~4)z$k6q4aSY+Op1gybmFI|2#ljwL?gcz6gLfqy uyK0-h&A}yY{INU#c=6t=KI8l*FFO^GI+ZBxvX^Js literal 0 HcmV?d00001 diff --git a/jadx-gui/src/main/resources/icons-16/field_public_obj.png b/jadx-gui/src/main/resources/icons-16/field_public_obj.png new file mode 100644 index 0000000000000000000000000000000000000000..40a5ef1c8e99b56f5a241662542972610ea853ef GIT binary patch literal 221 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFP2=EDU6=?G{nUUeLu%veH{D~*l z$#n)5ZY=HG(fA(>fU3}dSbDQ9P>8D}$S)Y821qh&I-_$QD9V`R?e4-av5Vm*ki%Kv z5m^kRJ;2!QWVRiUVc_ZF7{YNqc?KsdkBiZq5QQGiN3HV`jveVS^E60ya>$j<3?z5j>~{cCJOMr-t{O?xg6o(32ZKqS9Mgc} zEG0pH!9W292d$M~fPBUzZ+91FK^~t^Kn`btM`SUO_5fqIli7AahK#3+V+hCf>$j-3?$8JydD54i2$Dv*ViAuY9vj|pSE-Uu8W5+ zKl~2{KuL2+_02$vy(Gvl7%C{`&FlsgVodUOcVQPx?0602a29w(76WMyFm^kcZ3kq? zd%8G=a9mIJ5as1b@DODaZ0d1vauM+8&}!;%;9z8^oXqxp`G(o9K&1?xu6{1-oD!M< Du!uF( literal 0 HcmV?d00001 diff --git a/jadx-gui/src/main/resources/icons-16/run_co.png b/jadx-gui/src/main/resources/icons-16/run_co.png new file mode 100644 index 0000000000000000000000000000000000000000..d253a17ceef9d364d79596083a11d3ba3d241f49 GIT binary patch literal 180 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)H!VDyjYyB_tgPI zFaXK`!Tr^8CxH}ONswPKM0oT5CHsLQj7i?^F6?579j}2L&H|6fVj%4S#%?FG?SKqr zPZ!4!j_b)Byu7>#9EFTRd_8&&hdM4c9z1xXf$UHx3v IIVCg!0Bu7z-v9sr literal 0 HcmV?d00001 diff --git a/jadx-gui/src/main/resources/icons-16/static_co.png b/jadx-gui/src/main/resources/icons-16/static_co.png new file mode 100644 index 0000000000000000000000000000000000000000..655c9c069114cf3a2b2815a3456d9f0f4ed34b71 GIT binary patch literal 174 zcmeAS@N?(olHy`uVBq!ia0vp^>>$j-3?$8JydD54i2$Dv*W*mO&w3Yp*mL|p7yyMK zpdh68G?2ww666;Ql41D0jV~A|z?kIi?!qpX*zp?3;VkfoEC$jZVC;4>+YZQ(^K@|x z;kce$5SCz+P>^QEAb8Ay>5QpaLno(!6a$050ZWzU+dqqeDj7Uo{an^LB{Ts5V~jDq literal 0 HcmV?d00001 diff --git a/jadx-gui/src/main/resources/icons-16/synch_co.png b/jadx-gui/src/main/resources/icons-16/synch_co.png new file mode 100644 index 0000000000000000000000000000000000000000..2e950e988a99e79a68eeef865a35c3cc7c341361 GIT binary patch literal 254 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)H!3-psZi$)zse}NZ5ZAx|{xgJ5Gbvwv`tB=} z{QghB|KIrX|L)iSPk;P>^XvcHU;n@S`Tzaz|KI=qfBN$u0)SeH2W}sK-v+8?O!9Vj zk!R%<*bC%v7I;J!18EO1b~~AE2V|Igx;Tb#TsJ-C$#=kkgW2)+1fIx-|NqM-Ec&ul zDtFPE#2yYQk&`V4N-OrZoO}Jv{c))2zyE)?GHNz3_U3->_XQfk;OXk;vd$@?2>{Lz BT;2cx literal 0 HcmV?d00001 diff --git a/jadx-gui/src/main/resources/icons-16/transient_co.png b/jadx-gui/src/main/resources/icons-16/transient_co.png new file mode 100644 index 0000000000000000000000000000000000000000..4f54e545ee9b33bbcd50fc67df0f49910cf10653 GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0vp^>>$j-3?$8JydHo!C7!;n?6=sMcukDMq*fLHg*Xd5 zB8wRqxP?HN@zUM8KR`i=0G|-o|Ns9d_%F_kSXrFBzAkTDXT_e0O^2p;9J8Dks|HlV zSQ6wH%;50sMjDW#;OXKRQgJIeL0>;TH6bCHL1>La6RV3sGl!bM!4n6#7?edB^?v#> RcLCKhc)I$ztaD0e0syzyGK2sC literal 0 HcmV?d00001 diff --git a/jadx-gui/src/main/resources/icons-16/volatile_co.png b/jadx-gui/src/main/resources/icons-16/volatile_co.png new file mode 100644 index 0000000000000000000000000000000000000000..cd99249f52221f0a6672e1372873a1a54c83eacb GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0vp^>>$j-3?$8JydD54i2$Dv*I$Rv9cR+L=9>B+41mHA z@WXC@1(3yB666;Ql41D0jV~A|z?kIi?!qpX*zp?3;VkfoEC$jZVC;4>+YZQ3@^o