3
3
import javax .swing .*;
4
4
import java .awt .*;
5
5
import java .io .File ;
6
+ import java .io .IOException ;
6
7
7
8
import dev .skidfuscator .jvm .Jvm ;
8
9
import dev .skidfuscator .obfuscator .gui .autosave .AutoSaveDocumentListener ;
9
10
import dev .skidfuscator .obfuscator .gui .config .SkidfuscatorConfig ;
11
+ import dev .skidfuscator .obfuscator .util .JdkDownloader ;
12
+
10
13
import javax .swing .*;
11
14
import java .awt .*;
12
15
import java .io .File ;
13
16
import java .awt .event .WindowAdapter ;
14
17
import java .awt .event .WindowEvent ;
18
+ import javax .swing .event .DocumentEvent ;
19
+ import javax .swing .event .DocumentListener ;
15
20
16
21
public class ConfigPanel extends JPanel {
17
22
private final JTextField inputField ;
@@ -36,55 +41,230 @@ public ConfigPanel() {
36
41
add (new JLabel ("Input JAR:" ), gbc );
37
42
gbc .gridx = 1 ;
38
43
inputField = new JTextField (30 );
44
+ JLabel inputCheck = new JLabel ("✗" );
45
+ inputCheck .setForeground (new Color (255 , 65 , 54 ));
46
+ DocumentListener inputListener = new DocumentListener () {
47
+ private void updateCheck () {
48
+ boolean valid = new File (inputField .getText ()).exists ();
49
+ inputCheck .setText (valid ? "✓" : "✗" );
50
+ inputCheck .setForeground (valid ? new Color (46 , 204 , 64 ) : new Color (255 , 65 , 54 ));
51
+
52
+ if (!valid ) {
53
+ StringBuilder tooltip = new StringBuilder ("<html><body style='width: 250px; padding: 3px; background-color: #FFF3CD; border: 2px solid #FFE69C; border-radius: 4px'>" );
54
+ tooltip .append ("<div style='color: #856404; font-weight: bold; margin-bottom: 5px'>⚠ Warning: Invalid Input Configuration</div>" );
55
+ tooltip .append ("<div style='color: #664D03; margin: 3px 0'>Input file does not exist</div>" );
56
+ tooltip .append ("</body></html>" );
57
+
58
+ ToolTipManager .sharedInstance ().setInitialDelay (0 );
59
+ ToolTipManager .sharedInstance ().setDismissDelay (10000 );
60
+ inputField .setToolTipText (tooltip .toString ());
61
+ } else {
62
+ inputField .setToolTipText (null );
63
+ }
64
+ }
65
+ public void insertUpdate (DocumentEvent e ) { updateCheck (); }
66
+ public void removeUpdate (DocumentEvent e ) { updateCheck (); }
67
+ public void changedUpdate (DocumentEvent e ) { updateCheck (); }
68
+ };
69
+ inputField .getDocument ().addDocumentListener (inputListener );
39
70
if (config .getLastInputPath () != null ) {
40
71
inputField .setText (config .getLastInputPath ());
72
+ inputListener .insertUpdate (null ); // Trigger initial validation
41
73
}
42
74
add (inputField , gbc );
43
75
gbc .gridx = 2 ;
44
- add (createBrowseButton (inputField , false ), gbc );
76
+ JPanel inputButtonPanel = new JPanel (new FlowLayout (FlowLayout .LEFT , 5 , 0 ));
77
+ inputButtonPanel .setPreferredSize (new Dimension (150 , 30 ));
78
+ JButton inputBrowseButton = createBrowseButton (inputField , false );
79
+ inputButtonPanel .add (inputBrowseButton );
80
+ inputButtonPanel .add (Box .createHorizontalGlue ()); // Push label to right
81
+ inputButtonPanel .add (inputCheck );
82
+ add (inputButtonPanel , gbc );
45
83
46
84
// Output file
47
85
gbc .gridx = 0 ; gbc .gridy = 1 ;
48
86
add (new JLabel ("Output JAR:" ), gbc );
49
87
gbc .gridx = 1 ;
50
88
outputField = new JTextField (30 );
89
+ JLabel outputCheck = new JLabel ("✗" );
90
+ outputCheck .setForeground (new Color (255 , 65 , 54 ));
91
+ DocumentListener outputListener = new DocumentListener () {
92
+ private void updateCheck () {
93
+ final String output = outputField .getText ();
94
+ File parent = new File (output ).getParentFile ();
95
+
96
+ // [condition] must be valid file extension
97
+ final boolean validEnd = output .endsWith (".jar" )
98
+ || output .endsWith (".apk" )
99
+ || output .endsWith (".dex" );
100
+
101
+ // [condition] must not be input
102
+ final boolean validInput = !inputField .getText ().equals (output );
103
+
104
+ boolean valid = parent != null && parent .exists () && validEnd && validInput ;
105
+ outputCheck .setText (valid ? "✓" : "✗" );
106
+ outputCheck .setForeground (valid ? new Color (46 , 204 , 64 ) : new Color (255 , 65 , 54 ));
107
+ // Set tooltip explaining validation failure
108
+ StringBuilder tooltip = new StringBuilder ("<html><body style='width: 250px; padding: 3px; background-color: #FFF3CD; border: 2px solid #FFE69C; border-radius: 4px'>" );
109
+ tooltip .append ("<div style='color: #856404; font-weight: bold; margin-bottom: 5px'>⚠ Warning: Invalid Output Configuration</div>" );
110
+
111
+ if (parent == null || !parent .exists ()) {
112
+ tooltip .append ("<div style='color: #664D03; margin: 3px 0'>Output directory does not exist</div>" );
113
+ }
114
+ if (!validEnd ) {
115
+ tooltip .append ("<div style='color: #664D03; margin: 3px 0'>File must end with .jar, .apk or .dex</div>" );
116
+ }
117
+ if (!validInput ) {
118
+ tooltip .append ("<div style='color: #664D03; margin: 3px 0'>Output file cannot be the same as input file</div>" );
119
+ }
120
+ tooltip .append ("</body></html>" );
121
+
122
+ if (!valid ) {
123
+ ToolTipManager .sharedInstance ().setInitialDelay (0 );
124
+ ToolTipManager .sharedInstance ().setDismissDelay (10000 );
125
+
126
+ outputField .setToolTipText (tooltip .toString ());
127
+ } else {
128
+ outputCheck .setToolTipText (null );
129
+ }
130
+ }
131
+ public void insertUpdate (DocumentEvent e ) { updateCheck (); }
132
+ public void removeUpdate (DocumentEvent e ) { updateCheck (); }
133
+ public void changedUpdate (DocumentEvent e ) { updateCheck (); }
134
+ };
135
+ outputField .getDocument ().addDocumentListener (outputListener );
51
136
if (config .getLastOutputPath () != null ) {
52
137
outputField .setText (config .getLastOutputPath ());
138
+ outputListener .insertUpdate (null );
53
139
} else if (config .getLastInputPath () != null ) {
54
140
outputField .setText (config .getLastInputPath ().replace (".jar" , "-obf.jar" ));
141
+ outputListener .insertUpdate (null );
55
142
}
56
143
add (outputField , gbc );
57
144
gbc .gridx = 2 ;
58
- add (createBrowseButton (outputField , false ), gbc );
145
+ JPanel outputButtonPanel = new JPanel (new FlowLayout (FlowLayout .LEFT , 5 , 0 ));
146
+ outputButtonPanel .setPreferredSize (new Dimension (150 , 30 ));
147
+ JButton outputBrowseButton = createBrowseButton (outputField , false );
148
+ outputButtonPanel .add (outputBrowseButton );
149
+ outputButtonPanel .add (Box .createHorizontalGlue ());
150
+ outputButtonPanel .add (outputCheck );
151
+ add (outputButtonPanel , gbc );
59
152
60
153
// Libraries
61
154
gbc .gridx = 0 ; gbc .gridy = 2 ;
62
155
add (new JLabel ("Libraries:" ), gbc );
63
156
gbc .gridx = 1 ;
64
157
libsField = new JTextField (30 );
158
+ JLabel libsCheck = new JLabel ("✗" );
159
+ libsCheck .setForeground (new Color (255 , 65 , 54 ));
160
+ DocumentListener libsListener = new DocumentListener () {
161
+ private void updateCheck () {
162
+ boolean valid ;
163
+ if (libsField .getText ().isEmpty ()) {
164
+ valid = true ; // Empty is valid
165
+ // Set orange dot for pending state
166
+ libsCheck .setText ("●" );
167
+ libsCheck .setForeground (new Color (255 , 140 , 0 )); // Orange color
168
+ return ;
169
+ } else {
170
+ File dir = new File (libsField .getText ());
171
+ valid = dir .exists () && dir .isDirectory ();
172
+ }
173
+ libsCheck .setText (valid ? "✓" : "✗" );
174
+ libsCheck .setForeground (valid ? new Color (46 , 204 , 64 ) : new Color (255 , 65 , 54 ));
175
+ }
176
+ public void insertUpdate (DocumentEvent e ) { updateCheck (); }
177
+ public void removeUpdate (DocumentEvent e ) { updateCheck (); }
178
+ public void changedUpdate (DocumentEvent e ) { updateCheck (); }
179
+ };
180
+ libsField .getDocument ().addDocumentListener (libsListener );
65
181
if (config .getLastLibsPath () != null ) {
66
182
libsField .setText (config .getLastLibsPath ());
183
+ libsListener .insertUpdate (null );
67
184
}
68
185
add (libsField , gbc );
69
186
gbc .gridx = 2 ;
70
- add (createBrowseButton (libsField , true ), gbc );
187
+ JPanel libsButtonPanel = new JPanel (new FlowLayout (FlowLayout .LEFT , 5 , 0 ));
188
+ libsButtonPanel .setPreferredSize (new Dimension (150 , 30 ));
189
+ JButton libsBrowseButton = createBrowseButton (libsField , true );
190
+ libsButtonPanel .add (libsBrowseButton );
191
+ libsButtonPanel .add (Box .createHorizontalGlue ());
192
+ libsButtonPanel .add (libsCheck );
193
+ add (libsButtonPanel , gbc );
71
194
72
195
// Runtime
73
196
gbc .gridx = 0 ; gbc .gridy = 3 ;
74
197
add (new JLabel ("Runtime:" ), gbc );
75
198
gbc .gridx = 1 ;
76
199
runtimeField = new JTextField (30 );
77
- if (config .getLastRuntimePath () != null ) {
78
- if (config .getLastRuntimePath ().isEmpty ()) {
79
- runtimeField .setText (Jvm .getLibsPath ());
80
- runtimeField .setEnabled (false );
81
- } else {
82
- runtimeField .setText (config .getLastRuntimePath ());
200
+
201
+ // Check if JDK was previously downloaded
202
+ try {
203
+ String jmodPath = JdkDownloader .getCachedJmodPath ();
204
+ runtimeField .setText (jmodPath );
205
+ runtimeField .setEnabled (!JdkDownloader .isJdkDownloaded ());
206
+ } catch (IOException e ) {
207
+ // Fallback to config
208
+ if (config .getLastRuntimePath () != null ) {
209
+ if (config .getLastRuntimePath ().isEmpty ()) {
210
+ runtimeField .setText (Jvm .getLibsPath ());
211
+ runtimeField .setEnabled (false );
212
+ } else {
213
+ runtimeField .setText (config .getLastRuntimePath ());
214
+ }
83
215
}
84
216
}
217
+
85
218
add (runtimeField , gbc );
219
+
220
+ // Add download button next to browse button
86
221
gbc .gridx = 2 ;
87
- add (createBrowseButton (runtimeField , false ), gbc );
222
+ JPanel runtimeButtonPanel = new JPanel (new FlowLayout (FlowLayout .LEFT , 5 , 0 ));
223
+ JButton downloadButton = new JButton ("Download JDK" );
224
+
225
+ // Set initial button state based on JDK download status
226
+ if (JdkDownloader .isJdkDownloaded ()) {
227
+ downloadButton .setText ("Downloaded" );
228
+ downloadButton .setEnabled (false );
229
+ }
230
+
231
+ downloadButton .addActionListener (e -> {
232
+ downloadButton .setEnabled (false );
233
+ downloadButton .setText ("Downloading..." );
234
+
235
+ SwingWorker <String , Void > worker = new SwingWorker <>() {
236
+ @ Override
237
+ protected String doInBackground () throws Exception {
238
+ return JdkDownloader .getJmodPath ();
239
+ }
240
+
241
+ @ Override
242
+ protected void done () {
243
+ try {
244
+ String path = get ();
245
+ runtimeField .setText (path );
246
+ runtimeField .setEnabled (false );
247
+ downloadButton .setText ("Downloaded" );
248
+ downloadButton .setEnabled (false );
249
+ } catch (Exception ex ) {
250
+ JOptionPane .showMessageDialog (
251
+ ConfigPanel .this ,
252
+ "Failed to download JDK: " + ex .getMessage (),
253
+ "Download Error" ,
254
+ JOptionPane .ERROR_MESSAGE
255
+ );
256
+ downloadButton .setText ("Download JDK" );
257
+ downloadButton .setEnabled (true );
258
+ }
259
+ }
260
+ };
261
+ worker .execute ();
262
+ });
263
+ runtimeButtonPanel .add (downloadButton );
264
+
265
+ // removing for now
266
+ //runtimeButtonPanel.add(createBrowseButton(runtimeField, false));
267
+ add (runtimeButtonPanel , gbc );
88
268
89
269
// Checkboxes
90
270
JPanel checkboxPanel = new JPanel (new FlowLayout (FlowLayout .LEFT ));
0 commit comments