feat(screencast,chat): roteamento read-only, avisos de sessão e TCP privado
SessionNoticeMessage encerra abas de recebimento; abas ↑/↓; chat unifica bucket peer e conexão TCP. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
fc657345ba
commit
112b6edaeb
14 files changed
+406
-36
No files matched your search
@@ -21,5 +21,6 @@
|
|||||||
| D21 | `onShellTabClosed` | Fechar aba do plugin na shell notifica o plugin (ex.: parar screencast) | [09_sistema_de_plugins.md](documentation/09_sistema_de_plugins.md) |
|
| D21 | `onShellTabClosed` | Fechar aba do plugin na shell notifica o plugin (ex.: parar screencast) | [09_sistema_de_plugins.md](documentation/09_sistema_de_plugins.md) |
|
||||||
| D22 | `PeerIpv4` para IP de peer na UI | No Windows, `InetAddress.getHostAddress()` pode devolver IPv6-mapeado e sufixo de zona (`%n`); chat e roteamento usam IPv4 pontilhado | [appendix_portas_e_multicast.md](documentation/appendix_portas_e_multicast.md) |
|
| D22 | `PeerIpv4` para IP de peer na UI | No Windows, `InetAddress.getHostAddress()` pode devolver IPv6-mapeado e sufixo de zona (`%n`); chat e roteamento usam IPv4 pontilhado | [appendix_portas_e_multicast.md](documentation/appendix_portas_e_multicast.md) |
|
||||||
| D23 | Foco de atividade e badges | **Adiado na v1** — núcleo (`PeerActivityFocus` / `PeerActivityInbox`) mantido; UI sem badges; recebimento passivo em sub-abas (`addTabIfAbsent`) | [09_sistema_de_plugins.md](documentation/09_sistema_de_plugins.md) |
|
| D23 | Foco de atividade e badges | **Adiado na v1** — núcleo (`PeerActivityFocus` / `PeerActivityInbox`) mantido; UI sem badges; recebimento passivo em sub-abas (`addTabIfAbsent`) | [09_sistema_de_plugins.md](documentation/09_sistema_de_plugins.md) |
|
||||||
|
| D24 | Avisos de sessão (`SessionNoticeMessage`) | Início/fim de atividades (ex. `screen_cast` END ao parar share); extensível por plugin/canal | [09_sistema_de_plugins.md](documentation/09_sistema_de_plugins.md) |
|
||||||
|
|
||||||
Novas decisões: adicionar linha nesta tabela e detalhar no arquivo técnico correspondente.
|
Novas decisões: adicionar linha nesta tabela e detalhar no arquivo técnico correspondente.
|
||||||
@@ -77,3 +77,9 @@ Na `MainShellFrame`, `createPlugin(ConnectionManager, PluginWorkspace)` registra
|
|||||||
`PluginRoutingPanel` (núcleo) padroniza destino, forma de envio e dica embaixo para chat, screencast e futuros plugins.
|
`PluginRoutingPanel` (núcleo) padroniza destino, forma de envio e dica embaixo para chat, screencast e futuros plugins.
|
||||||
|
|
||||||
`MessageDispatcher` envia com `TransportMode` e `SendTarget`. `PluginTransportCatalog` define quais transportes cada plugin mostra na UI (sala geral vs um IP). Os combos usam textos didáticos (`describeForUi`) com o termo técnico entre parênteses (multicast UDP, UDP unicast, TCP). Na sala geral o campo IP fica desativado; um resumo **“Como esta mensagem vai”** (chat) deixa explícito o transporte escolhido — na sala pode ser multicast ou cópias unicast/TCP para cada pessoa, conforme o plugin. Para um peer use o IP manualmente ou a peer action na lista lateral.
|
`MessageDispatcher` envia com `TransportMode` e `SendTarget`. `PluginTransportCatalog` define quais transportes cada plugin mostra na UI (sala geral vs um IP). Os combos usam textos didáticos (`describeForUi`) com o termo técnico entre parênteses (multicast UDP, UDP unicast, TCP). Na sala geral o campo IP fica desativado; um resumo **“Como esta mensagem vai”** (chat) deixa explícito o transporte escolhido — na sala pode ser multicast ou cópias unicast/TCP para cada pessoa, conforme o plugin. Para um peer use o IP manualmente ou a peer action na lista lateral.
|
||||||
|
|
||||||
|
Em abas de **recebimento** (ex. screencast `view:`), `PluginRoutingPanel.bindReceiveSession` deixa IP e forma de envio somente leitura, refletindo o que o remetente usou.
|
||||||
|
|
||||||
|
### Avisos de sessão (D24)
|
||||||
|
|
||||||
|
`SessionNoticeMessage` (núcleo) sinaliza fases `START` / `END` por `pluginId` e canal (`SessionNoticeChannel`, ex. `screen_cast`). Plugins registram o tipo em `getMessages()` e reagem na UI (ex. fechar aba de recebimento ao `END` de compartilhamento). Campos incluem remetente, escopo (broadcast/direto) e `TransportMode` observado.
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package pitiupi.application.session;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Canal lógico de um aviso de sessão (extensível por plugins).
|
||||||
|
*/
|
||||||
|
public enum SessionNoticeChannel {
|
||||||
|
SCREEN_CAST("screen_cast");
|
||||||
|
|
||||||
|
private final String wireId;
|
||||||
|
|
||||||
|
SessionNoticeChannel(String wireId) {
|
||||||
|
this.wireId = wireId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String wireId() {
|
||||||
|
return wireId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SessionNoticeChannel fromWireId(String id) {
|
||||||
|
if (id == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (SessionNoticeChannel channel : values()) {
|
||||||
|
if (channel.wireId.equals(id)) {
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package pitiupi.application.session;
|
||||||
|
|
||||||
|
public enum SessionNoticePhase {
|
||||||
|
START,
|
||||||
|
END
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package pitiupi.application.session;
|
||||||
|
|
||||||
|
public enum SessionNoticeScope {
|
||||||
|
BROADCAST,
|
||||||
|
DIRECT
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package pitiupi.models.Messages;
|
||||||
|
|
||||||
|
import pitiupi.application.messaging.TransportMode;
|
||||||
|
import pitiupi.application.session.SessionNoticeChannel;
|
||||||
|
import pitiupi.application.session.SessionNoticePhase;
|
||||||
|
import pitiupi.application.session.SessionNoticeScope;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aviso de início/fim de atividade de sessão (compartilhamento, canais, etc.).
|
||||||
|
*/
|
||||||
|
public class SessionNoticeMessage extends Message {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private final String pluginId;
|
||||||
|
private final SessionNoticePhase phase;
|
||||||
|
private final String channelWireId;
|
||||||
|
private final String senderDisplayName;
|
||||||
|
private final String senderIp;
|
||||||
|
private final SessionNoticeScope scope;
|
||||||
|
private final TransportMode transportMode;
|
||||||
|
|
||||||
|
public SessionNoticeMessage(
|
||||||
|
String pluginId,
|
||||||
|
SessionNoticePhase phase,
|
||||||
|
SessionNoticeChannel channel,
|
||||||
|
String senderDisplayName,
|
||||||
|
String senderIp,
|
||||||
|
SessionNoticeScope scope,
|
||||||
|
TransportMode transportMode
|
||||||
|
) {
|
||||||
|
super(true);
|
||||||
|
this.pluginId = pluginId;
|
||||||
|
this.phase = phase;
|
||||||
|
this.channelWireId = channel != null ? channel.wireId() : null;
|
||||||
|
this.senderDisplayName = senderDisplayName;
|
||||||
|
this.senderIp = senderIp;
|
||||||
|
this.scope = scope;
|
||||||
|
this.transportMode = transportMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPluginId() {
|
||||||
|
return pluginId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionNoticePhase getPhase() {
|
||||||
|
return phase;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionNoticeChannel getChannel() {
|
||||||
|
return SessionNoticeChannel.fromWireId(channelWireId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSenderDisplayName() {
|
||||||
|
return senderDisplayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSenderIp() {
|
||||||
|
return senderIp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionNoticeScope getScope() {
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TransportMode getTransportMode() {
|
||||||
|
return transportMode;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -48,6 +48,8 @@ public final class PluginRoutingPanel extends JPanel {
|
|||||||
private Consumer<Boolean> privateTcpListener = on -> {
|
private Consumer<Boolean> privateTcpListener = on -> {
|
||||||
};
|
};
|
||||||
private boolean everyoneInRoom = true;
|
private boolean everyoneInRoom = true;
|
||||||
|
private boolean receiveOnly;
|
||||||
|
private TransportMode observedReceiveTransport;
|
||||||
private TransportMode lastRoomTransport = TransportMode.MULTICAST_UDP;
|
private TransportMode lastRoomTransport = TransportMode.MULTICAST_UDP;
|
||||||
private TransportMode lastPeerTransport = TransportMode.UNICAST_UDP;
|
private TransportMode lastPeerTransport = TransportMode.UNICAST_UDP;
|
||||||
|
|
||||||
@@ -82,6 +84,8 @@ public final class PluginRoutingPanel extends JPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void bindRoomSession() {
|
public void bindRoomSession() {
|
||||||
|
receiveOnly = false;
|
||||||
|
observedReceiveTransport = null;
|
||||||
everyoneInRoom = true;
|
everyoneInRoom = true;
|
||||||
ipField.setText("");
|
ipField.setText("");
|
||||||
sessionBanner.setText("<html><b>Sessão:</b> Sala geral (Todos)</html>");
|
sessionBanner.setText("<html><b>Sessão:</b> Sala geral (Todos)</html>");
|
||||||
@@ -94,6 +98,8 @@ public final class PluginRoutingPanel extends JPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void bindPeerSession(String ip, String displayName, String receiveScopeHtml) {
|
public void bindPeerSession(String ip, String displayName, String receiveScopeHtml) {
|
||||||
|
receiveOnly = false;
|
||||||
|
observedReceiveTransport = null;
|
||||||
everyoneInRoom = false;
|
everyoneInRoom = false;
|
||||||
String normalized = PeerIpv4.normalize(ip != null ? ip : "");
|
String normalized = PeerIpv4.normalize(ip != null ? ip : "");
|
||||||
if (!normalized.isEmpty()) {
|
if (!normalized.isEmpty()) {
|
||||||
@@ -107,6 +113,30 @@ public final class PluginRoutingPanel extends JPanel {
|
|||||||
applyRoutingState();
|
applyRoutingState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sessão de recebimento: IP e transporte só leitura (definidos pelo remetente).
|
||||||
|
*/
|
||||||
|
public void bindReceiveSession(
|
||||||
|
String ip,
|
||||||
|
String displayName,
|
||||||
|
String receiveScopeHtml,
|
||||||
|
TransportMode observedMode
|
||||||
|
) {
|
||||||
|
receiveOnly = true;
|
||||||
|
everyoneInRoom = false;
|
||||||
|
observedReceiveTransport = observedMode != null ? observedMode : TransportMode.UNICAST_UDP;
|
||||||
|
String normalized = PeerIpv4.normalize(ip != null ? ip : "");
|
||||||
|
if (!normalized.isEmpty()) {
|
||||||
|
ipField.setText(normalized);
|
||||||
|
} else if (ip != null) {
|
||||||
|
ipField.setText(ip);
|
||||||
|
}
|
||||||
|
String who = displayName != null && !displayName.isBlank() ? displayName : normalized;
|
||||||
|
sessionBanner.setText(SessionPeerLabels.sessionBannerHtml(who, normalized.isEmpty() ? ip : normalized));
|
||||||
|
setReceiveScopeHint(receiveScopeHtml);
|
||||||
|
applyRoutingState();
|
||||||
|
}
|
||||||
|
|
||||||
public void setReceiveScopeHint(String html) {
|
public void setReceiveScopeHint(String html) {
|
||||||
if (html == null || html.isBlank()) {
|
if (html == null || html.isBlank()) {
|
||||||
receiveScopeHint.setText("");
|
receiveScopeHint.setText("");
|
||||||
@@ -181,9 +211,20 @@ public final class PluginRoutingPanel extends JPanel {
|
|||||||
private void applyRoutingState() {
|
private void applyRoutingState() {
|
||||||
ipLabel.setVisible(!everyoneInRoom);
|
ipLabel.setVisible(!everyoneInRoom);
|
||||||
ipField.setVisible(!everyoneInRoom);
|
ipField.setVisible(!everyoneInRoom);
|
||||||
|
rowHint.setVisible(!everyoneInRoom);
|
||||||
|
if (receiveOnly) {
|
||||||
|
ipField.setEnabled(false);
|
||||||
|
privateTcpToggle.setEnabled(false);
|
||||||
|
transportCombo.setEnabled(false);
|
||||||
|
DefaultComboBoxModel<TransportMode> readOnly = new DefaultComboBoxModel<>();
|
||||||
|
readOnly.addElement(observedReceiveTransport);
|
||||||
|
transportCombo.setModel(readOnly);
|
||||||
|
transportCombo.setSelectedItem(observedReceiveTransport);
|
||||||
|
updateDestinationModeHint();
|
||||||
|
return;
|
||||||
|
}
|
||||||
ipField.setEnabled(!everyoneInRoom);
|
ipField.setEnabled(!everyoneInRoom);
|
||||||
privateTcpToggle.setEnabled(!everyoneInRoom);
|
privateTcpToggle.setEnabled(!everyoneInRoom);
|
||||||
rowHint.setVisible(!everyoneInRoom);
|
|
||||||
if (everyoneInRoom) {
|
if (everyoneInRoom) {
|
||||||
privateTcpToggle.setSelected(false);
|
privateTcpToggle.setSelected(false);
|
||||||
updatePrivateToggleLabel();
|
updatePrivateToggleLabel();
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package pitiupi.presentation.shell;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sufixos visuais para distinguir abas de envio e recebimento no mesmo plugin.
|
||||||
|
*/
|
||||||
|
public final class SessionTabDirectionLabels {
|
||||||
|
|
||||||
|
private SessionTabDirectionLabels() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String outboundTitle(String displayName) {
|
||||||
|
String base = displayName != null && !displayName.isBlank() ? displayName.trim() : "?";
|
||||||
|
return base + " ↑";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String inboundTitle(String displayName) {
|
||||||
|
String base = displayName != null && !displayName.isBlank() ? displayName.trim() : "?";
|
||||||
|
return base + " ↓";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package pitiupi.presentation.shell;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
class SessionTabDirectionLabelsTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void outboundAndInboundTitles() {
|
||||||
|
assertEquals("Vostro ↑", SessionTabDirectionLabels.outboundTitle("Vostro"));
|
||||||
|
assertEquals("Vostro ↓", SessionTabDirectionLabels.inboundTitle("Vostro"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -305,8 +305,8 @@ public class ChatPanel extends JPanel implements PrivateChatUiSink {
|
|||||||
@Override
|
@Override
|
||||||
public void onPrivateChannelReady(String peerIp) {
|
public void onPrivateChannelReady(String peerIp) {
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
appendSystemLine(privateKey(peerIp), "Canal TCP privado conectado com " + peerIp + ".");
|
appendSystemLine(peerKey(canonicalPeerIp(peerIp)), "Canal TCP privado conectado com " + peerIp + ".");
|
||||||
if (privateKey(peerIp).equals(activeConversationKey)) {
|
if (peerKey(canonicalPeerIp(peerIp)).equals(activeConversationKey)) {
|
||||||
flushTranscriptToPane();
|
flushTranscriptToPane();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -315,8 +315,8 @@ public class ChatPanel extends JPanel implements PrivateChatUiSink {
|
|||||||
@Override
|
@Override
|
||||||
public void onPrivateChannelClosed(String peerIp) {
|
public void onPrivateChannelClosed(String peerIp) {
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
appendSystemLine(privateKey(peerIp), "Canal TCP privado encerrado (" + peerIp + ").");
|
appendSystemLine(peerKey(canonicalPeerIp(peerIp)), "Canal TCP privado encerrado (" + peerIp + ").");
|
||||||
if (privateKey(peerIp).equals(activeConversationKey)) {
|
if (peerKey(canonicalPeerIp(peerIp)).equals(activeConversationKey)) {
|
||||||
flushTranscriptToPane();
|
flushTranscriptToPane();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -324,7 +324,13 @@ public class ChatPanel extends JPanel implements PrivateChatUiSink {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void appendPrivateLine(String peerIp, LegacyMpChatMessage message, boolean outgoing) {
|
public void appendPrivateLine(String peerIp, LegacyMpChatMessage message, boolean outgoing) {
|
||||||
SwingUtilities.invokeLater(() -> appendLegacyLine(privateKey(peerIp), message));
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
String canonical = canonicalPeerIp(peerIp);
|
||||||
|
if (!outgoing) {
|
||||||
|
ensurePeerTabForIncoming(canonical, message.getNome());
|
||||||
|
}
|
||||||
|
appendLegacyLine(peerKey(canonical), message);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buildUi() {
|
private void buildUi() {
|
||||||
@@ -418,8 +424,8 @@ public class ChatPanel extends JPanel implements PrivateChatUiSink {
|
|||||||
if (privateChatController != null) {
|
if (privateChatController != null) {
|
||||||
privateChatController.openChat(user);
|
privateChatController.openChat(user);
|
||||||
}
|
}
|
||||||
appendSystemLine(privateKey(PeerIpv4.fromInetAddress(user.getAddress())),
|
appendSystemLine(peerKey(canonicalPeerIp(PeerIpv4.fromInetAddress(user.getAddress()))),
|
||||||
"Canal TCP privado conectando… envie de novo em instantes.");
|
"Canal TCP privado conectando…");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -499,7 +505,7 @@ public class ChatPanel extends JPanel implements PrivateChatUiSink {
|
|||||||
public void receiveLegacyMessage(String tipo, String nome, String ip, String text) {
|
public void receiveLegacyMessage(String tipo, String nome, String ip, String text) {
|
||||||
runOnEdt(() -> {
|
runOnEdt(() -> {
|
||||||
LegacyMpChatMessage legacy = new LegacyMpChatMessage(nome, ip, tipo, text);
|
LegacyMpChatMessage legacy = new LegacyMpChatMessage(nome, ip, tipo, text);
|
||||||
String bucket = ip != null && !ip.isBlank() ? privateKey(canonicalPeerIp(ip)) : ROOM_KEY;
|
String bucket = ip != null && !ip.isBlank() ? peerKey(canonicalPeerIp(ip)) : ROOM_KEY;
|
||||||
appendLegacyLine(bucket, legacy);
|
appendLegacyLine(bucket, legacy);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -551,7 +557,7 @@ public class ChatPanel extends JPanel implements PrivateChatUiSink {
|
|||||||
}
|
}
|
||||||
if (key != null && key.startsWith("peer:") && !PluginSessionKeys.PEER_DRAFT.equals(key)) {
|
if (key != null && key.startsWith("peer:") && !PluginSessionKeys.PEER_DRAFT.equals(key)) {
|
||||||
String tabIp = canonicalPeerIp(key.substring("peer:".length()));
|
String tabIp = canonicalPeerIp(key.substring("peer:".length()));
|
||||||
return peerKey(tabIp).equals(bucket);
|
return peerKey(tabIp).equals(bucket) || privateKey(tabIp).equals(bucket);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -609,9 +615,6 @@ public class ChatPanel extends JPanel implements PrivateChatUiSink {
|
|||||||
if (ip == null || ip.isEmpty()) {
|
if (ip == null || ip.isEmpty()) {
|
||||||
return ROOM_KEY;
|
return ROOM_KEY;
|
||||||
}
|
}
|
||||||
if (routing.privateTcpOn()) {
|
|
||||||
return privateKey(ip);
|
|
||||||
}
|
|
||||||
return peerKey(ip);
|
return peerKey(ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ public class PrivateChatController {
|
|||||||
private final PrivateChatHost host;
|
private final PrivateChatHost host;
|
||||||
private final Map<String, TcpConnection> connections = new HashMap<>();
|
private final Map<String, TcpConnection> connections = new HashMap<>();
|
||||||
private final Set<String> pendingConnections = new HashSet<>();
|
private final Set<String> pendingConnections = new HashSet<>();
|
||||||
|
private final Map<String, PendingSend> pendingTextByAddress = new HashMap<>();
|
||||||
private PrivateChatUiSink uiSink;
|
private PrivateChatUiSink uiSink;
|
||||||
|
|
||||||
public PrivateChatController(ConnectionManager connectionManager, PrivateChatHost host) {
|
public PrivateChatController(ConnectionManager connectionManager, PrivateChatHost host) {
|
||||||
@@ -38,10 +39,9 @@ public class PrivateChatController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String address = PeerIpv4.fromInetAddress(user.getAddress());
|
String address = PeerIpv4.fromInetAddress(user.getAddress());
|
||||||
if (connections.containsKey(address)) {
|
TcpConnection existing = connections.get(address);
|
||||||
if (uiSink != null) {
|
if (existing != null && !existing.isClosed()) {
|
||||||
uiSink.onPrivateChannelReady(address);
|
flushPendingSend(address, user);
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (pendingConnections.contains(address)) {
|
if (pendingConnections.contains(address)) {
|
||||||
@@ -51,6 +51,17 @@ public class PrivateChatController {
|
|||||||
host.openConnection(address);
|
host.openConnection(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void registerOutgoingConnection(String address, TcpConnection connection) {
|
||||||
|
if (address == null || connection == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String canonical = PeerIpv4.normalize(address);
|
||||||
|
pendingConnections.remove(canonical);
|
||||||
|
replaceConnection(canonical, connection, false);
|
||||||
|
User user = new User("", connection.getAddress(), System.currentTimeMillis(), null);
|
||||||
|
flushPendingSend(canonical, user);
|
||||||
|
}
|
||||||
|
|
||||||
public void receiveMessage(TcpConnection connection, LegacyMpChatMessage message) {
|
public void receiveMessage(TcpConnection connection, LegacyMpChatMessage message) {
|
||||||
if (uiSink != null) {
|
if (uiSink != null) {
|
||||||
uiSink.appendPrivateLine(addressKey(connection), message, false);
|
uiSink.appendPrivateLine(addressKey(connection), message, false);
|
||||||
@@ -60,8 +71,17 @@ public class PrivateChatController {
|
|||||||
public void connectionReceived(TcpConnection connection) {
|
public void connectionReceived(TcpConnection connection) {
|
||||||
String address = addressKey(connection);
|
String address = addressKey(connection);
|
||||||
pendingConnections.remove(address);
|
pendingConnections.remove(address);
|
||||||
|
replaceConnection(address, connection, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void replaceConnection(String address, TcpConnection connection, boolean notifyReady) {
|
||||||
|
TcpConnection existing = connections.get(address);
|
||||||
|
if (existing != null && !existing.isClosed() && existing != connection) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
boolean first = existing == null || existing.isClosed();
|
||||||
connections.put(address, connection);
|
connections.put(address, connection);
|
||||||
if (uiSink != null) {
|
if (notifyReady && first && uiSink != null) {
|
||||||
uiSink.onPrivateChannelReady(address);
|
uiSink.onPrivateChannelReady(address);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -69,6 +89,8 @@ public class PrivateChatController {
|
|||||||
public void closeChat(TcpConnection connection) {
|
public void closeChat(TcpConnection connection) {
|
||||||
String address = addressKey(connection);
|
String address = addressKey(connection);
|
||||||
connections.remove(address);
|
connections.remove(address);
|
||||||
|
pendingConnections.remove(address);
|
||||||
|
pendingTextByAddress.remove(address);
|
||||||
if (uiSink != null) {
|
if (uiSink != null) {
|
||||||
uiSink.onPrivateChannelClosed(address);
|
uiSink.onPrivateChannelClosed(address);
|
||||||
}
|
}
|
||||||
@@ -80,9 +102,24 @@ public class PrivateChatController {
|
|||||||
}
|
}
|
||||||
String address = PeerIpv4.fromInetAddress(user.getAddress());
|
String address = PeerIpv4.fromInetAddress(user.getAddress());
|
||||||
TcpConnection connection = connections.get(address);
|
TcpConnection connection = connections.get(address);
|
||||||
if (connection == null) {
|
if (connection == null || connection.isClosed()) {
|
||||||
|
if (pendingConnections.contains(address)) {
|
||||||
|
pendingTextByAddress.put(address, new PendingSend(user, text));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return sendOnConnection(connection, address, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void flushPendingSend(String address, User user) {
|
||||||
|
PendingSend pending = pendingTextByAddress.remove(address);
|
||||||
|
if (pending != null) {
|
||||||
|
sendText(user != null ? user : pending.user, pending.text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean sendOnConnection(TcpConnection connection, String address, String text) {
|
||||||
try {
|
try {
|
||||||
LegacyMpChatMessage message = new LegacyMpChatMessage(
|
LegacyMpChatMessage message = new LegacyMpChatMessage(
|
||||||
connectionManager.getUserName(),
|
connectionManager.getUserName(),
|
||||||
@@ -105,6 +142,10 @@ public class PrivateChatController {
|
|||||||
if (user == null || user.getAddress() == null) {
|
if (user == null || user.getAddress() == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return connections.containsKey(PeerIpv4.fromInetAddress(user.getAddress()));
|
TcpConnection connection = connections.get(PeerIpv4.fromInetAddress(user.getAddress()));
|
||||||
|
return connection != null && !connection.isClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
private record PendingSend(User user, String text) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,11 +139,12 @@ public class ChatPlugin extends Plugin implements PrivateChatHost {
|
|||||||
@Override
|
@Override
|
||||||
public void openConnection(String ip) {
|
public void openConnection(String ip) {
|
||||||
try {
|
try {
|
||||||
connectionManager.connectPluginTcp(
|
TcpConnection connection = connectionManager.connectPluginTcp(
|
||||||
InetAddress.getByName(ip),
|
InetAddress.getByName(ip),
|
||||||
getIdentifier(),
|
getIdentifier(),
|
||||||
getIdentifier()
|
getIdentifier()
|
||||||
);
|
);
|
||||||
|
privateChatController.registerOutgoingConnection(ip, connection);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.out.println("Error opening connection: " + e.getMessage());
|
System.out.println("Error opening connection: " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,11 @@ import pitiupi.application.messaging.TransportMode;
|
|||||||
import pitiupi.api.plugin.PeerActivityFocusTokens;
|
import pitiupi.api.plugin.PeerActivityFocusTokens;
|
||||||
import pitiupi.application.presence.UserPeerView;
|
import pitiupi.application.presence.UserPeerView;
|
||||||
import pitiupi.control.ConnectionManager;
|
import pitiupi.control.ConnectionManager;
|
||||||
|
import pitiupi.application.session.SessionNoticeChannel;
|
||||||
|
import pitiupi.application.session.SessionNoticePhase;
|
||||||
|
import pitiupi.application.session.SessionNoticeScope;
|
||||||
|
import pitiupi.models.Messages.SessionNoticeMessage;
|
||||||
|
import pitiupi.presentation.shell.LocalNetworkInfo;
|
||||||
import pitiupi.presentation.shell.PeerDraftPanel;
|
import pitiupi.presentation.shell.PeerDraftPanel;
|
||||||
import pitiupi.presentation.shell.PeerIpv4;
|
import pitiupi.presentation.shell.PeerIpv4;
|
||||||
import pitiupi.presentation.shell.PluginRoutingPanel;
|
import pitiupi.presentation.shell.PluginRoutingPanel;
|
||||||
@@ -14,6 +19,7 @@ import pitiupi.presentation.shell.PluginSessionKeys;
|
|||||||
import pitiupi.presentation.shell.PluginSessionWorkspace;
|
import pitiupi.presentation.shell.PluginSessionWorkspace;
|
||||||
import pitiupi.presentation.shell.SessionPeerLabels;
|
import pitiupi.presentation.shell.SessionPeerLabels;
|
||||||
import pitiupi.presentation.shell.SessionTabBar;
|
import pitiupi.presentation.shell.SessionTabBar;
|
||||||
|
import pitiupi.presentation.shell.SessionTabDirectionLabels;
|
||||||
import pitiupi.presentation.shell.ui.ShellUi;
|
import pitiupi.presentation.shell.ui.ShellUi;
|
||||||
|
|
||||||
import pitiupi.screenCast.control.AlarmClock;
|
import pitiupi.screenCast.control.AlarmClock;
|
||||||
@@ -74,6 +80,8 @@ public class ScreenCastPanel extends JPanel {
|
|||||||
private final Map<String, String> peerIpsByViewKey = new HashMap<>();
|
private final Map<String, String> peerIpsByViewKey = new HashMap<>();
|
||||||
private final Map<String, String> peerDisplayNames = new HashMap<>();
|
private final Map<String, String> peerDisplayNames = new HashMap<>();
|
||||||
private final Map<String, ScreenCastReceiveScope> receiveScopes = new HashMap<>();
|
private final Map<String, ScreenCastReceiveScope> receiveScopes = new HashMap<>();
|
||||||
|
private final Map<String, TransportMode> receiveTransportByViewKey = new HashMap<>();
|
||||||
|
private PluginRoutingPanel.RoutingSnapshot lastShareRoutingSnapshot;
|
||||||
private final CardLayout bodyLayout = new CardLayout();
|
private final CardLayout bodyLayout = new CardLayout();
|
||||||
private final JPanel bodyCards = new JPanel(bodyLayout);
|
private final JPanel bodyCards = new JPanel(bodyLayout);
|
||||||
private final Map<String, JPanel> bodyPanels = new HashMap<>();
|
private final Map<String, JPanel> bodyPanels = new HashMap<>();
|
||||||
@@ -105,7 +113,11 @@ public class ScreenCastPanel extends JPanel {
|
|||||||
if (!ScreenCastOutboundTabs.isOutboundShareTabKey(key)) {
|
if (!ScreenCastOutboundTabs.isOutboundShareTabKey(key)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sharingByTabKey.put(key, shareToggle.isSelected());
|
boolean nowOn = shareToggle.isSelected();
|
||||||
|
if (!nowOn) {
|
||||||
|
sendShareEndedNoticeForActiveTab();
|
||||||
|
}
|
||||||
|
sharingByTabKey.put(key, nowOn);
|
||||||
updateShareToggleLabel();
|
updateShareToggleLabel();
|
||||||
applySharingForActiveTab();
|
applySharingForActiveTab();
|
||||||
});
|
});
|
||||||
@@ -136,7 +148,7 @@ public class ScreenCastPanel extends JPanel {
|
|||||||
private void initSessionTabs() {
|
private void initSessionTabs() {
|
||||||
sessionTabs.addOrSelectTab(
|
sessionTabs.addOrSelectTab(
|
||||||
PluginSessionKeys.ROOM,
|
PluginSessionKeys.ROOM,
|
||||||
"Todos",
|
SessionTabDirectionLabels.outboundTitle("Todos"),
|
||||||
"Transmissão para todos na rede (multicast)",
|
"Transmissão para todos na rede (multicast)",
|
||||||
new JPanel(),
|
new JPanel(),
|
||||||
false
|
false
|
||||||
@@ -164,10 +176,15 @@ public class ScreenCastPanel extends JPanel {
|
|||||||
if (key != null && key.startsWith("view:")) {
|
if (key != null && key.startsWith("view:")) {
|
||||||
peerIpsByViewKey.remove(key);
|
peerIpsByViewKey.remove(key);
|
||||||
receiveScopes.remove(key);
|
receiveScopes.remove(key);
|
||||||
|
receiveTransportByViewKey.remove(key);
|
||||||
bodyPanels.remove(key);
|
bodyPanels.remove(key);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
boolean wasSharing = isSharingForTab(key);
|
boolean wasSharing = isSharingForTab(key);
|
||||||
|
if (wasSharing) {
|
||||||
|
applyRoutingForSessionKey(key);
|
||||||
|
sendShareEndedNotice(routingPanel.snapshot());
|
||||||
|
}
|
||||||
sharingByTabKey.remove(key);
|
sharingByTabKey.remove(key);
|
||||||
if (wasSharing && key != null && key.equals(shareContextTabKey)) {
|
if (wasSharing && key != null && key.equals(shareContextTabKey)) {
|
||||||
cancelShareTimer();
|
cancelShareTimer();
|
||||||
@@ -304,6 +321,7 @@ public class ScreenCastPanel extends JPanel {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
syncRoutingFromActiveSessionTab();
|
syncRoutingFromActiveSessionTab();
|
||||||
|
lastShareRoutingSnapshot = routingPanel.snapshot();
|
||||||
timer = new Timer();
|
timer = new Timer();
|
||||||
alarmClock = new AlarmClock(this);
|
alarmClock = new AlarmClock(this);
|
||||||
timer.scheduleAtFixedRate(alarmClock, 0, TIME);
|
timer.scheduleAtFixedRate(alarmClock, 0, TIME);
|
||||||
@@ -356,14 +374,25 @@ public class ScreenCastPanel extends JPanel {
|
|||||||
String ip = peerIpsByViewKey.get(key);
|
String ip = peerIpsByViewKey.get(key);
|
||||||
String title = key.substring("view:".length());
|
String title = key.substring("view:".length());
|
||||||
ScreenCastReceiveScope scope = receiveScopes.getOrDefault(key, ScreenCastReceiveScope.BROADCAST);
|
ScreenCastReceiveScope scope = receiveScopes.getOrDefault(key, ScreenCastReceiveScope.BROADCAST);
|
||||||
if (ip != null && !ip.isBlank()) {
|
TransportMode mode = receiveTransportByViewKey.getOrDefault(
|
||||||
routingPanel.bindPeerSession(ip, title, scope.bannerHtml());
|
key,
|
||||||
} else {
|
transportForReceiveScope(scope)
|
||||||
routingPanel.bindPeerSession("", title, scope.bannerHtml());
|
);
|
||||||
}
|
routingPanel.bindReceiveSession(
|
||||||
|
ip != null ? ip : "",
|
||||||
|
title,
|
||||||
|
scope.bannerHtml(),
|
||||||
|
mode
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static TransportMode transportForReceiveScope(ScreenCastReceiveScope scope) {
|
||||||
|
return scope == ScreenCastReceiveScope.DIRECT
|
||||||
|
? TransportMode.UNICAST_UDP
|
||||||
|
: TransportMode.MULTICAST_UDP;
|
||||||
|
}
|
||||||
|
|
||||||
private void openDraftPeerTab() {
|
private void openDraftPeerTab() {
|
||||||
String previous = sessionTabs.selectedKey();
|
String previous = sessionTabs.selectedKey();
|
||||||
if (previous != null && !PluginSessionKeys.PEER_DRAFT.equals(previous)) {
|
if (previous != null && !PluginSessionKeys.PEER_DRAFT.equals(previous)) {
|
||||||
@@ -401,11 +430,12 @@ public class ScreenCastPanel extends JPanel {
|
|||||||
peerDisplayNames.put(canonical, resolved);
|
peerDisplayNames.put(canonical, resolved);
|
||||||
ensureBodyPanel(key);
|
ensureBodyPanel(key);
|
||||||
setBodyMessage(key, ShellUi.mutedLabel(IDLE_PEER_HTML));
|
setBodyMessage(key, ShellUi.mutedLabel(IDLE_PEER_HTML));
|
||||||
|
String tabTitle = SessionTabDirectionLabels.outboundTitle(resolved);
|
||||||
if (sessionTabs.hasTab(key)) {
|
if (sessionTabs.hasTab(key)) {
|
||||||
sessionTabs.setTabTitle(key, resolved, canonical);
|
sessionTabs.setTabTitle(key, tabTitle, canonical);
|
||||||
sessionTabs.selectTab(key);
|
sessionTabs.selectTab(key);
|
||||||
} else {
|
} else {
|
||||||
sessionTabs.addOrSelectTab(key, resolved, canonical, new JPanel(), true);
|
sessionTabs.addOrSelectTab(key, tabTitle, canonical, new JPanel(), true);
|
||||||
}
|
}
|
||||||
routingPanel.bindPeerSession(canonical, resolved);
|
routingPanel.bindPeerSession(canonical, resolved);
|
||||||
showBodyCard(key);
|
showBodyCard(key);
|
||||||
@@ -440,7 +470,7 @@ public class ScreenCastPanel extends JPanel {
|
|||||||
if (!sessionTabs.hasTab(PluginSessionKeys.ROOM)) {
|
if (!sessionTabs.hasTab(PluginSessionKeys.ROOM)) {
|
||||||
sessionTabs.addOrSelectTab(
|
sessionTabs.addOrSelectTab(
|
||||||
PluginSessionKeys.ROOM,
|
PluginSessionKeys.ROOM,
|
||||||
"Todos",
|
SessionTabDirectionLabels.outboundTitle("Todos"),
|
||||||
"Transmissão para todos na rede (multicast)",
|
"Transmissão para todos na rede (multicast)",
|
||||||
new JPanel(),
|
new JPanel(),
|
||||||
false
|
false
|
||||||
@@ -464,6 +494,9 @@ public class ScreenCastPanel extends JPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void stopSharing() {
|
public void stopSharing() {
|
||||||
|
if (isSharingForTab(sessionTabs.selectedKey())) {
|
||||||
|
sendShareEndedNoticeForActiveTab();
|
||||||
|
}
|
||||||
sharingByTabKey.replaceAll((k, v) -> false);
|
sharingByTabKey.replaceAll((k, v) -> false);
|
||||||
setShareToggleSelectedSilently(false);
|
setShareToggleSelectedSilently(false);
|
||||||
cancelShareTimer();
|
cancelShareTimer();
|
||||||
@@ -560,9 +593,12 @@ public class ScreenCastPanel extends JPanel {
|
|||||||
updateTabError(viewKey, message.getName(), "Não foi possível decodificar a imagem.", scope);
|
updateTabError(viewKey, message.getName(), "Não foi possível decodificar a imagem.", scope);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
receiveScopes.put(viewKey, scope != null ? scope : ScreenCastReceiveScope.BROADCAST);
|
ScreenCastReceiveScope resolvedScope = scope != null ? scope : ScreenCastReceiveScope.BROADCAST;
|
||||||
|
receiveScopes.put(viewKey, resolvedScope);
|
||||||
|
receiveTransportByViewKey.put(viewKey, transportForReceiveScope(resolvedScope));
|
||||||
resolveSenderIp(message.getName(), viewKey);
|
resolveSenderIp(message.getName(), viewKey);
|
||||||
sessionTabs.addTabIfAbsent(viewKey, message.getName(), message.getName(), new JPanel(), true);
|
String tabTitle = SessionTabDirectionLabels.inboundTitle(message.getName());
|
||||||
|
sessionTabs.addTabIfAbsent(viewKey, tabTitle, message.getName(), new JPanel(), true);
|
||||||
setBodyContent(viewKey, new JScrollPane(new JLabel(new ImageIcon(image))));
|
setBodyContent(viewKey, new JScrollPane(new JLabel(new ImageIcon(image))));
|
||||||
if (viewKey.equals(sessionTabs.selectedKey())) {
|
if (viewKey.equals(sessionTabs.selectedKey())) {
|
||||||
showBodyCard(viewKey);
|
showBodyCard(viewKey);
|
||||||
@@ -586,15 +622,83 @@ public class ScreenCastPanel extends JPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateTabError(String tabKey, String title, String error, ScreenCastReceiveScope scope) {
|
private void updateTabError(String tabKey, String title, String error, ScreenCastReceiveScope scope) {
|
||||||
receiveScopes.put(tabKey, scope != null ? scope : ScreenCastReceiveScope.BROADCAST);
|
ScreenCastReceiveScope resolvedScope = scope != null ? scope : ScreenCastReceiveScope.BROADCAST;
|
||||||
|
receiveScopes.put(tabKey, resolvedScope);
|
||||||
|
receiveTransportByViewKey.put(tabKey, transportForReceiveScope(resolvedScope));
|
||||||
JLabel label = new JLabel("<html><p style='color:#c62828'>" + error + "</p></html>");
|
JLabel label = new JLabel("<html><p style='color:#c62828'>" + error + "</p></html>");
|
||||||
setBodyContent(tabKey, label);
|
setBodyContent(tabKey, label);
|
||||||
sessionTabs.addTabIfAbsent(tabKey, title, title, new JPanel(), true);
|
String tabTitle = SessionTabDirectionLabels.inboundTitle(title);
|
||||||
|
sessionTabs.addTabIfAbsent(tabKey, tabTitle, title, new JPanel(), true);
|
||||||
if (tabKey.equals(sessionTabs.selectedKey())) {
|
if (tabKey.equals(sessionTabs.selectedKey())) {
|
||||||
showBodyCard(tabKey);
|
showBodyCard(tabKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void closeReceiveTab(String senderDisplayName) {
|
||||||
|
if (senderDisplayName == null || senderDisplayName.isBlank()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String viewKey = "view:" + senderDisplayName;
|
||||||
|
if (!sessionTabs.hasTab(viewKey)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
if (viewKey.equals(sessionTabs.selectedKey())) {
|
||||||
|
String fallback = sessionTabs.hasTab(PluginSessionKeys.ROOM)
|
||||||
|
? PluginSessionKeys.ROOM
|
||||||
|
: lastSessionKey;
|
||||||
|
if (fallback != null && sessionTabs.hasTab(fallback) && !fallback.startsWith("view:")) {
|
||||||
|
sessionTabs.selectTabSilently(fallback);
|
||||||
|
onSessionTabSelected(fallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
peerIpsByViewKey.remove(viewKey);
|
||||||
|
receiveScopes.remove(viewKey);
|
||||||
|
receiveTransportByViewKey.remove(viewKey);
|
||||||
|
bodyPanels.remove(viewKey);
|
||||||
|
sessionTabs.closeTab(viewKey);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendShareEndedNoticeForActiveTab() {
|
||||||
|
PluginRoutingPanel.RoutingSnapshot snap = lastShareRoutingSnapshot != null
|
||||||
|
? lastShareRoutingSnapshot
|
||||||
|
: routingPanel.snapshot();
|
||||||
|
sendShareEndedNotice(snap);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendShareEndedNotice(PluginRoutingPanel.RoutingSnapshot routing) {
|
||||||
|
if (pluginId == null || routing == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TransportMode mode = routing.transport();
|
||||||
|
if (mode == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
SessionNoticeScope scope = routing.everyoneInRoom()
|
||||||
|
? SessionNoticeScope.BROADCAST
|
||||||
|
: SessionNoticeScope.DIRECT;
|
||||||
|
String localIp = PeerIpv4.normalize(LocalNetworkInfo.bestLocalIpv4());
|
||||||
|
SessionNoticeMessage notice = new SessionNoticeMessage(
|
||||||
|
pluginId,
|
||||||
|
SessionNoticePhase.END,
|
||||||
|
SessionNoticeChannel.SCREEN_CAST,
|
||||||
|
connectionManager.getUserName(),
|
||||||
|
localIp,
|
||||||
|
scope,
|
||||||
|
mode
|
||||||
|
);
|
||||||
|
SendTarget target = resolveSendTarget(routing);
|
||||||
|
if (target != null) {
|
||||||
|
MessageDispatcher.send(connectionManager, notice, target, mode);
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.getLogger(ScreenCastPanel.class.getName()).log(Level.WARNING, ex.getMessage());
|
||||||
|
}
|
||||||
|
lastShareRoutingSnapshot = null;
|
||||||
|
}
|
||||||
|
|
||||||
private void ensureBodyPanel(String key) {
|
private void ensureBodyPanel(String key) {
|
||||||
if (!bodyPanels.containsKey(key)) {
|
if (!bodyPanels.containsKey(key)) {
|
||||||
JPanel panel = new JPanel(new BorderLayout());
|
JPanel panel = new JPanel(new BorderLayout());
|
||||||
|
|||||||
@@ -11,7 +11,10 @@ import pitiupi.application.peeractions.PeerTarget;
|
|||||||
import pitiupi.control.ConnectionManager;
|
import pitiupi.control.ConnectionManager;
|
||||||
import pitiupi.control.GuiManager;
|
import pitiupi.control.GuiManager;
|
||||||
import pitiupi.models.Menus.MenuCategory;
|
import pitiupi.models.Menus.MenuCategory;
|
||||||
|
import pitiupi.application.session.SessionNoticeChannel;
|
||||||
|
import pitiupi.application.session.SessionNoticePhase;
|
||||||
import pitiupi.models.Messages.Message;
|
import pitiupi.models.Messages.Message;
|
||||||
|
import pitiupi.models.Messages.SessionNoticeMessage;
|
||||||
import pitiupi.models.Plugin;
|
import pitiupi.models.Plugin;
|
||||||
import pitiupi.screenCast.GUI.ScreenCastMenu;
|
import pitiupi.screenCast.GUI.ScreenCastMenu;
|
||||||
import pitiupi.screenCast.GUI.ScreenCastPanel;
|
import pitiupi.screenCast.GUI.ScreenCastPanel;
|
||||||
@@ -98,11 +101,15 @@ public class ScreenCastPlugin extends Plugin {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Class<? extends Message>> getMessages() {
|
public List<Class<? extends Message>> getMessages() {
|
||||||
return List.of(ScreenCastMessage.class);
|
return List.of(ScreenCastMessage.class, SessionNoticeMessage.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void receiveMessage(Message message) {
|
public void receiveMessage(Message message) {
|
||||||
|
if (message instanceof SessionNoticeMessage notice && screenCastPanel != null) {
|
||||||
|
handleSessionNotice(notice);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (message instanceof ScreenCastMessage msg && screenCastPanel != null) {
|
if (message instanceof ScreenCastMessage msg && screenCastPanel != null) {
|
||||||
if (isOwnOutboundFrame(msg)) {
|
if (isOwnOutboundFrame(msg)) {
|
||||||
return;
|
return;
|
||||||
@@ -134,4 +141,25 @@ public class ScreenCastPlugin extends Plugin {
|
|||||||
return ScreenCastReceiveScope.BROADCAST;
|
return ScreenCastReceiveScope.BROADCAST;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleSessionNotice(SessionNoticeMessage notice) {
|
||||||
|
if (notice.getChannel() != SessionNoticeChannel.SCREEN_CAST) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (notice.getPhase() != SessionNoticePhase.END) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!getIdentifier().equals(notice.getPluginId())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String sender = notice.getSenderDisplayName();
|
||||||
|
if (sender == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String localName = connectionManager != null ? connectionManager.getUserName() : null;
|
||||||
|
if (localName != null && localName.equals(sender.trim())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
screenCastPanel.closeReceiveTab(sender);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user