277 lines
9.5 KiB
Java
277 lines
9.5 KiB
Java
package ctbrec.ui;
|
|
|
|
import java.awt.AWTException;
|
|
import java.awt.Desktop;
|
|
import java.awt.Image;
|
|
import java.awt.MenuItem;
|
|
import java.awt.PopupMenu;
|
|
import java.awt.SystemTray;
|
|
import java.awt.Toolkit;
|
|
import java.awt.TrayIcon;
|
|
import java.awt.TrayIcon.MessageType;
|
|
import java.awt.event.MouseAdapter;
|
|
import java.awt.event.MouseEvent;
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.net.URI;
|
|
import java.security.InvalidKeyException;
|
|
import java.security.NoSuchAlgorithmException;
|
|
import java.util.Map;
|
|
|
|
import javax.swing.SwingUtilities;
|
|
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
import ctbrec.OS;
|
|
import ctbrec.event.EventBusHolder;
|
|
import ctbrec.io.StreamRedirector;
|
|
import ctbrec.recorder.Recorder;
|
|
import ctbrec.ui.controls.Dialogs;
|
|
import javafx.application.Platform;
|
|
import javafx.geometry.Insets;
|
|
import javafx.scene.control.Alert;
|
|
import javafx.scene.control.Label;
|
|
import javafx.scene.control.TextField;
|
|
import javafx.scene.layout.BorderPane;
|
|
import javafx.stage.Stage;
|
|
|
|
public class DesktopIntegration {
|
|
|
|
private DesktopIntegration() {}
|
|
|
|
private static final Logger LOG = LoggerFactory.getLogger(DesktopIntegration.class);
|
|
|
|
private static SystemTray tray;
|
|
private static TrayIcon trayIcon;
|
|
private static Recorder recorder;
|
|
private static Stage primaryStage;
|
|
|
|
public static void open(String uri) {
|
|
try {
|
|
CamrecApplication.hostServices.showDocument(uri);
|
|
return;
|
|
} catch (Exception e) {
|
|
LOG.debug("Couldn't open URL with host services {}", uri);
|
|
}
|
|
|
|
// opening with HostServices failed, now try Desktop
|
|
try {
|
|
Desktop.getDesktop().browse(new URI(uri));
|
|
return;
|
|
} catch (Exception e) {
|
|
LOG.debug("Couldn't open URL with Desktop {}", uri);
|
|
}
|
|
|
|
// try external helpers
|
|
String[] externalHelpers = { "kde-open5", "kde-open", "gnome-open", "xdg-open" };
|
|
Runtime rt = Runtime.getRuntime();
|
|
for (String helper : externalHelpers) {
|
|
try {
|
|
rt.exec(helper + " " + uri);
|
|
return;
|
|
} catch (IOException e) {
|
|
LOG.debug("Couldn't open URL with {} {}", helper, uri);
|
|
}
|
|
}
|
|
|
|
// all attempts failed, show a dialog with URL at least
|
|
Alert info = new AutosizeAlert(Alert.AlertType.ERROR);
|
|
info.setTitle("Open URL");
|
|
info.setContentText("Couldn't open URL");
|
|
BorderPane pane = new BorderPane();
|
|
pane.setTop(new Label());
|
|
TextField urlField = new TextField(uri);
|
|
urlField.setPadding(new Insets(10));
|
|
urlField.setEditable(false);
|
|
pane.setCenter(urlField);
|
|
info.getDialogPane().setExpandableContent(pane);
|
|
info.getDialogPane().setExpanded(true);
|
|
info.show();
|
|
}
|
|
|
|
public static void open(File f) {
|
|
try {
|
|
Desktop.getDesktop().open(f);
|
|
return;
|
|
} catch (Exception e) {
|
|
LOG.debug("Couldn't open file with Desktop {}", f);
|
|
}
|
|
|
|
// try external helpers
|
|
String[] externalHelpers = { "kde-open5", "kde-open", "gnome-open", "xdg-open" };
|
|
Runtime rt = Runtime.getRuntime();
|
|
for (String helper : externalHelpers) {
|
|
try {
|
|
rt.exec(helper + " " + f.getAbsolutePath());
|
|
return;
|
|
} catch (IOException e) {
|
|
LOG.debug("Couldn't open file with {} {}", helper, f);
|
|
}
|
|
}
|
|
|
|
// all attempts failed, show a dialog with path at least
|
|
Alert info = new AutosizeAlert(Alert.AlertType.ERROR);
|
|
info.setTitle("Open file");
|
|
info.setContentText("Couldn't open file");
|
|
BorderPane pane = new BorderPane();
|
|
pane.setTop(new Label());
|
|
TextField urlField = new TextField(f.toString());
|
|
urlField.setPadding(new Insets(10));
|
|
urlField.setEditable(false);
|
|
pane.setCenter(urlField);
|
|
info.getDialogPane().setExpandableContent(pane);
|
|
info.getDialogPane().setExpanded(true);
|
|
info.show();
|
|
}
|
|
|
|
public static void notification(String title, String header, String msg) {
|
|
if (OS.getOsType() == OS.TYPE.LINUX) {
|
|
notifyLinux(title, header, msg);
|
|
} else if (OS.getOsType() == OS.TYPE.WINDOWS) {
|
|
notifyWindows(title, header, msg);
|
|
} else if (OS.getOsType() == OS.TYPE.MAC) {
|
|
notifyMac(title, header, msg);
|
|
} else {
|
|
// unknown system, try systemtray notification anyways
|
|
notifySystemTray(title, header, msg);
|
|
}
|
|
}
|
|
|
|
private static void notifyLinux(String title, String header, String msg) {
|
|
try {
|
|
Process p = Runtime.getRuntime().exec(new String[] {
|
|
"notify-send",
|
|
"-u", "normal",
|
|
"-t", "5000",
|
|
"-a", title,
|
|
header,
|
|
msg.replace("-", "\\\\-").replace("\\s", "\\\\ "),
|
|
"--icon=dialog-information"
|
|
});
|
|
new Thread(new StreamRedirector(p.getInputStream(), System.out)).start(); // NOSONAR
|
|
new Thread(new StreamRedirector(p.getErrorStream(), System.err)).start(); // NOSONAR
|
|
} catch (NullPointerException e) {
|
|
// can happen at start, ignore
|
|
} catch (IOException e1) {
|
|
LOG.error("Notification failed", e1);
|
|
}
|
|
}
|
|
|
|
private static void notifyWindows(String title, String header, String msg) {
|
|
notifySystemTray(title, header, msg);
|
|
}
|
|
|
|
private static void notifyMac(String title, String header, String msg) {
|
|
notifySystemTray(title, header, msg);
|
|
}
|
|
|
|
private static synchronized void notifySystemTray(String title, String header, String msg) {
|
|
if (SystemTray.isSupported()) {
|
|
createTrayIcon(primaryStage);
|
|
trayIcon.displayMessage(header, msg, MessageType.INFO);
|
|
} else {
|
|
LOG.error("SystemTray notifications not supported by this OS");
|
|
}
|
|
}
|
|
|
|
public static void minimizeToTray(Stage primaryStage) {
|
|
Platform.setImplicitExit(false);
|
|
boolean supported = createTrayIcon(primaryStage);
|
|
if (supported) {
|
|
primaryStage.hide();
|
|
}
|
|
}
|
|
|
|
private static boolean createTrayIcon(Stage stage) {
|
|
if (SystemTray.isSupported()) {
|
|
if (tray == null) {
|
|
String title = CamrecApplication.title;
|
|
tray = SystemTray.getSystemTray();
|
|
Image image = Toolkit.getDefaultToolkit().createImage(DesktopIntegration.class.getResource("/icon64.png"));
|
|
|
|
PopupMenu menu = createTrayContextMenu(stage);
|
|
trayIcon = new TrayIcon(image, title, menu);
|
|
trayIcon.setImageAutoSize(true);
|
|
trayIcon.setToolTip(title);
|
|
try {
|
|
tray.add(trayIcon);
|
|
} catch (AWTException e) {
|
|
LOG.error("Couldn't add tray icon", e);
|
|
}
|
|
trayIcon.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 static PopupMenu createTrayContextMenu(Stage stage) {
|
|
PopupMenu menu = new PopupMenu();
|
|
MenuItem show = new MenuItem("Show");
|
|
show.addActionListener(evt -> restoreStage(stage));
|
|
menu.add(show);
|
|
menu.addSeparator();
|
|
MenuItem 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);
|
|
MenuItem 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();
|
|
MenuItem exit = new MenuItem("Exit");
|
|
exit.addActionListener(evt -> exit(stage));
|
|
menu.add(exit);
|
|
return menu;
|
|
}
|
|
|
|
private static void toggleVisibility(Stage stage) {
|
|
if (stage.isShowing()) {
|
|
Platform.runLater(stage::hide);
|
|
} else {
|
|
restoreStage(stage);
|
|
}
|
|
}
|
|
|
|
private static void restoreStage(Stage stage) {
|
|
Platform.runLater(() -> {
|
|
stage.setIconified(false);
|
|
stage.show();
|
|
stage.toFront();
|
|
});
|
|
}
|
|
|
|
private static void exit(Stage stage) {
|
|
EventBusHolder.BUS.post(Map.of("event", "shutdown"));
|
|
}
|
|
|
|
public static void setRecorder(Recorder recorder) {
|
|
DesktopIntegration.recorder = recorder;
|
|
}
|
|
|
|
public static void setPrimaryStage(Stage primaryStage) {
|
|
DesktopIntegration.primaryStage = primaryStage;
|
|
}
|
|
}
|