1 package edu.jiangxin.apktoolbox.reverse; 2 3 import edu.jiangxin.apktoolbox.swing.extend.plugin.PluginPanel; 4 import edu.jiangxin.apktoolbox.swing.extend.listener.SelectDirectoryListener; 5 import edu.jiangxin.apktoolbox.swing.extend.listener.SelectFileListener; 6 import edu.jiangxin.apktoolbox.utils.Constants; 7 import edu.jiangxin.apktoolbox.utils.Utils; 8 import org.apache.commons.io.FilenameUtils; 9 10 import javax.swing.*; 11 import javax.swing.filechooser.FileFilter; 12 import java.awt.*; 13 import java.awt.event.ActionEvent; 14 import java.awt.event.ActionListener; 15 import java.io.File; 16 import java.io.Serial; 17 18 public class ApktoolPanel extends PluginPanel { 19 @Serial 20 private static final long serialVersionUID = 1L; 21 22 public ApktoolPanel() throws HeadlessException { 23 super(); 24 } 25 26 @Override 27 public String getPluginFilename() { 28 return "apktool_2.12.0.jar"; 29 } 30 31 @Override 32 public void initUI() { 33 BoxLayout boxLayout = new BoxLayout(this, BoxLayout.Y_AXIS); 34 setLayout(boxLayout); 35 36 JTabbedPane categoryTabbedPane = new JTabbedPane(); 37 add(categoryTabbedPane); 38 39 JPanel decodeCategoryPanel = new ApktoolDecodePanel(); 40 JPanel rebuildCategoryPanel = new ApktoolRebuildPanel(); 41 42 categoryTabbedPane.addTab("Decode", null, decodeCategoryPanel, "Decode the APK file"); 43 categoryTabbedPane.setSelectedIndex(0); 44 categoryTabbedPane.addTab("Rebuild", null, rebuildCategoryPanel, "Rebuild the APK file"); 45 } 46 47 abstract class ApktoolPanelBase extends JPanel { 48 @Serial 49 private static final long serialVersionUID = 1L; 50 51 protected JPanel srcPanel; 52 53 protected JPanel targetPanel; 54 55 protected JPanel optionPanel; 56 57 protected JPanel operationPanel; 58 59 protected ApktoolPanelBase() throws HeadlessException { 60 super(); 61 initUI(); 62 } 63 64 private void initUI() { 65 BoxLayout boxLayout = new BoxLayout(this, BoxLayout.Y_AXIS); 66 setLayout(boxLayout); 67 68 createSrcPanel(); 69 add(srcPanel); 70 add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER)); 71 72 createTargetPanel(); 73 add(targetPanel); 74 add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER)); 75 76 createOptionPanel(); 77 add(optionPanel); 78 add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER)); 79 80 createOperationPanel(); 81 add(operationPanel); 82 } 83 84 protected void createSrcPanel() { 85 srcPanel = new JPanel(); 86 srcPanel.setLayout(new BoxLayout(srcPanel, BoxLayout.X_AXIS)); 87 } 88 89 protected void createTargetPanel() { 90 targetPanel = new JPanel(); 91 targetPanel.setLayout(new BoxLayout(targetPanel, BoxLayout.X_AXIS)); 92 } 93 94 protected void createOptionPanel() { 95 optionPanel = new JPanel(); 96 optionPanel.setLayout(new BoxLayout(optionPanel, BoxLayout.X_AXIS)); 97 } 98 99 protected void createOperationPanel() { 100 operationPanel = new JPanel(); 101 operationPanel.setLayout(new BoxLayout(operationPanel, BoxLayout.X_AXIS)); 102 } 103 } 104 105 class ApktoolDecodePanel extends ApktoolPanelBase { 106 @Serial 107 private static final long serialVersionUID = 1L; 108 109 private JTextField srcTextField; 110 111 private JTextField targetTextField; 112 113 private JCheckBox resourceIgnoreCheckBox; 114 115 private JCheckBox overrideCheckBox; 116 117 public ApktoolDecodePanel() throws HeadlessException { 118 super(); 119 } 120 121 @Override 122 public void createSrcPanel() { 123 super.createSrcPanel(); 124 125 srcTextField = new JTextField(); 126 srcTextField.setText(conf.getString("apktool.decode.src.file")); 127 128 JButton srcButton = new JButton(bundle.getString("choose.file.button")); 129 srcButton.addActionListener(new SelectFileListener("select a file", srcTextField)); 130 131 srcPanel.add(srcTextField); 132 srcPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER)); 133 srcPanel.add(srcButton); 134 } 135 136 @Override 137 public void createTargetPanel() { 138 super.createTargetPanel(); 139 140 targetTextField = new JTextField(); 141 targetTextField.setText(conf.getString("apktool.decode.target.dir")); 142 143 JButton targetButton = new JButton(bundle.getString("save.dir.button")); 144 targetButton.addActionListener(new SelectDirectoryListener("Save To", targetTextField)); 145 146 targetPanel.add(targetTextField); 147 targetPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER)); 148 targetPanel.add(targetButton); 149 } 150 151 @Override 152 public void createOptionPanel() { 153 super.createOptionPanel(); 154 155 resourceIgnoreCheckBox = new JCheckBox("Ignore res"); 156 resourceIgnoreCheckBox.setSelected(false); 157 optionPanel.add(resourceIgnoreCheckBox); 158 159 overrideCheckBox = new JCheckBox("Override"); 160 overrideCheckBox.setSelected(true); 161 optionPanel.add(overrideCheckBox); 162 } 163 164 @Override 165 public void createOperationPanel() { 166 super.createOperationPanel(); 167 168 JButton decodeButton = new JButton("Decode"); 169 decodeButton.addActionListener(new ApktoolDecodePanel.DecodeButtonActionListener()); 170 171 operationPanel.add(decodeButton); 172 } 173 174 private final class DecodeButtonActionListener implements ActionListener { 175 @Override 176 public void actionPerformed(ActionEvent e) { 177 String srcPath = checkAndGetFileContent(srcTextField, "apktool.decode.src.file", "Source file is invalid"); 178 if (srcPath == null) { 179 return; 180 } 181 182 String targetPath = checkAndGetDirContent(targetTextField, "apktool.decode.target.dir", "Target directory is invalid"); 183 if (targetPath == null) { 184 return; 185 } 186 187 String srcBaseName = FilenameUtils.getBaseName(srcPath); 188 StringBuilder sb = new StringBuilder(); 189 sb.append(ApktoolPanel.this.getPluginStartupCmd()).append(" d ") 190 .append(srcPath).append(" -o ").append(targetPath).append(File.separator).append(srcBaseName); 191 if (resourceIgnoreCheckBox.isSelected()) { 192 sb.append(" -r"); 193 } 194 if (overrideCheckBox.isSelected()) { 195 sb.append(" -f"); 196 } 197 Utils.executor(sb.toString(), true); 198 } 199 } 200 } 201 202 class ApktoolRebuildPanel extends ApktoolPanelBase { 203 @Serial 204 private static final long serialVersionUID = 1L; 205 206 private JTextField srcTextField; 207 208 private JTextField targetTextField; 209 210 private JCheckBox signAPK; 211 212 public ApktoolRebuildPanel() throws HeadlessException { 213 super(); 214 } 215 216 @Override 217 public void createSrcPanel() { 218 super.createSrcPanel(); 219 220 srcTextField = new JTextField(); 221 srcTextField.setText(conf.getString("apktool.rebuild.src.dir")); 222 223 JButton srcButton = new JButton(bundle.getString("choose.dir.button")); 224 srcButton.addActionListener(new SelectDirectoryListener("Select Directory", srcTextField)); 225 226 srcPanel.add(srcTextField); 227 srcPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER)); 228 srcPanel.add(srcButton); 229 } 230 231 @Override 232 public void createTargetPanel() { 233 super.createTargetPanel(); 234 235 targetTextField = new JTextField(); 236 targetTextField.setText(conf.getString("apktool.rebuild.target.file")); 237 238 JButton targetButton = new JButton(bundle.getString("save.file.button")); 239 targetButton.addActionListener(new SelectFileListener("save to", targetTextField, new ApktoolRebuildPanel.ApkFileFilter())); 240 241 targetPanel.add(targetTextField); 242 targetPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER)); 243 targetPanel.add(targetButton); 244 } 245 246 @Override 247 public void createOptionPanel() { 248 super.createOptionPanel(); 249 250 signAPK = new JCheckBox("sign APK"); 251 signAPK.setSelected(false); 252 optionPanel.add(signAPK); 253 } 254 255 @Override 256 public void createOperationPanel() { 257 super.createOperationPanel(); 258 259 JButton rebuildButton = new JButton("Rebuild"); 260 rebuildButton.addActionListener(new ApktoolRebuildPanel.RebuildButtonActionListener()); 261 operationPanel.add(rebuildButton); 262 } 263 264 private final class RebuildButtonActionListener implements ActionListener { 265 @Override 266 public void actionPerformed(ActionEvent e) { 267 String srcPath = checkAndGetDirContent(srcTextField, "apktool.rebuild.src.dir", "Source directory is invalid"); 268 if (srcPath == null) { 269 return; 270 } 271 272 String targetPath = checkAndGetNewFileContent(targetTextField, "apktool.rebuild.target.file", "Target file is invalid"); 273 if (targetPath == null) { 274 return; 275 } 276 277 StringBuilder sb = new StringBuilder(); 278 sb.append(ApktoolPanel.this.getPluginStartupCmd()).append(" b ") 279 .append(srcPath).append(" -o ").append(targetPath); 280 Utils.executor(sb.toString(), true); 281 if (signAPK.isSelected()) { 282 sb = new StringBuilder(); 283 sb.append(ApktoolPanel.this.getPluginStartupCmd()) 284 .append(" -keystore ").append(Utils.getToolsPath()).append(File.separator) 285 .append("debug.keystore").append(" -alias androiddebugkey -pswd android ") 286 .append(targetPath); 287 Utils.executor(sb.toString(), true); 288 } 289 } 290 } 291 292 private final class ApkFileFilter extends FileFilter { 293 294 @Override 295 public boolean accept(File f) { 296 String nameString = f.getName(); 297 return nameString.toLowerCase().endsWith(".apk"); 298 } 299 300 @Override 301 public String getDescription() { 302 return "*.apk"; 303 } 304 } 305 } 306 } 307