jafea7-ctbrec-v5.3.2-based/client/src/main/java/ctbrec/ui/TrayIcon.java

182 lines
6.7 KiB
Java

package ctbrec.ui;
import ctbrec.Config;
import ctbrec.event.EventBusHolder;
import ctbrec.recorder.Recorder;
import ctbrec.ui.controls.Dialogs;
import javafx.application.Platform;
import javafx.stage.Stage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.font.LineMetrics;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import static java.awt.Font.BOLD;
import static java.awt.RenderingHints.KEY_ANTIALIASING;
import static java.awt.RenderingHints.VALUE_ANTIALIAS_ON;
public class TrayIcon {
private static final Logger LOG = LoggerFactory.getLogger(TrayIcon.class);
private final Stage stage;
private final Recorder recorder;
private SystemTray tray;
private java.awt.TrayIcon awtTrayIcon;
private BufferedImage background;
public TrayIcon(Stage stage, Recorder recorder) {
this.stage = stage;
this.recorder = recorder;
}
boolean createTrayIcon() {
if (SystemTray.isSupported()) {
if (tray == null) {
String title = CamrecApplication.title;
tray = SystemTray.getSystemTray();
BufferedImage image = null;
try {
image = createImage(recorder.getCurrentlyRecording().size());
} catch (Exception e) {
// fail silently
}
PopupMenu menu = createTrayContextMenu(stage);
awtTrayIcon = new java.awt.TrayIcon(image, title, menu);
awtTrayIcon.setImageAutoSize(true);
awtTrayIcon.setToolTip(title);
try {
tray.add(awtTrayIcon);
} catch (AWTException e) {
LOG.error("Couldn't add tray icon", e);
}
awtTrayIcon.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
toggleVisibility(stage);
}
}
});
}
return true;
} else {
LOG.error("SystemTray notifications not supported by this OS");
return false;
}
}
private PopupMenu createTrayContextMenu(Stage stage) {
var menu = new PopupMenu();
var show = new MenuItem("Show");
show.addActionListener(evt -> restoreStage(stage));
menu.add(show);
menu.addSeparator();
var pauseRecording = new MenuItem("Pause recording");
pauseRecording.addActionListener(evt -> {
try {
recorder.pause();
} catch (InvalidKeyException | NoSuchAlgorithmException | IOException e) {
Dialogs.showError(stage.getScene(), "Pausing recording", "Pausing of the recorder failed", e);
}
});
menu.add(pauseRecording);
var resumeRecording = new MenuItem("Resume recording");
resumeRecording.addActionListener(evt -> {
try {
recorder.resume();
} catch (InvalidKeyException | NoSuchAlgorithmException | IOException e) {
Dialogs.showError(stage.getScene(), "Resuming recording", "Resuming of the recorder failed", e);
}
});
menu.add(resumeRecording);
menu.addSeparator();
var exit = new MenuItem("Exit");
exit.addActionListener(evt -> exit());
menu.add(exit);
return menu;
}
private void restoreStage(Stage stage) {
Platform.runLater(() -> {
stage.setX(Config.getInstance().getSettings().windowX);
stage.setY(Config.getInstance().getSettings().windowY);
LOG.debug("Restoring window location {},{}", stage.getX(), stage.getY());
stage.setIconified(false);
stage.show();
stage.toFront();
EventBusHolder.BUS.post(Map.of("event", "stage_restored"));
});
}
private void toggleVisibility(Stage stage) {
if (stage.isShowing()) {
Platform.runLater(stage::hide);
} else {
restoreStage(stage);
}
}
private void exit() {
EventBusHolder.BUS.post(Map.of("event", "shutdown"));
}
public void displayMessage(String header, String msg, java.awt.TrayIcon.MessageType info) {
createTrayIcon();
awtTrayIcon.displayMessage(header, msg, info);
}
public void updateActiveRecordings(int activeRecordings) {
try {
createTrayIcon();
if (awtTrayIcon != null) {
awtTrayIcon.setImage(createImage(activeRecordings));
}
} catch (IOException e) {
LOG.error("Couldn't update tray icon image", e);
}
}
private BufferedImage createImage(int number) throws IOException {
if (this.background == null) {
this.background = ImageIO.read(TrayIcon.class.getResource("/icon64.png"));
}
BufferedImage image = new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = (Graphics2D) image.getGraphics();
g2.drawImage(background, 0, 0, (img, infoflags, x, y, width, height) -> false);
if (number > 0 && Config.getInstance().getSettings().showActiveRecordingsInTray) {
g2.setColor(Color.decode("#dc4444"));
g2.fillOval(0, 0, 64, 64);
String text = String.valueOf(number);
String fontFamily = Config.getInstance().getSettings().showActiveRecordingsInTrayFont;
int fontSize = Config.getInstance().getSettings().showActiveRecordingsInTrayFontSize;
g2.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);
Font font = new Font(fontFamily, BOLD, fontSize);
g2.setFont(font);
FontMetrics fontMetrics = g2.getFontMetrics(font);
LineMetrics lineMetrics = fontMetrics.getLineMetrics(text, g2);
Rectangle2D stringBounds = fontMetrics.getStringBounds(text, g2);
g2.setColor(Color.decode(Config.getInstance().getSettings().showActiveRecordingsInTrayColor));
int x = (int) (image.getWidth() - stringBounds.getWidth()) / 2;
int y = (int) (((image.getHeight() - lineMetrics.getAscent()) / 2) - 8 - stringBounds.getY());
g2.drawString(text, x, y);
g2.dispose();
}
return image;
}
}