diff --git a/.gitignore b/.gitignore index 237cb8c..79f7dce 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,3 @@ -/pitiupi/build/ -/pitiupi/dist/ -/pitiupi.chat/build/ -/pitiupi.chat/dist/ -/pitiupi.chat/nbproject/private/ -/pitiupi.screenCast/build/ -/pitiupi.screenCast/dist/ - -.idea/ +build/ +dist/ +*.class diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..30cf57e --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Ignored default folder with query files +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/libraries/pitiupi.xml b/.idea/libraries/pitiupi.xml new file mode 100644 index 0000000..4207ae9 --- /dev/null +++ b/.idea/libraries/pitiupi.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/pitiupi_chat.xml b/.idea/libraries/pitiupi_chat.xml new file mode 100644 index 0000000..a84f0a3 --- /dev/null +++ b/.idea/libraries/pitiupi_chat.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/pitiupi_screenCast.xml b/.idea/libraries/pitiupi_screenCast.xml new file mode 100644 index 0000000..410fe64 --- /dev/null +++ b/.idea/libraries/pitiupi_screenCast.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/plugins.xml b/.idea/libraries/plugins.xml new file mode 100644 index 0000000..5d318c9 --- /dev/null +++ b/.idea/libraries/plugins.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..b1566b1 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..b6bf55e --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/pitiupi.iml b/.idea/pitiupi.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/.idea/pitiupi.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index e2b66f8..1f92201 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,13 @@ # pitiupi -Ferramenta p2p para práticas em rede +Este repositório tem 4 projetos Java que são independentes: -Este repositório tem 3 projetos Java que são independentes: +### Sistema: - A ferramenta pitiupi (P2P) + +### Plugins: - Um plugin de Chat +- Um plugin de Chat multiprotocolo (TCP/UDP) - Um plugin de ScreenCast Cada um destes projetos deve ser compilado individualmente. Para isso, use a ferramenta ant no diretório do projeto. @@ -21,9 +24,26 @@ Para criar links simbólicos no Linux, use o comando: ``` cd pitiupi/plugins ln -s ../../pitiupi.chat/dist/*.jar . +ln -s ../../pitiupi.mpChat/dist/*.jar . ln -s ../../pitiupi.screenCast/dist/*.jar . ``` Isso deve resolver! -Para criar um novo plugin: +## Como um plugin conversa com o pitiupi +Um plugin nunca recebe acesso direto às classes internas do núcleo (ConnectionManager, GuiManager). Em vez disso, recebe uma facade, a classe Pitiupi, que expõe só o que um plugin precisa: enviar mensagem (multicast, unicast ou tcp), abrir conexão tcp privada, ler nome de usuário e porta, criar menu. + +```java +public abstract void createPlugin(Pitiupi pitiupi); +``` + +## Para criar um novo plugin + +1. Implemente `pitiupi.models.Plugin`, incluindo `createPlugin(Pitiupi pitiupi)` +2. Registre a classe em `src/META-INF/services/pitiupi.models.Plugin`, com o nome completo da classe numa linha +3. Compile com `ant jar` +4. Linke o `.jar` em `pitiupi/plugins` + +Use `pitiupi.chat` como referência — é o exemplo mais simples e completo. + +Plugins que ainda não foram atualizados para a Pitiupi (como uma versão mais antiga do screenCast) não compilam contra o núcleo atual e precisam de migração antes de serem usados. diff --git a/pitiupi.chat/build/built-jar.properties b/pitiupi.chat/build/built-jar.properties deleted file mode 100644 index 54fc683..0000000 --- a/pitiupi.chat/build/built-jar.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Fri, 02 Oct 2026 20:11:21 -0300 - - -C\:\\Users\\extre\\OneDrive\\Documentos\\Faculdade\\2026_02\\Monitoria\\Pitiupi\\pitiupi.chat= - -C\:\\Users\\extre\\OneDrive\\Documentos\\Faculdade\\2026_02\\Monitoria\\Pitiupi\\pitiupi= diff --git a/pitiupi.chat/build/classes/META-INF/services/pitiupi.plugin.Plugin b/pitiupi.chat/build/classes/META-INF/services/pitiupi.plugin.Plugin deleted file mode 100644 index eea07c1..0000000 --- a/pitiupi.chat/build/classes/META-INF/services/pitiupi.plugin.Plugin +++ /dev/null @@ -1 +0,0 @@ -pitiupi.chat.plugin.ChatPlugin diff --git a/pitiupi.chat/build/classes/pitiupi/chat/GUI/ChatMenu.class b/pitiupi.chat/build/classes/pitiupi/chat/GUI/ChatMenu.class deleted file mode 100644 index af3942e..0000000 Binary files a/pitiupi.chat/build/classes/pitiupi/chat/GUI/ChatMenu.class and /dev/null differ diff --git a/pitiupi.chat/build/classes/pitiupi/chat/GUI/ChatWindow$1.class b/pitiupi.chat/build/classes/pitiupi/chat/GUI/ChatWindow$1.class deleted file mode 100644 index 3d3023d..0000000 Binary files a/pitiupi.chat/build/classes/pitiupi/chat/GUI/ChatWindow$1.class and /dev/null differ diff --git a/pitiupi.chat/build/classes/pitiupi/chat/GUI/ChatWindow.class b/pitiupi.chat/build/classes/pitiupi/chat/GUI/ChatWindow.class deleted file mode 100644 index 34763e9..0000000 Binary files a/pitiupi.chat/build/classes/pitiupi/chat/GUI/ChatWindow.class and /dev/null differ diff --git a/pitiupi.chat/build/classes/pitiupi/chat/net/ChatMessage.class b/pitiupi.chat/build/classes/pitiupi/chat/net/ChatMessage.class deleted file mode 100644 index 661793b..0000000 Binary files a/pitiupi.chat/build/classes/pitiupi/chat/net/ChatMessage.class and /dev/null differ diff --git a/pitiupi.chat/build/classes/pitiupi/chat/plugin/ChatPlugin.class b/pitiupi.chat/build/classes/pitiupi/chat/plugin/ChatPlugin.class deleted file mode 100644 index dca047b..0000000 Binary files a/pitiupi.chat/build/classes/pitiupi/chat/plugin/ChatPlugin.class and /dev/null differ diff --git a/pitiupi.chat/nbproject/project.properties b/pitiupi.chat/nbproject/project.properties index b87aa66..6f8cc9a 100644 --- a/pitiupi.chat/nbproject/project.properties +++ b/pitiupi.chat/nbproject/project.properties @@ -47,8 +47,8 @@ javac.modulepath= javac.processormodulepath= javac.processorpath=\ ${javac.classpath} -javac.source=25 -javac.target=25 +javac.source=17 +javac.target=17 javac.test.classpath=\ ${javac.classpath}:\ ${build.classes.dir} diff --git a/pitiupi.chat/src/pitiupi/chat/GUI/ChatWindow.java b/pitiupi.chat/src/pitiupi/chat/GUI/ChatWindow.java index 7aeed97..708ddb9 100644 --- a/pitiupi.chat/src/pitiupi/chat/GUI/ChatWindow.java +++ b/pitiupi.chat/src/pitiupi/chat/GUI/ChatWindow.java @@ -8,13 +8,13 @@ package pitiupi.chat.GUI; import java.text.SimpleDateFormat; import java.util.Date; import pitiupi.chat.net.ChatMessage; -import pitiupi.control.ConnectionManager; +import pitiupi.plugin.Pitiupi; import pitiupi.models.Messages.Message; import javax.swing.*; /** - * + * @author tony * @author flavio */ public class ChatWindow extends JDialog { @@ -28,13 +28,12 @@ public class ChatWindow extends JDialog { private JSplitPane jSplitPane1; private JTextArea jTextArea1; - private final ConnectionManager connectionManager; - - public ChatWindow(ConnectionManager connectionManager) { - this.connectionManager = connectionManager; - initComponents(); - } + private final Pitiupi pitiupi; + public ChatWindow(Pitiupi pitiupi) { + this.pitiupi = pitiupi; + initComponents(); + } /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always @@ -93,13 +92,13 @@ public class ChatWindow extends JDialog { if (jTextArea1.getText().trim().isEmpty()) { return; } - ChatMessage message = new ChatMessage(connectionManager.getUserName(), jTextArea1.getText()); + ChatMessage message = new ChatMessage(pitiupi.getUserName(), jTextArea1.getText()); this.sendChatMessage(message); jTextArea1.setText(""); } public void sendChatMessage(Message message) { - connectionManager.sendUdpMulticast(message); + pitiupi.sendUdpMulticast(message); } public void receiveChatMessage(ChatMessage message) { diff --git a/pitiupi.chat/src/pitiupi/chat/plugin/ChatPlugin.java b/pitiupi.chat/src/pitiupi/chat/plugin/ChatPlugin.java index cb9b16f..e33709c 100644 --- a/pitiupi.chat/src/pitiupi/chat/plugin/ChatPlugin.java +++ b/pitiupi.chat/src/pitiupi/chat/plugin/ChatPlugin.java @@ -8,29 +8,29 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import pitiupi.plugin.Pitiupi; import pitiupi.chat.GUI.ChatMenu; import pitiupi.chat.GUI.ChatWindow; import pitiupi.chat.net.ChatMessage; -import pitiupi.control.ConnectionManager; -import pitiupi.control.GuiManager; import pitiupi.models.Menus.MenuCategory; import pitiupi.models.Messages.Message; import pitiupi.models.Plugin; /** - * + *@author tony * @author flavio */ public class ChatPlugin extends Plugin { private ChatWindow chatWindow; - @Override - public void createPlugin(ConnectionManager connectionManager, GuiManager guiManager) { - this.chatWindow = new ChatWindow(connectionManager); - guiManager.createMenu(MenuCategory.Social, new ChatMenu(chatWindow)); + @Override + public void createPlugin(Pitiupi pitiupi) { + this.chatWindow = new ChatWindow(pitiupi); + pitiupi.createMenu(MenuCategory.Social, new ChatMenu(chatWindow)); } + @Override public List> getMessages() { return List.of(ChatMessage.class); diff --git a/pitiupi.mpChat/src/control/ChatController.java b/pitiupi.mpChat/src/control/ChatController.java deleted file mode 100644 index 07f7bb7..0000000 --- a/pitiupi.mpChat/src/control/ChatController.java +++ /dev/null @@ -1,73 +0,0 @@ -package control; - -import GUI.ChatMenu; -import GUI.ChatWindow; -import GUI.PrivateChatWindow; -import GUI.PublicChatWindow; -import net.MpChatMessage; -import pitiupi.control.ConnectionManager; -import pitiupi.control.PeerListener; -import pitiupi.models.User; -import pitiupi.net.tcp.TcpConnection; - -import javax.swing.*; -import java.net.InetAddress; -import java.util.HashMap; -import java.util.List; - -public class ChatController implements PeerListener { - ConnectionManager connectionManager; - ChatMenu chatMenu; - - PublicChatWindow publicChatWindow; - - private final HashMap chatWindows; - - public ChatController(ConnectionManager connectionManager, ChatMenu chatMenu) { - this.connectionManager = connectionManager; - publicChatWindow = new PublicChatWindow(connectionManager); - - chatMenus = new HashMap<>(); - chatWindows = new HashMap<>(); - - this.chatMenu = chatMenu; - - this.chatMenu.newMenuItem(new JMenuItem("Show global chat"), publicChatWindow); - } - - public void addChatWindow(TcpConnection tcpConnection) { - if (chatWindows.containsKey(tcpConnection)) return; - - PrivateChatWindow newChat = new PrivateChatWindow(connectionManager, tcpConnection); - chatWindows.put(tcpConnection, newChat); - JMenuItem newMenuItem = new JMenuItem(); - chatMenus.put(tcpConnection, newMenuItem); - newMenuItem.setText(tcpConnection.getAddress().toString()); - chatMenu.newMenuItem(newMenuItem, newChat); - } - - public void removeChatWindow(TcpConnection tcpConnection) { - ChatWindow removedWindow = chatWindows.remove(tcpConnection); - JMenuItem removedMenu = chatMenus.remove(tcpConnection); - - chatMenu.removeMenuItem(removedMenu); - removedWindow.setVisible(false); - removedWindow.dispose(); - } - - public void receivePublic(MpChatMessage message) { - publicChatWindow.receiveMessage(message); - } - public void receivePrivate(TcpConnection tcpConnection, MpChatMessage message) { - chatWindows.get(tcpConnection).receiveMessage(message); - } - - public void openChatWindow(InetAddress address) { - - } - - @Override - public void onPeersChanged(List list) { - - } -} diff --git a/pitiupi.testSocket/.idea/.gitignore b/pitiupi.testSocket/.idea/.gitignore new file mode 100644 index 0000000..30cf57e --- /dev/null +++ b/pitiupi.testSocket/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Ignored default folder with query files +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/pitiupi.testSocket/.idea/misc.xml b/pitiupi.testSocket/.idea/misc.xml new file mode 100644 index 0000000..07115cd --- /dev/null +++ b/pitiupi.testSocket/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/pitiupi.testSocket/.idea/modules.xml b/pitiupi.testSocket/.idea/modules.xml new file mode 100644 index 0000000..83d481a --- /dev/null +++ b/pitiupi.testSocket/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/pitiupi.testSocket/.idea/vcs.xml b/pitiupi.testSocket/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/pitiupi.testSocket/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/pitiupi/docs/PluginTutorial.md b/pitiupi/docs/PluginTutorial.md index 120dcfd..9734902 100644 --- a/pitiupi/docs/PluginTutorial.md +++ b/pitiupi/docs/PluginTutorial.md @@ -1,2 +1,512 @@ -# Como criar plugins para o Share my Sheet +# Como criar plugins para o Pitiupi +Aqui serão discutidos os métodos e classes que um desenvolvedor pode ou deve usar para criar um plugin para a plataforma Pitiupi. + +Alguns deles estarão marcados com um [REF], isso indica que eles estão sendo alterados para a versão 3.0 e deixarão de existir ou vão mudar consideravelmente. + +## GUI + +O Pitiupi é feito usando a biblioteca Swing do Java, por isso é altamente recomendado que os plugins também a utilizem na hora de criar sua interface gráfica, porém não é obrigatório. + +## Como tornar o seu plugin visível para o Pitiupi + +A busca por plugins é realizada em três etapas pelo pitiupi: + +1. Encontra todos os jars em `plugins`. +2. Verifica o `META-INF` dos jars para saber onde estão as implementações da classe `Plugin`. +3. Carrega o plugin no sistema. + +Em relação ao `META-INF`, ele deve conter um diretório `resources` e dentro dele um arquivo contendo o nome completamente qualificado da classe `Plugin` do pitiupi: `pitiupi.models.Plugin`, por fim dentro desse arquivo você deve colocar o nome completamente qualificado de todas as classes que implementam `Plugin` do seu projeto, um em cada linha. + +``` +build/ + |---->META-INF/resources/pitiupi.models.Plugin + |---->MeusPlugins/... + |---->MinhasMensagens/... + |---->Restante dos .class +``` + + +``` +src/MeusPlugins/MeuPrimeiroPlugin +src/MeusPlugins/MeuSegundoPlugin +... +``` + +### Dicas + +- Faça um makefile que automaticamente mova o META-INF para o build/ quando você compilar uma nova versão caso ele esteja sendo alterado com frequência. +Ex: +``` +JAR = ../pitiupi/dist/pitiupi.jar +BUILD_DIR = build +META-INF_DIR = src/META-INF +DIST = dist/mpChat.jar + +SOURCES = $(shell find -name "*.java") + +all: + mkdir -p "$(BUILD_DIR)" + mkdir -p "$(dir $(DIST))" + javac -cp "$(JAR)" -d "$(BUILD_DIR)" $(SOURCES) + cp -r "$(META-INF_DIR)" "$(BUILD_DIR)" + jar cf "$(DIST)" -C "$(BUILD_DIR)" . + +clean: + rm -rf "$(BUILD_DIR)"/* +``` + +- Para a versão 3.0 será necessário fazer o mesmo processo para as implementações da classe `Message`, então se quiser deixar preparado para a nova versão adicione `pitiupi.models.Messages.Message` em `resources` e coloque nele todas suas implementações de `Message`, assim como você fez para os de `Plugin`. + +## Pitiupi + +A classe `Pitiupi` é a interface que o seu plugin vai receber quando for criado, ela fornece os métodos necessários para interagir com a interface gráfica e comunicação para outros peers. + +### boolean sendUdpMulticast(Message message) + +Envia uma mensagem multicast, retorna `boolean` indicando se teve sucesso. + +### boolean sendUdpUnicast(Message message, InetAddress address) + +Envia uma mensagem unicast via udp para o endereço fornecido, retorna `boolean` indicando se teve sucesso. + +### boolean sendTcp(Message message, InetAddress address) + +Envia uma mensagem unicast via tcp para o endereço fornecido, retorna `boolean` indicando se teve sucesso. + +### [REF] TcpConnection connectTcp(InetAddress address, String senderId, String receiverId) + +Pede uma conexão tcp privada para o seu plugin com outro usuário, para utilizar essa funcionalidade é necessário implementar alguns métodos opcionais em Plugin. Maisinformações serão discutidas na seção TcpConnection. + +### String getUserName() + +Retorna o nome do usuário conectado + +### int getPort() + +Retorna a porta que o usuário está usando o Pitiupi + +### void createMenu(MenuCategory category, Menu menu) + +Adiciona um menu ao pitiupi, category define em qual das categoria de Menus ele será adicionado: Jogos, Social, Streaming. + +Os `Menus` serão discutidos em uma seção posterior. + +## Classe Plugin + +Essa é a classe que o pitiupi vai procurar ao carregar o seu plugin, ela define um contrato de métodos que todo plugin deve possuir. + +### getName() + +Indica o nome público do plugin. + +### getAuthor() + +Indica o nome dos desenvolvedores do plugin. + +### getVersion() + +Indica a versão atual do plugin. + +### getIdentifier() + +Indica um id que o pitiupi vai utilizar para enontrar seu plugin, por isso é importante que ele seja bem especifico para evitar que dois plugins sejam confundidos durante a comunicação. Procure usar algo como: MeuPlugin.Autor.Números + +### createPlugin(Pitiupi pitiupi) + +Após o pitiupi identificar e carregar o seu plugin, ele vai chamar esse método para inicializá-lo, por isso pense nele como o construtor do seu plugin, é aqui que você inicializa atributos, adiciona os menus inciais, etc. + +### getMessages() + +Esse método indica ao pitiupi quais tipos de mensagem seu plugin quer receber, normalmente as que ele mesmo implementar. +Nesse caso, é normal que ele faça: + +``` +return List.of( + SuaMensagem1.class, + SuaMensagem2.class, + ... + ); +``` + +Caso você queira que seu plugin receba todo tipo de mensagem, existe um método `receiveAllTypes()` que ele pode sobreescrever para receber todas mensagens +que não sejam consideradas privadas. + +### receiveMessage(Message message) + +Esse é o método que o pitiupi vai chamar quando quiser te entregar uma mensagem pelo canal publico de mensagens. Note que ele entrega uma `Message`, então é necessário que +seu plugin confira o tipo real ele mesmo. Como em: + +``` +if (message instanceOf MinhaMensagem minhaMensagem) {...} +``` + +O pitiupi garante que é uma mensagem de um tipo que o seu plugin diz aceitar. + +### [REF] métodos opcionais + +Os métodos a seguir possuem uma implementação padrão da própria classe que retornam false. Os três ultimos são necessários caso seu plugin deseje criar e receber conexões privadas. + +### [REF] receiveAllTypes() + +Indica ao Pitiupi que seu plugin deseja receber todas as mensagens publicas da plataforma, independente do tipo. + +### [REF] receivePrivateConnection(TcpConnection connection) + +Esse método é chamado quando o Pitiupi precisa entregar uma `TcpConnection` ao seu plugin, seja um que você pediu ou recebeu de um peer. + +### [REF] receiveBytes(TcpConnection connection, byte[] bytes) + +Esse método é chamado para entregar as mensagens recebidas por um `TcpConnection`, o tcpConnection no parametro indica qual a fonte da mensagem. + +### [REF] onSocketClosed(TcpConnection connection) + +Esse é método é chamado quando o socket de um `TcpConnection` pertencente ao plugin é fechado. + +## Message + +Essa é a classe abstrata que o pitiupi usa para comunicação nos canais publicos. + +[REF] Ela possui um atributo isPublic que por padrão é true, mas é possível defini-lo no construtor como false caso a mensagem deva ser privada. + +Essa classe implementa `Serializable` e possui um método `toByteArray` que transforma a mensagem em `byte[]`. Porém é importante notar que caso ela possua um objeto de outra classe como atributo aquela classe também deve implementar `Serializable`. + +## [REF] TcpConnection + +Representa uma conexão Tcp ativa para algum outro peer, mensagens enviadas por essa conexão não chegam ao canal publico e em vez disso vão direto para destinatário. + +Caso fique inativa (Sem enviar ou receber mensagens) por 5 minutos a conexão é fechada automaticamente e o dono é notificado. + +### [REF] void send(byte[] message) + +Envia um conjunto de bytes[] para o destinatario. + +Caso queira enviar uma `Message` chame o método toByteArray para transforma-la em bytes primeiro. + +### [REF] boolean isClosed() + +Indica se a conexão esta fechada + +### [REF] InetAddress getAddress() + +Retorna o endereço do peer que ela esta conectada. + +### [REF] String getReceiver() + +Retorna o identificador do plugin que ela esta conectada. + +### [REF] getReceiver() + +Retorna o identifador do plugin que pediu pela conexão. + +## Menu + +Essa é a classe que indica ao pitiupi que um menu deve ser adicionado a janela principal da aplicação. + +Menus podem ser de dois tipos `UnicastMenu` e `MulticastMenu` com a principal diferença entre eles sendo onde eles são adicionados na janela principal. Outras mudanças serão discutidas quando for relevante nas classes a seguir. + +Atualmente Menus não podem ser alterados uma vez que são adicionados, então é importante que eles sejam feitos com isso em mente. + +Tem um método `String getText()` que retorna o que deve ser escrito no Menu`. + +### SubMenu + +Permite que o `Menu` expanda em uma nova seção de menus. + +`UnicastSubMenu` o método `getMenu()` retorna `List` + +`MulticastSubMenu` o método `getMenu()` retorna `List` + +Exemplo: + +``` + +ChatCasualMulticastMenu chatCasualMulticastMenu; +ChatEstudoMutlticastMenu chatEstudoMutlticastMenu; + +... + +@Override + public List getMenus() { + return List.of(chatCasualMulticastMenu, chatEstudoMutlticastMenu); + } +``` + +### ActionMenu + +É um `Menu` que que realiza alguma ação quando selecionado pelo usuário. + +`UnicastActionMenu` o método `onClick(User user)` é chamado quando selecionado pelo usuário, recebendo como parametro o peer escolhido pelo usuário. + +`MulticastActionMenu` o método `onClick()` é chamado quando selecionado pelo usuário. + +Exemplo: + +``` + +@Override + public void onClick(User user) { + callManager.openCallWindow(user); + } +``` + +## Exemplo de plugin no pitiupi: + +### Plugin + +Implementa o contrato de plugin. + +Inicializa a janela principal e cria os menus. + +Também define quais tipos de mensagem ele quer receber. + +``` +public class ChatPlugin extends Plugin { + + private ChatWindow chatWindow; + + @Override + public void createPlugin(Pitiupi pitiupi) { + this.chatWindow = new ChatWindow(pitiupi); + pitiupi.createMenu(MenuCategory.Social, new ChatMenu(chatWindow)); + } + + + @Override + public List> getMessages() { + return List.of(ChatMessage.class); + } + + + @Override + public void receiveMessage(Message message) { + if (message instanceof ChatMessage msg) { + this.chatWindow.receiveChatMessage(msg); + } + } + + @Override + public String getName() { + return "Chat"; + } + + @Override + public String getAuthor() { + return "Flávio Schiavoni"; + } + + @Override + public String getVersion() { + return "0"; + } + + @Override + public String getIdentifier() { + return "Pitiupi.Chat.Example.123456"; + } +} +``` + +### Message + +Implementa uma mensagem customizada que o plugin vai utilizar para comunicação. + +``` +public class ChatMessage extends Message { + + private String text; + private String name; + + + public ChatMessage(){ + + } + + public ChatMessage(String name, String text){ + this.name = name; + this.text = text; + } + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the text + */ + public String getText() { + return text; + } + + /** + * @param text the text to set + */ + public void setText(String text) { + this.text = text; + } +} +``` + +### Menus + +Implementa os dois menus de Multicast utilizados pela aplicação. + +#### ActionMenu + +Esse menu apenas abre a janela principal do plugin. + +``` +public class ChatMulticastMenu extends MulticastActionMenu { + ChatWindow chatWindow; + + public ChatMulticastMenu(ChatWindow chatWindow) { + this.chatWindow = chatWindow; + } + + @Override + public String getText() { + return "Show Chat Window"; + } + + @Override + public void onClick() { + chatWindow.setVisible(true); + } +} +``` + +Esse menu abre a seção de menus do plugin Chat. + +#### SubMenu + +``` +public class ChatMenu extends MulticastSubMenu { + ChatMulticastMenu chatMulticastMenu; + + public ChatMenu(ChatWindow chatWindow) { + this.chatMulticastMenu = new ChatMulticastMenu(chatWindow); + } + + @Override + public String getText() { + return "Chat"; + } + + @Override + public List getMenus() { + return List.of(chatMulticastMenu); + } +} +``` + +### GUI + +Essa é a janela principal do plugin de chat. + +``` +public class ChatWindow extends JDialog { + + private JButton jButton1; + private JEditorPane jEditorPane1; + private JPanel jPanel1; + private JPanel jPanel2; + private JScrollPane jScrollPane1; + private JScrollPane jScrollPane2; + private JSplitPane jSplitPane1; + private JTextArea jTextArea1; + + private final Pitiupi pitiupi; + + public ChatWindow(Pitiupi pitiupi) { + this.pitiupi = pitiupi; + initComponents(); + } + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + private void initComponents() { + + this.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE); + this.setTitle("Chat Window"); + this.setBounds(new java.awt.Rectangle(0, 0, 800, 600)); + this.getContentPane().setLayout(new java.awt.BorderLayout(5, 5)); + this.setPreferredSize(new java.awt.Dimension(800, 600)); + + jSplitPane1 = new JSplitPane(); + jScrollPane1 = new JScrollPane(); + jEditorPane1 = new JEditorPane(); + jPanel1 = new JPanel(); + jScrollPane2 = new JScrollPane(); + jTextArea1 = new JTextArea(); + jPanel2 = new JPanel(); + jButton1 = new JButton(); + + jSplitPane1.setDividerLocation(500); + jSplitPane1.setOrientation(JSplitPane.VERTICAL_SPLIT); + jEditorPane1.setContentType("text/html"); + jPanel1.setLayout(new java.awt.BorderLayout()); + jTextArea1.setColumns(20); + jTextArea1.setRows(5); + jTextArea1.addKeyListener(new java.awt.event.KeyAdapter() { + @Override + public void keyTyped(java.awt.event.KeyEvent evt) { + if (evt.getKeyChar() == '\n') { + sendMessage(); + } + } + }); + jButton1.setText("Send"); + + jScrollPane1.setViewportView(jEditorPane1); + jScrollPane2.setViewportView(jTextArea1); + jSplitPane1.setTopComponent(jScrollPane1); + + jPanel1.add(jScrollPane2, java.awt.BorderLayout.CENTER); + jPanel2.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.RIGHT)); + jButton1.addActionListener((java.awt.event.ActionEvent evt) -> { + sendMessage(); + }); + jPanel2.add(jButton1); + jPanel1.add(jPanel2, java.awt.BorderLayout.PAGE_END); + jSplitPane1.setRightComponent(jPanel1); + getContentPane().add(jSplitPane1, java.awt.BorderLayout.CENTER); + pack(); + } + + private void sendMessage() { + if (jTextArea1.getText().trim().isEmpty()) { + return; + } + ChatMessage message = new ChatMessage(pitiupi.getUserName(), jTextArea1.getText()); + this.sendChatMessage(message); + jTextArea1.setText(""); + } + + public void sendChatMessage(Message message) { + pitiupi.sendUdpMulticast(message); + } + + public void receiveChatMessage(ChatMessage message) { + SimpleDateFormat dt = new SimpleDateFormat("dd/MM/yyyyy hh:mm:ss"); + Date date = new Date(); + this.receiveChatMessage(dt.format(date) + " " + + "" + message.getName() + ": " + + message.getText() + "
"); + } + + public void receiveChatMessage(String message) { + jEditorPane1.setText(message + jEditorPane1.getText()); + } +} +``` \ No newline at end of file diff --git a/pitiupi/src/pitiupi/control/GuiManager.java b/pitiupi/src/pitiupi/control/GuiManager.java index b2a6f63..02c3c29 100644 --- a/pitiupi/src/pitiupi/control/GuiManager.java +++ b/pitiupi/src/pitiupi/control/GuiManager.java @@ -30,18 +30,6 @@ public class GuiManager { } public void createMenu(MenuCategory category, Menu menu) { - System.out.println( - menu.getClass().getSuperclass().getSuperclass() - ); - - System.out.println( - MulticastMenu.class - ); - - System.out.println( - menu.getClass().getSuperclass().getSuperclass() - == MulticastMenu.class - ); try { JMenuItem newMenu = buildMenu(menu); if (menu instanceof MulticastMenu) { @@ -55,7 +43,6 @@ public class GuiManager { logger.log(Level.WARNING, e.getMessage()); } } - private JMenuItem buildMenu(Menu menu) { if (menu instanceof MulticastSubMenu multicastSubMenu) { JMenu newMenu = new JMenu(menu.getText()); diff --git a/pitiupi/src/pitiupi/control/plugins/PluginManager.java b/pitiupi/src/pitiupi/control/plugins/PluginManager.java index ada270d..60f29f4 100644 --- a/pitiupi/src/pitiupi/control/plugins/PluginManager.java +++ b/pitiupi/src/pitiupi/control/plugins/PluginManager.java @@ -1,5 +1,6 @@ package pitiupi.control.plugins; +import pitiupi.plugin.Pitiupi; import pitiupi.Main; import pitiupi.control.ConnectionManager; import pitiupi.control.GuiManager; @@ -92,6 +93,7 @@ public class PluginManager { public void loadPlugins(ConnectionManager connectionManager, GuiManager guiManager) { File pluginDir = new File("plugins"); File[] jarFiles = pluginDir.listFiles((dir, name) -> name.endsWith(".jar")); + Pitiupi pitiupi = new Pitiupi(connectionManager, guiManager); if (jarFiles == null || jarFiles.length == 0) { System.out.println("No plugins found in the " + pluginDir.getAbsolutePath() + " directory."); @@ -118,7 +120,7 @@ public class PluginManager { System.out.println("Loading: " + plugin.getName()); LoadedPlugin loadedPlugin = new LoadedPlugin(plugin, classLoader); - plugin.createPlugin(connectionManager, guiManager); + plugin.createPlugin(pitiupi); loadedPlugins.add(loadedPlugin); loadedJars.add(file.getName()); } diff --git a/pitiupi/src/pitiupi/models/Plugin.java b/pitiupi/src/pitiupi/models/Plugin.java index ffd898c..f28e62b 100644 --- a/pitiupi/src/pitiupi/models/Plugin.java +++ b/pitiupi/src/pitiupi/models/Plugin.java @@ -3,9 +3,7 @@ * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Interface.java to edit this template */ package pitiupi.models; - -import pitiupi.control.ConnectionManager; -import pitiupi.control.GuiManager; +import pitiupi.plugin.Pitiupi; import pitiupi.models.Messages.Message; import pitiupi.net.tcp.TcpConnection; @@ -30,10 +28,9 @@ public abstract class Plugin { * *

Este método é chamado pela aplicação após o carregamento do plugin.

* - * @param connectionManager interface com o sistema de conexões da aplicação. - * @param guiManager interface com a janela principal da aplicação + * @param pitiupi interface com o sistema de conexões da aplicação. */ - public abstract void createPlugin(ConnectionManager connectionManager, GuiManager guiManager); + public abstract void createPlugin(Pitiupi pitiupi); /** * Retorna uma mensagem utilizada pelo plugin. @@ -62,4 +59,4 @@ public abstract class Plugin { public boolean onSocketClosed(TcpConnection connection) { return false; } -} \ No newline at end of file +} diff --git a/pitiupi/src/pitiupi/plugin/Pitiupi.java b/pitiupi/src/pitiupi/plugin/Pitiupi.java new file mode 100644 index 0000000..071de18 --- /dev/null +++ b/pitiupi/src/pitiupi/plugin/Pitiupi.java @@ -0,0 +1,49 @@ +package pitiupi.plugin; + +import pitiupi.control.ConnectionManager; +import pitiupi.control.GuiManager; +import pitiupi.models.Menus.Menu; +import pitiupi.models.Menus.MenuCategory; +import pitiupi.models.Messages.Message; +import pitiupi.net.tcp.TcpConnection; + +import java.net.InetAddress; + +public class Pitiupi { + + private final ConnectionManager connectionManager; + private final GuiManager guiManager; + + public Pitiupi(ConnectionManager connectionManager, GuiManager guiManager) { + this.connectionManager = connectionManager; + this.guiManager = guiManager; + } + + public boolean sendUdpMulticast(Message message) { + return connectionManager.sendUdpMulticast(message); + } + + public boolean sendUdpUnicast(Message message, InetAddress address) { + return connectionManager.sendUdpUnicast(message, address); + } + + public boolean sendTcp(Message message, InetAddress address) { + return connectionManager.sendTcp(message, address); + } + + public TcpConnection connectTcp(InetAddress address, String senderId, String receiverId) { + return connectionManager.connectPluginTcp(address, senderId, receiverId); + } + + public String getUserName() { + return connectionManager.getUserName(); + } + + public int getPort() { + return connectionManager.getPort(); + } + + public void createMenu(MenuCategory category, Menu menu) { + guiManager.createMenu(category, menu); + } +}