Merge branch 'develop' into f/themes
This commit is contained in:
		| @@ -1,5 +1,7 @@ | ||||
| package envoy.client; | ||||
|  | ||||
| import java.util.logging.Logger; | ||||
|  | ||||
| import javax.ws.rs.client.ClientBuilder; | ||||
| import javax.ws.rs.client.Entity; | ||||
| import javax.ws.rs.client.WebTarget; | ||||
| @@ -28,18 +30,20 @@ public class Client { | ||||
| 	private Config			config; | ||||
| 	private User			sender, recipient; | ||||
|  | ||||
| 	private static final Logger logger = Logger.getLogger(Client.class.getSimpleName()); | ||||
|  | ||||
| 	public Client(Config config, String username) { | ||||
| 		this.config	= config; | ||||
| 		sender		= getUser(username); | ||||
| 		System.out.println("ID: " + sender.getID()); | ||||
|  | ||||
| 		logger.info("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); | ||||
| 		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(); | ||||
|  | ||||
| @@ -92,7 +96,7 @@ public class Client { | ||||
| 		if (returnSenderSync.getUsers().size() == 1) { | ||||
| 			returnSender = returnSenderSync.getUsers().get(0); | ||||
| 		} else { | ||||
| 			System.out.println("ERROR exiting..."); | ||||
| 			logger.warning("ERROR exiting..."); | ||||
| 		} | ||||
|  | ||||
| 		return returnSender; | ||||
| @@ -129,7 +133,7 @@ public class Client { | ||||
| 	 * Updating UserStatus of all users in LocalDB. (Server sends all users with | ||||
| 	 * their updated UserStatus to the client.) <br> | ||||
| 	 *  | ||||
| 	 * @param userId the client who sends the sync | ||||
| 	 * @param userId the id of the {@link Client} who sends the {@link Sync} | ||||
| 	 * @param sync the sync object (yet to be converted from java class to sync.xml) | ||||
| 	 * @return a returnSync.xml file | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| @@ -166,16 +170,16 @@ public class Client { | ||||
| 	public User getRecipient() { return recipient; } | ||||
|  | ||||
| 	/** | ||||
|      * Sets the 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; } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -32,7 +32,7 @@ public class Config { | ||||
| 	 *  | ||||
| 	 * @param properties a {@link Properties} object containing information about | ||||
| 	 *                   the server and port, as well as the path to the local | ||||
| 	 *                   database | ||||
| 	 *                   database and the synchronization timeout | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public void load(Properties properties) { | ||||
| @@ -65,8 +65,6 @@ public class Config { | ||||
| 					localDB = new File(args[++i]); | ||||
| 					break; | ||||
| 			} | ||||
| 		if (localDB == null) localDB = new File(".\\localDB"); | ||||
| 		if (syncTimeout == 0) syncTimeout = 1000; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| @@ -116,7 +114,7 @@ public class Config { | ||||
| 	/** | ||||
| 	 * Changes the default local database. | ||||
| 	 * Exclusively intended for development purposes. | ||||
| 	 *  | ||||
|    * | ||||
| 	 * @param localDB the file containing the local database | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 **/ | ||||
|   | ||||
| @@ -1,313 +1,295 @@ | ||||
| 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 sender 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 | ||||
| 	 */ | ||||
| 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 java.util.logging.Logger; | ||||
|  | ||||
| import javax.xml.datatype.DatatypeConfigurationException; | ||||
| import javax.xml.datatype.DatatypeFactory; | ||||
|  | ||||
| import envoy.client.event.EventBus; | ||||
| import envoy.client.event.MessageCreationEvent; | ||||
| 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(); | ||||
|  | ||||
|     private static final Logger logger = Logger.getLogger(LocalDB.class.getSimpleName()); | ||||
|  | ||||
| 	/** | ||||
| 	 * Constructs an empty local database. | ||||
| 	 * | ||||
| 	 * @param sender 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())); | ||||
| 		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.<br> | ||||
| 	 * It is theoretically possible to fail due to unknown causes.<br> | ||||
| 	 * In such a case, every message sent/ received during that session will be | ||||
| 	 * lost. | ||||
| 	 *  | ||||
| 	 * @throws IOException gets thrown, if saving failed for some reason | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public void saveToLocalDB() throws IOException{ | ||||
| 		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 | ||||
| 	 * @param recipientID self explanatory | ||||
| 	 * @return prepared {@link Message} object | ||||
| 	 */ | ||||
| 	public Message createMessage(String textContent, long recipientID) { | ||||
| 		Message.Metadata metaData = objectFactory.createMessageMetadata(); | ||||
| 		metaData.setSender(sender.getID()); | ||||
| 		metaData.setRecipient(recipientID); | ||||
| 		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(); | ||||
| 	} | ||||
|  | ||||
| 		if (localDB.exists()) loadFromLocalDB(); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Adds a message to the "sync" Sync object. | ||||
| 	 *  | ||||
| 	 * @param message the message to send | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public void addMessageToSync(Message message) { sync.getMessages().add(message); } | ||||
|  | ||||
| 	 * 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() throws IOException { | ||||
| 		localDB.getParentFile().mkdirs(); | ||||
| 		localDB.createNewFile(); | ||||
| 		try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(localDB))) { | ||||
| 			out.writeObject(chats); | ||||
| 		} catch (IOException ex) { | ||||
| 			throw ex; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Adds the unread messages returned from the server in the latest sync to the | ||||
| 	 * right chats in the 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)); | ||||
| 				} | ||||
| 	} | ||||
|  | ||||
| 	 * 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); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * 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 the chat that was just opened | ||||
| 	 * @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. | ||||
| 	 * Creates a {@link Message} object serializable to XML. | ||||
| 	 * | ||||
| 	 * @param textContent The content (text) of the message | ||||
| 	 * @param recipientID The recipient of the message | ||||
| 	 * @return prepared {@link Message} object | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public Message createMessage(String textContent, long recipientID) { | ||||
| 		Message.Metadata metaData = objectFactory.createMessageMetadata(); | ||||
| 		metaData.setSender(sender.getID()); | ||||
| 		metaData.setRecipient(recipientID); | ||||
| 		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; | ||||
| 	} | ||||
|  | ||||
|     /** | ||||
| 	 * Creates a {@link Sync} object filled with the changes that occurred to the | ||||
| 	 * local database since the last synchronization. | ||||
| 	 *  | ||||
| 	 * @param message     the message that is not yet received by the other user | ||||
| 	 * @param currentChat the chat that is currently open | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 * @param userId the ID of the user that is synchronized by this client | ||||
| 	 * @return {@link Sync} object filled with the current changes | ||||
|      * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public void addWaitingMessageToLocalDB(Message message, Chat currentChat) { currentChat.appendMessage(message); } | ||||
|  | ||||
| 	/** | ||||
| 	 * Adds all messages with State WAITING from the {@link LocalDB} to the Sync. | ||||
| 	 *  | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	public Sync fillSync(long userId) { | ||||
| 		addWaitingMessagesToSync(); | ||||
|  | ||||
| 		sync.getMessages().addAll(readMessages.getMessages()); | ||||
| 		readMessages.getMessages().clear(); | ||||
|  | ||||
| 		logger.finest(String.format("Filled sync with %d messages.", sync.getMessages().size())); | ||||
| 		return sync; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Applies the changes carried by a {@link Sync} object to the local database | ||||
| 	 *  | ||||
| 	 * @param returnSync the {@link Sync} object to apply | ||||
|      * @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; } | ||||
| } | ||||
| 	public void applySync(Sync returnSync) { | ||||
| 		for (int i = 0; i < returnSync.getMessages().size(); i++) { | ||||
|  | ||||
| 			// The message has an ID | ||||
| 			if (returnSync.getMessages().get(i).getMetadata().getMessageId() != 0) { | ||||
|  | ||||
| 				// Messages are processes differently corresponding to their state | ||||
| 				switch (returnSync.getMessages().get(i).getMetadata().getState()) { | ||||
| 					case SENT: | ||||
| 						// Update previously waiting and now sent messages that were assigned an ID by | ||||
| 						// the server | ||||
| 						sync.getMessages().get(i).getMetadata().setMessageId(returnSync.getMessages().get(i).getMetadata().getMessageId()); | ||||
| 						sync.getMessages().get(i).getMetadata().setState(returnSync.getMessages().get(i).getMetadata().getState()); | ||||
| 						break; | ||||
| 					case RECEIVED: | ||||
| 						if (returnSync.getMessages().get(i).getMetadata().getSender() != 0) { | ||||
| 							// these are the unread Messages from the server | ||||
| 							unreadMessagesSync.getMessages().add(returnSync.getMessages().get(i)); | ||||
|  | ||||
| 							// Create and dispatch message creation event | ||||
| 							EventBus.getInstance().dispatch(new MessageCreationEvent(returnSync.getMessages().get(i))); | ||||
| 						} else { | ||||
| 							// Update Messages in localDB to state RECEIVED | ||||
| 							for (Chat chat : getChats()) | ||||
| 								if (chat.getRecipient().getID() == returnSync.getMessages().get(i).getMetadata().getRecipient()) | ||||
| 									for (int j = 0; j < chat.getModel().getSize(); j++) | ||||
| 									if (chat.getModel().get(j).getMetadata().getMessageId() == returnSync.getMessages() | ||||
| 										.get(i) | ||||
| 										.getMetadata() | ||||
| 										.getMessageId()) | ||||
| 										chat.getModel().get(j).getMetadata().setState(returnSync.getMessages().get(i).getMetadata().getState()); | ||||
| 						} | ||||
| 						break; | ||||
| 					case READ: | ||||
| 						// Update local Messages to state READ | ||||
| 						logger.info("Message with ID: " + returnSync.getMessages().get(i).getMetadata().getMessageId() | ||||
| 								+ "was initialized to be set to READ in localDB."); | ||||
| 						for (Chat chat : getChats()) | ||||
| 							if (chat.getRecipient().getID() == returnSync.getMessages().get(i).getMetadata().getRecipient()) { | ||||
| 								logger.info("Chat with: " + chat.getRecipient().getID() + "was selected."); | ||||
| 								for (int k = 0; k < chat.getModel().getSize(); k++) | ||||
| 									if (chat.getModel().get(k).getMetadata().getMessageId() == returnSync.getMessages() | ||||
| 										.get(i) | ||||
| 										.getMetadata() | ||||
| 										.getMessageId()) { | ||||
| 										logger.info("Message with ID: " + chat.getModel().get(k).getMetadata().getMessageId() + "was selected."); | ||||
| 										chat.getModel().get(k).getMetadata().setState(returnSync.getMessages().get(i).getMetadata().getState()); | ||||
| 										logger.info("Message State is now: " + chat.getModel().get(k).getMetadata().getState()); | ||||
| 									} | ||||
| 							} | ||||
| 						break; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// Updating UserStatus of all users in LocalDB | ||||
| 		for (User user : returnSync.getUsers()) | ||||
| 			for (Chat chat : getChats()) | ||||
| 				if (user.getID() == chat.getRecipient().getID()) { | ||||
| 					chat.getRecipient().setStatus(user.getStatus()); | ||||
| 					logger.info(chat.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. | ||||
| 	 * | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public void addUnreadMessagesToLocalDB() { | ||||
| 		for (Message message : unreadMessagesSync.getMessages()) | ||||
| 			for (Chat chat : getChats()) | ||||
| 				if (message.getMetadata().getSender() == chat.getRecipient().getID()) { | ||||
| 					chat.appendMessage(message); | ||||
| 					break; | ||||
| 				} | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * 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 the {@link Chat} that was just opened | ||||
| 	 * @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) { | ||||
| 					logger.info("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 saved {@link Chat} objects that list the client user as the | ||||
| 	 *         sender | ||||
| 	 * @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; } | ||||
| } | ||||
|   | ||||
							
								
								
									
										17
									
								
								src/main/java/envoy/client/event/Event.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/main/java/envoy/client/event/Event.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| package envoy.client.event; | ||||
|  | ||||
| /** | ||||
|  * Project: <strong>envoy-clientChess</strong><br> | ||||
|  * File: <strong>Event.javaEvent.java</strong><br> | ||||
|  * Created: <strong>04.12.2019</strong><br> | ||||
|  *  | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @since Envoy v0.2-alpha | ||||
|  */ | ||||
| public interface Event<T> { | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the data associated with this event | ||||
| 	 */ | ||||
| 	T get(); | ||||
| } | ||||
							
								
								
									
										71
									
								
								src/main/java/envoy/client/event/EventBus.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								src/main/java/envoy/client/event/EventBus.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| package envoy.client.event; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.Arrays; | ||||
| import java.util.List; | ||||
|  | ||||
| /** | ||||
|  * This class handles events by allowing {@link EventHandler} object to register | ||||
|  * themselves and then be notified about certain events dispatched by the event | ||||
|  * bus.<br> | ||||
|  * <br> | ||||
|  * The event bus is a singleton and can be used across the entire application to | ||||
|  * guarantee the propagation of events. | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>EventBus.java</strong><br> | ||||
|  * Created: <strong>04.12.2019</strong><br> | ||||
|  *  | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @since Envoy v0.2-alpha | ||||
|  */ | ||||
| public class EventBus { | ||||
|  | ||||
| 	/** | ||||
| 	 * Contains all {@link EventHandler} instances registered at this | ||||
| 	 * {@link EventBus}. | ||||
| 	 */ | ||||
| 	private List<EventHandler> handlers = new ArrayList<>(); | ||||
|  | ||||
| 	/** | ||||
| 	 * The singleton instance of this {@link EventBus} that is used across the | ||||
| 	 * entire application. | ||||
| 	 */ | ||||
| 	private static EventBus eventBus = new EventBus(); | ||||
|  | ||||
| 	/** | ||||
| 	 * This constructor is not accessible from outside this class because a | ||||
| 	 * singleton instance of it is provided by the {@link EventBus#getInstance()} | ||||
| 	 * method. | ||||
| 	 */ | ||||
| 	private EventBus() {} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the singleton instance of the {@link EventBus} | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 */ | ||||
| 	public static EventBus getInstance() { return eventBus; } | ||||
|  | ||||
| 	/** | ||||
| 	 * Registers a list of {@link EventHandler} objects to be notified when a | ||||
| 	 * {@link Event} is dispatched that they are subscribed to. | ||||
| 	 *  | ||||
| 	 * @param handlers the {@link EventHandler} objects to register | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 */ | ||||
| 	public void register(EventHandler... handlers) { this.handlers.addAll(Arrays.asList(handlers)); } | ||||
|  | ||||
| 	/** | ||||
| 	 * Dispatches a {@link Event} to every {@link EventHandler} subscribed to it. | ||||
| 	 *  | ||||
| 	 * @param event the {@link Event} to dispatch | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 */ | ||||
| 	public void dispatch(Event<?> event) { handlers.stream().filter(h -> h.supports().contains(event.getClass())).forEach(h -> h.handle(event)); } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return a list of all {@link EventHandler} instances currently registered at | ||||
| 	 *         this {@link EventBus} | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 */ | ||||
| 	public List<EventHandler> getHandlers() { return handlers; } | ||||
| } | ||||
							
								
								
									
										25
									
								
								src/main/java/envoy/client/event/EventHandler.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/main/java/envoy/client/event/EventHandler.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| package envoy.client.event; | ||||
|  | ||||
| import java.util.Set; | ||||
|  | ||||
| /** | ||||
|  * Project: <strong>envoy-clientChess</strong><br> | ||||
|  * File: <strong>EventHandler.javaEvent.java</strong><br> | ||||
|  * Created: <strong>04.12.2019</strong><br> | ||||
|  *  | ||||
|  * @author Kai S. K. Engelbart | ||||
|  */ | ||||
| public interface EventHandler { | ||||
|  | ||||
| 	/** | ||||
| 	 * Consumes an event dispatched by the event bus. | ||||
| 	 *  | ||||
| 	 * @param event The event dispatched by the event bus, only of supported type | ||||
| 	 */ | ||||
| 	void handle(Event<?> event); | ||||
|  | ||||
| 	/** | ||||
| 	 * @return A set of classes this class is supposed to handle in events | ||||
| 	 */ | ||||
| 	Set<Class<? extends Event<?>>> supports(); | ||||
| } | ||||
							
								
								
									
										18
									
								
								src/main/java/envoy/client/event/MessageCreationEvent.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/main/java/envoy/client/event/MessageCreationEvent.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| package envoy.client.event; | ||||
|  | ||||
| import envoy.schema.Message; | ||||
|  | ||||
| /** | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>MessageCreationEvent.java</strong><br> | ||||
|  * Created: <strong>4 Dec 2019</strong><br> | ||||
|  * | ||||
|  * @author Kai S. K. Engelbart | ||||
|  */ | ||||
| public class MessageCreationEvent extends MessageEvent { | ||||
|  | ||||
| 	/** | ||||
| 	 * @param message the {@link Message} that has been created | ||||
| 	 */ | ||||
| 	public MessageCreationEvent(Message message) { super(message); } | ||||
| } | ||||
							
								
								
									
										20
									
								
								src/main/java/envoy/client/event/MessageEvent.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/main/java/envoy/client/event/MessageEvent.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| package envoy.client.event; | ||||
|  | ||||
| import envoy.schema.Message; | ||||
|  | ||||
| /** | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>MessageCreationEvent.java</strong><br> | ||||
|  * Created: <strong>4 Dec 2019</strong><br> | ||||
|  * | ||||
|  * @author Kai S. K. Engelbart | ||||
|  */ | ||||
| public class MessageEvent implements Event<Message> { | ||||
|  | ||||
| 	protected final Message message; | ||||
|  | ||||
| 	public MessageEvent(Message message) { this.message = message; } | ||||
|  | ||||
| 	@Override | ||||
| 	public Message get() { return message; } | ||||
| } | ||||
| @@ -0,0 +1,18 @@ | ||||
| package envoy.client.event; | ||||
|  | ||||
| import envoy.schema.Message; | ||||
|  | ||||
| /** | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>MessageModificationEvent.java</strong><br> | ||||
|  * Created: <strong>4 Dec 2019</strong><br> | ||||
|  * | ||||
|  * @author Kai S. K. Engelbart | ||||
|  */ | ||||
| public class MessageModificationEvent extends MessageEvent { | ||||
|  | ||||
| 	/** | ||||
| 	 * @param message the {@link Message} that has been modified | ||||
| 	 */ | ||||
| 	public MessageModificationEvent(Message message) { super(message); } | ||||
| } | ||||
| @@ -1,381 +1,373 @@ | ||||
| package envoy.client.ui; | ||||
|  | ||||
| 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 java.io.IOException; | ||||
|  | ||||
| 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.client.Settings; | ||||
| 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; | ||||
|  | ||||
| 	// user specific objects | ||||
| 	private Client	client; | ||||
| 	private LocalDB	localDB; | ||||
| 	// GUI components | ||||
| 	private JPanel			contentPane				= new JPanel(); | ||||
| 	private JTextArea		messageEnterTextArea	= new JTextArea(); | ||||
| 	private JList<User>		userList				= new JList<>(); | ||||
| 	private Chat			currentChat; | ||||
| 	private JList<Message>	messageList				= new JList<>(); | ||||
| 	private JScrollPane		scrollPane				= new JScrollPane(); | ||||
| 	private JTextPane		textPane				= new JTextPane(); | ||||
| 	// private JCheckBox jCbChangeMode; | ||||
| 	private JButton	postButton		= new JButton("Post"); | ||||
| 	private JButton	settingsButton	= new JButton("Settings"); | ||||
|  | ||||
| 	private static int space = 4; | ||||
|  | ||||
| 	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) { | ||||
| 				try { | ||||
| 					localDB.saveToLocalDB(); | ||||
| 					Settings.getInstance().save(); | ||||
| 				} catch (IOException e1) { | ||||
| 					e1.printStackTrace(); | ||||
| 					System.err.println("Could not save localDB"); | ||||
| 				} | ||||
| 			} | ||||
| 		}); | ||||
|  | ||||
| 		contentPane.setBorder(new EmptyBorder(space, space, space, space)); | ||||
| 		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); | ||||
|  | ||||
| 		messageList.setCellRenderer(new MessageListRenderer()); | ||||
| 		messageList.setFocusTraversalKeysEnabled(false); | ||||
| 		messageList.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); | ||||
|  | ||||
| 		DefaultListModel<Message> messageListModel = new DefaultListModel<>(); | ||||
| 		messageList.setModel(messageListModel); | ||||
| 		messageList.setFont(new Font("Arial", Font.PLAIN, 17)); | ||||
| 		messageList.setFixedCellHeight(60); | ||||
| 		messageList.setBorder(new EmptyBorder(space, space, space, space)); | ||||
|  | ||||
| 		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(space, space, space, space); | ||||
| 		contentPane.add(scrollPane, gbc_scrollPane); | ||||
|  | ||||
| 		// Message enter field | ||||
| 		messageEnterTextArea.addKeyListener(new KeyAdapter() { | ||||
|  | ||||
| 			@Override | ||||
| 			public void keyReleased(KeyEvent e) { | ||||
|  | ||||
| 				if (e.getKeyCode() == KeyEvent.VK_ENTER | ||||
| 						&& ((Settings.getInstance().isEnterToSend() && e.getModifiersEx() == 0) | ||||
| 								|| (e.getModifiersEx() == KeyEvent.CTRL_DOWN_MASK))) { | ||||
|  | ||||
| 					postMessage(messageList); | ||||
| 				} | ||||
|  | ||||
| 			} | ||||
| 		}); | ||||
| 		// Checks for changed Message | ||||
| 		messageEnterTextArea.setWrapStyleWord(true); | ||||
| 		messageEnterTextArea.setLineWrap(true); | ||||
| 		messageEnterTextArea.setBorder(null); | ||||
| 		messageEnterTextArea.setFont(new Font("Arial", Font.PLAIN, 17)); | ||||
| 		messageEnterTextArea.setBorder(new EmptyBorder(space, space, space, space)); | ||||
|  | ||||
| 		GridBagConstraints gbc_messageEnterTextfield = new GridBagConstraints(); | ||||
| 		gbc_messageEnterTextfield.fill	= GridBagConstraints.BOTH; | ||||
| 		gbc_messageEnterTextfield.gridx	= 1; | ||||
| 		gbc_messageEnterTextfield.gridy	= 2; | ||||
|  | ||||
| 		gbc_messageEnterTextfield.insets = new Insets(space, space, space, space); | ||||
|  | ||||
| 		contentPane.add(messageEnterTextArea, gbc_messageEnterTextfield); | ||||
|  | ||||
| 		// Post Button | ||||
| 		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(space, space, space, space); | ||||
|  | ||||
| 		postButton.addActionListener((evt) -> { postMessage(messageList); }); | ||||
| 		contentPane.add(postButton, gbc_moveSelectionPostButton); | ||||
|  | ||||
| 		// Settings Button | ||||
| 		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(space, space, space, space); | ||||
|  | ||||
| 		settingsButton.addActionListener((evt) -> { | ||||
| 			try { | ||||
| 				SettingsScreen.open(); | ||||
| 				 | ||||
| 				changeChatWindowColors(Settings.getInstance().getCurrentTheme()); | ||||
| 				 | ||||
| 			} catch (Exception e) { | ||||
| 				System.err.println("An error occured while opening the settings screen: " + e); | ||||
| 				e.printStackTrace(); | ||||
| 			} | ||||
| 		}); | ||||
| 		contentPane.add(settingsButton, gbc_moveSelectionSettingsButton); | ||||
|  | ||||
| 		// Partner name display | ||||
| 		textPane.setFont(new Font("Arial", Font.PLAIN, 20)); | ||||
| 		textPane.setEditable(false); | ||||
|  | ||||
| 		GridBagConstraints gbc_partnerName = new GridBagConstraints(); | ||||
| 		gbc_partnerName.fill	= GridBagConstraints.HORIZONTAL; | ||||
| 		gbc_partnerName.gridx	= 1; | ||||
| 		gbc_partnerName.gridy	= 0; | ||||
|  | ||||
| 		gbc_partnerName.insets = new Insets(space, space, space, space); | ||||
| 		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.setFont(new Font("Arial", Font.PLAIN, 17)); | ||||
| 		userList.setBorder(new EmptyBorder(space, space, space, space)); | ||||
|  | ||||
| 		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(space, space, space, space); | ||||
|  | ||||
| 		changeChatWindowColors(Settings.getInstance().getCurrentTheme()); | ||||
|  | ||||
| 		contentPane.add(userList, gbc_userList); | ||||
| 		contentPane.revalidate(); | ||||
|  | ||||
| 		loadUsersAndChats(); | ||||
| 		startSyncThread(Config.getInstance().getSyncTimeout()); | ||||
|  | ||||
| 		contentPane.revalidate(); | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	/** | ||||
| 	 * Used to immediately reload the ChatWindow when settings were changed. | ||||
| 	 *  | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public void changeChatWindowColors(String key) { | ||||
| 		Theme theme = Settings.getInstance().getThemes().get(key); | ||||
|  | ||||
| 		// contentPane | ||||
| 		contentPane.setBackground(theme.getBackgroundColor()); | ||||
| 		contentPane.setForeground(theme.getUserNameColor()); | ||||
| 		// messageList | ||||
| 		messageList.setSelectionForeground(theme.getUserNameColor()); | ||||
| 		messageList.setSelectionBackground(theme.getSelectionColor()); | ||||
| 		messageList.setForeground(theme.getMessageColorChat()); | ||||
| 		messageList.setBackground(theme.getCellColor()); | ||||
| 		// scrollPane | ||||
| 		scrollPane.setForeground(theme.getBackgroundColor()); | ||||
| 		scrollPane.setBackground(theme.getCellColor()); | ||||
| 		// messageEnterTextArea | ||||
| 		messageEnterTextArea.setCaretColor(theme.getTypingMessageColor()); | ||||
| 		messageEnterTextArea.setForeground(theme.getTypingMessageColor()); | ||||
| 		messageEnterTextArea.setBackground(theme.getCellColor()); | ||||
| 		// postButton | ||||
| 		postButton.setForeground(theme.getInteractableForegroundColor()); | ||||
| 		postButton.setBackground(theme.getInteractableBackgroundColor()); | ||||
| 		// settingsButton | ||||
| 		settingsButton.setForeground(theme.getInteractableForegroundColor()); | ||||
| 		settingsButton.setBackground(theme.getInteractableBackgroundColor()); | ||||
| 		// textPane | ||||
| 		textPane.setBackground(theme.getBackgroundColor()); | ||||
| 		textPane.setForeground(theme.getUserNameColor()); | ||||
| 		// userList | ||||
| 		userList.setSelectionForeground(theme.getUserNameColor()); | ||||
| 		userList.setSelectionBackground(theme.getSelectionColor()); | ||||
| 		userList.setForeground(theme.getUserNameColor()); | ||||
| 		userList.setBackground(theme.getCellColor()); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	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().getID()); | ||||
| 			localDB.addWaitingMessageToLocalDB(message, currentChat); | ||||
| 			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); } } | ||||
| } | ||||
| 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.Toolkit; | ||||
| import java.awt.event.KeyAdapter; | ||||
| import java.awt.event.KeyEvent; | ||||
| import java.awt.event.WindowAdapter; | ||||
| import java.awt.event.WindowEvent; | ||||
| import java.io.IOException; | ||||
| import java.util.logging.Level; | ||||
| import java.util.logging.Logger; | ||||
|  | ||||
| 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.client.Settings; | ||||
| 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; | ||||
|  | ||||
| 	// user specific objects | ||||
| 	private Client	client; | ||||
| 	private LocalDB	localDB; | ||||
| 	// GUI components | ||||
| 	private JPanel			contentPane				= new JPanel(); | ||||
| 	private JTextArea		messageEnterTextArea	= new JTextArea(); | ||||
| 	private JList<User>		userList				= new JList<>(); | ||||
| 	private Chat			currentChat; | ||||
| 	private JList<Message>	messageList				= new JList<>(); | ||||
| 	private JScrollPane		scrollPane				= new JScrollPane(); | ||||
| 	private JTextPane		textPane				= new JTextPane(); | ||||
| 	// private JCheckBox jCbChangeMode; | ||||
| 	private JButton	postButton		= new JButton("Post"); | ||||
| 	private JButton	settingsButton	= new JButton("Settings"); | ||||
|  | ||||
| 	private static int space = 4; | ||||
|  | ||||
| 	private static final Logger logger = Logger.getLogger(ChatWindow.class.getSimpleName()); | ||||
|  | ||||
| 	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); | ||||
| 		setIconImage(Toolkit.getDefaultToolkit().createImage(getClass().getClassLoader().getResource("envoy_logo.png"))); | ||||
|  | ||||
| 		// Save chats when window closes | ||||
| 		addWindowListener(new WindowAdapter() { | ||||
|  | ||||
| 			@Override | ||||
| 			public void windowClosing(WindowEvent evt) { | ||||
| 				try { | ||||
| 					localDB.saveToLocalDB(); | ||||
| 					Settings.getInstance().save(); | ||||
| 				} catch (IOException e1) { | ||||
| 					e1.printStackTrace(); | ||||
| 					logger.log(Level.WARNING, "Unable to save the messages", e1); | ||||
| 				} | ||||
| 			} | ||||
| 		}); | ||||
|  | ||||
| 		contentPane.setBorder(new EmptyBorder(space, space, space, space)); | ||||
| 		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); | ||||
|  | ||||
| 		messageList.setCellRenderer(new MessageListRenderer()); | ||||
| 		messageList.setFocusTraversalKeysEnabled(false); | ||||
| 		messageList.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); | ||||
|  | ||||
| 		DefaultListModel<Message> messageListModel = new DefaultListModel<>(); | ||||
| 		messageList.setModel(messageListModel); | ||||
| 		messageList.setFont(new Font("Arial", Font.PLAIN, 17)); | ||||
| 		messageList.setFixedCellHeight(60); | ||||
| 		messageList.setBorder(new EmptyBorder(space, space, space, space)); | ||||
|  | ||||
| 		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(space, space, space, space); | ||||
| 		contentPane.add(scrollPane, gbc_scrollPane); | ||||
|  | ||||
| 		// Message enter field | ||||
| 		messageEnterTextArea.addKeyListener(new KeyAdapter() { | ||||
|  | ||||
| 			@Override | ||||
| 			public void keyReleased(KeyEvent e) { | ||||
| 				if (e.getKeyCode() == KeyEvent.VK_ENTER | ||||
| 						&& ((Settings.getInstance().isEnterToSend() && e.getModifiersEx() == 0) | ||||
| 								|| (e.getModifiersEx() == KeyEvent.CTRL_DOWN_MASK))) { | ||||
| 					postMessage(messageList); | ||||
| 			} | ||||
| 		}); | ||||
| 		// Checks for changed Message | ||||
| 		messageEnterTextArea.setWrapStyleWord(true); | ||||
| 		messageEnterTextArea.setLineWrap(true); | ||||
| 		messageEnterTextArea.setBorder(null); | ||||
| 		messageEnterTextArea.setFont(new Font("Arial", Font.PLAIN, 17)); | ||||
| 		messageEnterTextArea.setBorder(new EmptyBorder(space, space, space, space)); | ||||
|  | ||||
| 		GridBagConstraints gbc_messageEnterTextfield = new GridBagConstraints(); | ||||
| 		gbc_messageEnterTextfield.fill	= GridBagConstraints.BOTH; | ||||
| 		gbc_messageEnterTextfield.gridx	= 1; | ||||
| 		gbc_messageEnterTextfield.gridy	= 2; | ||||
|  | ||||
| 		gbc_messageEnterTextfield.insets = new Insets(space, space, space, space); | ||||
|  | ||||
| 		contentPane.add(messageEnterTextArea, gbc_messageEnterTextfield); | ||||
|  | ||||
| 		// Post Button | ||||
| 		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(space, space, space, space); | ||||
|  | ||||
| 		postButton.addActionListener((evt) -> { postMessage(messageList); }); | ||||
| 		contentPane.add(postButton, gbc_moveSelectionPostButton); | ||||
|  | ||||
| 		// Settings Button | ||||
| 		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(space, space, space, space); | ||||
|  | ||||
| 		settingsButton.addActionListener((evt) -> { | ||||
| 			try { | ||||
| 				SettingsScreen.open(); | ||||
| 				changeChatWindowColors(Settings.getInstance().getCurrentTheme());			 | ||||
|                 } catch (Exception e) { | ||||
| 				SettingsScreen.open(); | ||||
| 				logger.log(Level.WARNING, "An error occured while opening the settings screen", e); | ||||
| 				e.printStackTrace(); | ||||
| 			} | ||||
| 		}); | ||||
| 		contentPane.add(settingsButton, gbc_moveSelectionSettingsButton); | ||||
|  | ||||
| 		// Partner name display | ||||
| 		textPane.setFont(new Font("Arial", Font.PLAIN, 20)); | ||||
| 		textPane.setEditable(false); | ||||
|  | ||||
| 		GridBagConstraints gbc_partnerName = new GridBagConstraints(); | ||||
| 		gbc_partnerName.fill	= GridBagConstraints.HORIZONTAL; | ||||
| 		gbc_partnerName.gridx	= 1; | ||||
| 		gbc_partnerName.gridy	= 0; | ||||
|  | ||||
| 		gbc_partnerName.insets = new Insets(space, space, space, space); | ||||
| 		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.setFont(new Font("Arial", Font.PLAIN, 17)); | ||||
| 		userList.setBorder(new EmptyBorder(space, space, space, space)); | ||||
|  | ||||
| 		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(space, space, space, space); | ||||
|  | ||||
| 		changeChatWindowColors(Settings.getInstance().getCurrentTheme()); | ||||
|          | ||||
| 		contentPane.add(userList, gbc_userList); | ||||
| 		contentPane.revalidate(); | ||||
|  | ||||
| 		loadUsersAndChats(); | ||||
| 		startSyncThread(Config.getInstance().getSyncTimeout()); | ||||
|  | ||||
| 		contentPane.revalidate(); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Used to immediately reload the ChatWindow when settings were changed. | ||||
| 	 *  | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 */ | ||||
| 	public void changeChatWindowColors(String key) { | ||||
| 		Theme theme = Settings.getInstance().getThemes().get(key); | ||||
|  | ||||
| 		// contentPane | ||||
| 		contentPane.setBackground(theme.getBackgroundColor()); | ||||
| 		contentPane.setForeground(theme.getUserNameColor()); | ||||
| 		// messageList | ||||
| 		messageList.setSelectionForeground(theme.getUserNameColor()); | ||||
| 		messageList.setSelectionBackground(theme.getSelectionColor()); | ||||
| 		messageList.setForeground(theme.getMessageColorChat()); | ||||
| 		messageList.setBackground(theme.getCellColor()); | ||||
| 		// scrollPane | ||||
| 		scrollPane.setForeground(theme.getBackgroundColor()); | ||||
| 		scrollPane.setBackground(theme.getCellColor()); | ||||
| 		// messageEnterTextArea | ||||
| 		messageEnterTextArea.setCaretColor(theme.getTypingMessageColor()); | ||||
| 		messageEnterTextArea.setForeground(theme.getTypingMessageColor()); | ||||
| 		messageEnterTextArea.setBackground(theme.getCellColor()); | ||||
| 		// postButton | ||||
| 		postButton.setForeground(theme.getInteractableForegroundColor()); | ||||
| 		postButton.setBackground(theme.getInteractableBackgroundColor()); | ||||
| 		// settingsButton | ||||
| 		settingsButton.setForeground(theme.getInteractableForegroundColor()); | ||||
| 		settingsButton.setBackground(theme.getInteractableBackgroundColor()); | ||||
| 		// textPane | ||||
| 		textPane.setBackground(theme.getBackgroundColor()); | ||||
| 		textPane.setForeground(theme.getUserNameColor()); | ||||
| 		// userList | ||||
| 		userList.setSelectionForeground(theme.getUserNameColor()); | ||||
| 		userList.setSelectionBackground(theme.getSelectionColor()); | ||||
| 		userList.setForeground(theme.getUserNameColor()); | ||||
| 		userList.setBackground(theme.getCellColor()); | ||||
|  | ||||
| 	} | ||||
|      | ||||
| 	private void postMessage(JList<Message> messageList) { | ||||
| 		if (!client.hasRecipient()) { | ||||
| 			JOptionPane.showMessageDialog(this, "Please select a recipient!", "Cannot send message", JOptionPane.INFORMATION_MESSAGE); | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		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); } } | ||||
| } | ||||
|   | ||||
| @@ -1,78 +1,76 @@ | ||||
| package envoy.client.ui; | ||||
|  | ||||
| import java.awt.Color; | ||||
| import java.awt.Component; | ||||
| import java.text.SimpleDateFormat; | ||||
|  | ||||
| import javax.swing.JLabel; | ||||
| import javax.swing.JList; | ||||
| import javax.swing.ListCellRenderer; | ||||
|  | ||||
| import envoy.client.Settings; | ||||
| 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()); | ||||
|  | ||||
| 		// Getting the MessageColor in the Chat of the current theme | ||||
| 		String textColor = null; | ||||
|  | ||||
| 		textColor = toHex( | ||||
| 				Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getMessageColorChat()); | ||||
|  | ||||
| 		// Getting the DateColor in the Chat of the current theme | ||||
| 		String dateColor = null; | ||||
| 		dateColor = toHex( | ||||
| 				Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getDateColorChat()); | ||||
|  | ||||
| 		setText(String.format( | ||||
| 				"<html><p style=\"color:%s\"><b><small>%s</b></small><br><p style=\"color:%s\">%s :%s</html>", | ||||
| 				dateColor, | ||||
| 				date, | ||||
| 				textColor, | ||||
| 				text, | ||||
| 				state)); | ||||
|  | ||||
| 		return this; | ||||
| 	} | ||||
|  | ||||
| 	public String toHex(Color c) { | ||||
| 		int		r	= c.getRed(); | ||||
| 		int		g	= c.getGreen(); | ||||
| 		int		b	= c.getBlue(); | ||||
| 		String	hex	= String.format("#%02x%02x%02x", r, g, b); | ||||
| 		return hex; | ||||
| 	} | ||||
| package envoy.client.ui; | ||||
|  | ||||
| import java.awt.Color; | ||||
| import java.awt.Component; | ||||
| import java.text.SimpleDateFormat; | ||||
|  | ||||
| import javax.swing.JLabel; | ||||
| import javax.swing.JList; | ||||
| import javax.swing.ListCellRenderer; | ||||
|  | ||||
| import envoy.client.Settings; | ||||
| 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()); | ||||
|  | ||||
| 		// Getting the MessageColor in the Chat of the current theme | ||||
| 		String textColor = null; | ||||
|  | ||||
| 		textColor = toHex( | ||||
| 				Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getMessageColorChat()); | ||||
|  | ||||
| 		// Getting the DateColor in the Chat of the current theme | ||||
| 		String dateColor = null; | ||||
| 		dateColor = toHex( | ||||
| 				Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getDateColorChat()); | ||||
|  | ||||
| 		setText(String.format( | ||||
| 				"<html><p style=\"color:%s\"><b><small>%s</b></small><br><p style=\"color:%s\">%s :%s</html>", | ||||
| 				dateColor, | ||||
| 				date, | ||||
| 				textColor, | ||||
| 				text, | ||||
| 				state)); | ||||
| 		return this; | ||||
| 	} | ||||
|  | ||||
| 	public String toHex(Color c) { | ||||
| 		int		r	= c.getRed(); | ||||
| 		int		g	= c.getGreen(); | ||||
| 		int		b	= c.getBlue(); | ||||
| 		String	hex	= String.format("#%02x%02x%02x", r, g, b); | ||||
| 		return hex; | ||||
| 	} | ||||
| } | ||||
| @@ -509,4 +509,5 @@ public class SettingsScreen extends JDialog { | ||||
|  | ||||
| 		colorsPanel.add(panel); | ||||
| 	} | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -3,6 +3,8 @@ package envoy.client.ui; | ||||
| import java.awt.EventQueue; | ||||
| import java.io.IOException; | ||||
| import java.util.Properties; | ||||
| import java.util.logging.Level; | ||||
| import java.util.logging.Logger; | ||||
|  | ||||
| import javax.swing.JOptionPane; | ||||
|  | ||||
| @@ -26,29 +28,35 @@ import envoy.exception.EnvoyException; | ||||
|  */ | ||||
| public class Startup { | ||||
|  | ||||
| 	private static final Logger logger = Logger.getLogger(Startup.class.getSimpleName()); | ||||
|  | ||||
| 	public static void main(String[] args) { | ||||
| 		logger.setLevel(Level.ALL); | ||||
|  | ||||
| 		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(); | ||||
| 			} | ||||
|  | ||||
| 		// Load the configuration from client.properties first | ||||
| 		ClassLoader loader = Thread.currentThread().getContextClassLoader(); | ||||
| 		try { | ||||
| 			Properties configProperties = new Properties(); | ||||
| 			configProperties.load(loader.getResourceAsStream("client.properties")); | ||||
| 			config.load(configProperties); | ||||
| 		} catch (IOException e) { | ||||
| 			e.printStackTrace(); | ||||
| 		} | ||||
|  | ||||
| 		// Override configuration values with command line arguments | ||||
| 		if (args.length > 0) config.load(args); | ||||
|  | ||||
| 		if (!config.isInitialized()) { | ||||
| 			System.err.println("Server or port are not defined. Exiting..."); | ||||
| 			logger.severe("Server or port are not defined. Exiting..."); | ||||
| 			JOptionPane.showMessageDialog(null, "Error loading configuration values.", "Configuration error", JOptionPane.ERROR_MESSAGE); | ||||
| 			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..."); | ||||
| 			logger.severe("User name is not set or empty. Exiting..."); | ||||
| 			System.exit(1); | ||||
| 		} | ||||
| 		Client	client	= new Client(config, userName); | ||||
| @@ -66,8 +74,9 @@ public class Startup { | ||||
| 		 | ||||
| 		EventQueue.invokeLater(() -> { | ||||
| 			try { | ||||
| 				ChatWindow frame = new ChatWindow(client, localDB); | ||||
| 				frame.setVisible(true); | ||||
| 				ChatWindow chatWindow = new ChatWindow(client, localDB); | ||||
| 				new StatusTrayIcon(chatWindow).show(); | ||||
| 				chatWindow.setVisible(true); | ||||
| 			} catch (Exception e) { | ||||
| 				e.printStackTrace(); | ||||
| 			} | ||||
|   | ||||
							
								
								
									
										133
									
								
								src/main/java/envoy/client/ui/StatusTrayIcon.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								src/main/java/envoy/client/ui/StatusTrayIcon.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,133 @@ | ||||
| package envoy.client.ui; | ||||
|  | ||||
| import java.awt.AWTException; | ||||
| import java.awt.Image; | ||||
| import java.awt.MenuItem; | ||||
| import java.awt.PopupMenu; | ||||
| import java.awt.SystemTray; | ||||
| import java.awt.Toolkit; | ||||
| import java.awt.TrayIcon; | ||||
| import java.awt.TrayIcon.MessageType; | ||||
| import java.awt.Window; | ||||
| import java.awt.event.WindowAdapter; | ||||
| import java.awt.event.WindowEvent; | ||||
| import java.util.HashSet; | ||||
| import java.util.Set; | ||||
|  | ||||
| import envoy.client.event.Event; | ||||
| import envoy.client.event.EventBus; | ||||
| import envoy.client.event.EventHandler; | ||||
| import envoy.client.event.MessageCreationEvent; | ||||
| import envoy.exception.EnvoyException; | ||||
| import envoy.schema.Message; | ||||
|  | ||||
| /** | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>StatusTrayIcon.java</strong><br> | ||||
|  * Created: <strong>3 Dec 2019</strong><br> | ||||
|  * | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @since Envoy v0.2-alpha | ||||
|  */ | ||||
| public class StatusTrayIcon implements EventHandler { | ||||
|  | ||||
| 	/** | ||||
| 	 * The {@link TrayIcon} provided by the System Tray API for controlling the | ||||
| 	 * system tray. This includes displaying the icon, but also creating | ||||
| 	 * notifications when new messages are received. | ||||
| 	 */ | ||||
| 	private TrayIcon trayIcon; | ||||
|  | ||||
| 	/** | ||||
| 	 * A received {@link Message} is only displayed as a system tray notification if | ||||
| 	 * this variable is set to {@code true}. | ||||
| 	 */ | ||||
| 	private boolean displayMessages = false; | ||||
|  | ||||
| 	/** | ||||
| 	 * Creates a {@link StatusTrayIcon} with the Envoy logo, a tool tip and a pop-up | ||||
| 	 * menu. | ||||
| 	 * | ||||
| 	 * @param focusTarget the {@link Window} which focus determines if message | ||||
| 	 *                    notifications are displayed | ||||
| 	 * @throws EnvoyException if the currently used OS does not support the System | ||||
| 	 *                        Tray API | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 */ | ||||
| 	public StatusTrayIcon(Window focusTarget) throws EnvoyException { | ||||
| 		if (!SystemTray.isSupported()) throw new EnvoyException("The Envoy tray icon is not supported."); | ||||
|  | ||||
| 		ClassLoader	loader	= Thread.currentThread().getContextClassLoader(); | ||||
| 		Image		img		= Toolkit.getDefaultToolkit().createImage(loader.getResource("envoy_logo.png")); | ||||
| 		trayIcon = new TrayIcon(img, "Envoy Client"); | ||||
| 		trayIcon.setImageAutoSize(true); | ||||
| 		trayIcon.setToolTip("You are notified if you have unread messages."); | ||||
|  | ||||
| 		PopupMenu popup = new PopupMenu(); | ||||
|  | ||||
| 		MenuItem exitMenuItem = new MenuItem("Exit"); | ||||
| 		exitMenuItem.addActionListener((evt) -> System.exit(0)); | ||||
| 		popup.add(exitMenuItem); | ||||
|  | ||||
| 		trayIcon.setPopupMenu(popup); | ||||
|  | ||||
| 		// Only display messages if the chat window is not focused | ||||
| 		focusTarget.addWindowFocusListener(new WindowAdapter() { | ||||
|  | ||||
| 			@Override | ||||
| 			public void windowGainedFocus(WindowEvent e) { | ||||
| 				displayMessages = false; | ||||
| 			} | ||||
|  | ||||
| 			@Override | ||||
| 			public void windowLostFocus(WindowEvent e) { | ||||
| 				displayMessages = true; | ||||
| 			} | ||||
| 		}); | ||||
|  | ||||
| 		// Start processing message events | ||||
| 		EventBus.getInstance().register(this); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Makes this {@link StatusTrayIcon} appear in the system tray. | ||||
| 	 * | ||||
| 	 * @throws EnvoyException if the status icon could not be attaches to the system | ||||
| 	 *                        tray for system-internal reasons | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 */ | ||||
| 	public void show() throws EnvoyException { | ||||
| 		try { | ||||
| 			SystemTray.getSystemTray().add(trayIcon); | ||||
| 		} catch (AWTException e) { | ||||
| 			throw new EnvoyException("Could not attach Envoy tray icon to system tray.", e); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Notifies the user of a message by displaying a pop-up every time a new | ||||
| 	 * message is received. | ||||
| 	 * | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 */ | ||||
| 	@Override | ||||
| 	public void handle(Event<?> event) { | ||||
| 		System.out.println("Message received. Displaying message: " + displayMessages); | ||||
| 		if (displayMessages) | ||||
| 			trayIcon.displayMessage("New message received", ((MessageCreationEvent) event).get().getContent().get(0).getText(), MessageType.INFO); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * The {@link StatusTrayIcon} only reacts to {@link MessageCreationEvent} | ||||
| 	 * instances which signify newly received messages. | ||||
| 	 * | ||||
| 	 * @return A set with the single element {@code MessageCreationEvent.class} | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 */ | ||||
| 	@Override | ||||
| 	public Set<Class<? extends Event<?>>> supports() { | ||||
| 		Set<Class<? extends Event<?>>> supportedEvents = new HashSet<>(); | ||||
| 		supportedEvents.add(MessageCreationEvent.class); | ||||
| 		return supportedEvents; | ||||
| 	} | ||||
| } | ||||
| @@ -1,84 +1,77 @@ | ||||
| package envoy.client.ui; | ||||
|  | ||||
| import java.awt.Color; | ||||
| import java.awt.Component; | ||||
|  | ||||
| import javax.swing.JLabel; | ||||
| import javax.swing.JList; | ||||
| import javax.swing.ListCellRenderer; | ||||
|  | ||||
| import envoy.client.Settings; | ||||
| 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; | ||||
|  | ||||
| 	@SuppressWarnings("incomplete-switch") | ||||
| 	@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(); | ||||
|  | ||||
| 		// Getting the UserNameColor of the current theme | ||||
| 		String				textColor	= null; | ||||
|  | ||||
| 		textColor = toHex( | ||||
| 				Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getUserNameColor()); | ||||
| 		 | ||||
| 		switch (status) { | ||||
| 			case ONLINE: | ||||
| 				setText(String.format( | ||||
| 						"<html><p style=\"color:#03fc20\"><b><small>%s</b></small><br><p style=\"color:%s\">%s</html>", | ||||
| 						status, | ||||
| 						textColor, | ||||
| 						name)); | ||||
| 				break; | ||||
|  | ||||
| 			case OFFLINE: | ||||
| 				setText(String.format( | ||||
| 						"<html><p style=\"color:#fc0303\"><b><small>%s</b></small><br><p style=\"color:%s\">%s</html>", | ||||
| 						status, | ||||
| 						textColor, | ||||
| 						name)); | ||||
| 				break; | ||||
| 		} | ||||
|  | ||||
| 		 | ||||
|  | ||||
| 		return this; | ||||
| 	} | ||||
|  | ||||
| 	public String toHex(Color c) { | ||||
| 		int		r	= c.getRed(); | ||||
| 		int		g	= c.getGreen(); | ||||
| 		int		b	= c.getBlue(); | ||||
| 		String	hex	= String.format("#%02x%02x%02x", r, g, b); | ||||
| 		return hex; | ||||
| 	} | ||||
| package envoy.client.ui; | ||||
|  | ||||
| import java.awt.Color; | ||||
| import java.awt.Component; | ||||
|  | ||||
| import javax.swing.JLabel; | ||||
| import javax.swing.JList; | ||||
| import javax.swing.ListCellRenderer; | ||||
|  | ||||
| import envoy.client.Settings; | ||||
| 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; | ||||
|  | ||||
| 	@SuppressWarnings("incomplete-switch") | ||||
| 	@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(); | ||||
|  | ||||
| 		// Getting the UserNameColor of the current theme | ||||
| 		String				textColor	= null; | ||||
|  | ||||
| 		textColor = toHex( | ||||
| 				Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getUserNameColor()); | ||||
| 		switch (status) { | ||||
| 			case ONLINE: | ||||
| 				setText(String.format( | ||||
| 						"<html><p style=\"color:#03fc20\"><b><small>%s</b></small><br><p style=\"color:%s\">%s</html>", | ||||
| 						status, | ||||
| 						textColor, | ||||
| 						name)); | ||||
| 				break; | ||||
| 			case OFFLINE: | ||||
| 				setText(String.format( | ||||
| 						"<html><p style=\"color:#fc0303\"><b><small>%s</b></small><br><p style=\"color:%s\">%s</html>", | ||||
| 						status, | ||||
| 						textColor, | ||||
| 						name)); | ||||
| 				break; | ||||
| 		} | ||||
| 		return this; | ||||
| 	} | ||||
|  | ||||
| 	public String toHex(Color c) { | ||||
| 		int		r	= c.getRed(); | ||||
| 		int		g	= c.getGreen(); | ||||
| 		int		b	= c.getBlue(); | ||||
| 		String	hex	= String.format("#%02x%02x%02x", r, g, b); | ||||
| 		return hex; | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 delvh
					delvh