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

jiangxincode / ApkToolBoxGUI / #1137

23 Aug 2025 02:52AM UTC coverage: 2.94% (-0.01%) from 2.954%
#1137

push

jiangxincode
bugfix: cannot cancel immediately

0 of 47 new or added lines in 3 files covered. (0.0%)

2 existing lines in 2 files now uncovered.

236 of 8026 relevant lines covered (2.94%)

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/pdf/PdfFinderPanel.java
1
package edu.jiangxin.apktoolbox.pdf;
2

3
import edu.jiangxin.apktoolbox.swing.extend.EasyPanel;
4
import edu.jiangxin.apktoolbox.swing.extend.FileListPanel;
5
import edu.jiangxin.apktoolbox.utils.Constants;
6
import edu.jiangxin.apktoolbox.utils.DateUtils;
7
import edu.jiangxin.apktoolbox.utils.FileUtils;
8
import edu.jiangxin.apktoolbox.utils.RevealFileUtils;
9

10
import javax.swing.*;
11
import javax.swing.table.DefaultTableModel;
12
import java.awt.*;
13
import java.awt.event.*;
14
import java.io.File;
15
import java.io.Serial;
16
import java.util.*;
17
import java.util.List;
18
import java.util.concurrent.ExecutorService;
19
import java.util.concurrent.Executors;
20
import java.util.concurrent.Future;
21
import java.util.concurrent.atomic.AtomicInteger;
22

23
public class PdfFinderPanel extends EasyPanel {
×
24

25
    @Serial
26
    private static final long serialVersionUID = 1L;
27

28
    private JTabbedPane tabbedPane;
29

30
    private JPanel optionPanel;
31

32
    private FileListPanel fileListPanel;
33

34
    private JRadioButton scannedRadioButton;
35

36
    private JRadioButton encryptedRadioButton;
37

38
    private JRadioButton nonOutlineRadioButton;
39

40
    private JRadioButton hasAnnotationsRadioButton;
41

42
    private JSpinner thresholdSpinner;
43

44
    private JCheckBox isRecursiveSearched;
45

46
    private JPanel resultPanel;
47

48
    private JTable resultTable;
49

50
    private DefaultTableModel resultTableModel;
51

52
    private JButton searchButton;
53
    private JButton cancelButton;
54

55
    private JProgressBar progressBar;
56

57
    private JMenuItem openDirMenuItem;
58

59
    private SearchThread searchThread;
60

61
    final private List<File> scannedFileList = new ArrayList<>();
×
62

63

64
    @Override
65
    public void initUI() {
66
        tabbedPane = new JTabbedPane();
×
67
        add(tabbedPane);
×
68

69
        createOptionPanel();
×
70
        tabbedPane.addTab("Option", null, optionPanel, "Show Search Options");
×
71

72
        createResultPanel();
×
73
        tabbedPane.addTab("Result", null, resultPanel, "Show Search Result");
×
74
    }
×
75

76
    private void createOptionPanel() {
77
        optionPanel = new JPanel();
×
78
        optionPanel.setLayout(new BoxLayout(optionPanel, BoxLayout.Y_AXIS));
×
79

80
        fileListPanel = new FileListPanel();
×
81

82
        JPanel checkOptionPanel = new JPanel();
×
83
        checkOptionPanel.setLayout(new BoxLayout(checkOptionPanel, BoxLayout.X_AXIS));
×
84
        checkOptionPanel.setBorder(BorderFactory.createTitledBorder("Check Options"));
×
85

86
        ButtonGroup buttonGroup = new ButtonGroup();
×
87
        ItemListener itemListener = new RadioButtonItemListener();
×
88

89
        scannedRadioButton = new JRadioButton("查找扫描的PDF文件");
×
90
        scannedRadioButton.setSelected(true);
×
91
        scannedRadioButton.addItemListener(itemListener);
×
92
        buttonGroup.add(scannedRadioButton);
×
93

94
        encryptedRadioButton = new JRadioButton("查找加密的PDF文件");
×
95
        encryptedRadioButton.addItemListener(itemListener);
×
96
        buttonGroup.add(encryptedRadioButton);
×
97

98
        nonOutlineRadioButton = new JRadioButton("查找没有目录的PDF文件");
×
99
        nonOutlineRadioButton.addItemListener(itemListener);
×
100
        buttonGroup.add(nonOutlineRadioButton);
×
101

102
        hasAnnotationsRadioButton = new JRadioButton("查找有注释的PDF文件");
×
103
        hasAnnotationsRadioButton.addItemListener(itemListener);
×
104
        buttonGroup.add(hasAnnotationsRadioButton);
×
105

106
        JPanel typePanel = new JPanel();
×
107
        typePanel.setLayout(new FlowLayout(FlowLayout.LEFT,10,3));
×
108
        typePanel.add(scannedRadioButton);
×
109
        typePanel.add(encryptedRadioButton);
×
110
        typePanel.add(nonOutlineRadioButton);
×
111
        typePanel.add(hasAnnotationsRadioButton);
×
112

113
        JLabel thresholdLabel = new JLabel("Threshold: ");
×
114
        thresholdSpinner = new JSpinner();
×
115
        thresholdSpinner.setModel(new SpinnerNumberModel(1, 0, 100, 1));
×
116

117
        checkOptionPanel.add(typePanel);
×
118
        checkOptionPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
×
119
        checkOptionPanel.add(thresholdLabel);
×
120
        checkOptionPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
×
121
        checkOptionPanel.add(thresholdSpinner);
×
122
        checkOptionPanel.add(Box.createHorizontalGlue());
×
123

124
        JPanel searchOptionPanel = new JPanel();
×
125
        searchOptionPanel.setLayout(new BoxLayout(searchOptionPanel, BoxLayout.X_AXIS));
×
126
        searchOptionPanel.setBorder(BorderFactory.createTitledBorder("Search Options"));
×
127

128
        isRecursiveSearched = new JCheckBox("Recursive");
×
129
        isRecursiveSearched.setSelected(true);
×
130
        searchOptionPanel.add(isRecursiveSearched);
×
131
        searchOptionPanel.add(Box.createHorizontalGlue());
×
132

133
        JPanel operationPanel = new JPanel();
×
134
        operationPanel.setLayout(new BoxLayout(operationPanel, BoxLayout.X_AXIS));
×
135
        operationPanel.setBorder(BorderFactory.createTitledBorder("Operations"));
×
136

137
        JPanel buttonPanel = new JPanel();
×
138
        buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
×
139

140
        searchButton = new JButton("Search");
×
141
        cancelButton = new JButton("Cancel");
×
142
        cancelButton.setEnabled(false);
×
143
        searchButton.addActionListener(new OperationButtonActionListener());
×
144
        cancelButton.addActionListener(new OperationButtonActionListener());
×
145
        operationPanel.add(searchButton);
×
146
        operationPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
×
147
        operationPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
×
148
        operationPanel.add(cancelButton);
×
149
        operationPanel.add(Box.createHorizontalGlue());
×
150

151
        progressBar = new JProgressBar();
×
152
        progressBar.setStringPainted(true);
×
153
        progressBar.setString("Ready");
×
154

155
        optionPanel.add(fileListPanel);
×
156
        optionPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
×
157
        optionPanel.add(checkOptionPanel);
×
158
        optionPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
×
159
        optionPanel.add(searchOptionPanel);
×
160
        optionPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
×
161
        optionPanel.add(operationPanel);
×
162
        optionPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
×
163
        optionPanel.add(progressBar);
×
164
    }
×
165

166
    private void createResultPanel() {
167
        resultPanel = new JPanel();
×
168
        resultPanel.setLayout(new BoxLayout(resultPanel, BoxLayout.Y_AXIS));
×
169

170
        resultTableModel = new PdfFilesTableModel(new Vector<>(), PdfFilesConstants.COLUMN_NAMES);
×
171
        resultTable = new JTable(resultTableModel);
×
172

173
        resultTable.setDefaultRenderer(Vector.class, new PdfFilesTableCellRenderer());
×
174

175
        for (int i = 0; i < resultTable.getColumnCount(); i++) {
×
176
            resultTable.getColumn(resultTable.getColumnName(i)).setCellRenderer(new PdfFilesTableCellRenderer());
×
177
        }
178

179
        resultTable.addMouseListener(new MyMouseListener());
×
180

181
        resultTable.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
×
182

183
        JScrollPane scrollPane = new JScrollPane(resultTable);
×
184
        resultPanel.add(scrollPane);
×
185
    }
×
186

187
    private void processFile(File file) {
188
        if (scannedRadioButton.isSelected()) {
×
189
            int threshold = (Integer) thresholdSpinner.getValue();
×
190
            if (PdfUtils.isScannedPdf(file, threshold)) {
×
191
                scannedFileList.add(file);
×
192
            }
193
        } else if (encryptedRadioButton.isSelected()) {
×
194
            if (PdfUtils.isEncryptedPdf(file)) {
×
195
                scannedFileList.add(file);
×
196
            }
197
        } else if (nonOutlineRadioButton.isSelected()) {
×
198
            if (PdfUtils.isNonOutlinePdf(file)) {
×
199
                scannedFileList.add(file);
×
200
            }
201
        } else if (hasAnnotationsRadioButton.isSelected()) {
×
202
            if (PdfUtils.hasAnnotations(file)) {
×
203
                scannedFileList.add(file);
×
204
            }
205
        } else {
206
            logger.error("Invalid option selected");
×
207
        }
208
    }
×
209

210
    class MyMouseListener extends MouseAdapter {
×
211
        @Override
212
        public void mouseReleased(MouseEvent e) {
213
            super.mouseReleased(e);
×
214
            int r = resultTable.rowAtPoint(e.getPoint());
×
215
            if (r >= 0 && r < resultTable.getRowCount()) {
×
216
                resultTable.setRowSelectionInterval(r, r);
×
217
            } else {
218
                resultTable.clearSelection();
×
219
            }
220
            int rowIndex = resultTable.getSelectedRow();
×
221
            if (rowIndex < 0) {
×
222
                return;
×
223
            }
224
            if (e.isPopupTrigger() && e.getComponent() instanceof JTable) {
×
225
                JPopupMenu popupmenu = new JPopupMenu();
×
226
                MyMenuActionListener menuActionListener = new MyMenuActionListener();
×
227

228
                openDirMenuItem = new JMenuItem("Open parent folder of this file");
×
229
                openDirMenuItem.addActionListener(menuActionListener);
×
230
                popupmenu.add(openDirMenuItem);
×
231

232
                popupmenu.show(e.getComponent(), e.getX(), e.getY());
×
233
            }
234
        }
×
235
    }
236

237
    class MyMenuActionListener implements ActionListener {
×
238
        @Override
239
        public void actionPerformed(ActionEvent actionEvent) {
240
            Object source = actionEvent.getSource();
×
241
            if (source.equals(openDirMenuItem)) {
×
242
                onOpenDir();
×
243
            } else {
244
                logger.error("invalid source");
×
245
            }
246
        }
×
247

248
        private void onOpenDir() {
249
            int rowIndex = resultTable.getSelectedRow();
×
250
            String parentPath = resultTableModel.getValueAt(rowIndex, resultTable.getColumn(PdfFilesConstants.COLUMN_NAME_FILE_PARENT).getModelIndex()).toString();
×
251
            File parent = new File(parentPath);
×
NEW
252
            RevealFileUtils.revealDirectory(parent);
×
UNCOV
253
        }
×
254
    }
255

256
    class OperationButtonActionListener implements ActionListener {
×
257
        @Override
258
        public void actionPerformed(ActionEvent e) {
259
            Object source = e.getSource();
×
260
            if (source.equals(searchButton)) {
×
261
                searchButton.setEnabled(false);
×
262
                cancelButton.setEnabled(true);
×
263
                searchThread = new SearchThread(isRecursiveSearched.isSelected(), scannedFileList);
×
264
                searchThread.start();
×
265
            } else if (source.equals(cancelButton)) {
×
266
                searchButton.setEnabled(true);
×
267
                cancelButton.setEnabled(false);
×
268
                if (searchThread.isAlive()) {
×
269
                    searchThread.interrupt();
×
270
                    searchThread.executorService.shutdownNow();
×
271
                }
272
            }
273

274
        }
×
275
    }
276

277
    private void showResult() {
278
        SwingUtilities.invokeLater(() -> {
×
279
            int index = 0;
×
280
            for (File file : scannedFileList) {
×
281
                index++;
×
282
                Vector<Object> rowData = getRowVector(index, file);
×
283
                resultTableModel.addRow(rowData);
×
284
            }
×
285
            tabbedPane.setSelectedIndex(1);
×
286
        });
×
287
    }
×
288

289
    private Vector<Object> getRowVector(int index, File file) {
290
        Vector<Object> rowData = new Vector<>();
×
291
        rowData.add(index);
×
292
        rowData.add(file.getParent());
×
293
        rowData.add(file.getName());
×
294
        rowData.add(FileUtils.sizeOfInHumanFormat(file));
×
295
        rowData.add(DateUtils.millisecondToHumanFormat(file.lastModified()));
×
296
        return rowData;
×
297
    }
298

299
    class SearchThread extends Thread {
300
        public final ExecutorService executorService;
301
        private final AtomicInteger processedFiles = new AtomicInteger(0);
×
302
        private int totalFiles = 0;
×
303
        private final boolean isRecursiveSearched;
304
        private final List<File> scannedFileList;
305

306
        public SearchThread(boolean isRecursiveSearched, List<File> scannedFileList) {
×
307
            super();
×
308
            this.isRecursiveSearched = isRecursiveSearched;
×
309
            this.scannedFileList = scannedFileList;
×
310
            this.executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
×
311

312
            SwingUtilities.invokeLater(() -> {
×
313
                progressBar.setValue(0);
×
314
                progressBar.setString("Starting search...");
×
315
            });
×
316
        }
×
317

318
        @Override
319
        public void run() {
320
            try {
321
                scannedFileList.clear();
×
322
                SwingUtilities.invokeLater(() -> resultTableModel.setRowCount(0));
×
323

324
                List<File> fileList = fileListPanel.getFileList();
×
325
                Set<File> fileSet = new TreeSet<>();
×
326
                String[] extensions = new String[]{"pdf", "PDF"};
×
327
                for (File file : fileList) {
×
328
                    fileSet.addAll(org.apache.commons.io.FileUtils.listFiles(file, extensions, isRecursiveSearched));
×
329
                }
×
330

331
                List<Future<?>> futures = new ArrayList<>();
×
332
                totalFiles = fileSet.size();
×
333
                updateProgress();
×
334

335
                for (File file : fileSet) {
×
336
                    if (Thread.currentThread().isInterrupted()) {
×
337
                        return;
×
338
                    }
339
                    futures.add(executorService.submit(() -> {
×
340
                        if (Thread.currentThread().isInterrupted()) {
×
341
                            return null;
×
342
                        }
343
                        processFile(file);
×
344
                        incrementProcessedFiles();
×
345
                        return null;
×
346
                    }));
347
                }
×
348

349
                // Wait for all tasks to complete
350
                for (Future<?> future : futures) {
×
351
                    try {
352
                        future.get();
×
353
                    } catch (InterruptedException e) {
×
354
                        logger.error("Search interrupted", e);
×
355
                        Thread.currentThread().interrupt(); // Restore interrupted status
×
356
                        return;
×
357
                    }
×
358
                }
×
359

360
                showResult();
×
361
            } catch (Exception e) {
×
362
                logger.error("Search failed", e);
×
363
                SwingUtilities.invokeLater(() -> progressBar.setString("Search failed"));
×
364
            } finally {
365
                executorService.shutdown();
×
366
                SwingUtilities.invokeLater(() -> {
×
367
                    searchButton.setEnabled(true);
×
368
                    cancelButton.setEnabled(false);
×
369
                });
×
370
            }
371
        }
×
372

373
        private void incrementProcessedFiles() {
374
            processedFiles.incrementAndGet();
×
375
            updateProgress();
×
376
        }
×
377

378
        private void updateProgress() {
379
            if (totalFiles > 0) {
×
380
                SwingUtilities.invokeLater(() -> {
×
381
                    int processed = processedFiles.get();
×
382
                    int percentage = (int) ((processed * 100.0) / totalFiles);
×
383
                    progressBar.setValue(percentage);
×
384
                    progressBar.setString(String.format("Processing: %d/%d files (%d%%)", processed, totalFiles, percentage));
×
385
                });
×
386
            }
387
        }
×
388
    }
389

390
    class RadioButtonItemListener implements ItemListener {
×
391
        @Override
392
        public void itemStateChanged(ItemEvent e) {
393
            thresholdSpinner.setEnabled(scannedRadioButton.isSelected());
×
394
        }
×
395
    }
396
}
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