225 lines
6.5 KiB
Java
225 lines
6.5 KiB
Java
package envoy.client.ui;
|
|
|
|
import static java.awt.Image.SCALE_SMOOTH;
|
|
|
|
import java.awt.*;
|
|
import java.awt.TrayIcon.MessageType;
|
|
import java.awt.image.BufferedImage;
|
|
|
|
import javafx.application.Platform;
|
|
import javafx.stage.Stage;
|
|
|
|
import dev.kske.eventbus.*;
|
|
import dev.kske.eventbus.Event;
|
|
|
|
import envoy.data.Message;
|
|
import envoy.data.User.UserStatus;
|
|
|
|
import envoy.client.data.*;
|
|
import envoy.client.event.*;
|
|
import envoy.client.helper.ShutdownHelper;
|
|
import envoy.client.util.*;
|
|
|
|
/**
|
|
* A tray icon with the Envoy logo, an "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
|
|
* @since Envoy Client v0.2-alpha
|
|
*/
|
|
public final class StatusTrayIcon implements EventListener {
|
|
|
|
/**
|
|
* The {@link TrayIcon} provided by the System Tray API for controlling the system tray. This
|
|
* includes displaying the icon, but also creating notifications when new messages are received.
|
|
*/
|
|
private final TrayIcon trayIcon;
|
|
|
|
/**
|
|
* A received {@link Message} is only displayed as a system tray notification if this variable
|
|
* is set to {@code true}.
|
|
*/
|
|
private boolean displayMessageNotification;
|
|
|
|
/**
|
|
* The size of the tray icon's image.
|
|
*/
|
|
private final Dimension size;
|
|
|
|
/**
|
|
* 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;
|
|
|
|
private static final Font unreadMessageFont = new Font("sans-serif", Font.PLAIN, 8);
|
|
|
|
/**
|
|
* @return {@code true} if the status tray icon is supported on this platform
|
|
* @since Envoy Client v0.2-beta
|
|
*/
|
|
public static boolean isSupported() { return SystemTray.isSupported(); }
|
|
|
|
/**
|
|
* Creates a {@link StatusTrayIcon} with the Envoy logo, a tool tip and a pop-up menu.
|
|
*
|
|
* @param stage the stage whose focus determines if message notifications are displayed
|
|
* @since Envoy Client v0.2-beta
|
|
*/
|
|
public StatusTrayIcon(Stage stage) {
|
|
size = SystemTray.getSystemTray().getTrayIconSize();
|
|
logo = IconUtil.loadAWTCompatible("/icons/envoy_logo.png").getScaledInstance(size.width,
|
|
size.height, SCALE_SMOOTH);
|
|
|
|
final var popup = new PopupMenu();
|
|
|
|
// Adding the exit menu item
|
|
final var exitMenuItem = new MenuItem("Exit");
|
|
exitMenuItem.addActionListener(evt -> ShutdownHelper.exit(true));
|
|
popup.add(exitMenuItem);
|
|
|
|
// Adding the logout menu item
|
|
final var logoutMenuItem = new MenuItem("Logout");
|
|
logoutMenuItem.addActionListener(evt -> Platform.runLater(UserUtil::logout));
|
|
popup.add(logoutMenuItem);
|
|
|
|
// Adding the status change items
|
|
final var statusSubMenu = new Menu("Change status");
|
|
for (final var status : UserStatus.values()) {
|
|
final var statusMenuItem = new MenuItem(status.toString().toLowerCase());
|
|
statusMenuItem
|
|
.addActionListener(evt -> Platform.runLater(() -> UserUtil.changeStatus(status)));
|
|
statusSubMenu.add(statusMenuItem);
|
|
}
|
|
popup.add(statusSubMenu);
|
|
|
|
// Initialize the icon
|
|
trayIcon = new TrayIcon(createImage(), "Envoy", popup);
|
|
|
|
// Only display messages if the stage is not focused and the current user status
|
|
// is not BUSY (if BUSY, displayMessageNotification will be false)
|
|
stage.focusedProperty()
|
|
.addListener((ov, wasFocused, isFocused) -> displayMessageNotification =
|
|
!displayMessageNotification && wasFocused ? false : !isFocused);
|
|
|
|
// Listen to changes in the total unread message amount
|
|
Chat.getTotalUnreadAmount().addListener((ov, oldValue, newValue) -> updateImage());
|
|
|
|
// Show the window if the user clicks on the icon
|
|
trayIcon.addActionListener(evt -> Platform.runLater(() -> {
|
|
stage.setIconified(false);
|
|
stage.toFront();
|
|
stage.requestFocus();
|
|
}));
|
|
|
|
// Start processing message events
|
|
EventBus.getInstance().registerListener(this);
|
|
}
|
|
|
|
/**
|
|
* Makes the icon appear in the system tray.
|
|
*
|
|
* @since Envoy Client v0.2-alpha
|
|
*/
|
|
public void show() {
|
|
try {
|
|
SystemTray.getSystemTray().add(trayIcon);
|
|
} catch (AWTException e) {}
|
|
}
|
|
|
|
/**
|
|
* Removes the icon from the system tray.
|
|
*
|
|
* @since Envoy Client v0.2-beta
|
|
*/
|
|
@Event(eventType = Logout.class)
|
|
public void hide() {
|
|
SystemTray.getSystemTray().remove(trayIcon);
|
|
}
|
|
|
|
@Event
|
|
private void onOwnStatusChange(OwnStatusChange statusChange) {
|
|
displayMessageNotification = !statusChange.get().equals(UserStatus.BUSY);
|
|
trayIcon.getImage().flush();
|
|
trayIcon.setImage(createImage());
|
|
}
|
|
|
|
@Event
|
|
private void onMessage(Message message) {
|
|
if (displayMessageNotification)
|
|
trayIcon
|
|
.displayMessage(message.hasAttachment()
|
|
? "New " + message.getAttachment().getType().toString().toLowerCase()
|
|
+ " message received"
|
|
: "New message received", message.getText(), MessageType.INFO);
|
|
}
|
|
|
|
/**
|
|
* Updates the tray icon's image by first releasing the resources held by the current image and
|
|
* then setting a new one generated by the {@link StatusTrayIcon#createImage()} method.
|
|
*
|
|
* @since Envoy Client v0.3-beta
|
|
*/
|
|
private void updateImage() {
|
|
trayIcon.getImage().flush();
|
|
trayIcon.setImage(createImage());
|
|
}
|
|
|
|
/**
|
|
* 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.width, size.height, 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.ORANGE);
|
|
break;
|
|
case BUSY:
|
|
g.setColor(Color.RED);
|
|
break;
|
|
case OFFLINE:
|
|
g.setColor(Color.GRAY);
|
|
}
|
|
g.fillOval(size.width / 2, size.height / 2, size.width / 2, size.height / 2);
|
|
|
|
// Draw total amount of unread messages, if any are present
|
|
if (Chat.getTotalUnreadAmount().get() > 0) {
|
|
|
|
// Draw black background circle
|
|
g.setColor(Color.BLACK);
|
|
g.fillOval(size.width / 2, 0, size.width / 2, size.height / 2);
|
|
|
|
// Unread amount in white
|
|
String unreadAmount = Chat.getTotalUnreadAmount().get() > 9 ? "9+"
|
|
: String.valueOf(Chat.getTotalUnreadAmount().get());
|
|
g.setColor(Color.WHITE);
|
|
g.setFont(unreadMessageFont);
|
|
g.drawString(unreadAmount,
|
|
3 * size.width / 4 - g.getFontMetrics().stringWidth(unreadAmount) / 2,
|
|
size.height / 2);
|
|
}
|
|
|
|
// Finish drawing
|
|
g.dispose();
|
|
return img;
|
|
}
|
|
}
|