Skip to content

Commit a1028ce

Browse files
committed
uses JEditorPane to display messages with embedded links, fixes #293
1 parent e992ff0 commit a1028ce

File tree

3 files changed

+141
-131
lines changed

3 files changed

+141
-131
lines changed

src/processing/mode/android/AndroidEditor.java

Lines changed: 16 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,6 @@
4242

4343
import java.awt.event.ActionEvent;
4444
import java.awt.event.ActionListener;
45-
import java.awt.event.MouseAdapter;
46-
import java.awt.event.MouseEvent;
4745
import java.io.File;
4846
import java.io.IOException;
4947
import java.util.TimerTask;
@@ -65,6 +63,16 @@ public class AndroidEditor extends JavaEditor {
6563
private JCheckBoxMenuItem watchfaceItem;
6664
private JCheckBoxMenuItem cardboardItem;
6765

66+
private static final String USB_DRIVER_TITLE = "USB Driver warning";
67+
private static final String USB_DRIVER_URL =
68+
"http://developer.android.com/sdk/win-usb.html";
69+
private static final String USB_DRIVER_MESSAGEA =
70+
"You might need to install Google USB Driver to run " +
71+
"the sketch on your device. Please follow <a href=\"" + USB_DRIVER_URL + "\">this guide</a> " +
72+
"to install the driver.<br>";
73+
private static final String USB_DRIVER_MESSAGEB =
74+
"<br>For your reference, the driver is located in:<br>";
75+
6876
protected AndroidEditor(Base base, String path, EditorState state,
6977
Mode mode) throws EditorException {
7078
super(base, path, state, mode);
@@ -641,56 +649,16 @@ public void run() {
641649
*/
642650
public void handleRunDevice() {
643651
if (Platform.isWindows() && !Preferences.getBoolean("android.warnings.usb_driver")) {
644-
Preferences.setBoolean("android.warnings.usb_driver", true);
645-
652+
Preferences.setBoolean("android.warnings.usb_driver", true);
646653
File sdkFolder = androidMode.getSDK().getSdkFolder();
647-
File usbDriverFile = new File(sdkFolder, "extras/google/usb_driver");
648-
JComponent[] text = null;
654+
String text = "";
655+
File usbDriverFile = new File(sdkFolder, "extras/google/usb_driver");
649656
if (usbDriverFile.exists()) {
650-
JLabel text1 = new JLabel("<html>You might need to install Google USB Driver to run<br>" +
651-
"the sketch on your device.</html>");
652-
JLabel text2 = new JLabel("<html>Please follow</html>");
653-
final String url1 = "http://developer.android.com/sdk/win-usb.html";
654-
String urlText1 = "<html><a href=\"" + url1 + "\">this guide</a></html>";
655-
JLabel link1 = new JLabel(urlText1);
656-
link1.setCursor(new java.awt.Cursor(java.awt.Cursor.HAND_CURSOR));
657-
link1.addMouseListener(new MouseAdapter() {
658-
public void mouseClicked(MouseEvent e) {
659-
Platform.openURL(url1);
660-
}
661-
});
662-
JLabel text3 = new JLabel("<html>to install the driver.</html>");
663-
JLabel text4 = new JLabel("<html>For your reference, the driver is located in:<br>" +
664-
"C:/blah/blah/blah/lala</body></html>");
665-
text = new JComponent[] { text1, text2, link1, text3, text4 };
657+
text = USB_DRIVER_MESSAGEA + USB_DRIVER_MESSAGEB + usbDriverFile.getAbsolutePath();
666658
} else {
667-
JLabel text1 = new JLabel("<html>You might need to install Google USB Driver to run the sketch<br>" +
668-
"on your device.</html>");
669-
JLabel text2 = new JLabel("<html>Please follow</html>");
670-
final String url1 = "http://developer.android.com/sdk/win-usb.html";
671-
String urlText1 = "<html><a href=\"" + url1 + "\">this guide</a></html>";
672-
JLabel link1 = new JLabel(urlText1);
673-
link1.setCursor(new java.awt.Cursor(java.awt.Cursor.HAND_CURSOR));
674-
link1.addMouseListener(new MouseAdapter() {
675-
public void mouseClicked(MouseEvent e) {
676-
Platform.openURL(url1);
677-
}
678-
});
679-
JLabel text3 = new JLabel("<html>to install the driver.</html>");
680-
JLabel text4 = new JLabel("<html>You will also need to download the driver from</html>");
681-
final String url2 = "https://dl-ssl.google.com/android/repository/latest_usb_driver_windows.zip";
682-
String urlText2 = "<html><a href=\"" + url2 + "\">here</a>.</html>";
683-
JLabel link2 = new JLabel(urlText2);
684-
link2.setCursor(new java.awt.Cursor(java.awt.Cursor.HAND_CURSOR));
685-
link2.addMouseListener(new MouseAdapter() {
686-
public void mouseClicked(MouseEvent e) {
687-
Platform.openURL(url2);
688-
}
689-
});
690-
text = new JComponent[] { text1, text2, link1, text3, text4, link2 };
659+
text = USB_DRIVER_MESSAGEA;
691660
}
692-
String title = "USB Driver warning";
693-
AndroidMode.showMessage(title, text, 400, 100);
661+
AndroidMode.showMessage(USB_DRIVER_TITLE, text);
694662
} else {
695663
new Thread() {
696664
public void run() {

src/processing/mode/android/AndroidMode.java

Lines changed: 51 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,6 @@
3636
import processing.mode.android.AndroidSDK.CancelException;
3737
import processing.mode.java.JavaMode;
3838

39-
import java.awt.Container;
40-
import java.awt.Dimension;
41-
import java.awt.FlowLayout;
42-
import java.awt.event.MouseAdapter;
43-
import java.awt.event.MouseEvent;
4439
import java.io.BufferedInputStream;
4540
import java.io.BufferedOutputStream;
4641
import java.io.File;
@@ -54,10 +49,11 @@
5449
import java.util.zip.ZipEntry;
5550
import java.util.zip.ZipFile;
5651

57-
import javax.swing.JComponent;
58-
import javax.swing.JFrame;
52+
import javax.swing.JEditorPane;
5953
import javax.swing.JLabel;
6054
import javax.swing.JOptionPane;
55+
import javax.swing.event.HyperlinkEvent;
56+
import javax.swing.event.HyperlinkListener;
6157

6258

6359
public class AndroidMode extends JavaMode {
@@ -71,6 +67,28 @@ public class AndroidMode extends JavaMode {
7167
private boolean checkingSDK = false;
7268
private boolean userCancelledSDKSearch = false;
7369

70+
private static final String BLUETOOTH_DEBUG_URL =
71+
"http://developer.android.com/training/wearables/apps/bt-debugging.html";
72+
73+
private static final String WATCHFACE_DEBUG_TITLE =
74+
"Is Debugging over Bluetooth enabled?";
75+
76+
private static final String WATCHFACE_DEBUG_MESSAGE =
77+
"Processing will access the smartwatch through the phone " +
78+
"currently paired to it. Your watch won't show up in the device list, " +
79+
"select the phone instead.<br><br>" +
80+
"Make sure to enable <a href=\"" + BLUETOOTH_DEBUG_URL + "\">debugging over bluetooth</a> " +
81+
"for this to work.";
82+
83+
private static final String WALLPAPER_INSTALL_TITLE =
84+
"Wallpaper installed!";
85+
86+
private static final String WALLPAPER_INSTALL_MESSAGE =
87+
"Processing just built and installed your sketch as a " +
88+
"live wallpaper on the selected device.<br><br>" +
89+
"You need to open the wallpaper picker in the device in order "+
90+
"to select it as the new background.";
91+
7492
public AndroidMode(Base base, File folder) {
7593
super(base, folder);
7694
}
@@ -326,38 +344,18 @@ public void handleRunDevice(Sketch sketch, AndroidEditor editor,
326344
showPostBuildMessage(build.getAppComponent());
327345
}
328346

347+
329348
public void showSelectComponentMessage(int appComp) {
330349
if (showBluetoothDebugMessage && appComp == AndroidBuild.WATCHFACE) {
331-
JLabel text1 = new JLabel("<html>Processing will access the watch through the phone<br>" +
332-
"paired to it. Your watch won't show up in the device list,<br>"+
333-
"select the paired phone.</html>");
334-
JLabel text2 = new JLabel("<html>Make sure to enable</html>");
335-
final String url = "http://developer.android.com/training/wearables/apps/bt-debugging.html";
336-
String urlText = "<html><a href=\"" + url + "\">debugging over bluetooth</a></html>";
337-
JLabel link = new JLabel(urlText);
338-
link.setCursor(new java.awt.Cursor(java.awt.Cursor.HAND_CURSOR));
339-
link.addMouseListener(new MouseAdapter() {
340-
public void mouseClicked(MouseEvent e) {
341-
Platform.openURL(url);
342-
}
343-
});
344-
JLabel text3 = new JLabel("<html>for this to work.</html>");
345-
JComponent[] text = new JComponent[] { text1, text2, link, text3 };
346-
String title = "Is Debugging over Bluetooth enabled?";
347-
showMessage(title, text, 400, 100);
350+
showMessage(WATCHFACE_DEBUG_TITLE, WATCHFACE_DEBUG_MESSAGE);
348351
showBluetoothDebugMessage = false;
349352
}
350353
}
351354

355+
352356
public void showPostBuildMessage(int appComp) {
353357
if (showWallpaperSelectMessage && appComp == AndroidBuild.WALLPAPER) {
354-
JLabel text1 = new JLabel("<html>Processing just built and installed your sketch as a<br>" +
355-
"live wallpaper on the selected device.<br><br>" +
356-
"You need to open the wallpaper selector in the device in order<br>"+
357-
"to set it as the new background.</html>");
358-
JComponent[] text = new JComponent[] { text1 };
359-
String title = "Wallpaper installed!";
360-
showMessage(title, text, 400, 100);
358+
showMessage(WALLPAPER_INSTALL_TITLE, WALLPAPER_INSTALL_MESSAGE);
361359
showWallpaperSelectMessage = false;
362360
}
363361
}
@@ -467,22 +465,30 @@ public static void extractFolder(File file, File newPath,
467465
zip.close();
468466
}
469467

470-
// Based on some ideas seen in this thread
471-
// http://stackoverflow.com/questions/527719/how-to-add-hyperlink-in-jlabel
472-
static public void showMessage(String title, JComponent[] text,
473-
int w, int h) {
468+
469+
static public void showMessage(String title, String text) {
474470
if (title == null) title = "Message";
475-
if (Base.isCommandLine()) {
476-
String concat = "";
477-
for (JComponent txt: text) concat += txt.toString();
478-
System.out.println(title + ": " + concat);
471+
if (Base.isCommandLine()) {
472+
System.out.println(title + ": " + text);
479473
} else {
480-
JFrame frame = new JFrame();
481-
Container outer = frame.getContentPane();
482-
outer.setLayout(new FlowLayout(FlowLayout.LEFT));
483-
for (JComponent txt: text) outer.add(txt);
484-
outer.setPreferredSize(new Dimension(w, h));
485-
JOptionPane.showMessageDialog(frame, outer, title,
474+
String htmlString = "<html> " +
475+
"<head> <style type=\"text/css\">"+
476+
"p { font: 11pt \"Lucida Grande\"; margin-top: 8px; width: 300px }"+
477+
"</style> </head>" +
478+
"<body> <p>" + text + "</p> </body> </html>";
479+
JEditorPane pane = new JEditorPane("text/html", htmlString);
480+
pane.addHyperlinkListener(new HyperlinkListener() {
481+
@Override
482+
public void hyperlinkUpdate(HyperlinkEvent e) {
483+
if (e.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) {
484+
Platform.openURL(e.getURL().toString());
485+
}
486+
}
487+
});
488+
pane.setEditable(false);
489+
JLabel label = new JLabel();
490+
pane.setBackground(label.getBackground());
491+
JOptionPane.showMessageDialog(null, pane, title,
486492
JOptionPane.INFORMATION_MESSAGE);
487493
}
488494
}

src/processing/mode/android/AndroidSDK.java

Lines changed: 74 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
import processing.core.PApplet;
3030

3131
import javax.swing.*;
32+
import javax.swing.event.HyperlinkEvent;
33+
import javax.swing.event.HyperlinkListener;
34+
3235
import java.awt.*;
3336
import java.io.BufferedReader;
3437
import java.io.File;
@@ -53,21 +56,36 @@ class AndroidSDK {
5356
private final File buildTools;
5457
private final File androidTool;
5558

56-
static final String DOWNLOAD_URL ="https://developer.android.com/studio/index.html#downloads";
59+
private static final String SDK_DOWNLOAD_URL =
60+
"https://developer.android.com/studio/index.html#downloads";
5761

58-
private static final String ANDROID_SDK_PRIMARY =
62+
private static final String MISSING_SDK_TITLE =
5963
"Is the Android SDK installed?";
60-
61-
private static final String ANDROID_SDK_SECONDARY =
62-
"The Android SDK does not appear to be installed, <br>" +
63-
"because the ANDROID_SDK variable is not set. <br>" +
64-
"If it is installed, click “Locate SDK path” to select the <br>" +
65-
"location of the SDK, or “Download SDK” to let <br>" +
64+
65+
private static final String MISSING_SDK_MESSAGE =
66+
"The Android SDK does not appear to be installed, " +
67+
"because the ANDROID_SDK variable is not set. " +
68+
"If it is installed, click “Locate SDK path” to select the" +
69+
"location of the SDK, or “Download SDK” to let " +
6670
"Processing download the SDK automatically.<br><br>" +
67-
"If you want to download the SDK manually, you can get <br>"+
68-
"the command line tools from <a href=\"" + DOWNLOAD_URL + "\">here</a>. Make sure to install<br>" +
69-
"the SDK platform for API " + AndroidBuild.target_sdk + ".";
71+
"If you want to download the SDK manually, you can get "+
72+
"the command line tools from <a href=\"" + SDK_DOWNLOAD_URL + "\">here</a>. " +
73+
"Make sure to install the SDK platform for API " + AndroidBuild.target_sdk + ".";
7074

75+
private static final String INVALID_SDK_TITLE =
76+
"Is the required Android API installed?";
77+
78+
private static final String INVALID_SDK_MESSAGE =
79+
"The Android SDK appears to be installed, " +
80+
"however the SDK platform for API " + AndroidBuild.target_sdk +
81+
" was not found. If it is available in a different location, " +
82+
"click “Locate SDK path” to select the " +
83+
"location of the alternative SDK, or “Download SDK” to let " +
84+
"Processing download the SDK automatically.<br><br>" +
85+
"If you want to download the SDK manually, you can get "+
86+
"the command line tools from <a href=\"" + SDK_DOWNLOAD_URL + "\">here</a>. " +
87+
"Make sure to install the SDK platform for API " + AndroidBuild.target_sdk + ".";
88+
7189
private static final String ANDROID_SYS_IMAGE_PRIMARY =
7290
"Download emulator?";
7391

@@ -86,6 +104,11 @@ class AndroidSDK {
86104

87105
private static final String SELECT_ANDROID_SDK_FOLDER =
88106
"Choose the location of the Android SDK";
107+
108+
private static final int NO_ERROR = 0;
109+
private static final int MISSING_SDK = 1;
110+
private static final int INVALID_SDK = 2;
111+
private static int SDK_LOAD_ERROR = NO_ERROR;
89112

90113
public AndroidSDK(File folder) throws BadSDKException, IOException {
91114
this.folder = folder;
@@ -263,6 +286,8 @@ private static File findAndroidTool(final File tools) throws BadSDKException {
263286
* @throws IOException
264287
*/
265288
public static AndroidSDK load() throws IOException {
289+
SDK_LOAD_ERROR = NO_ERROR;
290+
266291
// The environment variable is king. The preferences.txt entry is a page.
267292
final String sdkEnvPath = Platform.getenv("ANDROID_SDK");
268293
if (sdkEnvPath != null) {
@@ -289,7 +314,10 @@ public static AndroidSDK load() throws IOException {
289314
return androidSDK;
290315
} catch (final BadSDKException wellThatsThat) {
291316
Preferences.unset("android.sdk.path");
317+
SDK_LOAD_ERROR = INVALID_SDK;
292318
}
319+
} else {
320+
SDK_LOAD_ERROR = MISSING_SDK;
293321
}
294322
return null;
295323
}
@@ -359,36 +387,44 @@ static public boolean downloadSysImage(final Frame editor,
359387
return res;
360388
}
361389

390+
362391
static public int showLocateDialog(Frame editor) {
363-
// Pane formatting adapted from the Quaqua guide
364-
// http://www.randelshofer.ch/quaqua/guide/joptionpane.html
365-
JOptionPane pane =
366-
new JOptionPane("<html> " +
367-
"<head> <style type=\"text/css\">"+
368-
"b { font: 13pt \"Lucida Grande\" }"+
369-
"p { font: 11pt \"Lucida Grande\"; margin-top: 8px; width: 300px }"+
370-
"</style> </head>" +
371-
"<b>" + ANDROID_SDK_PRIMARY + "</b>" +
372-
"<p>" + ANDROID_SDK_SECONDARY + "</p>",
373-
JOptionPane.QUESTION_MESSAGE);
374-
392+
// How to show a option dialog containing clickable links:
393+
// http://stackoverflow.com/questions/8348063/clickable-links-in-joptionpane
394+
String htmlString = "<html> " +
395+
"<head> <style type=\"text/css\">"+
396+
"p { font: 11pt \"Lucida Grande\"; margin-top: 8px; width: 300px }"+
397+
"</style> </head>";
398+
String title = "";
399+
if (SDK_LOAD_ERROR == MISSING_SDK) {
400+
htmlString += "<body> <p>" + MISSING_SDK_MESSAGE + "</p> </body> </html>";
401+
title = MISSING_SDK_TITLE;
402+
} else if (SDK_LOAD_ERROR == INVALID_SDK) {
403+
htmlString += "<body> <p>" + INVALID_SDK_MESSAGE + "</p> </body> </html>";
404+
title = INVALID_SDK_TITLE;
405+
}
406+
JEditorPane pane = new JEditorPane("text/html", htmlString);
407+
pane.addHyperlinkListener(new HyperlinkListener() {
408+
@Override
409+
public void hyperlinkUpdate(HyperlinkEvent e) {
410+
if (e.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) {
411+
Platform.openURL(e.getURL().toString());
412+
}
413+
}
414+
});
415+
pane.setEditable(false);
416+
JLabel label = new JLabel();
417+
pane.setBackground(label.getBackground());
418+
375419
String[] options = new String[] {
376-
"Download SDK automatically", "Locate SDK path manually"
377-
};
378-
pane.setOptions(options);
379-
380-
// highlight the safest option ala apple hig
381-
pane.setInitialValue(options[0]);
382-
383-
JDialog dialog = pane.createDialog(editor, null);
384-
dialog.setTitle("");
385-
dialog.setModalityType(Dialog.ModalityType.APPLICATION_MODAL);
386-
dialog.setVisible(true);
387-
388-
Object result = pane.getValue();
389-
if (result == options[0]) {
420+
"Download SDK automatically", "Locate SDK path manually"
421+
};
422+
int result = JOptionPane.showOptionDialog(null, pane, title,
423+
JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE,
424+
null, options, options[0]);
425+
if (result == JOptionPane.YES_OPTION) {
390426
return JOptionPane.YES_OPTION;
391-
} else if (result == options[1]) {
427+
} else if (result == JOptionPane.NO_OPTION) {
392428
return JOptionPane.NO_OPTION;
393429
} else {
394430
return JOptionPane.CLOSED_OPTION;

0 commit comments

Comments
 (0)