Notify of pokes and private messages on the desktop
While none of the client's windows has the focus, a poke or a private message shows a desktop notification, which the desktop's notification centre keeps. A private conversation keeps one notification, updated as messages come; clicking it brings the window forward with that chat open, and they all go once a window of the client is in front again. On Linux they go to the freedesktop.org notification service, spoken to over the session bus by a small D-Bus client of our own on Java's Unix sockets, so no library or native code is involved; Plasma also takes a reply typed into the notification. Elsewhere they are AWT tray messages, which Windows and macOS put in their notification centres. The options can turn them off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,7 @@ import com.ts3client.session.ServerSession;
|
||||
import com.ts3client.session.Sessions;
|
||||
import com.ts3client.sound.SoundNotifier;
|
||||
import com.ts3client.sound.SoundPlayer;
|
||||
import com.ts3client.ui.tray.TrayBackend;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.JCheckBox;
|
||||
@@ -35,6 +36,7 @@ import javax.swing.SwingUtilities;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.KeyboardFocusManager;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
@@ -81,6 +83,7 @@ public final class MainFrame extends JFrame implements ServerTabPane.Listener {
|
||||
|
||||
/** Mirrors the active server's own client state next to the clock; set once the window is up. */
|
||||
private volatile TrayController tray;
|
||||
private final DesktopNotifier notifier;
|
||||
|
||||
private MainToolbar toolbar;
|
||||
private StatusBar statusBar;
|
||||
@@ -100,6 +103,11 @@ public final class MainFrame extends JFrame implements ServerTabPane.Listener {
|
||||
this.sounds = new SoundNotifier(settings);
|
||||
this.soundPlayer = audio.createSoundPlayer(settings);
|
||||
this.sounds.setPlayer(soundPlayer);
|
||||
this.notifier = new DesktopNotifier(this, settings);
|
||||
// Any of the client's windows coming to the front counts, a dialog included.
|
||||
KeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener("activeWindow", e -> {
|
||||
if (e.getNewValue() != null) notifier.windowActivated();
|
||||
});
|
||||
this.hotkeys = new HotkeyService(new HotkeyActions(this), this::hotkeyArgumentChoices);
|
||||
hotkeys.addKeyPressListener(() -> SwingUtilities.invokeLater(sessions::keyPressed));
|
||||
contacts.addListener(() -> SwingUtilities.invokeLater(this::contactsChanged));
|
||||
@@ -437,9 +445,21 @@ public final class MainFrame extends JFrame implements ServerTabPane.Listener {
|
||||
void tabDisconnected(ServerTab tab) {
|
||||
if (!tabs.contains(tab)) return;
|
||||
sessions.disconnected(tab.session());
|
||||
notifier.forget(tab);
|
||||
tabUpdated(tab);
|
||||
}
|
||||
|
||||
/** Tells the user of a poke or a private message while the client is in the background. */
|
||||
DesktopNotifier notifier() {
|
||||
return notifier;
|
||||
}
|
||||
|
||||
/** The tray icon when AWT docked it, whose messages are the notifications on Windows and macOS. */
|
||||
TrayBackend awtTray() {
|
||||
TrayController current = tray;
|
||||
return current == null ? null : current.backend();
|
||||
}
|
||||
|
||||
// ---- hotkeys ----
|
||||
|
||||
/**
|
||||
@@ -537,7 +557,14 @@ public final class MainFrame extends JFrame implements ServerTabPane.Listener {
|
||||
IconTheme.get().reload(settings);
|
||||
}
|
||||
|
||||
/** Brings the window back from the tray, the taskbar or behind other windows. */
|
||||
void bringToFront() {
|
||||
TrayController current = tray;
|
||||
if (current != null) {
|
||||
// The tray can ask the window manager in a way it honours for a window without focus.
|
||||
current.show();
|
||||
return;
|
||||
}
|
||||
setVisible(true);
|
||||
setExtendedState(getExtendedState() & ~JFrame.ICONIFIED);
|
||||
toFront();
|
||||
@@ -743,6 +770,7 @@ public final class MainFrame extends JFrame implements ServerTabPane.Listener {
|
||||
soundPlayer.shutdown();
|
||||
hotkeys.dispose();
|
||||
if (tray != null) tray.dispose();
|
||||
notifier.shutdown();
|
||||
}
|
||||
|
||||
/** Leaves every server and ends the process. */
|
||||
|
||||
Reference in New Issue
Block a user