package org.peerbox.view.tray; import java.awt.AWTException; import java.awt.Image; import java.awt.PopupMenu; import java.awt.TrayIcon; import java.awt.TrayIcon.MessageType; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import net.engio.mbassy.listener.Handler; import org.peerbox.app.Constants; import org.peerbox.app.config.AppConfig; import org.peerbox.app.manager.file.messages.FileExecutionFailedMessage; import org.peerbox.app.manager.user.IUserMessageListener; import org.peerbox.app.manager.user.LoginMessage; import org.peerbox.app.manager.user.LogoutMessage; import org.peerbox.app.manager.user.RegisterMessage; import org.peerbox.events.IMessageListener; import org.peerbox.notifications.AggregatedFileEventStatus; import org.peerbox.notifications.ITrayNotifications; import org.peerbox.notifications.InformationNotification; import org.peerbox.presenter.tray.TrayActionHandler; import org.peerbox.presenter.tray.TrayException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.inject.Inject; public class JSystemTray extends AbstractSystemTray implements ITrayNotifications, IMessageListener, IUserMessageListener { private final static Logger logger = LoggerFactory.getLogger(JSystemTray.class); private String tooltip; private java.awt.TrayIcon trayIcon; private final JTrayIcons iconProvider; private AppConfig appConfig; private boolean hasFailedOperations = false; @Inject public JSystemTray(AppConfig appConfig, TrayActionHandler actionHandler) { super(actionHandler); this.appConfig = appConfig; this.iconProvider = new JTrayIcons(); setTooltip(Constants.APP_NAME); } private TrayIcon create(Image image) throws IOException { TrayIcon trayIcon = new java.awt.TrayIcon(image); trayIcon.setImageAutoSize(true); trayIcon.setToolTip(tooltip); trayIcon.setPopupMenu(createMenu(false)); return trayIcon; } private PopupMenu createMenu(boolean isUserLoggedIn) { JTrayMenu menu = new JTrayMenu(trayActionHandler); PopupMenu root = menu.create(isUserLoggedIn); return root; } @Override public void show() throws TrayException { try { trayIcon = create(iconProvider.getDefaultIcon()); java.awt.SystemTray sysTray = java.awt.SystemTray.getSystemTray(); sysTray.add(trayIcon); } catch (AWTException e) { logger.debug("SysTray AWTException.", e); logger.error("Could not initialize systray (tray may not be supported?)"); throw new TrayException(e); } catch (IOException e) { logger.debug("SysTray.show IOException.", e); logger.error("Could not initialize systray (image not found?)"); throw new TrayException(e); } } @Override public void setTooltip(String tooltip) { this.tooltip = tooltip; if(trayIcon != null) { trayIcon.setToolTip(this.tooltip); } } @Override public void showDefaultIcon() throws TrayException { try { trayIcon.setImage(iconProvider.getDefaultIcon()); } catch (IOException e) { logger.debug("SysTray.show IOException.", e); logger.error("Could not change icon (image not found?)"); throw new TrayException(e); } } @Override public void showSyncingIcon() throws TrayException { try { trayIcon.setImage(iconProvider.getSyncingIcon()); } catch (IOException e) { logger.debug("SysTray.show IOException.", e); logger.error("Could not change icon (image not found?)"); throw new TrayException(e); } } @Override public void showSuccessIcon() throws TrayException { try { if (hasFailedOperations) { trayIcon.setImage(iconProvider.getErrorIcon()); } else { trayIcon.setImage(iconProvider.getSuccessIcon()); } } catch (IOException e) { logger.debug("SysTray.show IOException.", e); logger.error("Could not change icon (image not found?)"); throw new TrayException(e); } } @Override public void showInformationMessage(String title, String message) { setNewMessageActionListener(null); showMessage(title, message, MessageType.INFO); } private void showMessage(String title, String message, MessageType type) { if (!appConfig.isTrayNotificationEnabled()) { return; } if (title == null) { title = ""; } if (message == null) { message = ""; } logger.info("{} Message: \n{}\n{}", type.toString(), title, message); if (trayIcon != null) { trayIcon.displayMessage(title, message, type); } } /** * NOTIFICATIONS - implementation of the ITrayNotifications interface */ @Override @Handler public void showInformation(InformationNotification in) { ActionListener listener = new ShowActivityActionListener(); setNewMessageActionListener(listener); showMessage(in.getTitle(), in.getMessage(), MessageType.INFO); } @Override @Handler public void showFileEvents(AggregatedFileEventStatus event) { String msg = generateAggregatedFileEventStatusMessage(event); ActionListener listener = new ShowSettingsActionListener(); setNewMessageActionListener(listener); showMessage("File Synchronization", msg, MessageType.INFO); } /** * Removes all action listeners from the tray icon and adds the given listener. * * @param listener to add. If null, listeners are only and no listener is added. */ private void setNewMessageActionListener(ActionListener listener) { // remove all and add given action listener if (trayIcon != null) { for (ActionListener l : trayIcon.getActionListeners()) { trayIcon.removeActionListener(l); } if (listener != null) { trayIcon.addActionListener(listener); } } } /** * Action listener that opens the activity window */ private class ShowActivityActionListener implements ActionListener { @Override public void actionPerformed(ActionEvent e) { trayActionHandler.showActivity(); } } /** * Action listener that opens the settings window */ private class ShowSettingsActionListener implements ActionListener { @Override public void actionPerformed(ActionEvent e) { trayActionHandler.showSettings(); } } private String generateAggregatedFileEventStatusMessage(AggregatedFileEventStatus e) { StringBuilder sb = new StringBuilder(); sb.append("Hi there, some of your files changed.\n\n"); if(e.getNumFilesAdded() > 0) { sb.append("new files: ").append(e.getNumFilesAdded()).append("\n"); } if(e.getNumFilesModified() > 0) { sb.append("updated files: ").append(e.getNumFilesModified()).append("\n"); } if(e.getNumFilesDeleted() > 0) { sb.append("deleted files: ").append(e.getNumFilesDeleted()).append("\n"); } if(e.getNumFilesMoved() > 0) { sb.append("moved files: ").append(e.getNumFilesMoved()).append("\n"); } return sb.toString(); } @Handler public void onSynchronizationComplete(SynchronizationCompleteNotification message) throws TrayException { logger.trace("Set success icon."); showSuccessIcon(); } @Handler public void onSynchronizationStart(SynchronizationStartsNotification message) throws TrayException { logger.trace("Set synchronization icon."); showSyncingIcon(); } @Handler public void onSynchronizationFailed(FileExecutionFailedMessage message) throws TrayException { logger.trace("At least one operation failed. If the application is idle, " + "the error icon is shown."); hasFailedOperations = true; } @Handler public void onSynchronizationErrorResolved(SynchronizationErrorsResolvedNotification message) { logger.trace("All failed operations successfully resolved. If the application is idle, " + "the success icon is shown."); hasFailedOperations = false; } /*** * User event handling ***/ @Handler @Override public void onUserRegister(RegisterMessage register) { // nothing to do } @Handler @Override public void onUserLogin(LoginMessage login) { // refresh menu trayIcon.setPopupMenu(null); trayIcon.setPopupMenu(createMenu(true)); } @Handler @Override public void onUserLogout(LogoutMessage logout) { // refresh menu trayIcon.setPopupMenu(null); trayIcon.setPopupMenu(createMenu(false)); } }