Envoy Client v0.1-alpha
This commit is contained in:
		
							
								
								
									
										45
									
								
								src/main/java/envoy/client/Chat.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/main/java/envoy/client/Chat.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| package envoy.client; | ||||
|  | ||||
| import java.io.Serializable; | ||||
|  | ||||
| import javax.swing.DefaultListModel; | ||||
|  | ||||
| import envoy.schema.Message; | ||||
| import envoy.schema.User; | ||||
|  | ||||
| public class Chat implements Serializable { | ||||
|  | ||||
| 	private static final long serialVersionUID = -7751248474547242056L; | ||||
|  | ||||
| 	private User						recipient; | ||||
| 	private DefaultListModel<Message>	model	= new DefaultListModel<>(); | ||||
|  | ||||
| 	/** | ||||
| 	 * Provides the list of messages that the recipient receives.<br> | ||||
| 	 * Saves the Messages in the corresponding chat at that Point. | ||||
| 	 *  | ||||
| 	 * @param recipient the user who receives the messages | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public Chat(User recipient) { this.recipient = recipient; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the recipient of a message | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public User getRecipient() { return recipient; } | ||||
|  | ||||
| 	/** | ||||
| 	 * Adds the received message at the current Point in the current chat | ||||
| 	 *  | ||||
| 	 * @param message the message to add in said chat | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public void appendMessage(Message message) { model.addElement(message); } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return all messages in the current chat | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public DefaultListModel<Message> getModel() { return model; } | ||||
| } | ||||
							
								
								
									
										179
									
								
								src/main/java/envoy/client/Client.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								src/main/java/envoy/client/Client.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,179 @@ | ||||
| package envoy.client; | ||||
|  | ||||
| import javax.ws.rs.client.ClientBuilder; | ||||
| import javax.ws.rs.client.Entity; | ||||
| import javax.ws.rs.client.WebTarget; | ||||
| import javax.ws.rs.core.Response; | ||||
| import javax.xml.bind.JAXBContext; | ||||
| import javax.xml.bind.JAXBException; | ||||
| import javax.xml.bind.Marshaller; | ||||
|  | ||||
| import envoy.schema.ObjectFactory; | ||||
| import envoy.schema.Sync; | ||||
| import envoy.schema.User; | ||||
|  | ||||
| /** | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>Client.java</strong><br> | ||||
|  * Created: <strong>28 Sep 2019</strong><br> | ||||
|  *  | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @author Maximilian Käfer | ||||
|  * @author Leon Hofmeister | ||||
|  * @since Envoy v0.1-alpha | ||||
|  */ | ||||
| public class Client { | ||||
|  | ||||
| 	private ObjectFactory	objectFactory	= new ObjectFactory(); | ||||
| 	private Config			config; | ||||
| 	private User			sender, recipient; | ||||
|  | ||||
| 	public Client(Config config, String username) { | ||||
| 		this.config	= config; | ||||
| 		sender		= getUser(username); | ||||
| 		System.out.println("ID: " + sender.getID()); | ||||
| 	} | ||||
|  | ||||
| 	private <T, R> R post(String uri, T body, Class<R> responseBodyClass) { | ||||
| 		javax.ws.rs.client.Client	client	= ClientBuilder.newClient(); | ||||
| 		WebTarget					target	= client.target(uri); | ||||
|  | ||||
| 		Response	response		= target.request().post(Entity.entity(body, "application/xml")); | ||||
| 		R			responseBody	= response.readEntity(responseBodyClass); | ||||
| 		response.close(); | ||||
| 		client.close(); | ||||
|  | ||||
| 		return responseBody; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Returns a {@link Sync} with all users on the server. | ||||
| 	 *  | ||||
| 	 * @return Sync - List of all users on the server. | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public Sync getUsersListXml() { | ||||
| 		Sync	sendSync	= objectFactory.createSync(); | ||||
| 		User	user		= objectFactory.createUser(); | ||||
| 		user.setID(-1); | ||||
| 		sendSync.getUsers().add(user); | ||||
|  | ||||
| 		Sync returnSendSync = post( | ||||
| 				String | ||||
| 					.format("%s:%d/envoy-server/rest/sync/syncData?userId=%d", config.getServer(), config.getPort(), 0), | ||||
| 				sendSync, | ||||
| 				Sync.class); | ||||
| 		return returnSendSync; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Returns a {@link User} with a specific id by name. | ||||
| 	 *  | ||||
| 	 * @param name - the name of the {@link User} | ||||
| 	 * @return a {@link User} with the specified name | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	private User getUser(String name) { | ||||
| 		Sync	senderSync	= objectFactory.createSync(); | ||||
| 		User	user		= objectFactory.createUser(); | ||||
| 		user.setName(name); | ||||
| 		senderSync.getUsers().add(user); | ||||
|  | ||||
| 		Sync returnSenderSync = post( | ||||
| 				String | ||||
| 					.format("%s:%d/envoy-server/rest/sync/syncData?userId=%d", config.getServer(), config.getPort(), 0), | ||||
| 				senderSync, | ||||
| 				Sync.class); | ||||
|  | ||||
| 		User returnSender = objectFactory.createUser(); | ||||
|  | ||||
| 		if (returnSenderSync.getUsers().size() == 1) { | ||||
| 			returnSender = returnSenderSync.getUsers().get(0); | ||||
| 		} else { | ||||
| 			System.out.println("ERROR exiting..."); | ||||
| 		} | ||||
|  | ||||
| 		return returnSender; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Sends the "sync" Sync Object to the server and gets a "returnSync" Sync | ||||
| 	 * Object as response. <br> | ||||
| 	 * It is also used to get the own sender at the start of the client | ||||
| 	 * (Client sends "sync" Sync Object with single user in it(name: the name | ||||
| 	 * entered at login, id: 0, UserStatus:null))<br> | ||||
| 	 * and to get a complete list of all users saved on the server. | ||||
| 	 * (Client sends "sync" Sync Object with single user in it(name: "" (empty), id: | ||||
| 	 * -1, UserStatus:null)) <br> | ||||
| 	 * This method also processes the response Sync Object. <br> | ||||
| 	 * It sorts its users and messages by specific variables and does certain things | ||||
| 	 * with them. <br> | ||||
| 	 * <br> | ||||
| 	 * Messages: <br> | ||||
| 	 * -State SENT: Update Local message(s) with State WAITING (add Message ID and | ||||
| 	 * change State to SENT). (server sends these informations to the client if | ||||
| 	 * message(s) with State WAITING were successfully sent to the server)<br> | ||||
| 	 * -State RECEIVED, SenderID != 0: Adds the unread Messages returned from the | ||||
| 	 * server in the latest sync to the "unreadMessagesSync" Sync Object. <br> | ||||
| 	 * -State RECEIVED, SenderID == 0: Update message(s) in localDB to state | ||||
| 	 * RECEIVED. | ||||
| 	 * (server sends these informations to the client if the other client received | ||||
| 	 * the message(s).) <br> | ||||
| 	 * -State READ: Update message(s) in the LocalDB to state READ. (server sends | ||||
| 	 * these informations to the client if the other client read | ||||
| 	 * the message(s).) <br> | ||||
| 	 * <br> | ||||
| 	 * Users: <br> | ||||
| 	 * Updating UserStatus of all users in LocalDB. (Server sends all users with | ||||
| 	 * their updated UserStatus to the client.) <br> | ||||
| 	 *  | ||||
| 	 * @param userId | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public Sync sendSync(long userId, Sync sync) { | ||||
| 		// Print sync XML to console | ||||
| 		JAXBContext jc; | ||||
| 		try { | ||||
| 			jc = JAXBContext.newInstance("envoy.schema"); | ||||
| 			Marshaller m = jc.createMarshaller(); | ||||
| 			m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); | ||||
| 			m.marshal(sync, System.out); | ||||
| 		} catch (JAXBException e) { | ||||
| 			e.printStackTrace(); | ||||
| 		} | ||||
|  | ||||
| 		// Send sync | ||||
| 		return post(String | ||||
| 			.format("%s:%d/envoy-server/rest/sync/syncData?userId=%d", config.getServer(), config.getPort(), userId), | ||||
| 				sync, | ||||
| 				Sync.class); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the sender object that represents this client. | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public User getSender() { return sender; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the current recipient of the current chat. | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public User getRecipient() { return recipient; } | ||||
|  | ||||
| 	/** | ||||
|      * Sets the recipient. | ||||
| 	 * @param recipient - the recipient to set | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public void setRecipient(User recipient) { this.recipient = recipient; } | ||||
|  | ||||
|     /** | ||||
| 	 * @return true, if a recipient is selected | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public boolean hasRecipient() { return recipient != null; } | ||||
| } | ||||
|  | ||||
							
								
								
									
										127
									
								
								src/main/java/envoy/client/Config.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								src/main/java/envoy/client/Config.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,127 @@ | ||||
| package envoy.client; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.util.Properties; | ||||
|  | ||||
| /** | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>Config.java</strong><br> | ||||
|  * Created: <strong>12 Oct 2019</strong><br> | ||||
|  *  | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @since Envoy v0.1-alpha | ||||
|  */ | ||||
| public class Config { | ||||
|  | ||||
| 	private String	server; | ||||
| 	private int		port; | ||||
| 	private File	localDB; | ||||
| 	private int		syncTimeout; | ||||
|  | ||||
| 	private static Config config; | ||||
|  | ||||
| 	private Config() {} | ||||
|  | ||||
| 	public static Config getInstance() { | ||||
| 		if (config == null) config = new Config(); | ||||
| 		return config; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Defaults to the {@code client.properties} file for information. | ||||
| 	 *  | ||||
| 	 * @param properties a {@link Properties} object containing information about | ||||
| 	 *                   the server and port, as well as the path to the local | ||||
| 	 *                   database | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public void load(Properties properties) { | ||||
| 		if (properties.containsKey("server")) server = properties.getProperty("server"); | ||||
| 		if (properties.containsKey("port")) port = Integer.parseInt(properties.getProperty("port")); | ||||
| 		localDB		= new File(properties.getProperty("localDB", ".\\localDB")); | ||||
| 		syncTimeout	= Integer.parseInt(properties.getProperty("syncTimeout", "1000")); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Sets the server, port and localDB path via command line properties --server / | ||||
| 	 * -s, --port / -p and --localDB / -db. | ||||
| 	 *  | ||||
| 	 * @param args the command line arguments to parse | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public void load(String[] args) { | ||||
| 		for (int i = 0; i < args.length; i++) | ||||
| 			switch (args[i]) { | ||||
| 				case "--server": | ||||
| 				case "-s": | ||||
| 					server = args[++i]; | ||||
| 					break; | ||||
| 				case "--port": | ||||
| 				case "-p": | ||||
| 					port = Integer.parseInt(args[++i]); | ||||
| 					break; | ||||
| 				case "--localDB": | ||||
| 				case "-db": | ||||
| 					localDB = new File(args[++i]); | ||||
| 			} | ||||
| 		if (localDB == null) localDB = new File(".\\localDB"); | ||||
| 		if (syncTimeout == 0) syncTimeout = 1000; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return {@code true} if server, port and localDB directory are known. | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public boolean isInitialized() { | ||||
| 		return server != null && !server.isEmpty() && port > 0 && port < 65566 && localDB != null; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the host name of the Envoy server | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public String getServer() { return server; } | ||||
|  | ||||
| 	/** | ||||
| 	 * Changes the default server host name. | ||||
| 	 * Exclusively intended for development purposes. | ||||
| 	 *  | ||||
| 	 * @param server the host name of the Envoy server | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public void setServer(String server) { this.server = server; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the port at which the Envoy server is located on the host | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public int getPort() { return port; } | ||||
|  | ||||
| 	/** | ||||
| 	 * Changes the default port. | ||||
| 	 * Exclusively intended for development purposes. | ||||
| 	 *  | ||||
| 	 * @param port the port where an Envoy server is located | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public void setPort(int port) { this.port = port; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the local database specific to the client user | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 **/ | ||||
| 	public File getLocalDB() { return localDB; } | ||||
|  | ||||
| 	/** | ||||
| 	 * Changes the default local database. | ||||
| 	 * Exclusively intended for development purposes. | ||||
| 	 * | ||||
| 	 * @param the file containing the local database | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 **/ | ||||
| 	public void setLocalDB(File localDB) { this.localDB = localDB; } | ||||
|  | ||||
| 	public int getSyncTimeout() { return syncTimeout; } | ||||
|  | ||||
| 	public void setSyncTimeout(int syncTimeout) { this.syncTimeout = syncTimeout; } | ||||
| } | ||||
							
								
								
									
										293
									
								
								src/main/java/envoy/client/LocalDB.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										293
									
								
								src/main/java/envoy/client/LocalDB.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,293 @@ | ||||
| package envoy.client; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.io.FileInputStream; | ||||
| import java.io.FileOutputStream; | ||||
| import java.io.IOException; | ||||
| import java.io.ObjectInputStream; | ||||
| import java.io.ObjectOutputStream; | ||||
| import java.time.Instant; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| import javax.xml.datatype.DatatypeConfigurationException; | ||||
| import javax.xml.datatype.DatatypeFactory; | ||||
|  | ||||
| import envoy.exception.EnvoyException; | ||||
| import envoy.schema.Message; | ||||
| import envoy.schema.Message.Metadata.MessageState; | ||||
| import envoy.schema.ObjectFactory; | ||||
| import envoy.schema.Sync; | ||||
| import envoy.schema.User; | ||||
|  | ||||
| /** | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>LocalDB.java</strong><br> | ||||
|  * Created: <strong>27.10.2019</strong><br> | ||||
|  * | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @author Maximilian Käfer | ||||
|  * @since Envoy v0.1-alpha | ||||
|  */ | ||||
| public class LocalDB { | ||||
|  | ||||
| 	private File			localDB; | ||||
| 	private User			sender; | ||||
| 	private List<Chat>		chats			= new ArrayList<>(); | ||||
| 	private ObjectFactory	objectFactory	= new ObjectFactory(); | ||||
| 	private DatatypeFactory	datatypeFactory; | ||||
|  | ||||
| 	private Sync	unreadMessagesSync	= objectFactory.createSync(); | ||||
| 	private Sync	sync				= objectFactory.createSync(); | ||||
| 	private Sync	readMessages		= objectFactory.createSync(); | ||||
|  | ||||
| 	/** | ||||
| 	 * Constructs an empty local database. | ||||
| 	 * | ||||
| 	 * @param client the user that is logged in with this client | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
|  | ||||
| 	public LocalDB(User sender) { | ||||
| 		this.sender = sender; | ||||
| 		try { | ||||
| 			datatypeFactory = DatatypeFactory.newInstance(); | ||||
| 		} catch (DatatypeConfigurationException e) { | ||||
| 			e.printStackTrace(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Initializes the local database and fills it with values | ||||
| 	 * if the user has already sent or received messages. | ||||
| 	 * | ||||
| 	 * @param localDBDir the directory where we wish to save/load the database from. | ||||
| 	 * @throws EnvoyException if the directory selected is not an actual directory. | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public void initializeDBFile(File localDBDir) throws EnvoyException { | ||||
| 		if (localDBDir.exists() && !localDBDir.isDirectory()) | ||||
| 			throw new EnvoyException(String.format("LocalDBDir '%s' is not a directory!", localDBDir.getAbsolutePath())); | ||||
| 		localDB = new File(localDBDir, sender.getID() + ".db"); | ||||
| 		if (localDB.exists()) loadFromLocalDB(); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Saves the database into a file for future use. | ||||
| 	 * | ||||
| 	 * @throws IOException if something went wrong during saving | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public void saveToLocalDB() { | ||||
| 		try { | ||||
| 			localDB.getParentFile().mkdirs(); | ||||
| 			localDB.createNewFile(); | ||||
| 		} catch (IOException e) { | ||||
| 			e.printStackTrace(); | ||||
| 			System.err.println("unable to save the messages"); | ||||
| 		} | ||||
| 		try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(localDB))) { | ||||
| 			out.writeObject(chats); | ||||
| 		} catch (IOException ex) { | ||||
| 			ex.printStackTrace(); | ||||
| 			System.err.println("unable to save the messages"); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Loads all chats saved by Envoy for the client user. | ||||
| 	 * | ||||
| 	 * @throws EnvoyException if something fails while loading. | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	@SuppressWarnings("unchecked") | ||||
| 	private void loadFromLocalDB() throws EnvoyException { | ||||
| 		try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(localDB))) { | ||||
| 			Object obj = in.readObject(); | ||||
| 			if (obj instanceof ArrayList<?>) chats = (ArrayList<Chat>) obj; | ||||
| 		} catch (ClassNotFoundException | IOException e) { | ||||
| 			throw new EnvoyException(e); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Creates a {@link Message} object serializable to XML. | ||||
| 	 *  | ||||
| 	 * @param textContent The content (text) of the message | ||||
| 	 * @return prepared {@link Message} object | ||||
| 	 */ | ||||
| 	public Message createMessage(String textContent, User recipient) { | ||||
| 		Message.Metadata metaData = objectFactory.createMessageMetadata(); | ||||
| 		metaData.setSender(sender.getID()); | ||||
| 		metaData.setRecipient(recipient.getID()); | ||||
| 		metaData.setState(MessageState.WAITING); | ||||
| 		metaData.setDate(datatypeFactory.newXMLGregorianCalendar(Instant.now().toString())); | ||||
|  | ||||
| 		Message.Content content = objectFactory.createMessageContent(); | ||||
| 		content.setType("text"); | ||||
| 		content.setText(textContent); | ||||
|  | ||||
| 		Message message = objectFactory.createMessage(); | ||||
| 		message.setMetadata(metaData); | ||||
| 		message.getContent().add(content); | ||||
|  | ||||
| 		return message; | ||||
| 	} | ||||
|  | ||||
| 	public Sync fillSync(long userId) { | ||||
| 		addWaitingMessagesToSync(); | ||||
|  | ||||
| 		sync.getMessages().addAll(readMessages.getMessages()); | ||||
| 		readMessages.getMessages().clear(); | ||||
|  | ||||
| 		System.out.println(sync.getMessages().size()); | ||||
| 		return sync; | ||||
| 	} | ||||
|  | ||||
| 	public void applySync(Sync returnSync) { | ||||
| 		for (int i = 0; i < returnSync.getMessages().size(); i++) { | ||||
| 			if (returnSync.getMessages().get(i).getMetadata().getMessageId() != 0 | ||||
| 					&& returnSync.getMessages().get(i).getMetadata().getState() == MessageState.SENT) { | ||||
| 				// Update Local Messages with State WAITING (add Message ID and change State to | ||||
| 				// SENT) | ||||
| 				for (int j = 0; j < sync.getMessages().size(); j++) { | ||||
| 					if (j == i) { | ||||
| 						sync.getMessages().get(j).getMetadata().setMessageId(returnSync.getMessages().get(j).getMetadata().getMessageId()); | ||||
| 						sync.getMessages().get(j).getMetadata().setState(returnSync.getMessages().get(j).getMetadata().getState()); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			if (returnSync.getMessages().get(i).getMetadata().getMessageId() != 0 && returnSync.getMessages().get(i).getMetadata().getSender() != 0 | ||||
| 					&& returnSync.getMessages().get(i).getMetadata().getState() == MessageState.RECEIVED) { | ||||
| 				// these are the unread Messages from the server | ||||
| 				unreadMessagesSync.getMessages().add(returnSync.getMessages().get(i)); | ||||
| 			} | ||||
|  | ||||
| 			if (returnSync.getMessages().get(i).getMetadata().getMessageId() != 0 && returnSync.getMessages().get(i).getMetadata().getSender() == 0 | ||||
| 					&& returnSync.getMessages().get(i).getMetadata().getState() == MessageState.RECEIVED) { | ||||
| 				// Update Messages in localDB to state RECEIVED | ||||
| 				for (int j = 0; j < getChats().size(); j++) { | ||||
| 					if (getChats().get(j).getRecipient().getID() == returnSync.getMessages().get(i).getMetadata().getRecipient()) { | ||||
| 						for (int k = 0; k < getChats().get(j).getModel().getSize(); k++) { | ||||
| 							if (getChats().get(j).getModel().get(k).getMetadata().getMessageId() == returnSync.getMessages() | ||||
| 								.get(i) | ||||
| 								.getMetadata() | ||||
| 								.getMessageId()) { | ||||
| 								// Update Message in LocalDB | ||||
| 								getChats().get(j).getModel().get(k).getMetadata().setState(returnSync.getMessages().get(j).getMetadata().getState()); | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			if (returnSync.getMessages().get(i).getMetadata().getMessageId() != 0 | ||||
| 					&& returnSync.getMessages().get(i).getMetadata().getState() == MessageState.READ) { | ||||
| 				// Update local Messages to state READ | ||||
| 				System.out.println("Message with ID: " + returnSync.getMessages().get(i).getMetadata().getMessageId() | ||||
| 						+ "was initialized to be set to READ in localDB."); | ||||
| 				for (int j = 0; j < getChats().size(); j++) { | ||||
| 					if (getChats().get(j).getRecipient().getID() == returnSync.getMessages().get(i).getMetadata().getRecipient()) { | ||||
| 						System.out.println("Chat with: " + getChats().get(j).getRecipient().getID() + "was selected."); | ||||
| 						for (int k = 0; k < getChats().get(j).getModel().getSize(); k++) { | ||||
| 							if (getChats().get(j).getModel().get(k).getMetadata().getMessageId() == returnSync.getMessages() | ||||
| 								.get(i) | ||||
| 								.getMetadata() | ||||
| 								.getMessageId()) { | ||||
| 								System.out.println( | ||||
| 										"Message with ID: " + getChats().get(j).getModel().get(k).getMetadata().getMessageId() + "was selected."); | ||||
| 								getChats().get(j).getModel().get(k).getMetadata().setState(returnSync.getMessages().get(i).getMetadata().getState()); | ||||
| 								System.out | ||||
| 									.println("Message State is now: " + getChats().get(j).getModel().get(k).getMetadata().getState().toString()); | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// Updating UserStatus of all users in LocalDB | ||||
| 		for (int j = 0; j < returnSync.getUsers().size(); j++) { | ||||
| 			for (int k = 0; k < getChats().size(); k++) { | ||||
| 				if (getChats().get(k).getRecipient().getID() == returnSync.getUsers().get(j).getID()) { | ||||
|  | ||||
| 					getChats().get(k).getRecipient().setStatus(returnSync.getUsers().get(j).getStatus()); | ||||
| 					System.out.println(getChats().get(k).getRecipient().getStatus().toString()); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		sync.getMessages().clear(); | ||||
| 		sync.getUsers().clear(); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Adds the unread messages returned from the server in the latest sync to the | ||||
| 	 * right chats in the LocalDB. | ||||
| 	 *  | ||||
| 	 * @param localDB | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public void addUnreadMessagesToLocalDB() { | ||||
| 		Sync unreadMessages = unreadMessagesSync; | ||||
| 		for (int i = 0; i < unreadMessages.getMessages().size(); i++) | ||||
| 			for (int j = 0; j < getChats().size(); j++) | ||||
| 				if (getChats().get(j).getRecipient().getID() == unreadMessages.getMessages().get(i).getMetadata().getSender()) { | ||||
| 					getChats().get(j).appendMessage(unreadMessages.getMessages().get(i)); | ||||
| 				} | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Changes all messages with state {@code RECEIVED} of a specific chat to state | ||||
| 	 * {@code READ}. | ||||
| 	 * <br> | ||||
| 	 * Adds these messages to the {@code readMessages} {@link Sync} object. | ||||
| 	 *  | ||||
| 	 * @param currentChat | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public void setMessagesToRead(Chat currentChat) { | ||||
| 		for (int i = currentChat.getModel().size() - 1; i >= 0; --i) | ||||
| 			if (currentChat.getModel().get(i).getMetadata().getRecipient() != currentChat.getRecipient().getID()) | ||||
| 				if (currentChat.getModel().get(i).getMetadata().getState() == MessageState.RECEIVED) { | ||||
| 					currentChat.getModel().get(i).getMetadata().setState(MessageState.READ); | ||||
| 					readMessages.getMessages().add(currentChat.getModel().get(i)); | ||||
| 				} else break; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Adds all messages with state {@code WAITING} from the {@link LocalDB} to the | ||||
| 	 * {@link Sync} object. | ||||
| 	 *  | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	private void addWaitingMessagesToSync() { | ||||
| 		for (Chat chat : getChats()) | ||||
| 			for (int i = 0; i < chat.getModel().size(); i++) | ||||
| 				if (chat.getModel().get(i).getMetadata().getState() == MessageState.WAITING) { | ||||
| 					System.out.println("Got Waiting Message"); | ||||
| 					sync.getMessages().add(chat.getModel().get(i)); | ||||
| 				} | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Clears the {@code unreadMessagesSync} {@link Sync} object. | ||||
| 	 *  | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public void clearUnreadMessagesSync() { unreadMessagesSync.getMessages().clear(); } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return all saves {@link Chat} objects that list the client user as the | ||||
| 	 *         client | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 **/ | ||||
| 	public List<Chat> getChats() { return chats; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the {@link User} who initialized the local database | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public User getUser() { return sender; } | ||||
| } | ||||
							
								
								
									
										349
									
								
								src/main/java/envoy/client/ui/ChatWindow.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										349
									
								
								src/main/java/envoy/client/ui/ChatWindow.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,349 @@ | ||||
| package envoy.client.ui; | ||||
|  | ||||
| import java.awt.Color; | ||||
| import java.awt.ComponentOrientation; | ||||
| import java.awt.Font; | ||||
| import java.awt.GridBagConstraints; | ||||
| import java.awt.GridBagLayout; | ||||
| import java.awt.Insets; | ||||
| import java.awt.event.KeyAdapter; | ||||
| import java.awt.event.KeyEvent; | ||||
| import java.awt.event.WindowAdapter; | ||||
| import java.awt.event.WindowEvent; | ||||
|  | ||||
| import javax.swing.DefaultListModel; | ||||
| import javax.swing.JButton; | ||||
| import javax.swing.JFrame; | ||||
| import javax.swing.JList; | ||||
| import javax.swing.JOptionPane; | ||||
| import javax.swing.JPanel; | ||||
| import javax.swing.JScrollPane; | ||||
| import javax.swing.JTextArea; | ||||
| import javax.swing.JTextPane; | ||||
| import javax.swing.ListSelectionModel; | ||||
| import javax.swing.SwingUtilities; | ||||
| import javax.swing.Timer; | ||||
| import javax.swing.border.EmptyBorder; | ||||
|  | ||||
| import envoy.client.Chat; | ||||
| import envoy.client.Client; | ||||
| import envoy.client.Config; | ||||
| import envoy.client.LocalDB; | ||||
| import envoy.schema.Message; | ||||
| import envoy.schema.Sync; | ||||
| import envoy.schema.User; | ||||
|  | ||||
| /** | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>ChatWindow.java</strong><br> | ||||
|  * Created: <strong>28 Sep 2019</strong><br> | ||||
|  *  | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @author Maximilian Käfer | ||||
|  * @author Leon Hofmeister | ||||
|  * @since Envoy v0.1-alpha | ||||
|  */ | ||||
| public class ChatWindow extends JFrame { | ||||
|  | ||||
| 	private static final long serialVersionUID = 6865098428255463649L; | ||||
|  | ||||
| 	private JPanel contentPane = new JPanel(); | ||||
|  | ||||
| 	private Client	client; | ||||
| 	private LocalDB	localDB; | ||||
|  | ||||
| 	private JList<User>	userList	= new JList<>(); | ||||
| 	private Chat		currentChat; | ||||
|  | ||||
| 	private JTextArea messageEnterTextArea; | ||||
|  | ||||
| 	public ChatWindow(Client client, LocalDB localDB) { | ||||
| 		this.client		= client; | ||||
| 		this.localDB	= localDB; | ||||
|  | ||||
| 		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); | ||||
| 		setBounds(100, 100, 600, 800); | ||||
| 		setTitle("Envoy"); | ||||
| 		setLocationRelativeTo(null); | ||||
|  | ||||
| 		// Save chats when window closes | ||||
| 		addWindowListener(new WindowAdapter() { | ||||
|  | ||||
| 			@Override | ||||
| 			public void windowClosing(WindowEvent e) { localDB.saveToLocalDB(); } | ||||
| 		}); | ||||
|  | ||||
| 		contentPane.setBackground(new Color(0, 0, 0)); | ||||
| 		contentPane.setForeground(Color.white); | ||||
| 		contentPane.setBorder(new EmptyBorder(0, 5, 0, 0)); | ||||
| 		setContentPane(contentPane); | ||||
| 		GridBagLayout gbl_contentPane = new GridBagLayout(); | ||||
| 		gbl_contentPane.columnWidths	= new int[] { 1, 1, 1 }; | ||||
| 		gbl_contentPane.rowHeights		= new int[] { 1, 1, 1 }; | ||||
| 		gbl_contentPane.columnWeights	= new double[] { 0.3, 1.0, 0.1 }; | ||||
| 		gbl_contentPane.rowWeights		= new double[] { 0.05, 1.0, 0.07 }; | ||||
| 		contentPane.setLayout(gbl_contentPane); | ||||
|  | ||||
| 		JList<Message> messageList = new JList<>(); | ||||
| 		messageList.setCellRenderer(new MessageListRenderer()); | ||||
|  | ||||
| 		messageList.setFocusTraversalKeysEnabled(false); | ||||
| 		messageList.setSelectionForeground(new Color(255, 255, 255)); | ||||
| 		messageList.setSelectionBackground(new Color(102, 0, 153)); | ||||
| 		messageList.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); | ||||
| 		messageList.setForeground(new Color(255, 255, 255)); | ||||
| 		messageList.setBackground(new Color(51, 51, 51)); | ||||
|  | ||||
| 		DefaultListModel<Message> messageListModel = new DefaultListModel<>(); | ||||
| 		messageList.setModel(messageListModel); | ||||
| 		messageList.setFont(new Font("Arial", Font.PLAIN, 17)); | ||||
| 		messageList.setFixedCellHeight(60); | ||||
| 		messageList.setBorder(new EmptyBorder(5, 5, 5, 5)); | ||||
|  | ||||
| 		JScrollPane scrollPane = new JScrollPane(); | ||||
| 		scrollPane.setForeground(new Color(0, 0, 0)); | ||||
| 		scrollPane.setBackground(new Color(51, 51, 51)); | ||||
| 		scrollPane.setViewportView(messageList); | ||||
| 		scrollPane.setBorder(null); | ||||
|  | ||||
| 		GridBagConstraints gbc_scrollPane = new GridBagConstraints(); | ||||
| 		gbc_scrollPane.fill			= GridBagConstraints.BOTH; | ||||
| 		gbc_scrollPane.gridwidth	= 2; | ||||
| 		gbc_scrollPane.gridx		= 1; | ||||
| 		gbc_scrollPane.gridy		= 1; | ||||
|  | ||||
| 		gbc_scrollPane.insets = new Insets(0, 10, 10, 10); | ||||
|  | ||||
| 		contentPane.add(scrollPane, gbc_scrollPane); | ||||
|  | ||||
| 		// Message enter field | ||||
| 		messageEnterTextArea = new JTextArea(); | ||||
| 		messageEnterTextArea.addKeyListener(new KeyAdapter() { | ||||
|  | ||||
| 			@Override | ||||
| 			public void keyReleased(KeyEvent e) { | ||||
|  | ||||
| 				if (e.getKeyCode() == KeyEvent.VK_ENTER && ((SettingsScreen.enterToSend && e.getModifiersEx() == 0) | ||||
| 						|| (e.getModifiersEx() == KeyEvent.CTRL_DOWN_MASK))) { | ||||
|  | ||||
| 					postMessage(messageList); | ||||
| 				} | ||||
|  | ||||
| 			} | ||||
| 		}); | ||||
| 		// Checks for changed Message | ||||
| 		messageEnterTextArea.setWrapStyleWord(true); | ||||
| 		messageEnterTextArea.setCaretColor(new Color(255, 255, 255)); | ||||
| 		messageEnterTextArea.setForeground(new Color(255, 255, 255)); | ||||
| 		messageEnterTextArea.setBackground(new Color(51, 51, 51)); | ||||
| 		messageEnterTextArea.setLineWrap(true); | ||||
| 		messageEnterTextArea.setBorder(null); | ||||
| 		messageEnterTextArea.setFont(new Font("Arial", Font.PLAIN, 17)); | ||||
| 		messageEnterTextArea.setBorder(new EmptyBorder(5, 5, 5, 5)); | ||||
|  | ||||
| 		GridBagConstraints gbc_messageEnterTextfield = new GridBagConstraints(); | ||||
| 		gbc_messageEnterTextfield.fill	= GridBagConstraints.BOTH; | ||||
| 		gbc_messageEnterTextfield.gridx	= 1; | ||||
| 		gbc_messageEnterTextfield.gridy	= 2; | ||||
|  | ||||
| 		gbc_messageEnterTextfield.insets = new Insets(10, 10, 10, 10); | ||||
|  | ||||
| 		contentPane.add(messageEnterTextArea, gbc_messageEnterTextfield); | ||||
|  | ||||
| 		// Post Button | ||||
| 		JButton postButton = new JButton("Post"); | ||||
| 		postButton.setForeground(new Color(255, 255, 255)); | ||||
| 		postButton.setBackground(new Color(102, 51, 153)); | ||||
| 		postButton.setBorderPainted(false); | ||||
|  | ||||
| 		GridBagConstraints gbc_moveSelectionPostButton = new GridBagConstraints(); | ||||
|  | ||||
| 		gbc_moveSelectionPostButton.fill	= GridBagConstraints.BOTH; | ||||
| 		gbc_moveSelectionPostButton.gridx	= 2; | ||||
| 		gbc_moveSelectionPostButton.gridy	= 2; | ||||
|  | ||||
| 		gbc_moveSelectionPostButton.insets = new Insets(10, 10, 10, 10); | ||||
|  | ||||
| 		postButton.addActionListener((evt) -> { postMessage(messageList); }); | ||||
| 		contentPane.add(postButton, gbc_moveSelectionPostButton); | ||||
|  | ||||
| 		// Settings Button | ||||
| 		JButton settingsButton = new JButton("Settings"); | ||||
| 		settingsButton.setForeground(new Color(255, 255, 255)); | ||||
| 		settingsButton.setBackground(new Color(102, 51, 153)); | ||||
| 		settingsButton.setBorderPainted(false); | ||||
|  | ||||
| 		GridBagConstraints gbc_moveSelectionSettingsButton = new GridBagConstraints(); | ||||
|  | ||||
| 		gbc_moveSelectionSettingsButton.fill	= GridBagConstraints.BOTH; | ||||
| 		gbc_moveSelectionSettingsButton.gridx	= 2; | ||||
| 		gbc_moveSelectionSettingsButton.gridy	= 0; | ||||
|  | ||||
| 		gbc_moveSelectionSettingsButton.insets = new Insets(10, 10, 10, 10); | ||||
|  | ||||
| 		settingsButton.addActionListener((evt) -> { | ||||
| 			try { | ||||
| 				SettingsScreen.open(localDB.getUser().getName()); | ||||
| 			} catch (Exception e) { | ||||
| 				SettingsScreen.open(); | ||||
| 				System.err.println("An error occured while opening the settings screen: " + e); | ||||
| 				e.printStackTrace(); | ||||
| 			} | ||||
| 		}); | ||||
| 		contentPane.add(settingsButton, gbc_moveSelectionSettingsButton); | ||||
|  | ||||
| 		// Partner name display | ||||
| 		JTextPane textPane = new JTextPane(); | ||||
| 		textPane.setBackground(new Color(0, 0, 0)); | ||||
| 		textPane.setForeground(new Color(255, 255, 255)); | ||||
|  | ||||
| 		textPane.setFont(new Font("Arial", Font.PLAIN, 20)); | ||||
|  | ||||
| 		GridBagConstraints gbc_partnerName = new GridBagConstraints(); | ||||
| 		gbc_partnerName.fill	= GridBagConstraints.HORIZONTAL; | ||||
| 		gbc_partnerName.gridx	= 1; | ||||
| 		gbc_partnerName.gridy	= 0; | ||||
|  | ||||
| 		gbc_partnerName.insets = new Insets(0, 10, 0, 10); | ||||
| 		contentPane.add(textPane, gbc_partnerName); | ||||
|  | ||||
| 		userList.setCellRenderer(new UserListRenderer()); | ||||
| 		userList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); | ||||
| 		userList.addListSelectionListener((listSelectionEvent) -> { | ||||
| 			if (!listSelectionEvent.getValueIsAdjusting()) { | ||||
| 				@SuppressWarnings("unchecked") | ||||
| 				final JList<User>	selectedUserList	= (JList<User>) listSelectionEvent.getSource(); | ||||
| 				final User			user				= selectedUserList.getSelectedValue(); | ||||
| 				client.setRecipient(user); | ||||
|  | ||||
| 				currentChat = localDB.getChats() | ||||
| 					.stream() | ||||
| 					.filter(chat -> chat.getRecipient().getID() == user.getID()) | ||||
| 					.findFirst() | ||||
| 					.get(); | ||||
|  | ||||
| 				// Set all unread messages in the chat to read | ||||
| 				readCurrentChat(); | ||||
|  | ||||
| 				client.setRecipient(user); | ||||
|  | ||||
| 				textPane.setText(currentChat.getRecipient().getName()); | ||||
|  | ||||
| 				messageList.setModel(currentChat.getModel()); | ||||
| 				contentPane.revalidate(); | ||||
| 			} | ||||
| 		}); | ||||
|  | ||||
| 		userList.setSelectionForeground(new Color(255, 255, 255)); | ||||
| 		userList.setSelectionBackground(new Color(102, 0, 153)); | ||||
| 		userList.setForeground(new Color(255, 255, 255)); | ||||
| 		userList.setBackground(new Color(51, 51, 51)); | ||||
| 		userList.setFont(new Font("Arial", Font.PLAIN, 17)); | ||||
| 		userList.setBorder(new EmptyBorder(5, 5, 5, 5)); | ||||
|  | ||||
| 		GridBagConstraints gbc_userList = new GridBagConstraints(); | ||||
| 		gbc_userList.fill	= GridBagConstraints.VERTICAL; | ||||
| 		gbc_userList.gridx	= 0; | ||||
| 		gbc_userList.gridy	= 1; | ||||
| 		gbc_userList.anchor	= GridBagConstraints.PAGE_START; | ||||
| 		gbc_userList.insets	= new Insets(0, 0, 10, 0); | ||||
|  | ||||
| 		contentPane.add(userList, gbc_userList); | ||||
| 		contentPane.revalidate(); | ||||
|  | ||||
| 		loadUsersAndChats(); | ||||
| 		startSyncThread(Config.getInstance().getSyncTimeout()); | ||||
|  | ||||
| 		contentPane.revalidate(); | ||||
| 	} | ||||
|  | ||||
| 	private void postMessage(JList<Message> messageList) { | ||||
| 		if (!client.hasRecipient()) { | ||||
| 			JOptionPane.showMessageDialog(this, | ||||
| 					"Please select a recipient!", | ||||
| 					"Cannot send message", | ||||
| 					JOptionPane.INFORMATION_MESSAGE); | ||||
| 		} | ||||
|  | ||||
| 		if (!messageEnterTextArea.getText().isEmpty()) try { | ||||
|  | ||||
| 			// Create and send message object | ||||
| 			final Message message = localDB.createMessage(messageEnterTextArea.getText(), currentChat.getRecipient()); | ||||
| 			currentChat.appendMessage(message); | ||||
| 			messageList.setModel(currentChat.getModel()); | ||||
|  | ||||
| 			// Clear text field | ||||
| 			messageEnterTextArea.setText(""); | ||||
| 			contentPane.revalidate(); | ||||
| 		} catch (Exception e) { | ||||
| 			JOptionPane.showMessageDialog(this, | ||||
| 					"An exception occured while sending a message. See the log for more details.", | ||||
| 					"Exception occured", | ||||
| 					JOptionPane.ERROR_MESSAGE); | ||||
| 			e.printStackTrace(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Initializes the elements of the user list by downloading them from the | ||||
| 	 * server. | ||||
| 	 *  | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	private void loadUsersAndChats() { | ||||
| 		new Thread(() -> { | ||||
| 			Sync					users			= client.getUsersListXml(); | ||||
| 			DefaultListModel<User>	userListModel	= new DefaultListModel<>(); | ||||
| 			users.getUsers().forEach(user -> { | ||||
| 				userListModel.addElement(user); | ||||
|  | ||||
| 				// Check if user exists in local DB | ||||
| 				if (localDB.getChats().stream().filter(c -> c.getRecipient().getID() == user.getID()).count() == 0) | ||||
| 					localDB.getChats().add(new Chat(user)); | ||||
| 			}); | ||||
| 			SwingUtilities.invokeLater(() -> userList.setModel(userListModel)); | ||||
| 		}).start(); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Updates the data model and the UI repeatedly after a certain amount of | ||||
| 	 * time. | ||||
| 	 *  | ||||
| 	 * @param timeout the amount of time that passes between two requests sent to | ||||
| 	 *                the server | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	private void startSyncThread(int timeout) { | ||||
| 		new Timer(timeout, (evt) -> { | ||||
| 			new Thread(() -> { | ||||
|  | ||||
| 				// Synchronize | ||||
| 				localDB.applySync( | ||||
| 						client.sendSync(client.getSender().getID(), localDB.fillSync(client.getSender().getID()))); | ||||
|  | ||||
| 				// Process unread messages | ||||
| 				localDB.addUnreadMessagesToLocalDB(); | ||||
| 				localDB.clearUnreadMessagesSync(); | ||||
|  | ||||
| 				// Mark unread messages as read when they are in the current chat | ||||
| 				readCurrentChat(); | ||||
|  | ||||
| 				// Update UI | ||||
| 				SwingUtilities | ||||
| 					.invokeLater(() -> { updateUserStates(); contentPane.revalidate(); contentPane.repaint(); }); | ||||
| 			}).start(); | ||||
| 		}).start(); | ||||
| 	} | ||||
|  | ||||
| 	private void updateUserStates() { | ||||
| 		for (int i = 0; i < userList.getModel().getSize(); i++) | ||||
| 			for (int j = 0; j < localDB.getChats().size(); j++) | ||||
| 				if (userList.getModel().getElementAt(i).getID() == localDB.getChats().get(j).getRecipient().getID()) | ||||
| 					userList.getModel().getElementAt(i).setStatus(localDB.getChats().get(j).getRecipient().getStatus()); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Marks messages in the current chat as {@code READ}. | ||||
| 	 */ | ||||
| 	private void readCurrentChat() { if (currentChat != null) { localDB.setMessagesToRead(currentChat); } } | ||||
| } | ||||
							
								
								
									
										54
									
								
								src/main/java/envoy/client/ui/MessageListRenderer.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/main/java/envoy/client/ui/MessageListRenderer.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| package envoy.client.ui; | ||||
|  | ||||
| import java.awt.Component; | ||||
| import java.text.SimpleDateFormat; | ||||
|  | ||||
| import javax.swing.JLabel; | ||||
| import javax.swing.JList; | ||||
| import javax.swing.ListCellRenderer; | ||||
|  | ||||
| import envoy.schema.Message; | ||||
|  | ||||
| /** | ||||
|  * Defines how a message is displayed.<br> | ||||
|  * <br> | ||||
|  *  | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>UserListRenderer.java</strong><br> | ||||
|  * Created: <strong>19 Oct 2019</strong><br> | ||||
|  *  | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @author Maximilian Käfer | ||||
|  * @since Envoy v0.1-alpha | ||||
|  */ | ||||
| public class MessageListRenderer extends JLabel implements ListCellRenderer<Message> { | ||||
|  | ||||
| 	private static final long serialVersionUID = 5164417379767181198L; | ||||
|  | ||||
| 	@Override | ||||
| 	public Component getListCellRendererComponent(JList<? extends Message> list, Message value, int index, | ||||
| 			boolean isSelected, boolean cellHasFocus) { | ||||
| 		if (isSelected) { | ||||
| 			setBackground(list.getSelectionBackground()); | ||||
| 			setForeground(list.getSelectionForeground()); | ||||
| 		} else { | ||||
| 			setBackground(list.getBackground()); | ||||
| 			setForeground(list.getForeground()); | ||||
| 		} | ||||
|  | ||||
| 		setOpaque(true); | ||||
|  | ||||
| 		final String	text	= value.getContent().get(0).getText(); | ||||
| 		final String 	state 	= value.getMetadata().getState().toString(); | ||||
| 		final String	date	= value.getMetadata().getDate() == null ? "" | ||||
| 				: new SimpleDateFormat("dd.MM.yyyy HH:mm ") | ||||
| 					.format(value.getMetadata().getDate().toGregorianCalendar().getTime()); | ||||
|  | ||||
| 		setText(String.format( | ||||
| 				"<html><p style=\"color:#d2d235\"><b><small>%s</b></small><br><p style=\"color:white\">%s :%s</html>", | ||||
| 				date, | ||||
| 				text, | ||||
| 				state)); | ||||
| 		return this; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										156
									
								
								src/main/java/envoy/client/ui/SettingsScreen.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								src/main/java/envoy/client/ui/SettingsScreen.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,156 @@ | ||||
| package envoy.client.ui; | ||||
|  | ||||
| import java.awt.BorderLayout; | ||||
| import java.awt.Color; | ||||
| import java.awt.FlowLayout; | ||||
|  | ||||
| import javax.swing.JButton; | ||||
| import javax.swing.JDialog; | ||||
| import javax.swing.JPanel; | ||||
| import javax.swing.border.EmptyBorder; | ||||
|  | ||||
| /** | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>SettingsScreen.java</strong><br> | ||||
|  * Created: <strong>31 Oct 2019</strong><br> | ||||
|  *  | ||||
|  * @author Leon Hofmeister | ||||
|  */ | ||||
| public class SettingsScreen extends JDialog { | ||||
|  | ||||
| 	private static final long	serialVersionUID	= -4476913491263077107L; | ||||
| 	private final JPanel		contentPanel		= new JPanel(); | ||||
| 	public static boolean		enterToSend			= true; | ||||
|  | ||||
| 	// TODO: Add a JPanel with all the Information necessary: | ||||
| 	// change (Picture,Username, Email, Password) and toggle(light/dark mode, | ||||
| 	// "ctrl+enter"/"enter" | ||||
| 	// to send a message directly) | ||||
| 	/** | ||||
| 	 * Open the Settings screen. | ||||
| 	 * Only suited for Dev/Error mode. | ||||
| 	 * Avoid usage. | ||||
| 	 *  | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public static void open() { open(new SettingsScreen()); } | ||||
|  | ||||
| 	/** | ||||
| 	 * Opens the Settings screen.<br> | ||||
| 	 * Use preferably since everyone is already initialised.<br> | ||||
| 	 * It personalises the screen more. | ||||
| 	 *  | ||||
| 	 * @param username The name of the User | ||||
| 	 * @param Email    The Email that is associated with that Account | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public static void open(String username) {// , String Email) {AUSKLAMMERN, WENN ANMELDUNG PER | ||||
| 												// EMAIL IMPLEMENTIERT IST! | ||||
| 		open(new SettingsScreen(username)); | ||||
| 	} | ||||
|  | ||||
| 	public static void open(SettingsScreen dialog) { | ||||
| 		dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); | ||||
| 		dialog.setModal(true); | ||||
| 		dialog.setVisible(true); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Builds the Settings screen.<br> | ||||
| 	 * Use only as Dev/Error Mode.<br> | ||||
| 	 * Avoid usage. | ||||
| 	 *  | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public SettingsScreen() { | ||||
| 		setBackground(Color.BLACK); | ||||
| 		setBounds(100, 100, 450, 300); | ||||
| 		getContentPane().setLayout(new BorderLayout()); | ||||
| 		contentPanel.setBackground(Color.BLACK); | ||||
| 		contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); | ||||
| 		getContentPane().add(contentPanel, BorderLayout.CENTER); | ||||
| 		contentPanel.setLayout(new BorderLayout(0, 0)); | ||||
| 		{ | ||||
| 			JPanel buttonPane = new JPanel(); | ||||
| 			buttonPane.setBackground(Color.BLACK); | ||||
| 			getContentPane().add(buttonPane, BorderLayout.SOUTH); | ||||
| 			buttonPane.setLayout(new BorderLayout(0, 0)); | ||||
| 			{ | ||||
| 				JButton okButton = new JButton("Save"); | ||||
| 				okButton.setActionCommand("OK"); | ||||
| 				buttonPane.add(okButton, BorderLayout.EAST); | ||||
| 				getRootPane().setDefaultButton(okButton); | ||||
| 				okButton.addActionListener((evt) -> { | ||||
| 					// Hier später die Daten abspeichern, wenn Datenmodell implementiert ist | ||||
| 					dispose(); | ||||
| 				}); | ||||
| 			} | ||||
| 			{ | ||||
| 				JButton cancelButton = new JButton("Cancel"); | ||||
| 				cancelButton.setActionCommand("Cancel"); | ||||
| 				buttonPane.add(cancelButton, BorderLayout.WEST); | ||||
|  | ||||
| 				cancelButton.addActionListener((evt) -> { dispose(); }); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Builds the Settings screen.<br> | ||||
| 	 * Use preferreably since everyone is already initialised.<br> | ||||
| 	 * It personalises the screen more. | ||||
| 	 *  | ||||
| 	 * @param Username The name of the User | ||||
| 	 * @param Email    The Email that is associated with that Account | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public SettingsScreen(String Username) {// , String Email, String hashedPwd) {AUSKLAMMERN, WENN ANMELDUNG PER EMAIL | ||||
| 											// IMPLEMENTIERT IST! | ||||
| 		setBackground(Color.BLACK); | ||||
| 		setBounds(100, 100, 450, 300); | ||||
| 		getContentPane().setLayout(new BorderLayout()); | ||||
| 		contentPanel.setBackground(Color.BLACK); | ||||
| 		contentPanel.setLayout(new FlowLayout()); | ||||
| 		contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); | ||||
| 		getContentPane().add(contentPanel, BorderLayout.CENTER); | ||||
| 		{ | ||||
| 			JPanel buttonPane = new JPanel(); | ||||
| 			buttonPane.setBackground(Color.BLACK); | ||||
| 			getContentPane().add(buttonPane, BorderLayout.SOUTH); | ||||
| 			buttonPane.setLayout(new BorderLayout(0, 0)); | ||||
| 			{ | ||||
| 				JButton okButton = new JButton("Save"); | ||||
| 				okButton.setActionCommand("OK"); | ||||
| 				buttonPane.add(okButton, BorderLayout.EAST); | ||||
| 				getRootPane().setDefaultButton(okButton); | ||||
| 				okButton.addActionListener((evt) -> { | ||||
| 					// Hier später die Daten abspeichern, wenn Datenmodell implementiert ist | ||||
| 					dispose(); | ||||
| 				}); | ||||
| 			} | ||||
| 			{ | ||||
| 				JButton cancelButton = new JButton("Cancel"); | ||||
| 				cancelButton.setActionCommand("Cancel"); | ||||
| 				buttonPane.add(cancelButton, BorderLayout.WEST); | ||||
|  | ||||
| 				cancelButton.addActionListener((evt) -> { dispose(); }); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return true if Enter should be used to send a message instead of ctrl+enter | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public static boolean isEnterToSend() { return enterToSend; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @param enterToSend <br> | ||||
| 	 *                    toggles whether a message should be sent via | ||||
| 	 *                    <br> | ||||
| 	 *                    buttonpress "enter" or "ctrl"+"enter" | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public static void setEnterToSend(boolean enterForSend) { enterToSend = enterForSend; } | ||||
| 	// TODO: Should be changed to private, but later to avoid warnings | ||||
| } | ||||
							
								
								
									
										73
									
								
								src/main/java/envoy/client/ui/Startup.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/main/java/envoy/client/ui/Startup.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| package envoy.client.ui; | ||||
|  | ||||
| import java.awt.EventQueue; | ||||
| import java.io.IOException; | ||||
| import java.util.Properties; | ||||
|  | ||||
| import javax.swing.JOptionPane; | ||||
|  | ||||
| import envoy.client.Client; | ||||
| import envoy.client.Config; | ||||
| import envoy.client.LocalDB; | ||||
| import envoy.exception.EnvoyException; | ||||
|  | ||||
| /** | ||||
|  * Starts the Envoy client and prompts the user to enter their name. | ||||
|  *  | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>Startup.java</strong><br> | ||||
|  * Created: <strong>12 Oct 2019</strong><br> | ||||
|  *  | ||||
|  * @author Leon Hofmeister | ||||
|  * @author Maximilian Käfer | ||||
|  * @since Envoy v0.1-alpha | ||||
|  */ | ||||
| public class Startup { | ||||
|  | ||||
| 	public static void main(String[] args) { | ||||
| 		Config config = Config.getInstance(); | ||||
| 		if (args.length > 0) { | ||||
| 			config.load(args); | ||||
| 		} else { | ||||
| 			ClassLoader loader = Thread.currentThread().getContextClassLoader(); | ||||
| 			try { | ||||
| 				Properties configProperties = new Properties(); | ||||
| 				configProperties.load(loader.getResourceAsStream("client.properties")); | ||||
| 				config.load(configProperties); | ||||
| 			} catch (IOException e) { | ||||
| 				e.printStackTrace(); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if (!config.isInitialized()) { | ||||
| 			System.err.println("Server or port are not defined. Exiting..."); | ||||
| 			System.exit(1); | ||||
| 		} | ||||
|  | ||||
| 		String userName = JOptionPane.showInputDialog("Please enter your username"); | ||||
| 		if (userName == null || userName.isEmpty()) { | ||||
| 			System.err.println("User name is not set or empty. Exiting..."); | ||||
| 			System.exit(1); | ||||
| 		} | ||||
| 		Client	client	= new Client(config, userName); | ||||
| 		LocalDB	localDB	= new LocalDB(client.getSender()); | ||||
| 		try { | ||||
| 			localDB.initializeDBFile(config.getLocalDB()); | ||||
| 		} catch (EnvoyException e) { | ||||
| 			e.printStackTrace(); | ||||
| 			JOptionPane.showMessageDialog(null, | ||||
| 					"Error while loading local database: " + e.toString() + "\nChats will not be stored locally.", | ||||
| 					"Local DB error", | ||||
| 					JOptionPane.WARNING_MESSAGE); | ||||
| 		} | ||||
|  | ||||
| 		EventQueue.invokeLater(() -> { | ||||
| 			try { | ||||
| 				ChatWindow frame = new ChatWindow(client, localDB); | ||||
| 				frame.setVisible(true); | ||||
| 			} catch (Exception e) { | ||||
| 				e.printStackTrace(); | ||||
| 			} | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										65
									
								
								src/main/java/envoy/client/ui/UserListRenderer.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								src/main/java/envoy/client/ui/UserListRenderer.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| package envoy.client.ui; | ||||
|  | ||||
| import java.awt.Component; | ||||
|  | ||||
| import javax.swing.JLabel; | ||||
| import javax.swing.JList; | ||||
| import javax.swing.ListCellRenderer; | ||||
|  | ||||
| import envoy.schema.User; | ||||
| import envoy.schema.User.UserStatus; | ||||
|  | ||||
| /** | ||||
|  * Defines how the {@code UserList} is displayed. | ||||
|  *  | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>UserListRenderer.java</strong><br> | ||||
|  * Created: <strong>12 Oct 2019</strong><br> | ||||
|  *  | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @author Maximilian Käfer | ||||
|  * @since Envoy v0.1-alpha | ||||
|  */ | ||||
| public class UserListRenderer extends JLabel implements ListCellRenderer<User> { | ||||
|  | ||||
| 	private static final long serialVersionUID = 5164417379767181198L; | ||||
|  | ||||
| 	@Override | ||||
| 	public Component getListCellRendererComponent(JList<? extends User> list, User value, int index, boolean isSelected, | ||||
| 			boolean cellHasFocus) { | ||||
| 		if (isSelected) { | ||||
| 			setBackground(list.getSelectionBackground()); | ||||
| 			setForeground(list.getSelectionForeground()); | ||||
| 		} else { | ||||
| 			setBackground(list.getBackground()); | ||||
| 			setForeground(list.getForeground()); | ||||
| 		} | ||||
|  | ||||
| 		// Enable background rendering | ||||
| 		setOpaque(true); | ||||
|  | ||||
|  | ||||
| 		final String		name	= value.getName(); | ||||
| 		final UserStatus	status	= value.getStatus(); | ||||
| 		 | ||||
| 		switch (status) { | ||||
| 			case ONLINE: | ||||
| 				setText(String.format( | ||||
| 						"<html><p style=\"color:#03fc20\"><b><small>%s</b></small><br><p style=\"color:white\">%s</html>", | ||||
| 						status, | ||||
| 						name)); | ||||
| 				break; | ||||
|  | ||||
| 			case OFFLINE: | ||||
| 				setText(String.format( | ||||
| 						"<html><p style=\"color:#fc0303\"><b><small>%s</b></small><br><p style=\"color:white\">%s</html>", | ||||
| 						status, | ||||
| 						name)); | ||||
| 				break; | ||||
| 		} | ||||
|  | ||||
| 		 | ||||
|  | ||||
| 		return this; | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user