Display current user status in status tray icon

This commit is contained in:
Kai S. K. Engelbart 2020-10-16 21:39:17 +02:00
parent 5b85c1bf54
commit 44f4d8f1e0
Signed by: kske
GPG Key ID: 8BEB13EC5DF7EF13

View File

@ -2,37 +2,58 @@ package envoy.client.ui;
import java.awt.*; import java.awt.*;
import java.awt.TrayIcon.MessageType; import java.awt.TrayIcon.MessageType;
import java.awt.image.BufferedImage;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.stage.Stage; import javafx.stage.Stage;
import envoy.client.event.OwnStatusChange;
import envoy.client.helper.ShutdownHelper;
import envoy.client.util.*;
import envoy.data.Message;
import envoy.data.User.UserStatus;
import dev.kske.eventbus.*; import dev.kske.eventbus.*;
import dev.kske.eventbus.Event; import dev.kske.eventbus.Event;
import envoy.data.Message;
import envoy.data.User.UserStatus;
import envoy.client.data.Context;
import envoy.client.event.OwnStatusChange;
import envoy.client.helper.ShutdownHelper;
import envoy.client.util.*;
/** /**
* A tray icon with the Envoy logo, a "Envoy" tool tip and a pop-up menu with menu items for
* <ul>
* <li>Changing the user status</li>
* <li>Logging out</li>
* <li>Quitting Envoy</li>
* </ul>
*
* @author Kai S. K. Engelbart * @author Kai S. K. Engelbart
* @since Envoy Client v0.2-alpha * @since Envoy Client v0.2-alpha
*/ */
public final class StatusTrayIcon implements EventListener { public final class StatusTrayIcon implements EventListener {
/** /**
* The {@link TrayIcon} provided by the System Tray API for controlling the * The {@link TrayIcon} provided by the System Tray API for controlling the system tray. This
* system tray. This includes displaying the icon, but also creating * includes displaying the icon, but also creating notifications when new messages are received.
* notifications when new messages are received.
*/ */
private final TrayIcon trayIcon; private final TrayIcon trayIcon;
/** /**
* A received {@link Message} is only displayed as a system tray notification if * A received {@link Message} is only displayed as a system tray notification if this variable
* this variable is set to {@code true}. * is set to {@code true}.
*/ */
private boolean displayMessages; private boolean displayMessageNotification;
/**
* The Envoy logo on which the current user status and unread message count will be drawn to
* compose the tray icon.
*/
private final Image logo = IconUtil.loadAWTCompatible("/icons/envoy_logo.png")
.getScaledInstance(size, size, BufferedImage.SCALE_SMOOTH);
/**
* The size of the tray icon, as defined by the system tray.
*/
private static final int size = (int) SystemTray.getSystemTray().getTrayIconSize().getWidth();
/** /**
* @return {@code true} if the status tray icon is supported on this platform * @return {@code true} if the status tray icon is supported on this platform
@ -41,18 +62,12 @@ public final class StatusTrayIcon implements EventListener {
public static boolean isSupported() { return SystemTray.isSupported(); } public static boolean isSupported() { return SystemTray.isSupported(); }
/** /**
* Creates a {@link StatusTrayIcon} with the Envoy logo, a tool tip and a pop-up * Creates a {@link StatusTrayIcon} with the Envoy logo, a tool tip and a pop-up menu.
* menu.
* *
* @param stage the stage whose focus determines if message * @param stage the stage whose focus determines if message notifications are displayed
* notifications are displayed
* @since Envoy Client v0.2-beta * @since Envoy Client v0.2-beta
*/ */
public StatusTrayIcon(Stage stage) { public StatusTrayIcon(Stage stage) {
trayIcon = new TrayIcon(IconUtil.loadAWTCompatible("/icons/envoy_logo.png"), "Envoy");
trayIcon.setImageAutoSize(true);
trayIcon.setToolTip("You are notified if you have unread messages.");
final var popup = new PopupMenu(); final var popup = new PopupMenu();
// Adding the exit menu item // Adding the exit menu item
@ -62,26 +77,37 @@ public final class StatusTrayIcon implements EventListener {
// Adding the logout menu item // Adding the logout menu item
final var logoutMenuItem = new MenuItem("Logout"); final var logoutMenuItem = new MenuItem("Logout");
logoutMenuItem.addActionListener(evt -> { hide(); Platform.runLater(UserUtil::logout); }); logoutMenuItem.addActionListener(evt -> {
hide();
Platform.runLater(UserUtil::logout);
});
popup.add(logoutMenuItem); popup.add(logoutMenuItem);
// Adding the status change items // Adding the status change items
final var statusSubMenu = new Menu("Change status"); final var statusSubMenu = new Menu("Change status");
for (final var status : UserStatus.values()) { for (final var status : UserStatus.values()) {
final var statusMenuItem = new MenuItem(status.toString().toLowerCase()); final var statusMenuItem = new MenuItem(status.toString().toLowerCase());
statusMenuItem.addActionListener(evt -> Platform.runLater(() -> UserUtil.changeStatus(status))); statusMenuItem
.addActionListener(evt -> Platform.runLater(() -> UserUtil.changeStatus(status)));
statusSubMenu.add(statusMenuItem); statusSubMenu.add(statusMenuItem);
} }
popup.add(statusSubMenu); popup.add(statusSubMenu);
trayIcon.setPopupMenu(popup); // Initialize the icon
trayIcon = new TrayIcon(createImage(), "Envoy", popup);
// Only display messages if the stage is not focused and the current user status // Only display messages if the stage is not focused and the current user status
// is not BUSY (if BUSY, displayMessages will be false) // is not BUSY (if BUSY, displayMessageNotification will be false)
stage.focusedProperty().addListener((ov, wasFocused, isFocused) -> displayMessages = !displayMessages && wasFocused ? false : !isFocused); stage.focusedProperty()
.addListener((ov, wasFocused, isFocused) -> displayMessageNotification =
!displayMessageNotification && wasFocused ? false : !isFocused);
// Show the window if the user clicks on the icon // Show the window if the user clicks on the icon
trayIcon.addActionListener(evt -> Platform.runLater(() -> { stage.setIconified(false); stage.toFront(); stage.requestFocus(); })); trayIcon.addActionListener(evt -> Platform.runLater(() -> {
stage.setIconified(false);
stage.toFront();
stage.requestFocus();
}));
// Start processing message events // Start processing message events
EventBus.getInstance().registerListener(this); EventBus.getInstance().registerListener(this);
@ -95,7 +121,7 @@ public final class StatusTrayIcon implements EventListener {
public void show() { public void show() {
try { try {
SystemTray.getSystemTray().add(trayIcon); SystemTray.getSystemTray().add(trayIcon);
} catch (final AWTException e) {} } catch (AWTException e) {}
} }
/** /**
@ -103,15 +129,60 @@ public final class StatusTrayIcon implements EventListener {
* *
* @since Envoy Client v0.2-beta * @since Envoy Client v0.2-beta
*/ */
public void hide() { SystemTray.getSystemTray().remove(trayIcon); } public void hide() {
SystemTray.getSystemTray().remove(trayIcon);
}
@Event @Event
private void onOwnStatusChange(OwnStatusChange statusChange) { displayMessages = !statusChange.get().equals(UserStatus.BUSY); } private void onOwnStatusChange(OwnStatusChange statusChange) {
displayMessageNotification = !statusChange.get().equals(UserStatus.BUSY);
trayIcon.getImage().flush();
trayIcon.setImage(createImage());
}
@Event @Event
private void onMessage(Message message) { private void onMessage(Message message) {
if (displayMessages) trayIcon if (displayMessageNotification)
.displayMessage(message.hasAttachment() ? "New " + message.getAttachment().getType().toString().toLowerCase() + " message received" trayIcon
.displayMessage(message.hasAttachment()
? "New " + message.getAttachment().getType().toString().toLowerCase()
+ " message received"
: "New message received", message.getText(), MessageType.INFO); : "New message received", message.getText(), MessageType.INFO);
} }
/**
* Composes an icon that displays the current user status and the amount of unread messages, if
* any are present.
*
* @since Envoy Client v0.3-beta
*/
private BufferedImage createImage() {
// Create a new image with the dimensions of the logo
var img = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
// Obtain the draw graphics of the image and copy the logo
var g = img.createGraphics();
g.drawImage(logo, 0, 0, null);
// Draw the current user status
switch (Context.getInstance().getLocalDB().getUser().getStatus()) {
case ONLINE:
g.setColor(Color.GREEN);
break;
case AWAY:
g.setColor(Color.YELLOW);
break;
case BUSY:
g.setColor(Color.RED);
break;
case OFFLINE:
g.setColor(Color.GRAY);
}
g.fillOval(size / 2, size / 2, size / 2, size / 2);
// Finish drawing
g.dispose();
return img;
}
} }