Skip to content

Commit 2b4c4b5

Browse files
Joe Wegnerfacchinm
Joe Wegner
authored andcommitted
Make UpdateNotification popup accessible
When accessible use buttons instead of links in in Updates Available dialog Handle buttons and prevent auto-close for accessible dialog box
1 parent 88bda6e commit 2b4c4b5

File tree

2 files changed

+184
-19
lines changed

2 files changed

+184
-19
lines changed

app/src/cc/arduino/contributions/ContributionsSelfCheck.java

Lines changed: 64 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,32 +29,34 @@
2929

3030
package cc.arduino.contributions;
3131

32+
import cc.arduino.UpdatableBoardsLibsFakeURLsHandler;
3233
import cc.arduino.contributions.libraries.LibraryInstaller;
3334
import cc.arduino.contributions.libraries.filters.UpdatableLibraryPredicate;
3435
import cc.arduino.contributions.packages.ContributionInstaller;
3536
import cc.arduino.contributions.packages.filters.UpdatablePlatformPredicate;
3637
import cc.arduino.view.NotificationPopup;
37-
import processing.app.Base;
38-
import processing.app.BaseNoGui;
39-
import processing.app.Editor;
40-
import processing.app.I18n;
38+
import org.apache.logging.log4j.LogManager;
39+
import processing.app.*;
4140

4241
import javax.swing.*;
4342
import javax.swing.event.HyperlinkListener;
4443

4544
import java.awt.event.WindowEvent;
4645
import java.awt.event.WindowFocusListener;
46+
import java.net.URL;
4747
import java.util.TimerTask;
4848

4949
import static processing.app.I18n.tr;
5050

51-
public class ContributionsSelfCheck extends TimerTask {
51+
public class ContributionsSelfCheck extends TimerTask implements NotificationPopup.OptionalButtonCallbacks {
5252

5353
private final Base base;
5454
private final HyperlinkListener hyperlinkListener;
5555
private final ContributionInstaller contributionInstaller;
5656
private final LibraryInstaller libraryInstaller;
5757
private final ProgressListener progressListener;
58+
private final String boardsManagerURL = "http://boardsmanager/DropdownUpdatableCoresItem";
59+
private final String libraryManagerURL = "http://librarymanager/DropdownUpdatableLibrariesItem";
5860

5961
private volatile boolean cancelled;
6062
private volatile NotificationPopup notificationPopup;
@@ -81,13 +83,41 @@ public void run() {
8183
return;
8284
}
8385

84-
String text;
86+
boolean setAccessible = PreferencesData.getBoolean("ide.accessible");
87+
final String text;
88+
final String button1Name;
89+
final String button2Name;
90+
String openAnchorBoards = "<a href=\"" + boardsManagerURL + "\">";
91+
String closeAnchorBoards = "</a>";
92+
String openAnchorLibraries = "<a href=\"" + libraryManagerURL + "\">";
93+
String closeAnchorLibraries = "</a>";
94+
95+
// if accessibility mode and board updates are available set the button name and clear the anchors
96+
if(setAccessible && updatablePlatforms) {
97+
button1Name = tr("Boards");
98+
openAnchorBoards = "";
99+
closeAnchorBoards = "";
100+
}
101+
else { // when not accessibility mode or no boards to update no button is needed
102+
button1Name = null;
103+
}
104+
105+
// if accessibility mode and libraries updates are available set the button name and clear the anchors
106+
if (setAccessible && updatableLibraries) {
107+
button2Name = tr("Libraries");
108+
openAnchorLibraries = "";
109+
closeAnchorLibraries = "";
110+
}
111+
else { // when not accessibility mode or no libraries to update no button is needed
112+
button2Name = null;
113+
}
114+
85115
if (updatableLibraries && !updatablePlatforms) {
86-
text = I18n.format(tr("Updates available for some of your {0}libraries{1}"), "<a href=\"http://librarymanager/DropdownUpdatableLibrariesItem\">", "</a>");
116+
text = I18n.format(tr("Updates available for some of your {0}libraries{1}"), openAnchorLibraries, closeAnchorLibraries);
87117
} else if (!updatableLibraries && updatablePlatforms) {
88-
text = I18n.format(tr("Updates available for some of your {0}boards{1}"), "<a href=\"http://boardsmanager/DropdownUpdatableCoresItem\">", "</a>");
118+
text = I18n.format(tr("Updates available for some of your {0}boards{1}"), openAnchorBoards, closeAnchorBoards);
89119
} else {
90-
text = I18n.format(tr("Updates available for some of your {0}boards{1} and {2}libraries{3}"), "<a href=\"http://boardsmanager/DropdownUpdatableCoresItem\">", "</a>", "<a href=\"http://librarymanager/DropdownUpdatableLibrariesItem\">", "</a>");
120+
text = I18n.format(tr("Updates available for some of your {0}libraries{1} and {2}libraries{3}"), openAnchorBoards, closeAnchorBoards, openAnchorLibraries, closeAnchorLibraries);
91121
}
92122

93123
if (cancelled) {
@@ -96,7 +126,13 @@ public void run() {
96126

97127
SwingUtilities.invokeLater(() -> {
98128
Editor ed = base.getActiveEditor();
99-
notificationPopup = new NotificationPopup(ed, hyperlinkListener, text);
129+
boolean accessibleIde = PreferencesData.getBoolean("ide.accessible");
130+
if (accessibleIde) {
131+
notificationPopup = new NotificationPopup(ed, hyperlinkListener, text, false, this, button1Name, button2Name);
132+
}
133+
else { // if not accessible view leave it the same
134+
notificationPopup = new NotificationPopup(ed, hyperlinkListener, text);
135+
}
100136
if (ed.isFocused()) {
101137
notificationPopup.begin();
102138
return;
@@ -122,6 +158,24 @@ public void windowGainedFocus(WindowEvent evt) {
122158
});
123159
}
124160

161+
private void goToManager(String link) {
162+
try {
163+
((UpdatableBoardsLibsFakeURLsHandler) hyperlinkListener).openBoardLibManager(new URL(link));
164+
}
165+
catch (Exception e){
166+
LogManager.getLogger(ContributionsSelfCheck.class).warn("Exception while attempting to go to board manager", e);
167+
}
168+
}
169+
// callback for boards button
170+
public void onOptionalButton1Callback() {
171+
goToManager(boardsManagerURL);
172+
}
173+
174+
// callback for libraries button
175+
public void onOptionalButton2Callback() {
176+
goToManager(libraryManagerURL);
177+
}
178+
125179
static boolean checkForUpdatablePlatforms() {
126180
return BaseNoGui.indexer.getPackages().stream()
127181
.flatMap(pack -> pack.getPlatforms().stream())

app/src/cc/arduino/view/NotificationPopup.java

Lines changed: 120 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,7 @@
3636
import java.awt.Frame;
3737
import java.awt.Image;
3838
import java.awt.Point;
39-
import java.awt.event.ComponentAdapter;
40-
import java.awt.event.ComponentEvent;
41-
import java.awt.event.MouseAdapter;
42-
import java.awt.event.MouseEvent;
43-
import java.awt.event.WindowAdapter;
44-
import java.awt.event.WindowEvent;
39+
import java.awt.event.*;
4540
import java.util.Timer;
4641
import java.util.TimerTask;
4742

@@ -55,22 +50,46 @@
5550
import javax.swing.event.HyperlinkListener;
5651

5752
import cc.arduino.Constants;
53+
import processing.app.PreferencesData;
5854
import processing.app.Theme;
5955

60-
public class NotificationPopup extends JDialog {
56+
import java.awt.event.KeyEvent;
57+
58+
import static processing.app.I18n.tr;
6159

60+
public class NotificationPopup extends JDialog {
6261
private Timer autoCloseTimer = new Timer(false);
6362
private boolean autoClose = true;
63+
private OptionalButtonCallbacks optionalButtonCallbacks;
64+
65+
public interface OptionalButtonCallbacks {
66+
void onOptionalButton1Callback();
67+
void onOptionalButton2Callback();
68+
}
6469

6570
public NotificationPopup(Frame parent, HyperlinkListener hyperlinkListener,
6671
String message) {
67-
this(parent, hyperlinkListener, message, true);
72+
this(parent, hyperlinkListener, message, true, null, null, null);
6873
}
6974

7075
public NotificationPopup(Frame parent, HyperlinkListener hyperlinkListener,
7176
String message, boolean _autoClose) {
77+
this(parent, hyperlinkListener, message, _autoClose, null, null, null);
78+
}
79+
80+
public NotificationPopup(Frame parent, HyperlinkListener hyperlinkListener,
81+
String message, boolean _autoClose, OptionalButtonCallbacks listener, String button1Name, String button2Name) {
7282
super(parent, false);
73-
autoClose = _autoClose;
83+
84+
if (!PreferencesData.getBoolean("ide.accessible")) {
85+
// often auto-close is too fast for users of screen readers, so don't allow it.
86+
autoClose = _autoClose;
87+
}
88+
else {
89+
autoClose = false;
90+
}
91+
optionalButtonCallbacks = listener;
92+
7493
setLayout(new FlowLayout());
7594
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
7695
setUndecorated(true);
@@ -90,13 +109,101 @@ public NotificationPopup(Frame parent, HyperlinkListener hyperlinkListener,
90109
text.addHyperlinkListener(hyperlinkListener);
91110
add(text);
92111

112+
if (button1Name != null) {
113+
JButton optionalButton1 = new JButton(tr(button1Name));
114+
MouseAdapter button1Action = new MouseAdapter() {
115+
@Override
116+
public void mouseClicked(MouseEvent e) {
117+
if (optionalButtonCallbacks != null) {
118+
optionalButtonCallbacks.onOptionalButton1Callback();
119+
}
120+
}
121+
};
122+
optionalButton1.addMouseListener(button1Action);
123+
124+
KeyListener button1Key = new KeyListener() {
125+
// Ignore when the key is typed - only act once the key is released
126+
public void keyTyped(KeyEvent e) {
127+
// do nothing here, wait until the key is released
128+
}
129+
130+
// Ignore when the key is pressed - only act once the key is released
131+
public void keyPressed(KeyEvent e) {
132+
// do nothing here, wait until the key is released
133+
}
134+
135+
public void keyReleased(KeyEvent e) {
136+
int key = e.getKeyCode();
137+
if ((key == KeyEvent.VK_ENTER) || (key == KeyEvent.VK_SPACE)) {
138+
optionalButtonCallbacks.onOptionalButton1Callback();
139+
}
140+
}
141+
};
142+
optionalButton1.addKeyListener(button1Key);
143+
add(optionalButton1);
144+
}
145+
146+
if (button2Name != null) {
147+
JButton optionalButton2 = new JButton(tr(button2Name));
148+
MouseAdapter button2Action = new MouseAdapter() {
149+
@Override
150+
public void mouseClicked(MouseEvent e) {
151+
if (optionalButtonCallbacks != null) {
152+
optionalButtonCallbacks.onOptionalButton2Callback();
153+
}
154+
}
155+
};
156+
optionalButton2.addMouseListener(button2Action);
157+
158+
KeyListener button2Key = new KeyListener() {
159+
// Ignore when the key is typed - only act once the key is released
160+
public void keyTyped(KeyEvent e) {
161+
// do nothing here, wait until the key is released
162+
}
163+
164+
// Ignore when the key is pressed - only act once the key is released
165+
public void keyPressed(KeyEvent e) {
166+
// do nothing here, wait until the key is released
167+
}
168+
169+
public void keyReleased(KeyEvent e) {
170+
int key = e.getKeyCode();
171+
if ((key == KeyEvent.VK_ENTER) || (key == KeyEvent.VK_SPACE)) {
172+
optionalButtonCallbacks.onOptionalButton2Callback();
173+
}
174+
}
175+
};
176+
optionalButton2.addKeyListener(button2Key);
177+
add(optionalButton2);
178+
}
179+
93180
Image close = Theme.getThemeImage("close", this, scale(22), scale(22));
94181
JButton closeButton = new JButton(new ImageIcon(close));
95182
closeButton.setBorder(null);
96183
closeButton.setBorderPainted(false);
97184
closeButton.setHideActionText(true);
98185
closeButton.setOpaque(false);
99186
closeButton.setBackground(new Color(0, 0, 0, 0));
187+
closeButton.getAccessibleContext().setAccessibleDescription(tr("Close"));
188+
KeyListener closeKey = new KeyListener() {
189+
// Ignore when the key is typed - only act once the key is released
190+
public void keyTyped(KeyEvent e) {
191+
// do nothing here, wait until the key is released
192+
}
193+
194+
// Ignore when the key is pressed - only act once the key is released
195+
public void keyPressed(KeyEvent e) {
196+
// do nothing here, wait until the key is released
197+
}
198+
199+
public void keyReleased(KeyEvent e) {
200+
int key = e.getKeyCode();
201+
if ((key == KeyEvent.VK_ENTER) || (key == KeyEvent.VK_SPACE)) {
202+
close();
203+
}
204+
}
205+
};
206+
closeButton.addKeyListener(closeKey);
100207
add(closeButton);
101208

102209
MouseAdapter closeOnClick = new MouseAdapter() {
@@ -158,5 +265,9 @@ public void run() {
158265
}, Constants.NOTIFICATION_POPUP_AUTOCLOSE_DELAY);
159266
}
160267
setVisible(true);
268+
if (PreferencesData.getBoolean("ide.accessible")) {
269+
requestFocus();
270+
setModal(true);
271+
}
161272
}
162273
}

0 commit comments

Comments
 (0)