• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

jiangxincode / ApkToolBoxGUI / #1207

13 Sep 2025 11:58PM UTC coverage: 2.895% (-0.006%) from 2.901%
#1207

push

jiangxincode
fix #569: 可能在完全初始化子类之前逃逸了 'this'

0 of 32 new or added lines in 22 files covered. (0.0%)

9 existing lines in 8 files now uncovered.

248 of 8567 relevant lines covered (2.89%)

0.03 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/main/java/edu/jiangxin/apktoolbox/file/password/recovery/RecoveryPanel.java
1
package edu.jiangxin.apktoolbox.file.password.recovery;
2

3
import edu.jiangxin.apktoolbox.file.password.recovery.category.CategoryFactory;
4
import edu.jiangxin.apktoolbox.file.password.recovery.category.CategoryType;
5
import edu.jiangxin.apktoolbox.file.password.recovery.category.ICategory;
6
import edu.jiangxin.apktoolbox.file.password.recovery.checker.*;
7
import edu.jiangxin.apktoolbox.file.password.recovery.checker.thirdparty.ThirdParty7ZipChecker;
8
import edu.jiangxin.apktoolbox.file.password.recovery.checker.thirdparty.ThirdPartyRarChecker;
9
import edu.jiangxin.apktoolbox.file.password.recovery.checker.thirdparty.ThirdPartyWinRarChecker;
10
import edu.jiangxin.apktoolbox.swing.extend.EasyPanel;
11
import edu.jiangxin.apktoolbox.swing.extend.filepanel.FilePanel;
12
import edu.jiangxin.apktoolbox.utils.Constants;
13
import org.apache.commons.collections4.CollectionUtils;
14
import org.apache.commons.io.FilenameUtils;
15
import org.apache.commons.lang3.ArrayUtils;
16
import org.apache.commons.lang3.StringUtils;
17

18
import javax.swing.*;
19
import java.awt.*;
20
import java.awt.datatransfer.Clipboard;
21
import java.awt.datatransfer.StringSelection;
22
import java.io.File;
23
import java.io.Serial;
24
import java.text.NumberFormat;
25
import java.util.ArrayList;
26
import java.util.Arrays;
27
import java.util.HashSet;
28
import java.util.List;
29
import java.util.Set;
30
import java.util.concurrent.ExecutorService;
31
import java.util.concurrent.Executors;
32

33
public final class RecoveryPanel extends EasyPanel {
34
    @Serial
35
    private static final long serialVersionUID = 1L;
36

37
    private JPanel optionPanel;
38

39
    private FilePanel recoveryFilePanel;
40

41
    private JTabbedPane categoryTabbedPane;
42

43
    private JPanel bruteForceCategoryPanel;
44

45
    private JPanel dictionaryCategoryPanel;
46

47
    private FilePanel dictionaryFilePanel;
48

49
    private CategoryType currentCategoryType = CategoryType.UNKNOWN;
×
50

51
    private JPanel operationPanel;
52

53
    private JCheckBox numberCheckBox;
54
    private JCheckBox lowercaseLetterCheckBox;
55
    private JCheckBox uppercaseLetterCheckBox;
56

57
    private JCheckBox userIncludedCheckBox;
58

59
    private JTextField userIncludedTextField;
60

61
    private JCheckBox userExcludedCheckBox;
62

63
    private JTextField userExcludedTextField;
64

65
    private JSpinner minSpinner;
66
    private JSpinner maxSpinner;
67

68
    private JCheckBox isUseMultiThreadCheckBox;
69

70
    private JProgressBar progressBar;
71

72
    private JComboBox<FileChecker> checkerTypeComboBox;
73

74
    private transient FileChecker currentFileChecker;
75

76
    private JButton startButton;
77
    private JButton stopButton;
78

79
    private JLabel currentStateLabel;
80

81
    private JLabel currentPasswordLabel;
82

83
    private JLabel currentSpeedLabel;
84

85
    private int passwordTryCount = 0;
×
86

87
    private NumberFormat numberFormat;
88

89
    private State currentState = State.IDLE;
×
90

91
    private Timer timer;
92

93
    private final transient ExecutorService startExecutorService = Executors.newFixedThreadPool(1);
×
94

95
    private final transient ExecutorService stopExecutorService = Executors.newFixedThreadPool(1);
×
96

97
    public RecoveryPanel() {
98
        super();
×
99
        initBase();
×
100
    }
×
101

102
    private void initBase() {
103
        numberFormat = NumberFormat.getPercentInstance();
×
104
        numberFormat.setMinimumFractionDigits(3);
×
105
    }
×
106

107
    @Override
108
    public void initUI() {
109
        BoxLayout boxLayout = new BoxLayout(this, BoxLayout.Y_AXIS);
×
110
        setLayout(boxLayout);
×
111

112
        createOptionPanel();
×
113
        add(optionPanel);
×
114
        add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
×
115

116
        createOperationPanel();
×
117
        add(operationPanel);
×
118

119
        timer = new Timer(1000, e -> {
×
120
            if (currentState == State.WORKING) {
×
121
                int currentValue = progressBar.getValue();
×
122
                int speed = currentValue - passwordTryCount;
×
123
                passwordTryCount = currentValue;
×
124
                currentSpeedLabel.setText("Speed: " + speed + " passwords/s");
×
125
            }
126
        });
×
127
    }
×
128

129
    private void createOptionPanel() {
130
        optionPanel = new JPanel();
×
131
        optionPanel.setLayout(new BoxLayout(optionPanel, BoxLayout.Y_AXIS));
×
132

133
        checkerTypeComboBox = new JComboBox<>();
×
134
        checkerTypeComboBox.addItem(new ThirdParty7ZipChecker());
×
135
        checkerTypeComboBox.addItem(new ThirdPartyWinRarChecker());
×
136
        checkerTypeComboBox.addItem(new ThirdPartyRarChecker());
×
137
        checkerTypeComboBox.addItem(new ZipChecker());
×
138
        checkerTypeComboBox.addItem(new RarChecker());
×
139
        checkerTypeComboBox.addItem(new SevenZipChecker());
×
140
        checkerTypeComboBox.addItem(new PdfChecker());
×
141
        checkerTypeComboBox.addItem(new XmlBasedOfficeChecker());
×
142
        checkerTypeComboBox.addItem(new BinaryOfficeChecker());
×
143
        checkerTypeComboBox.setSelectedIndex(0);
×
144
        checkerTypeComboBox.addItemListener(e -> {
×
145
            FileChecker fileChecker = (FileChecker) e.getItem();
×
146
            if (fileChecker == null) {
×
147
                logger.error("fileChecker is null");
×
148
                return;
×
149
            }
150
            recoveryFilePanel.setDescriptionAndFileExtensions(
×
151
                    fileChecker.getFileDescription(), fileChecker.getFileExtensions());
×
152
        });
×
153

154
        FileChecker fileChecker = (FileChecker) checkerTypeComboBox.getSelectedItem();
×
155
        if (fileChecker == null) {
×
156
            logger.error("fileChecker is null");
×
157
            return;
×
158
        }
159

160
        recoveryFilePanel = new FilePanel("Choose Recovery File");
×
NEW
161
        recoveryFilePanel.initialize();
×
UNCOV
162
        recoveryFilePanel.setDescriptionAndFileExtensions(fileChecker.getFileDescription(), fileChecker.getFileExtensions());
×
163

164
        categoryTabbedPane = new JTabbedPane();
×
165

166
        createBruteForcePanel();
×
167
        categoryTabbedPane.addTab("Brute Force", null, bruteForceCategoryPanel, "Brute Force");
×
168
        categoryTabbedPane.setSelectedIndex(0);
×
169

170
        createDictionaryPanel();
×
171
        categoryTabbedPane.addTab("Dictionary", null, dictionaryCategoryPanel, "Dictionary");
×
172

173
        progressBar = new JProgressBar();
×
174
        progressBar.setStringPainted(true);
×
175
        String text = numberFormat.format(0);
×
176
        progressBar.setString(text);
×
177

178
        optionPanel.add(checkerTypeComboBox);
×
179
        optionPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
×
180
        optionPanel.add(recoveryFilePanel);
×
181
        optionPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
×
182
        optionPanel.add(categoryTabbedPane);
×
183
        optionPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
×
184
        optionPanel.add(progressBar);
×
185
    }
×
186

187
    private void createBruteForcePanel() {
188
        bruteForceCategoryPanel = new JPanel();
×
189

190
        JPanel topLevelPanel = new JPanel();
×
191
        topLevelPanel.setLayout(new BoxLayout(topLevelPanel, BoxLayout.Y_AXIS));
×
192
        bruteForceCategoryPanel.add(topLevelPanel);
×
193

194
        JPanel charsetPanel = new JPanel();
×
195
        charsetPanel.setLayout(new BoxLayout(charsetPanel, BoxLayout.X_AXIS));
×
196

197
        JPanel passwordLengthPanel = new JPanel();
×
198
        passwordLengthPanel.setLayout(new BoxLayout(passwordLengthPanel, BoxLayout.X_AXIS));
×
199

200
        topLevelPanel.add(charsetPanel);
×
201
        topLevelPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
×
202
        topLevelPanel.add(passwordLengthPanel);
×
203

204
        numberCheckBox = new JCheckBox("Number");
×
205
        numberCheckBox.setSelected(true);
×
206
        lowercaseLetterCheckBox = new JCheckBox("Lowercase Letter");
×
207
        uppercaseLetterCheckBox = new JCheckBox("Uppercase Letter");
×
208
        userIncludedCheckBox = new JCheckBox("User-defined");
×
209
        userIncludedTextField = new JTextField(10);
×
210
        userExcludedCheckBox = new JCheckBox("User-excluded");
×
211
        userExcludedTextField = new JTextField(10);
×
212

213

214
        charsetPanel.add(numberCheckBox);
×
215
        charsetPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
×
216
        charsetPanel.add(lowercaseLetterCheckBox);
×
217
        charsetPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
×
218
        charsetPanel.add(uppercaseLetterCheckBox);
×
219
        charsetPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
×
220
        charsetPanel.add(userIncludedCheckBox);
×
221
        charsetPanel.add(userIncludedTextField);
×
222
        charsetPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
×
223
        charsetPanel.add(userExcludedCheckBox);
×
224
        charsetPanel.add(userExcludedTextField);
×
225

226
        JLabel minLabel = new JLabel("Minimum Length: ");
×
227
        JLabel maxLabel = new JLabel("Maximum Length: ");
×
228

229
        minSpinner = new JSpinner();
×
230
        minSpinner.setModel(new SpinnerNumberModel(1, 1, 9, 1));
×
231
        minSpinner.setToolTipText("Minimum Length");
×
232

233
        maxSpinner = new JSpinner();
×
234
        maxSpinner.setModel(new SpinnerNumberModel(6, 1, 9, 1));
×
235
        maxSpinner.setToolTipText("Maximum Length");
×
236

237
        passwordLengthPanel.add(minLabel);
×
238
        passwordLengthPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
×
239
        passwordLengthPanel.add(minSpinner);
×
240
        passwordLengthPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
×
241
        passwordLengthPanel.add(maxLabel);
×
242
        passwordLengthPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
×
243
        passwordLengthPanel.add(maxSpinner);
×
244
    }
×
245

246
    private void createDictionaryPanel() {
247
        dictionaryCategoryPanel = new JPanel();
×
248

249
        JPanel topLevelPanel = new JPanel();
×
250
        topLevelPanel.setLayout(new BoxLayout(topLevelPanel, BoxLayout.Y_AXIS));
×
251
        dictionaryCategoryPanel.add(topLevelPanel);
×
252

253
        dictionaryFilePanel = new FilePanel("Choose Dictionary File");
×
NEW
254
        dictionaryFilePanel.initialize();
×
UNCOV
255
        dictionaryFilePanel.setDescriptionAndFileExtensions("*.dic;*.txt", new String[]{"dic", "txt"});
×
256

257
        JPanel threadPanel = new JPanel();
×
258
        threadPanel.setLayout(new BoxLayout(threadPanel, BoxLayout.X_AXIS));
×
259

260
        topLevelPanel.add(dictionaryFilePanel);
×
261
        topLevelPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
×
262
        topLevelPanel.add(threadPanel);
×
263

264
        isUseMultiThreadCheckBox = new JCheckBox("Use Multi-thread");
×
265
        isUseMultiThreadCheckBox.setSelected(true);
×
266

267
        threadPanel.add(isUseMultiThreadCheckBox);
×
268
    }
×
269

270
    private void createOperationPanel() {
271
        operationPanel = new JPanel();
×
272
        operationPanel.setLayout(new BoxLayout(operationPanel, BoxLayout.X_AXIS));
×
273

274
        startButton = new JButton("Start");
×
275
        stopButton = new JButton("Stop");
×
276
        currentStateLabel = new JLabel("State: IDLE");
×
277
        currentPasswordLabel = new JLabel("Trying: ");
×
278
        currentSpeedLabel = new JLabel("Speed: 0 passwords/s");
×
279

280
        operationPanel.add(startButton);
×
281
        operationPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
×
282
        operationPanel.add(stopButton);
×
283
        operationPanel.add(Box.createHorizontalStrut(2 * Constants.DEFAULT_X_BORDER));
×
284
        operationPanel.add(currentStateLabel);
×
285
        operationPanel.add(Box.createHorizontalStrut(2 * Constants.DEFAULT_X_BORDER));
×
286
        operationPanel.add(currentPasswordLabel);
×
287
        operationPanel.add(Box.createHorizontalStrut(2 * Constants.DEFAULT_X_BORDER));
×
288
        operationPanel.add(currentSpeedLabel);
×
289
        operationPanel.add(Box.createHorizontalGlue());
×
290

291
        startButton.addActionListener(e -> startExecutorService.submit(this::onStart));
×
292
        stopButton.addActionListener(e -> stopExecutorService.submit(this::onStop));
×
293

294
        setCurrentState(State.IDLE);
×
295
    }
×
296

297
    private void onStart() {
298
        FileChecker fileChecker = (FileChecker) checkerTypeComboBox.getSelectedItem();
×
299
        if (fileChecker == null) {
×
300
            JOptionPane.showMessageDialog(this, "fileChecker is null!");
×
301
            return;
×
302
        }
303
        if (!fileChecker.prepareChecker()) {
×
304
            JOptionPane.showMessageDialog(this, "onStart failed: Checker condition does not been prepared!");
×
305
            return;
×
306
        }
307

308
        File selectedFile = recoveryFilePanel.getFile();
×
309
        if (!selectedFile.isFile()) {
×
310
            JOptionPane.showMessageDialog(this, "file is not a file!");
×
311
            return;
×
312
        }
313

314
        String extension = FilenameUtils.getExtension(recoveryFilePanel.getFile().getName());
×
315
        if (!ArrayUtils.contains(fileChecker.getFileExtensions(), StringUtils.toRootLowerCase(extension))) {
×
316
            JOptionPane.showMessageDialog(this, "invalid file!");
×
317
            return;
×
318
        }
319

320
        fileChecker.attachFile(selectedFile);
×
321
        currentFileChecker = fileChecker;
×
322

323
        Component selectedPanel = categoryTabbedPane.getSelectedComponent();
×
324
        if (selectedPanel.equals(bruteForceCategoryPanel)) {
×
325
            currentCategoryType = CategoryType.BRUTE_FORCE;
×
326
        } else if (selectedPanel.equals(dictionaryCategoryPanel)) {
×
327
            if (isUseMultiThreadCheckBox.isSelected()) {
×
328
                currentCategoryType = CategoryType.DICTIONARY_MULTI_THREAD;
×
329
            } else {
330
                currentCategoryType = CategoryType.DICTIONARY_SINGLE_THREAD;
×
331
            }
332
        } else {
333
            currentCategoryType = CategoryType.UNKNOWN;
×
334
        }
335
        logger.info("onStart: {}", currentCategoryType);
×
336
        if (currentCategoryType == CategoryType.UNKNOWN) {
×
337
            JOptionPane.showMessageDialog(this, "onStart failed: Invalid category!");
×
338
            return;
×
339
        }
340
        setCurrentState(State.WORKING);
×
341
        passwordTryCount = 0;
×
342
        timer.start();
×
343
        ICategory category = CategoryFactory.getCategoryInstance(currentCategoryType);
×
344
        category.start(this);
×
345
        if (currentState == State.WORKING) {
×
346
            onStop();
×
347
        }
348
    }
×
349

350
    private void onStop() {
351
        logger.info("onStop: currentState: {}, currentCategoryType: {}", currentState, currentCategoryType);
×
352
        if (currentState != State.WORKING) {
×
353
            logger.error("onStop failed: Not in working state!");
×
354
            return;
×
355
        }
356
        if (currentCategoryType == CategoryType.UNKNOWN) {
×
357
            logger.error("onStop failed: Invalid category!");
×
358
            return;
×
359
        }
360
        timer.stop();
×
361
        setCurrentState(State.STOPPING);
×
362
        ICategory category = CategoryFactory.getCategoryInstance(currentCategoryType);
×
363
        category.cancel();
×
364
        setCurrentState(State.IDLE);
×
365
        currentCategoryType = CategoryType.UNKNOWN;
×
366
    }
×
367

368
    public void showResultWithDialog(String password) {
369
        if (password == null) {
×
370
            logger.error("Can not find password");
×
371
            JOptionPane.showMessageDialog(this, "Can not find password");
×
372
        } else {
373
            logger.info("Find out the password: {}", password);
×
374
            JPanel panel = createPasswordShowPanel(password);
×
375
            JOptionPane.showMessageDialog(this, panel, "Find out the password", JOptionPane.INFORMATION_MESSAGE);
×
376
        }
377
    }
×
378

379
    private static JPanel createPasswordShowPanel(String password) {
380
        JPanel panel = new JPanel();
×
381
        panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
×
382
        JTextField textField = new JTextField(password);
×
383
        textField.setEditable(false);
×
384
        textField.setColumns(20);
×
385
        JButton button = new JButton("Copy");
×
386
        button.addActionListener(e -> {
×
387
            StringSelection stringSelection = new StringSelection(password);
×
388
            Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
×
389
            clipboard.setContents(stringSelection, null);
×
390
        });
×
391
        panel.add(textField);
×
392
        panel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
×
393
        panel.add(button);
×
394
        return panel;
×
395
    }
396

397
    public void setCurrentState(State currentState) {
398
        SwingUtilities.invokeLater(() -> {
×
399
            this.currentState = currentState;
×
400
            if (currentStateLabel != null) {
×
401
                currentStateLabel.setText("State: " + currentState.toString());
×
402
            }
403
            if (currentState == State.WORKING) {
×
404
                setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
×
405
                startButton.setEnabled(false);
×
406
                stopButton.setEnabled(true);
×
407
                updateUiComponent(false);
×
408
            } else if (currentState == State.STOPPING) {
×
409
                setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
×
410
                startButton.setEnabled(false);
×
411
                stopButton.setEnabled(false);
×
412
                updateUiComponent(false);
×
413
            } else if (currentState == State.IDLE) {
×
414
                setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
×
415
                startButton.setEnabled(true);
×
416
                stopButton.setEnabled(false);
×
417
                updateUiComponent(true);
×
418
            }
419
        });
×
420
    }
×
421

422
    private void updateUiComponent(boolean enable) {
423
        for (Component component : getComponents(optionPanel)) {
×
424
            component.setEnabled(enable);
×
425
        }
426
    }
×
427

428
    private Component[] getComponents(Component container) {
429
        List<Component> list;
430

431
        try {
432
            list = new ArrayList<>(Arrays.asList(
×
433
                    ((Container) container).getComponents()));
×
434
            for (int index = 0; index < list.size(); index++) {
×
435
                list.addAll(Arrays.asList(getComponents(list.get(index))));
×
436
            }
437
        } catch (ClassCastException e) {
×
438
            list = new ArrayList<>();
×
439
        }
×
440

441
        return list.toArray(new Component[0]);
×
442
    }
443

444
    public State getCurrentState() {
445
        return currentState;
×
446
    }
447

448
    public void resetProgressMaxValue(int maxValue) {
449
        SwingUtilities.invokeLater(() -> {
×
450
            progressBar.setMaximum(maxValue);
×
451
            setProgressBarValue(0);
×
452
        });
×
453
    }
×
454

455
    public void increaseProgressBarValue() {
456
        SwingUtilities.invokeLater(() -> setProgressBarValue(progressBar.getValue() + 1));
×
457
    }
×
458

459
    private void setProgressBarValue(int value) {
460
        int properValue = Math.min(value, progressBar.getMaximum());
×
461
        progressBar.setValue(properValue);
×
462
        String text = numberFormat.format(((double) properValue) / progressBar.getMaximum());
×
463
        progressBar.setString(text);
×
464
    }
×
465

466
    public void setCurrentPassword(String password) {
467
        SwingUtilities.invokeLater(() -> currentPasswordLabel.setText("Trying: " + password));
×
468
    }
×
469

470
    public String getCharset() {
471
        Set<Character> charsetSet = new HashSet<>();
×
472
        if (numberCheckBox.isSelected()) {
×
473
            CollectionUtils.addAll(charsetSet, ArrayUtils.toObject("0123456789".toCharArray()));
×
474
        }
475
        if (lowercaseLetterCheckBox.isSelected()) {
×
476
            CollectionUtils.addAll(charsetSet, ArrayUtils.toObject("abcdefghijklmnopqrstuvwxyz".toCharArray()));
×
477
        }
478
        if (uppercaseLetterCheckBox.isSelected()) {
×
479
            CollectionUtils.addAll(charsetSet, ArrayUtils.toObject("ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray()));
×
480
        }
481
        if (userIncludedCheckBox.isSelected()) {
×
482
            CollectionUtils.addAll(charsetSet, ArrayUtils.toObject(userIncludedTextField.getText().toCharArray()));
×
483
        }
484
        if (userExcludedCheckBox.isSelected()) {
×
485
            for (char ch : userExcludedTextField.getText().toCharArray()) {
×
486
                charsetSet.remove(ch);
×
487
            }
488
        }
489
        return String.valueOf(ArrayUtils.toPrimitive(charsetSet.toArray(new Character[0])));
×
490
    }
491

492
    public File getDictionaryFile() {
493
        return dictionaryFilePanel.getFile();
×
494
    }
495

496
    public int getMinLength() {
497
        return (Integer) minSpinner.getValue();
×
498
    }
499

500
    public int getMaxLength() {
501
        return (Integer) maxSpinner.getValue();
×
502
    }
503

504
    public FileChecker getCurrentFileChecker() {
505
        return currentFileChecker;
×
506
    }
507
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc