org.bidib.wizard.mvc.main.controller.AlertController Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bidibwizard-client Show documentation
Show all versions of bidibwizard-client Show documentation
jBiDiB BiDiB Wizard Client Application POM
package org.bidib.wizard.mvc.main.controller;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.GradientPaint;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.net.InetAddress;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections4.collection.SynchronizedCollection;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;
import org.bidib.api.json.types.SerialPortInfo;
import org.bidib.wizard.api.locale.Resources;
import org.bidib.wizard.api.model.common.NetBidibServiceInfo;
import org.bidib.wizard.api.model.common.PreferencesPortType;
import org.bidib.wizard.api.model.common.PreferencesPortType.ConnectionPortType;
import org.bidib.wizard.api.model.event.ConsoleMessageEvent;
import org.bidib.wizard.api.service.console.ConsoleColor;
import org.bidib.wizard.client.common.alert.BidibAlert;
import org.bidib.wizard.client.common.event.BidibConnectionEvent;
import org.bidib.wizard.client.common.event.BidibConnectionEvent.Action;
import org.bidib.wizard.client.common.uils.SwingUtils;
import org.bidib.wizard.client.common.view.statusbar.StatusBar;
import org.bidib.wizard.common.model.settings.WizardSettingsInterface;
import org.bidib.wizard.common.service.SettingsService;
import org.bidib.wizard.common.utils.ImageUtils;
import org.bidib.wizard.core.model.connection.ConnectionRegistry;
import org.bidib.wizard.core.service.SystemInfoService;
import org.bidib.wizard.discovery.listener.NetBidibServiceListener;
import org.bidib.wizard.mvc.main.controller.listener.AlertListener;
import org.bidib.wizard.mvc.main.controller.listener.AlertListener.AlertAction;
import org.bidib.wizard.mvc.main.view.MainView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import com.jidesoft.alert.Alert;
import com.jidesoft.alert.AlertGroup;
import com.jidesoft.animation.CustomAnimation;
import com.jidesoft.plaf.UIDefaultsLookup;
import com.jidesoft.swing.JideButton;
import com.jidesoft.swing.JideSwingUtilities;
import com.jidesoft.swing.PaintPanel;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
/**
* The {@code AlertController} shows alerts on the main frame.
*/
public class AlertController {
private static final Logger LOGGER = LoggerFactory.getLogger(AlertController.class);
@Autowired
private SettingsService settingsService;
@Autowired
private SystemInfoService systemInfoService;
@Autowired
private StatusBar statusBar;
// this will inject all instances of type NetBidibServiceListener
@Autowired
private List netBidibServiceListeners;
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
private final BidibAlertGroup alertGroup;
private CompositeDisposable dispUsbPortEvents = new CompositeDisposable();
private CompositeDisposable dispNetBidibServiceListenerEvents = new CompositeDisposable();
private final MainView mainView;
private final static class BidibAlertGroup extends AlertGroup {
private Collection listAlerts = SynchronizedCollection.synchronizedCollection(new LinkedList());
@Override
public void add(Alert alert) {
LOGGER.info("Add alert to group: {}", alert);
try {
super.add(alert);
listAlerts.add(alert);
}
catch (Exception ex) {
LOGGER.warn("Add alert to group failed.", ex);
}
}
@Override
public void remove(Alert alert) {
LOGGER.info("Remove alert from group: {}", alert);
try {
super.remove(alert);
listAlerts.remove(alert);
}
catch (Exception ex) {
LOGGER.warn("Remove alert from group failed.", ex);
}
}
public Collection getAlerts() {
return Collections.unmodifiableCollection(listAlerts);
}
}
public AlertController(final MainView mainView) {
this.mainView = mainView;
this.alertGroup = new BidibAlertGroup();
}
public void start() {
final Disposable disposable = systemInfoService.subscribeUsbPortEvents(upe -> {
LOGGER.info("Publish the USB port event: {}", upe);
final SerialPortInfo serialPort = upe.getSerialPort();
switch (upe.getAction()) {
case INSERTED:
usbDeviceAdded(serialPort);
break;
default:
usbDeviceRemoved(serialPort);
break;
}
}, error -> {
LOGGER.warn("The USB port event signalled a failure: {}", error);
});
dispUsbPortEvents.add(disposable);
// get the current USB devices from the systemInfoService
List registeredPorts = this.systemInfoService.getRegisteredSerialPorts();
for (SerialPortInfo port : registeredPorts) {
usbDeviceAdded(port);
}
if (CollectionUtils.isNotEmpty(netBidibServiceListeners)) {
for (NetBidibServiceListener listener : this.netBidibServiceListeners) {
Disposable disp = listener.subscribeNetBidibServiceEvents(sie -> {
LOGGER.info("Received netBidibServiceEvent: {}", sie);
try {
switch (sie.getAction()) {
case RESOLVED:
netBidibServiceResolved(sie);
break;
case REMOVED:
netBidibServiceRemoved(sie);
break;
default:
LOGGER.warn("Unhandled action in event: {}", sie);
break;
}
}
catch (Exception ex) {
LOGGER.warn("Publish changed netBidib service info event failed.", ex);
}
}, error -> {
LOGGER.warn("The netBidibServiceListener event signalled a failure: {}", error);
});
dispNetBidibServiceListenerEvents.add(disp);
}
}
}
public void stopWatcher() {
dispUsbPortEvents.dispose();
dispNetBidibServiceListenerEvents.dispose();
}
private void netBidibServiceResolved(final NetBidibServiceInfo sie) {
final StringBuilder sb = new StringBuilder();
// get the first address
InetAddress address = sie.getAddresses()[0];
LOGGER.info("Provided address: {}, port: {}", address, sie.getPort());
// prepare the connect string
sb
.append(ConnectionPortType.NetBidibClient.name()).append(':').append(address.getHostName()).append(':')
.append(sie.getPort());
// @formatter:off
final String text =
"" + Resources.getString(AlertController.class, "message.netbidib-device-resolved") + "
"
+ ""
+ Resources.getString(AlertController.class, "message.product", sie.getProps().get("prod")) + "
"
+ Resources.getString(AlertController.class, "message.username", sie.getProps().getOrDefault("user", "")) + "
"
+ "IP: " + address.getHostAddress()
+ ""
+ "";
// @formatter:on
final String highlightText = text;
final String portName = sb.toString();
try {
// create the alert
SwingUtils.executeInEDT(() -> {
final BidibAlert> alert = createAlert(portName, text, highlightText, sie);
notifyAlertAddedListeners(alert, AlertAction.DEVICE_ADDED);
showAlert(alert, AlertAction.DEVICE_ADDED, mainView.getFrame());
});
}
catch (Exception ex) {
LOGGER.warn("Show resolved event failed.", ex);
}
try {
final String messageText =
Resources.getString(AlertController.class, "message.netbidib-device-resolved") + ", "
+ Resources.getString(AlertController.class, "message.product", sie.getProps().get("prod")) + ", "
+ Resources
.getString(AlertController.class, "message.username", sie.getProps().getOrDefault("user", ""))
+ ", IP: " + address.getHostAddress();
this.applicationEventPublisher.publishEvent(new ConsoleMessageEvent(ConsoleColor.black, messageText));
}
catch (Exception ex) {
LOGGER.warn("Publish console event of netBiDiB device resolved failed.");
}
}
private String getColor(String colorKey) {
Color color = UIDefaultsLookup.getColor(colorKey);
return String.format("#%06x", Integer.valueOf(color.getRGB() & 0x00FFFFFF));
}
private void netBidibServiceRemoved(NetBidibServiceInfo sie) {
final StringBuilder sb = new StringBuilder();
// get the first address
InetAddress address = sie.getAddresses()[0];
LOGGER.info("Provided address: {}", address);
// prepare the connect string
sb
.append(ConnectionPortType.NetBidibClient.name()).append(':').append(address.getHostName()).append(':')
.append(sie.getPort());
// @formatter:off
final String text =
"" + Resources.getString(AlertController.class, "message.netbidib-device-removed") + "
"
+ ""
+ Resources.getString(AlertController.class, "message.product", sie.getProps().get("prod")) + "
"
+ Resources.getString(AlertController.class, "message.username", sie.getProps().getOrDefault("user", "")) + "
"
+ "IP: " + address.getHostAddress()
+ ""
+ "";
// @formatter:on
final String highlightText = text;
final String portName = sb.toString();
try {
// create the alert
SwingUtilities.invokeLater(() -> {
final BidibAlert> alert = createAlert(portName, highlightText, highlightText, sie);
notifyAlertAddedListeners(alert, AlertAction.DEVICE_REMOVED);
showAlert(alert, AlertAction.DEVICE_REMOVED, mainView.getFrame());
});
}
catch (Exception ex) {
LOGGER.warn("Show removed event failed.", ex);
}
}
private void usbDeviceRemoved(final SerialPortInfo serialPort) {
try {
// @formatter:off
final String text =
"" + Resources.getString(AlertController.class, "message.usb-device-removed") + "
" + "VID: 0x"
+ serialPort.getVendorId() + " PID: 0x"
+ serialPort.getProductId() + "
" + "Serial number: "
+ serialPort.getSerialNumber() + "
Product: "
+ serialPort.getProductString() + "
COM Port: "
+ serialPort.getPortName() + "";
final String highlightText =
"" + Resources.getString(AlertController.class, "message.usb-device-removed") + "
" + "VID: 0x"
+ serialPort.getVendorId() + " PID: 0x"
+ serialPort.getProductId() + "
" + "Serial number: "
+ serialPort.getSerialNumber() + "
Product: "
+ serialPort.getProductString() + "
COM Port: "
+ serialPort.getPortName() + "";
// @formatter:on
// create the alert
SwingUtilities.invokeLater(() -> {
final BidibAlert> alert = createAlert(null, text, highlightText, serialPort);
notifyAlertAddedListeners(alert, AlertAction.DEVICE_REMOVED);
showAlert(alert, AlertAction.DEVICE_REMOVED, mainView.getFrame());
});
}
catch (Exception ex) {
LOGGER.warn("Show device removed alert failed.", ex);
}
}
private void usbDeviceAdded(final SerialPortInfo serialPort) {
try {
// @formatter:off
final String text =
"" + Resources.getString(AlertController.class, "message.usb-device-resolved") + "
" + "VID: 0x"
+ serialPort.getVendorId() + " PID: 0x"
+ serialPort.getProductId() + "
" + "Serial number: "
+ serialPort.getSerialNumber() + "
Product: "
+ serialPort.getProductString() + "
Manufacturer: "
+ serialPort.getManufacturerString() + "
COM Port: " + serialPort.getPortName()
+ "";
final String highlightText =
"" + Resources.getString(AlertController.class, "message.usb-device-resolved") + "
" + "VID: 0x"
+ serialPort.getVendorId() + " PID: 0x"
+ serialPort.getProductId() + "
" + "Serial number: "
+ serialPort.getSerialNumber() + "
Product: "
+ serialPort.getProductString() + "
Manufacturer: "
+ serialPort.getManufacturerString() + "
COM Port: " + serialPort.getPortName()
+ "";
// @formatter:on
final String comPort = serialPort.getPortName();
final String serialNumber = serialPort.getSerialNumber();
SwingUtilities.invokeLater(() -> {
// create the alert
String portInfo = "SerialPort:" + comPort;
if (serialNumber != null) {
portInfo = "SerialPort:" + comPort + " - " + serialNumber;
}
final BidibAlert> alert = createAlert(portInfo, text, highlightText, serialPort);
LOGGER.info("Created alert: {}", alert);
notifyAlertAddedListeners(alert, AlertAction.DEVICE_ADDED);
showAlert(alert, AlertAction.DEVICE_ADDED, mainView.getFrame());
});
}
catch (Exception ex) {
LOGGER.warn("Show device added alert failed.", ex);
}
}
/**
* Show the provided alert.
*
* @param alert
* the alert
* @param owner
* the owner component
*/
public void showAlert(final BidibAlert> alert, AlertAction action, final Component owner) {
if (action == AlertAction.DEVICE_ADDED) {
this.applicationEventPublisher
.publishEvent(
new ConsoleMessageEvent(ConsoleColor.black, "New device detected: " + alert.getComPort(), false));
}
else {
// TODO show in console
// this.applicationEventPublisher
// .publishEvent(
// new ConsoleMessageEvent(ConsoleColor.black, "Device removed: " + alert.comPort, false));
}
final WizardSettingsInterface wizardSettings = this.settingsService.getWizardSettings();
if (!wizardSettings.isShowNewDeviceAlert()) {
LOGGER.info("Show new device alert is disabled in configuration (wizard settings).");
return;
}
alertGroup.add(alert);
alert.setAlertGroup(alertGroup);
alertGroup.add(alert);
LOGGER.info("Show the alert: {}, owner: {}", alert, owner);
alert.showPopup(SwingConstants.SOUTH_EAST, owner);
}
private BidibAlert createAlert(final String comPort, String text, String highlightText, final T serialPort) {
// create the alert
final BidibAlert alert = new BidibAlert(comPort, serialPort);
alert.getContentPane().setLayout(new BorderLayout());
ActionListener flagAction = null;
if (StringUtils.isNotBlank(comPort)) {
flagAction = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
LOGGER.info("Use the port to connect: {}", comPort);
final PreferencesPortType portType = PreferencesPortType.getValue(comPort);
settingsService.setSelectedPortType(ConnectionRegistry.CONNECTION_ID_MAIN, portType);
settingsService.storeSettings();
// show text in status bar
statusBar
.setStatusText(Resources.getString(AlertController.class, "message.comport-selected", comPort));
AlertGroup alertGroup = alert.getAlertGroup();
alert.hidePopupImmediately();
if (alertGroup instanceof BidibAlertGroup) {
BidibAlertGroup bidibAlertGroup = (BidibAlertGroup) alertGroup;
bidibAlertGroup.remove(alert);
Collection alerts = new LinkedList<>(bidibAlertGroup.getAlerts());
LOGGER.info("Current alerts in AlertGroup: {}", alerts);
for (Alert current : alerts) {
try {
LOGGER.info("Hide alert: {}", current);
current.hidePopupImmediately();
bidibAlertGroup.remove(current);
}
catch (Exception ex) {
LOGGER.warn("Hide alert failed.", ex);
}
notifyAlertRemoveListeners(alert);
}
}
else {
LOGGER.info("No AlertGroup assigned to alert: {}", alert);
}
// trigger connect
try {
LOGGER.info("Try to connect to port: {}", comPort);
AlertController.this.applicationEventPublisher
.publishEvent(
new BidibConnectionEvent(ConnectionRegistry.CONNECTION_ID_MAIN, Action.connect));
}
catch (Exception ex) {
LOGGER.warn("Connect to port failed: {}", comPort, ex);
}
}
};
}
else {
LOGGER.warn("No comPort identifier provided.");
}
alert.getContentPane().add(createUSBSerialAlert(flagAction, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
LOGGER.info("The user clicked on the alert: {}", alert);
try {
alert.hidePopupImmediately();
}
catch (Exception ex) {
LOGGER.warn("Hide popup immediately failed.", ex);
}
notifyAlertRemoveListeners(alert);
}
}, text, highlightText));
alert.setResizable(true);
alert.setMovable(true);
// Sets the transient attribute. If a popup is transient, it will hide automatically when mouse is clicked
// outside the popup. Otherwise, it will stay visible until timeout or hidePopup() is called.
alert.setTransient(true);
boolean showFadeAnimation = SystemUtils.IS_OS_WINDOWS;
if (showFadeAnimation) {
final CustomAnimation animation =
new CustomAnimation(CustomAnimation.TYPE_ENTRANCE,
showFadeAnimation ? CustomAnimation.EFFECT_FADE : CustomAnimation.EFFECT_FLY,
CustomAnimation.SMOOTHNESS_SMOOTH, CustomAnimation.SPEED_SLOW) {
private static final long serialVersionUID = 1L;
@Override
public void start(Component source) {
try {
super.start(source);
}
catch (Exception ex) {
LOGGER.warn("Start show animation failed.", ex);
}
}
};
animation.setFunctionFade(CustomAnimation.FUNC_BOUNCE);
alert.setShowAnimation(animation);
}
// else {
//
// Point p =
// new Point(
// (int) mainView.getFrame().getLocationOnScreen().getX() + mainView.getFrame().getWidth()
// - alert.getWidth(),
// (int) mainView.getFrame().getLocationOnScreen().getY() + mainView.getFrame().getHeight()
// - alert.getHeight());
// animation.setStartLocation(p);
// animation.setEndLocation(p);
// animation.setDirection(CustomAnimation.BOTTOM_RIGHT);
// }
// alert.setShowAnimation(animation);
if (showFadeAnimation) {
final CustomAnimation animationExit =
new CustomAnimation(CustomAnimation.TYPE_EXIT,
showFadeAnimation ? CustomAnimation.EFFECT_FADE : CustomAnimation.EFFECT_FLY,
CustomAnimation.SMOOTHNESS_SMOOTH, CustomAnimation.SPEED_SLOW) {
private static final long serialVersionUID = 1L;
@Override
public void start(Component source) {
LOGGER.info("The alert is removed: {}", alert);
try {
super.start(source);
}
catch (Exception ex) {
LOGGER.warn("Start exit animation failed.", ex);
}
}
};
animationExit.setFunctionFade(CustomAnimation.FUNC_POW_HALF);
alert.setHideAnimation(animationExit);
}
// else {
// Point p =
// new Point((int) mainView.getFrame().getLocationOnScreen().getX() + mainView.getFrame().getWidth(),
// (int) mainView.getFrame().getLocationOnScreen().getY() + mainView.getFrame().getHeight());
// animation.setStartLocation(p);
// animation.setDirection(CustomAnimation.BOTTOM_RIGHT);
// }
// alert.setHideAnimation(animationExit);
// }
// else {
// LOGGER.info("Don't use animation to show the alert.");
// }
// add listener when the alert gets invisible
alert.addPropertyChangeListener("visible", new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
LOGGER.info("Received pce: {}", evt);
if (Boolean.FALSE.equals(evt.getNewValue())) {
LOGGER.info("Remove alert from list: {}", alert);
try {
AlertGroup alertGroup = alert.getAlertGroup();
if (alertGroup instanceof BidibAlertGroup) {
BidibAlertGroup bidibAlertGroup = (BidibAlertGroup) alertGroup;
bidibAlertGroup.remove(alert);
}
else {
LOGGER.info("No AlertGroup assigned to alert: {}", alert);
}
}
catch (Exception ex) {
LOGGER.warn("Remove alert from group failed.", ex);
}
notifyAlertRemoveListeners(alert);
}
}
});
// alert.setPopupBorder(BorderFactory.createLineBorder(new Color(10, 30, 106)));
return alert;
}
private JideButton createButton(Icon icon) {
JideButton button = new JideButton(icon);
return button;
}
private JComponent createUSBSerialAlert(
final ActionListener flagAction, ActionListener closeAction, final String text, final String highlightText) {
JPanel bottomPanel = new JPanel(new GridLayout(1, 2, 0, 0));
if (flagAction != null) {
JideButton flagButton = createButton(ImageUtils.createImageIcon(getClass(), "/icons/16x16/connect.png"));
flagButton.addActionListener(flagAction);
bottomPanel.add(flagButton);
}
JideButton deleteButton = createButton(ImageUtils.createImageIcon(getClass(), "/icons/alert/delete.png"));
deleteButton.addActionListener(closeAction);
bottomPanel.add(deleteButton);
JPanel leftPanel = new JPanel(new BorderLayout(6, 6));
leftPanel.add(new JLabel(ImageUtils.createImageIcon(getClass(), "/icons/alert/usb_plug.png")));
leftPanel.add(bottomPanel, BorderLayout.AFTER_LAST_LINE);
JPanel rightPanel = new JPanel(new GridLayout(1, 2, 0, 0));
rightPanel.add(createButton(ImageUtils.createImageIcon(getClass(), "/icons/alert/option.png")));
JideButton closeButton = createButton(ImageUtils.createImageIcon(getClass(), "/icons/alert/close.png"));
closeButton.addActionListener(closeAction);
rightPanel.add(closeButton);
final JLabel message = new JLabel(text);
message.addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
message.setText(highlightText);
}
@Override
public void mouseExited(MouseEvent e) {
message.setText(text);
}
});
PaintPanel panel = new PaintPanel(new BorderLayout(6, 6));
panel.setBorder(BorderFactory.createEmptyBorder(6, 7, 7, 7));
panel.add(message, BorderLayout.CENTER);
JPanel topPanel = JideSwingUtilities.createTopPanel(rightPanel);
panel.add(topPanel, BorderLayout.AFTER_LINE_ENDS);
panel.add(leftPanel, BorderLayout.BEFORE_LINE_BEGINS);
for (int i = 0; i < panel.getComponentCount(); i++) {
JideSwingUtilities.setOpaqueRecursively(panel.getComponent(i), false);
}
panel.setOpaque(true);
final Color background = UIDefaultsLookup.getColor("AlertPanel.background");
if (background != null) {
panel.setBackgroundPaint(background);
}
else {
panel
.setBackgroundPaint(new GradientPaint(0, 0, new Color(231, 229, 224), 0,
panel.getPreferredSize().height, new Color(212, 208, 200)));
}
return panel;
}
private final List alertListeners = new LinkedList<>();
public void addAlertListener(final AlertListener alertListener) {
alertListeners.add(alertListener);
}
public void removeAlertListener(final AlertListener alertListener) {
alertListeners.remove(alertListener);
}
private void notifyAlertAddedListeners(final BidibAlert> alert, AlertAction alertAction) {
LOGGER.info("Notify the alertListeners about added, alertAction: {}, alert: {}", alertAction, alert);
for (AlertListener listener : alertListeners) {
listener.alertAdded(alert, alertAction);
}
}
private void notifyAlertRemoveListeners(final BidibAlert> alert) {
LOGGER.info("Notify the alertListeners about remove, alert: {}", alert);
for (AlertListener listener : alertListeners) {
listener.alertRemoved(alert);
}
}
}