Move what the servers share out of the main window
Which server captures with the microphone, handing it over, push-to-talk and key presses reaching the right microphones, and away state and nickname applied everywhere were all worked out in MainFrame. They move to a Sessions class in core, next to ServerSession, where another frontend can use them; the window keeps only the tabs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
package com.ts3client.session;
|
||||
|
||||
import com.ts3client.audio.VoiceInput;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* Every server the client is on, and what they share. Only one of them can capture with the
|
||||
* microphone at a time, so it is handed from one to another here; the away state and the
|
||||
* nickname are the user's own, so they can be set on every server at once.
|
||||
*
|
||||
* <p>Driven from one thread, the frontend's.
|
||||
*/
|
||||
public final class Sessions {
|
||||
|
||||
private final List<ServerSession> sessions = new ArrayList<>();
|
||||
private final Runnable onMicrophoneMoved;
|
||||
private ServerSession microphoneOwner;
|
||||
/**
|
||||
* Opens and closes capture lines, which can block briefly, off the frontend's thread. One
|
||||
* thread, so quick successive hand-overs stop and start in the order they were made.
|
||||
*/
|
||||
private final ExecutorService handover = Executors.newSingleThreadExecutor(r -> {
|
||||
Thread t = new Thread(r, "mic-handover");
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
});
|
||||
|
||||
/** @param onMicrophoneMoved told whenever the microphone changes hands */
|
||||
public Sessions(Runnable onMicrophoneMoved) {
|
||||
this.onMicrophoneMoved = onMicrophoneMoved;
|
||||
}
|
||||
|
||||
public void add(ServerSession session) {
|
||||
sessions.add(session);
|
||||
}
|
||||
|
||||
/** Forgets a session that is being closed; the microphone moves on if it had it. */
|
||||
public void remove(ServerSession session) {
|
||||
sessions.remove(session);
|
||||
if (microphoneOwner == session) microphoneOwner = null;
|
||||
assignMicrophoneIfFree();
|
||||
}
|
||||
|
||||
public boolean anyConnected() {
|
||||
for (ServerSession s : sessions) {
|
||||
if (s.isConnected()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ---- the microphone ----
|
||||
|
||||
/** The session capturing with the microphone, or null when none is. */
|
||||
public ServerSession microphoneOwner() {
|
||||
return microphoneOwner;
|
||||
}
|
||||
|
||||
/** Moves the microphone to {@code session}, taking it from whoever held it. */
|
||||
public void giveMicrophoneTo(ServerSession session) {
|
||||
if (microphoneOwner == session) return;
|
||||
ServerSession previous = microphoneOwner;
|
||||
microphoneOwner = session;
|
||||
handover.execute(() -> {
|
||||
if (previous != null) previous.setMicrophoneActive(false);
|
||||
if (session != null) session.setMicrophoneActive(true);
|
||||
});
|
||||
onMicrophoneMoved.run();
|
||||
}
|
||||
|
||||
/** Gives the microphone to some connected session when nobody who could use it holds it. */
|
||||
public void assignMicrophoneIfFree() {
|
||||
if (microphoneOwner != null && sessions.contains(microphoneOwner) && microphoneOwner.isConnected()) return;
|
||||
ServerSession previous = microphoneOwner;
|
||||
microphoneOwner = null;
|
||||
for (ServerSession s : sessions) {
|
||||
if (s.isConnected()) {
|
||||
giveMicrophoneTo(s);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (previous != null) onMicrophoneMoved.run();
|
||||
}
|
||||
|
||||
public void connected(ServerSession session) {
|
||||
assignMicrophoneIfFree();
|
||||
}
|
||||
|
||||
/** A disconnected session cannot capture; the microphone moves on if it had it. */
|
||||
public void disconnected(ServerSession session) {
|
||||
if (microphoneOwner == session) microphoneOwner = null;
|
||||
assignMicrophoneIfFree();
|
||||
}
|
||||
|
||||
/** Opens the microphone while push-to-talk is held. */
|
||||
public void setPushToTalk(boolean talking) {
|
||||
VoiceInput mic = microphoneOf(microphoneOwner);
|
||||
if (mic != null) mic.setPushToTalk(talking);
|
||||
}
|
||||
|
||||
/** Tells every connected microphone about a key press, as TS3 tells all its connections. */
|
||||
public void keyPressed() {
|
||||
for (ServerSession s : sessions) {
|
||||
VoiceInput mic = microphoneOf(s);
|
||||
if (mic != null) mic.keyPressed();
|
||||
}
|
||||
}
|
||||
|
||||
private static VoiceInput microphoneOf(ServerSession session) {
|
||||
return session == null || !session.isConnected() ? null : session.connection().getMicrophone();
|
||||
}
|
||||
|
||||
// ---- the user's own state ----
|
||||
|
||||
public void setAwayEverywhere(boolean away, String message) {
|
||||
for (ServerSession s : sessions) {
|
||||
if (s.isConnected()) s.setAway(away, message);
|
||||
}
|
||||
}
|
||||
|
||||
/** @return true when every connected server has us away, and there is one */
|
||||
public boolean isAwayEverywhere() {
|
||||
boolean any = false;
|
||||
for (ServerSession s : sessions) {
|
||||
if (!s.isConnected()) continue;
|
||||
any = true;
|
||||
if (!s.isAway()) return false;
|
||||
}
|
||||
return any;
|
||||
}
|
||||
|
||||
/** The away message to suggest: {@code preferred}'s, else any server's that has one. */
|
||||
public String awayMessage(ServerSession preferred) {
|
||||
if (preferred != null && !preferred.awayMessage().isEmpty()) return preferred.awayMessage();
|
||||
for (ServerSession s : sessions) {
|
||||
if (s.isConnected() && !s.awayMessage().isEmpty()) return s.awayMessage();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public void setNickname(String nickname) {
|
||||
for (ServerSession s : sessions) s.setNickname(nickname);
|
||||
}
|
||||
|
||||
/** Leaves every server, waiting until each has been told. */
|
||||
public void shutdown() {
|
||||
for (ServerSession s : new ArrayList<>(sessions)) s.shutdown();
|
||||
handover.shutdown();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user