1 package edu.jiangxin.apktoolbox.main;
2
3 import edu.jiangxin.apktoolbox.Version;
4 import edu.jiangxin.apktoolbox.android.dumpsys.DumpsysPanel;
5 import edu.jiangxin.apktoolbox.convert.color.ColorPickerPanel;
6 import edu.jiangxin.apktoolbox.convert.protobuf.supervised.SupervisedProtobufConvertPanel;
7 import edu.jiangxin.apktoolbox.convert.protobuf.unsupervised.UnsupervisedProtobufConvertPanel;
8 import edu.jiangxin.apktoolbox.file.batchrename.BatchRenamePanel;
9 import edu.jiangxin.apktoolbox.file.EncodeConvertPanel;
10 import edu.jiangxin.apktoolbox.file.OsConvertPanel;
11 import edu.jiangxin.apktoolbox.file.checksum.ChecksumPanel;
12 import edu.jiangxin.apktoolbox.file.password.recovery.RecoveryPanel;
13 import edu.jiangxin.apktoolbox.file.duplicate.DuplicateSearchPanel;
14 import edu.jiangxin.apktoolbox.file.zhconvert.ZhConvertPanel;
15 import edu.jiangxin.apktoolbox.help.*;
16 import edu.jiangxin.apktoolbox.android.i18n.I18nAddPanel;
17 import edu.jiangxin.apktoolbox.android.i18n.I18nFindLongestPanel;
18 import edu.jiangxin.apktoolbox.android.i18n.I18nRemovePanel;
19 import edu.jiangxin.apktoolbox.android.monkey.MonkeyPanel;
20 import edu.jiangxin.apktoolbox.help.settings.SettingsPanel;
21 import edu.jiangxin.apktoolbox.pdf.finder.PdfFinderPanel;
22 import edu.jiangxin.apktoolbox.pdf.passwordremover.PdfPasswordRemoverPanel;
23 import edu.jiangxin.apktoolbox.pdf.pic2pdf.Pic2PdfPanel;
24 import edu.jiangxin.apktoolbox.pdf.stat.PdfStatPanel;
25 import edu.jiangxin.apktoolbox.reverse.*;
26 import edu.jiangxin.apktoolbox.android.screenshot.ScreenShotPanel;
27 import edu.jiangxin.apktoolbox.reverse.ApktoolPanel;
28 import edu.jiangxin.apktoolbox.swing.extend.EasyFrame;
29 import edu.jiangxin.apktoolbox.swing.extend.EasyPanel;
30 import edu.jiangxin.apktoolbox.swing.extend.listener.ChangeMenuListener;
31 import edu.jiangxin.apktoolbox.swing.extend.listener.ChangeMenuToUrlListener;
32 import edu.jiangxin.apktoolbox.swing.extend.listener.IPreChangeMenuCallBack;
33 import edu.jiangxin.apktoolbox.swing.extend.plugin.ChangeMenuPreparePluginController;
34 import edu.jiangxin.apktoolbox.swing.extend.plugin.PluginPanel;
35 import edu.jiangxin.apktoolbox.swing.keeper.UiStateKeeper;
36 import edu.jiangxin.apktoolbox.utils.Utils;
37 import org.apache.commons.configuration2.Configuration;
38 import org.apache.commons.lang3.StringUtils;
39 import org.apache.logging.log4j.LogManager;
40 import org.apache.logging.log4j.Logger;
41
42 import javax.swing.*;
43 import javax.swing.border.EmptyBorder;
44 import java.awt.*;
45 import java.awt.event.*;
46 import java.io.Serial;
47 import java.lang.reflect.InvocationTargetException;
48 import java.text.MessageFormat;
49 import java.util.Locale;
50 import java.util.Objects;
51
52
53
54
55
56 public final class MainFrame extends EasyFrame {
57
58 @Serial
59 private static final long serialVersionUID = 1L;
60
61 private JPanel contentPane;
62 private EasyPanel currentEasyPanel = null;
63 private JMenuBar menuBar;
64
65 public static void main(String[] args) {
66 final Logger logger = LogManager.getLogger(MainFrame.class.getSimpleName());
67 boolean isEnvironmentOk = Utils.checkAndInitEnvironment();
68 if (!isEnvironmentOk) {
69 logger.error("Environment is not ready, exit");
70 return;
71 }
72 EventQueue.invokeLater(() -> {
73 Configuration conf = Utils.getConfiguration();
74 String lookAndFeelClassName = conf.getString("look.and.feel.class.name");
75 if (StringUtils.isEmpty(lookAndFeelClassName)) {
76 lookAndFeelClassName = "com.formdev.flatlaf.FlatDarculaLaf";
77 conf.setProperty("look.and.feel.class.name", lookAndFeelClassName);
78 }
79 try {
80 UIManager.setLookAndFeel(lookAndFeelClassName);
81 } catch (UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException e) {
82 logger.error("setLookAndFeel failed, use default instead", e);
83 }
84
85 String currentLocaleLanguage = conf.getString("locale.language");
86 if (StringUtils.isEmpty(currentLocaleLanguage)) {
87 currentLocaleLanguage = Locale.ENGLISH.getLanguage();
88 conf.setProperty("locale.language", currentLocaleLanguage);
89 }
90 Locale.setDefault(Locale.forLanguageTag(currentLocaleLanguage));
91
92 MainFrame frame = new MainFrame();
93
94 boolean isAlwaysOnTop = conf.getBoolean("always.on.top", false);
95 conf.setProperty("always.on.top", isAlwaysOnTop);
96 frame.setAlwaysOnTop(isAlwaysOnTop);
97
98 frame.setVisible(true);
99 });
100 }
101
102 public MainFrame() {
103 super();
104 initialize();
105 if (SystemTray.isSupported()) {
106 configSystemTray();
107 }
108
109 setTitle(MessageFormat.format(bundle.getString("main.title"), Version.VERSION));
110 setDefaultCloseOperation(EXIT_ON_CLOSE);
111 setMenuBar();
112 contentPane = new JPanel();
113 contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
114 contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS));
115 contentPane.add(Box.createVerticalGlue());
116 EasyPanel initPanel = new AboutPanel();
117 initPanel.init();
118 initPanel.setBorder(BorderFactory.createTitledBorder(bundle.getString("help.about.title")));
119 contentPane.add(initPanel);
120 contentPane.add(Box.createVerticalGlue());
121 setContentPane(contentPane);
122 refreshSizeAndLocation();
123 }
124
125 private void configSystemTray() {
126 SystemTray systemTray = SystemTray.getSystemTray();
127 PopupMenu popupMenu = new PopupMenu();
128 final MenuItem show = new MenuItem("Open");
129 final MenuItem exit = new MenuItem("Close");
130 ActionListener actionListener = e -> {
131 if (e.getSource() == show) {
132 setExtendedState(NORMAL);
133 setVisible(true);
134 }
135 if (e.getSource() == exit) {
136 dispatchEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING));
137 }
138 };
139 exit.addActionListener(actionListener);
140 show.addActionListener(actionListener);
141 popupMenu.add(show);
142 popupMenu.add(exit);
143 TrayIcon trayIcon = new TrayIcon(image, "系统托盘", popupMenu);
144 trayIcon.setImageAutoSize(true);
145 try {
146 systemTray.add(trayIcon);
147 } catch (AWTException e) {
148 logger.error("add icon to tray failed");
149 }
150 trayIcon.addMouseListener(new MouseAdapter() {
151 @Override
152 public void mouseClicked(MouseEvent e) {
153 if (e.getClickCount() == 2) {
154 setExtendedState(NORMAL);
155 setVisible(true);
156 }
157 }
158 });
159 }
160
161 private void setMenuBar() {
162 menuBar = new JMenuBar();
163 setJMenuBar(menuBar);
164
165 createReverseMenu();
166
167 createAndroidMenu();
168
169 createPdfMenu();
170
171 createFileMenu();
172
173 createConvertMenu();
174
175 createHelpMenu();
176 }
177
178 private void createHelpMenu() {
179 JMenu helpMenu = new JMenu(bundle.getString("help.title"));
180 menuBar.add(helpMenu);
181
182 JMenuItem documentMenuItem = new JMenuItem(bundle.getString("help.document.title"));
183 documentMenuItem.addActionListener(new ChangeMenuToUrlListener(Constant.URL_DOCUMENT));
184 helpMenu.add(documentMenuItem);
185
186 JMenuItem settingsMenuItem = new JMenuItem(bundle.getString("help.settings.title"));
187 settingsMenuItem.addActionListener(new ChangeMenuToPanelListener(SettingsPanel.class, settingsMenuItem.getText()));
188 helpMenu.add(settingsMenuItem);
189
190 JMenuItem feedbackMenuItem = new JMenuItem(bundle.getString("help.feedback.title"));
191 feedbackMenuItem.addActionListener(new ChangeMenuToUrlListener(Constant.URL_FEEDBACK));
192 helpMenu.add(feedbackMenuItem);
193
194 JMenuItem checkUpdateMenuItem = new JMenuItem(bundle.getString("help.checkupdate.title"));
195 checkUpdateMenuItem.addActionListener(new CheckUpdateActionListener(this));
196 helpMenu.add(checkUpdateMenuItem);
197
198 JMenuItem contributeMenuItem = new JMenuItem(bundle.getString("help.contribute.title"));
199 contributeMenuItem.addActionListener(new ChangeMenuToUrlListener(Constant.URL_CONTRIBUTE));
200 helpMenu.add(contributeMenuItem);
201
202 JMenuItem aboutMenuItem = new JMenuItem(bundle.getString("help.about.title"));
203 aboutMenuItem.addActionListener(new ChangeMenuToPanelListener(AboutPanel.class, aboutMenuItem.getText()));
204 helpMenu.add(aboutMenuItem);
205 }
206
207 private void createPdfMenu() {
208 JMenu pdfMenu = new JMenu(bundle.getString("pdf.title"));
209 menuBar.add(pdfMenu);
210
211 JMenuItem pdfStatMenuItem = new JMenuItem(bundle.getString("pdf.stat.title"));
212 pdfStatMenuItem.addActionListener(new ChangeMenuToPanelListener(PdfStatPanel.class, pdfStatMenuItem.getText()));
213 pdfMenu.add(pdfStatMenuItem);
214
215 JMenuItem pdfFinderMenuItem = new JMenuItem(bundle.getString("pdf.finder.title"));
216 pdfFinderMenuItem.addActionListener(new ChangeMenuToPanelListener(PdfFinderPanel.class, pdfFinderMenuItem.getText()));
217 pdfMenu.add(pdfFinderMenuItem);
218
219 JMenuItem pdfPasswordRemoverMenuItem = new JMenuItem(bundle.getString("pdf.password.remover.title"));
220 pdfPasswordRemoverMenuItem.addActionListener(new ChangeMenuToPanelListener(PdfPasswordRemoverPanel.class, pdfPasswordRemoverMenuItem.getText()));
221 pdfMenu.add(pdfPasswordRemoverMenuItem);
222
223 JMenuItem pic2PdfMenuItem = new JMenuItem(bundle.getString("pictures.to.pdf.title"));
224 pic2PdfMenuItem.addActionListener(new ChangeMenuToPanelListener(Pic2PdfPanel.class, pic2PdfMenuItem.getText()));
225 pdfMenu.add(pic2PdfMenuItem);
226 }
227
228 private void createFileMenu() {
229 JMenu fileMenu = new JMenu(bundle.getString("file.title"));
230 menuBar.add(fileMenu);
231
232 JMenuItem osConvertMenuItem = new JMenuItem(bundle.getString("file.os.convert.title"));
233 osConvertMenuItem.addActionListener(new ChangeMenuToPanelListener(OsConvertPanel.class, osConvertMenuItem.getText()));
234 fileMenu.add(osConvertMenuItem);
235
236 JMenuItem encodeConvertMenuItem = new JMenuItem(bundle.getString("file.encode.convert.title"));
237 encodeConvertMenuItem.addActionListener(new ChangeMenuToPanelListener(EncodeConvertPanel.class, encodeConvertMenuItem.getText()));
238 fileMenu.add(encodeConvertMenuItem);
239
240 JMenuItem zhConvertMenuItem = new JMenuItem(bundle.getString("file.zh.convert.title"));
241 zhConvertMenuItem.addActionListener(new ChangeMenuToPanelListener(ZhConvertPanel.class, zhConvertMenuItem.getText()));
242 fileMenu.add(zhConvertMenuItem);
243
244 JMenuItem duplicateFindMenuItem = new JMenuItem(bundle.getString("file.duplicate.title"));
245 duplicateFindMenuItem.addActionListener(new ChangeMenuToPanelListener(DuplicateSearchPanel.class, duplicateFindMenuItem.getText()));
246 fileMenu.add(duplicateFindMenuItem);
247
248 JMenuItem batchRenameMenuItem = new JMenuItem(bundle.getString("file.batch.rename.title"));
249 batchRenameMenuItem.addActionListener(new ChangeMenuToPanelListener(BatchRenamePanel.class, batchRenameMenuItem.getText()));
250 fileMenu.add(batchRenameMenuItem);
251
252 JMenuItem checkSumMenuItem = new JMenuItem(bundle.getString("file.check.summary.title"));
253 checkSumMenuItem.addActionListener(new ChangeMenuToPanelListener(ChecksumPanel.class, checkSumMenuItem.getText()));
254 fileMenu.add(checkSumMenuItem);
255
256 JMenuItem recoveryMenuItem = new JMenuItem(bundle.getString("file.password.recovery.title"));
257 recoveryMenuItem.addActionListener(new ChangeMenuToPanelListener(RecoveryPanel.class, recoveryMenuItem.getText()));
258 fileMenu.add(recoveryMenuItem);
259 }
260
261 private void createConvertMenu() {
262 JMenu convertMenu = new JMenu(bundle.getString("convert.title"));
263 menuBar.add(convertMenu);
264
265 JMenuItem colorPickerMenuItem = new JMenuItem(bundle.getString("picker.color.title"));
266 colorPickerMenuItem.addActionListener(new ChangeMenuToPanelListener(ColorPickerPanel.class, colorPickerMenuItem.getText()));
267 convertMenu.add(colorPickerMenuItem);
268
269 JMenuItem unsupervisedProtobufConvertMenuItem = new JMenuItem(bundle.getString("convert.protobuf.unsupervised.title"));
270 unsupervisedProtobufConvertMenuItem.addActionListener(new ChangeMenuToPanelListener(UnsupervisedProtobufConvertPanel.class, unsupervisedProtobufConvertMenuItem.getText()));
271 convertMenu.add(unsupervisedProtobufConvertMenuItem);
272
273 JMenuItem supervisedProtobufConvertMenuItem = new JMenuItem(bundle.getString("convert.protobuf.supervised.title"));
274 supervisedProtobufConvertMenuItem.addActionListener(new ChangeMenuToPanelListener(SupervisedProtobufConvertPanel.class, supervisedProtobufConvertMenuItem.getText()));
275 convertMenu.add(supervisedProtobufConvertMenuItem);
276 }
277
278 private void createAndroidMenu() {
279 JMenu androidMenu = new JMenu(bundle.getString("android.title"));
280 menuBar.add(androidMenu);
281
282 JMenuItem i18nAddMenuItem = new JMenuItem(bundle.getString("android.i18n.add.title"));
283 i18nAddMenuItem.addActionListener(new ChangeMenuToPanelListener(I18nAddPanel.class, i18nAddMenuItem.getText()));
284 androidMenu.add(i18nAddMenuItem);
285
286 JMenuItem i18nFindLongestMenuItem = new JMenuItem(bundle.getString("android.i18n.longest.title"));
287 i18nFindLongestMenuItem.addActionListener(new ChangeMenuToPanelListener(I18nFindLongestPanel.class, i18nFindLongestMenuItem.getText()));
288 androidMenu.add(i18nFindLongestMenuItem);
289
290 JMenuItem i18nRemoveMenuItem = new JMenuItem(bundle.getString("android.i18n.remove.title"));
291 i18nRemoveMenuItem.addActionListener(new ChangeMenuToPanelListener(I18nRemovePanel.class, i18nRemoveMenuItem.getText()));
292 androidMenu.add(i18nRemoveMenuItem);
293
294 JMenuItem screenShotMenuItem = new JMenuItem(bundle.getString("android.screenshot.title"));
295 screenShotMenuItem.addActionListener(new ChangeMenuToPanelListener(ScreenShotPanel.class, screenShotMenuItem.getText()));
296 screenShotMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK));
297 androidMenu.add(screenShotMenuItem);
298
299 JMenuItem monkeyMenuItem = new JMenuItem(bundle.getString("android.monkey.title"));
300 monkeyMenuItem.addActionListener(new ChangeMenuToPanelListener(MonkeyPanel.class, monkeyMenuItem.getText()));
301 androidMenu.add(monkeyMenuItem);
302
303 JMenuItem dumpsysMenuItem = new JMenuItem(bundle.getString("android.dumpsys.title"));
304 dumpsysMenuItem.addActionListener(new ChangeMenuToPanelListener(DumpsysPanel.class, dumpsysMenuItem.getText()));
305 androidMenu.add(dumpsysMenuItem);
306 }
307
308 private void createReverseMenu() {
309 JMenu reverseMenu = new JMenu(bundle.getString("reverse.title"));
310 reverseMenu.setMnemonic(KeyEvent.VK_R);
311 menuBar.add(reverseMenu);
312
313 JMenuItem pluginVersionMenuItem = new JMenuItem(bundle.getString("reverse.plugin.version.title"));
314 pluginVersionMenuItem.addActionListener(new ChangeMenuToUrlListener(Constant.URL_PLUGIN_VERSION));
315 reverseMenu.add(pluginVersionMenuItem);
316
317 JMenuItem apktoolMenuItem = new JMenuItem(bundle.getString("reverse.apktool.title"), KeyEvent.VK_D);
318 apktoolMenuItem.addActionListener(new ChangeMenuToPanelListener(ApktoolPanel.class, apktoolMenuItem.getText()));
319 reverseMenu.add(apktoolMenuItem);
320
321 JMenuItem apkSignMenuItem = new JMenuItem(bundle.getString("reverse.apksigner.title"));
322 apkSignMenuItem.addActionListener(new ChangeMenuToPanelListener(ApkSignerPanel.class, apkSignMenuItem.getText()));
323 reverseMenu.add(apkSignMenuItem);
324
325 JMenuItem jDMenuItem = new JMenuItem(bundle.getString("reverse.jd.gui.title"));
326 jDMenuItem.addActionListener(new ChangeMenToPluginJdListener());
327 reverseMenu.add(jDMenuItem);
328
329 JMenuItem bytecodeViewerMenuItem = new JMenuItem(bundle.getString("reverse.bytecode.viewer.title"));
330 bytecodeViewerMenuItem.addActionListener(new ChangeMenuToPluginBytecodeViewerListener());
331 reverseMenu.add(bytecodeViewerMenuItem);
332
333 JMenuItem luytenMenuItem = new JMenuItem(bundle.getString("reverse.luyten.title"));
334 luytenMenuItem.addActionListener(new ChangeMenuToPluginLuytenListener());
335 reverseMenu.add(luytenMenuItem);
336
337 JMenuItem jdDuoMenuItem = new JMenuItem(bundle.getString("reverse.jd.duo.title"));
338 jdDuoMenuItem.addActionListener(new ChangeMenuToPluginJdDuoListener());
339 reverseMenu.add(jdDuoMenuItem);
340
341 JMenuItem jdaMenuItem = new JMenuItem(bundle.getString("reverse.jda.title"));
342 jdaMenuItem.addActionListener(new ChangeMenuToPluginJdaListener());
343 reverseMenu.add(jdaMenuItem);
344
345 JMenuItem jADXMenuItem = new JMenuItem(bundle.getString("reverse.jadx.title"));
346 jADXMenuItem.addActionListener(new ChangeMenuToPluginJadxListener());
347 reverseMenu.add(jADXMenuItem);
348
349 JMenuItem aXMLPrinter = new JMenuItem(bundle.getString("reverse.axmlprinter.title"));
350 aXMLPrinter.addActionListener(new ChangeMenuToPanelListener(AxmlPrinterPanel.class, aXMLPrinter.getText()));
351 reverseMenu.add(aXMLPrinter);
352 }
353
354 class ChangeMenuToPanelListener implements ChangeMenuListener {
355
356 Class<? extends EasyPanel> easyPanelClass;
357
358 EasyPanel panel = null;
359
360 String title;
361
362 public ChangeMenuToPanelListener(Class<? extends EasyPanel> easyPanelClass, String title) {
363 this.easyPanelClass = easyPanelClass;
364 this.title = title;
365 panel = createEasyPanel();
366 }
367
368 @Override
369 public boolean isNeedPreChangeMenu() {
370 return panel.isNeedPreChangeMenu();
371 }
372
373 @Override
374 public void onPreChangeMenu(IPreChangeMenuCallBack callBack) {
375 if (panel instanceof PluginPanel pluginPanel) {
376 pluginPanel.preparePlugin(new ChangeMenuPreparePluginController(pluginPanel.getPluginFilename(), pluginPanel.isPluginNeedUnzip(), pluginPanel.isPluginNeedUnzipToSeparateDir(), callBack));
377 }
378 }
379
380 @Override
381 public void onChangeMenu() {
382 UiStateKeeper.save(currentEasyPanel);
383 contentPane.removeAll();
384 contentPane.add(Box.createVerticalGlue());
385 panel.init();
386 panel.setBorder(BorderFactory.createTitledBorder(title));
387 contentPane.add(panel);
388 logger.info("Panel changed: " + panel.getClass().getSimpleName());
389 contentPane.add(Box.createVerticalGlue());
390 contentPane.revalidate();
391 contentPane.repaint();
392 refreshSizeAndLocation();
393 panel.afterPainted();
394 UiStateKeeper.restore(panel);
395 currentEasyPanel = panel;
396 }
397
398 private EasyPanel createEasyPanel() {
399 if (easyPanelClass == null) {
400 return new EasyPanel();
401 }
402 EasyPanel retEasyPanel = null;
403 try {
404 retEasyPanel = easyPanelClass.getDeclaredConstructor().newInstance();
405 } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
406 NoSuchMethodException e) {
407 logger.info("createEasyPanel failed: {}", e.getMessage());
408 }
409 return Objects.requireNonNullElseGet(retEasyPanel, EasyPanel::new);
410 }
411 }
412
413 @Override
414 protected void onWindowClosing(WindowEvent e) {
415 super.onWindowClosing(e);
416 UiStateKeeper.save(currentEasyPanel);
417 }
418
419 @Override
420 protected void onWindowIconified(WindowEvent e) {
421 super.onWindowIconified(e);
422 UiStateKeeper.save(currentEasyPanel);
423 }
424 }