33import javax .swing .*;
44import java .awt .*;
55import java .io .File ;
6+ import java .io .IOException ;
67
78import dev .skidfuscator .jvm .Jvm ;
89import dev .skidfuscator .obfuscator .gui .autosave .AutoSaveDocumentListener ;
910import dev .skidfuscator .obfuscator .gui .config .SkidfuscatorConfig ;
11+ import dev .skidfuscator .obfuscator .util .JdkDownloader ;
12+
1013import javax .swing .*;
1114import java .awt .*;
1215import java .io .File ;
1316import java .awt .event .WindowAdapter ;
1417import java .awt .event .WindowEvent ;
18+ import javax .swing .event .DocumentEvent ;
19+ import javax .swing .event .DocumentListener ;
1520
1621public class ConfigPanel extends JPanel {
1722 private final JTextField inputField ;
@@ -36,55 +41,230 @@ public ConfigPanel() {
3641 add (new JLabel ("Input JAR:" ), gbc );
3742 gbc .gridx = 1 ;
3843 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 );
3970 if (config .getLastInputPath () != null ) {
4071 inputField .setText (config .getLastInputPath ());
72+ inputListener .insertUpdate (null ); // Trigger initial validation
4173 }
4274 add (inputField , gbc );
4375 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 );
4583
4684 // Output file
4785 gbc .gridx = 0 ; gbc .gridy = 1 ;
4886 add (new JLabel ("Output JAR:" ), gbc );
4987 gbc .gridx = 1 ;
5088 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 );
51136 if (config .getLastOutputPath () != null ) {
52137 outputField .setText (config .getLastOutputPath ());
138+ outputListener .insertUpdate (null );
53139 } else if (config .getLastInputPath () != null ) {
54140 outputField .setText (config .getLastInputPath ().replace (".jar" , "-obf.jar" ));
141+ outputListener .insertUpdate (null );
55142 }
56143 add (outputField , gbc );
57144 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 );
59152
60153 // Libraries
61154 gbc .gridx = 0 ; gbc .gridy = 2 ;
62155 add (new JLabel ("Libraries:" ), gbc );
63156 gbc .gridx = 1 ;
64157 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 );
65181 if (config .getLastLibsPath () != null ) {
66182 libsField .setText (config .getLastLibsPath ());
183+ libsListener .insertUpdate (null );
67184 }
68185 add (libsField , gbc );
69186 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 );
71194
72195 // Runtime
73196 gbc .gridx = 0 ; gbc .gridy = 3 ;
74197 add (new JLabel ("Runtime:" ), gbc );
75198 gbc .gridx = 1 ;
76199 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+ }
83215 }
84216 }
217+
85218 add (runtimeField , gbc );
219+
220+ // Add download button next to browse button
86221 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 );
88268
89269 // Checkboxes
90270 JPanel checkboxPanel = new JPanel (new FlowLayout (FlowLayout .LEFT ));
0 commit comments