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

3
import edu.jiangxin.apktoolbox.pdf.PdfUtils;
4
import edu.jiangxin.apktoolbox.swing.extend.EasyPanel;
5
import edu.jiangxin.apktoolbox.swing.extend.FileListPanel;
6
import edu.jiangxin.apktoolbox.swing.extend.filepanel.FilePanel;
7
import edu.jiangxin.apktoolbox.utils.Constants;
8
import edu.jiangxin.apktoolbox.utils.FileUtils;
9

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

24
public class PdfPasswordRemoverPanel extends EasyPanel {
×
25
    @Serial
26
    private static final long serialVersionUID = 1L;
27

28
    private FileListPanel fileListPanel;
29

30
    private JCheckBox isRecursiveSearched;
31

32
    private FilePanel targetDirPanel;
33

34
    private JButton startButton;
35
    private JButton cancelButton;
36

37
    private JProgressBar progressBar;
38

39
    private transient WorkderThread searchThread;
40

41
    @Override
42
    public void initUI() {
43
        BoxLayout boxLayout = new BoxLayout(this, BoxLayout.Y_AXIS);
×
44
        setLayout(boxLayout);
×
45

46
        createInputPanel();
×
47
        add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
×
48

49
        createOutputPanel();
×
50
        add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
×
51

52
        createOptionPanel();
×
53
        add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
×
54

55
        createOperationPanel();
×
56
        add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
×
57

58
        createProcessBarPanel();
×
59
    }
×
60

61
    private void createInputPanel() {
62
        fileListPanel = new FileListPanel();
×
NEW
63
        fileListPanel.initialize();
×
64
        add(fileListPanel);
×
65
    }
×
66

67
    private void createOutputPanel() {
68
        targetDirPanel = new FilePanel("Target Directory");
×
NEW
69
        targetDirPanel.initialize();
×
70
        targetDirPanel.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
×
71
        add(targetDirPanel);
×
72
    }
×
73

74
    private void createOptionPanel() {
75
        JPanel optionPanel = new JPanel();
×
76
        optionPanel.setLayout(new BoxLayout(optionPanel, BoxLayout.X_AXIS));
×
77
        optionPanel.setBorder(BorderFactory.createTitledBorder("Options"));
×
78

79
        isRecursiveSearched = new JCheckBox("Recursive Search");
×
80
        isRecursiveSearched.setSelected(true);
×
81
        optionPanel.add(isRecursiveSearched);
×
82
        optionPanel.add(Box.createHorizontalGlue());
×
83

84
        add(optionPanel);
×
85
    }
×
86

87
    private void createOperationPanel() {
88
        JPanel operationPanel = new JPanel();
×
89
        operationPanel.setLayout(new BoxLayout(operationPanel, BoxLayout.X_AXIS));
×
90
        operationPanel.setBorder(BorderFactory.createTitledBorder("Operations"));
×
91

92
        startButton = new JButton("Start");
×
93
        cancelButton = new JButton("Cancel");
×
94
        cancelButton.setEnabled(false);
×
95
        startButton.addActionListener(new OperationButtonActionListener());
×
96
        cancelButton.addActionListener(new OperationButtonActionListener());
×
97
        operationPanel.add(startButton);
×
98
        operationPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
×
99
        operationPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
×
100
        operationPanel.add(cancelButton);
×
101
        operationPanel.add(Box.createHorizontalGlue());
×
102

103
        add(operationPanel);
×
104
    }
×
105

106
    private void createProcessBarPanel() {
107
        progressBar = new JProgressBar();
×
108
        progressBar.setStringPainted(true);
×
109
        progressBar.setString("Ready");
×
110

111
        add(progressBar);
×
112
    }
×
113

114
    private void processFile(File encryptedFile, File targetDir) {
115
        try {
116
            if (Thread.currentThread().isInterrupted()) {
×
117
                return;
×
118
            }
119
            PdfUtils.removePasswordWithIText(encryptedFile, targetDir);
×
120
        } catch (Throwable t) {
×
121
            logger.error("Process file failed: {}", encryptedFile.getAbsolutePath(), t);
×
122
        }
×
123
    }
×
124

125
    class OperationButtonActionListener implements ActionListener {
×
126
        @Override
127
        public void actionPerformed(ActionEvent e) {
128
            Object source = e.getSource();
×
129
            if (source.equals(startButton)) {
×
130
                startButton.setEnabled(false);
×
131
                cancelButton.setEnabled(true);
×
132
                searchThread = new WorkderThread(isRecursiveSearched.isSelected());
×
133
                searchThread.start();
×
134
            } else if (source.equals(cancelButton)) {
×
135
                startButton.setEnabled(true);
×
136
                cancelButton.setEnabled(false);
×
137
                if (searchThread.isAlive()) {
×
138
                    searchThread.interrupt();
×
139
                    searchThread.executorService.shutdownNow();
×
140
                }
141
            }
142

143
        }
×
144
    }
145

146
    class WorkderThread extends Thread {
147
        public final ExecutorService executorService;
148
        private final AtomicInteger processedFiles = new AtomicInteger(0);
×
149
        private int totalFiles = 0;
×
150
        private final boolean isRecursiveSearched;
151

152
        public WorkderThread(boolean isRecursiveSearched) {
×
153
            super();
×
154
            this.isRecursiveSearched = isRecursiveSearched;
×
155
            this.executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
×
156

157
            SwingUtilities.invokeLater(() -> {
×
158
                progressBar.setValue(0);
×
159
                progressBar.setString("Starting remove process...");
×
160
            });
×
161
        }
×
162

163
        @Override
164
        public void run() {
165
            try {
166
                List<File> fileList = fileListPanel.getFileList();
×
167
                Set<File> fileSet = new TreeSet<>();
×
168
                String[] extensions = new String[]{"pdf", "PDF"};
×
169
                for (File file : fileList) {
×
170
                    fileSet.addAll(FileUtils.listFiles(file, extensions, isRecursiveSearched));
×
171
                }
×
172

173
                List<Future<?>> futures = new ArrayList<>();
×
174
                totalFiles = fileSet.size();
×
175
                updateProgress();
×
176

177
                for (File file : fileSet) {
×
178
                    if (currentThread().isInterrupted()) {
×
179
                        return;
×
180
                    }
181
                    futures.add(executorService.submit(() -> {
×
182
                        if (currentThread().isInterrupted()) {
×
183
                            return null;
×
184
                        }
185
                        processFile(file, targetDirPanel.getFile());
×
186
                        incrementProcessedFiles();
×
187
                        return null;
×
188
                    }));
189
                }
×
190

191
                // Wait for all tasks to complete
192
                for (Future<?> future : futures) {
×
193
                    try {
194
                        future.get();
×
195
                    } catch (InterruptedException e) {
×
196
                        logger.error("Remove process interrupted", e);
×
197
                        currentThread().interrupt(); // Restore interrupted status
×
198
                        return;
×
199
                    }
×
200
                }
×
201
            } catch (Exception e) {
×
202
                logger.error("Remove process failed", e);
×
203
                SwingUtilities.invokeLater(() -> progressBar.setString("Remove process failed"));
×
204
            } finally {
205
                executorService.shutdown();
×
206
                SwingUtilities.invokeLater(() -> {
×
207
                    startButton.setEnabled(true);
×
208
                    cancelButton.setEnabled(false);
×
209
                });
×
210
            }
211
        }
×
212

213
        private void incrementProcessedFiles() {
214
            processedFiles.incrementAndGet();
×
215
            updateProgress();
×
216
        }
×
217

218
        private void updateProgress() {
219
            if (totalFiles > 0) {
×
220
                SwingUtilities.invokeLater(() -> {
×
221
                    int processed = processedFiles.get();
×
222
                    int percentage = (int) (processed * 100.0 / totalFiles);
×
223
                    progressBar.setValue(percentage);
×
224
                    progressBar.setString(String.format("Processing: %d/%d files (%d%%)", processed, totalFiles, percentage));
×
225
                });
×
226
            }
227
        }
×
228
    }
229
}
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