All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bidib.wizard.mvc.main.controller.AlertController Maven / Gradle / Ivy

There is a newer version: 2.0.29
Show newest version
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); } } }