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;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								src/main/resources/envoy_logo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/main/resources/envoy_logo.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 30 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/main/resources/envoy_logo_alpha.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/main/resources/envoy_logo_alpha.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 28 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/main/resources/envoy_logo_old.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/main/resources/envoy_logo_old.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 8.0 KiB  | 
		Reference in New Issue
	
	Block a user