Merge branch 'develop' into f/themes

This commit is contained in:
delvh 2019-12-07 14:50:20 +01:00 committed by GitHub
commit 6679685b3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 1388 additions and 878 deletions

38
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,38 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: CyB3RC0nN0R, delvh, DieGurke, derharry333
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

View File

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: CyB3RC0nN0R, delvh, DieGurke
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -34,7 +34,5 @@
<natures> <natures>
<nature>org.eclipse.jdt.core.javanature</nature> <nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature> <nature>org.eclipse.m2e.core.maven2Nature</nature>
<nature>org.jboss.tools.jst.web.kb.kbnature</nature>
<nature>org.jboss.tools.cdi.core.cdinature</nature>
</natures> </natures>
</projectDescription> </projectDescription>

View File

@ -1,8 +1,107 @@
eclipse.preferences.version=1 eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled
org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore
org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull
org.eclipse.jdt.core.compiler.annotation.nonnull.secondary=
org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary=
org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
org.eclipse.jdt.core.compiler.annotation.nullable.secondary=
org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.problem.APILeak=warning
org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
org.eclipse.jdt.core.compiler.problem.deadCode=warning
org.eclipse.jdt.core.compiler.problem.deprecation=warning
org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore
org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled
org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore
org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled
org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore
org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning
org.eclipse.jdt.core.compiler.problem.nonnullTypeVariableFromLegacyInvocation=warning
org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error
org.eclipse.jdt.core.compiler.problem.nullReference=warning
org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning
org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning
org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore
org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore
org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore
org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore
org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled
org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
org.eclipse.jdt.core.compiler.problem.terminalDeprecation=warning
org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentType=warning
org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentTypeStrict=disabled
org.eclipse.jdt.core.compiler.problem.unlikelyEqualsArgumentType=info
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
org.eclipse.jdt.core.compiler.problem.unstableAutoModuleName=warning
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedImport=warning
org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore
org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
org.eclipse.jdt.core.compiler.release=disabled org.eclipse.jdt.core.compiler.release=disabled
org.eclipse.jdt.core.compiler.source=1.8 org.eclipse.jdt.core.compiler.source=1.8

76
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at kske@outlook.de. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

View File

@ -1,5 +1,7 @@
package envoy.client; package envoy.client;
import java.util.logging.Logger;
import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity; import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget; import javax.ws.rs.client.WebTarget;
@ -28,18 +30,20 @@ public class Client {
private Config config; private Config config;
private User sender, recipient; private User sender, recipient;
private static final Logger logger = Logger.getLogger(Client.class.getSimpleName());
public Client(Config config, String username) { public Client(Config config, String username) {
this.config = config; this.config = config;
sender = getUser(username); 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) { private <T, R> R post(String uri, T body, Class<R> responseBodyClass) {
javax.ws.rs.client.Client client = ClientBuilder.newClient(); javax.ws.rs.client.Client client = ClientBuilder.newClient();
WebTarget target = client.target(uri); WebTarget target = client.target(uri);
Response response = target.request().post(Entity.entity(body, "application/xml"));
Response response = target.request().post(Entity.entity(body, "application/xml")); R responseBody = response.readEntity(responseBodyClass);
R responseBody = response.readEntity(responseBodyClass);
response.close(); response.close();
client.close(); client.close();
@ -92,7 +96,7 @@ public class Client {
if (returnSenderSync.getUsers().size() == 1) { if (returnSenderSync.getUsers().size() == 1) {
returnSender = returnSenderSync.getUsers().get(0); returnSender = returnSenderSync.getUsers().get(0);
} else { } else {
System.out.println("ERROR exiting..."); logger.warning("ERROR exiting...");
} }
return returnSender; return returnSender;
@ -129,7 +133,7 @@ public class Client {
* Updating UserStatus of all users in LocalDB. (Server sends all users with * Updating UserStatus of all users in LocalDB. (Server sends all users with
* their updated UserStatus to the client.) <br> * 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) * @param sync the sync object (yet to be converted from java class to sync.xml)
* @return a returnSync.xml file * @return a returnSync.xml file
* @since Envoy v0.1-alpha * @since Envoy v0.1-alpha
@ -166,16 +170,16 @@ public class Client {
public User getRecipient() { return recipient; } public User getRecipient() { return recipient; }
/** /**
* Sets the recipient. * Sets the recipient.
*
* @param recipient - the recipient to set * @param recipient - the recipient to set
* @since Envoy v0.1-alpha * @since Envoy v0.1-alpha
*/ */
public void setRecipient(User recipient) { this.recipient = recipient; } public void setRecipient(User recipient) { this.recipient = recipient; }
/** /**
* @return true, if a recipient is selected * @return true, if a recipient is selected
* @since Envoy v0.1-alpha * @since Envoy v0.1-alpha
*/ */
public boolean hasRecipient() { return recipient != null; } public boolean hasRecipient() { return recipient != null; }
} }

View File

@ -32,7 +32,7 @@ public class Config {
* *
* @param properties a {@link Properties} object containing information about * @param properties a {@link Properties} object containing information about
* the server and port, as well as the path to the local * the server and port, as well as the path to the local
* database * database and the synchronization timeout
* @since Envoy v0.1-alpha * @since Envoy v0.1-alpha
*/ */
public void load(Properties properties) { public void load(Properties properties) {
@ -65,8 +65,6 @@ public class Config {
localDB = new File(args[++i]); localDB = new File(args[++i]);
break; 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. * Changes the default local database.
* Exclusively intended for development purposes. * Exclusively intended for development purposes.
* *
* @param localDB the file containing the local database * @param localDB the file containing the local database
* @since Envoy v0.1-alpha * @since Envoy v0.1-alpha
**/ **/

View File

@ -9,10 +9,13 @@ import java.io.ObjectOutputStream;
import java.time.Instant; import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.logging.Logger;
import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.DatatypeFactory;
import envoy.client.event.EventBus;
import envoy.client.event.MessageCreationEvent;
import envoy.exception.EnvoyException; import envoy.exception.EnvoyException;
import envoy.schema.Message; import envoy.schema.Message;
import envoy.schema.Message.Metadata.MessageState; import envoy.schema.Message.Metadata.MessageState;
@ -41,15 +44,16 @@ public class LocalDB {
private Sync sync = objectFactory.createSync(); private Sync sync = objectFactory.createSync();
private Sync readMessages = objectFactory.createSync(); private Sync readMessages = objectFactory.createSync();
private static final Logger logger = Logger.getLogger(LocalDB.class.getSimpleName());
/** /**
* Constructs an empty local database. * Constructs an empty local database.
* *
* @param sender the user that is logged in with this client * @param sender the user that is logged in with this client
* @since Envoy v0.1-alpha * @since Envoy v0.1-alpha
*/ */
public LocalDB(User sender) { public LocalDB(User sender) {
this.sender = sender; this.sender = sender;
try { try {
datatypeFactory = DatatypeFactory.newInstance(); datatypeFactory = DatatypeFactory.newInstance();
} catch (DatatypeConfigurationException e) { } catch (DatatypeConfigurationException e) {
@ -66,34 +70,25 @@ public class LocalDB {
* @since Envoy v0.1-alpha * @since Envoy v0.1-alpha
*/ */
public void initializeDBFile(File localDBDir) throws EnvoyException { public void initializeDBFile(File localDBDir) throws EnvoyException {
if (localDBDir.exists() && !localDBDir.isDirectory()) throw new EnvoyException( if (localDBDir.exists() && !localDBDir.isDirectory())
String.format("LocalDBDir '%s' is not a directory!", localDBDir.getAbsolutePath())); throw new EnvoyException(String.format("LocalDBDir '%s' is not a directory!", localDBDir.getAbsolutePath()));
localDB = new File(localDBDir, sender.getID() + ".db"); localDB = new File(localDBDir, sender.getID() + ".db");
if (localDB.exists()) loadFromLocalDB(); if (localDB.exists()) loadFromLocalDB();
} }
/** /**
* Saves the database into a file for future use.<br> * Saves the database into a file for future use.
* 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 * @throws IOException if something went wrong during saving
* @since Envoy v0.1-alpha * @since Envoy v0.1-alpha
*/ */
public void saveToLocalDB() throws IOException{ public void saveToLocalDB() throws IOException {
try { localDB.getParentFile().mkdirs();
localDB.getParentFile().mkdirs(); localDB.createNewFile();
localDB.createNewFile();
} catch (IOException e) {
e.printStackTrace();
System.err.println("unable to save the messages");
}
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(localDB))) { try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(localDB))) {
out.writeObject(chats); out.writeObject(chats);
} catch (IOException ex) { } catch (IOException ex) {
ex.printStackTrace(); throw ex;
System.err.println("unable to save the messages");
} }
} }
@ -117,8 +112,9 @@ public class LocalDB {
* Creates a {@link Message} object serializable to XML. * Creates a {@link Message} object serializable to XML.
* *
* @param textContent The content (text) of the message * @param textContent The content (text) of the message
* @param recipientID self explanatory * @param recipientID The recipient of the message
* @return prepared {@link Message} object * @return prepared {@link Message} object
* @since Envoy v0.1-alpha
*/ */
public Message createMessage(String textContent, long recipientID) { public Message createMessage(String textContent, long recipientID) {
Message.Metadata metaData = objectFactory.createMessageMetadata(); Message.Metadata metaData = objectFactory.createMessageMetadata();
@ -138,102 +134,97 @@ public class LocalDB {
return message; return message;
} }
/**
* Creates a {@link Sync} object filled with the changes that occurred to the
* local database since the last synchronization.
*
* @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 Sync fillSync(long userId) { public Sync fillSync(long userId) {
addWaitingMessagesToSync(); addWaitingMessagesToSync();
sync.getMessages().addAll(readMessages.getMessages()); sync.getMessages().addAll(readMessages.getMessages());
readMessages.getMessages().clear(); readMessages.getMessages().clear();
System.out.println(sync.getMessages().size()); logger.finest(String.format("Filled sync with %d messages.", sync.getMessages().size()));
return sync; 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
*/
public void applySync(Sync returnSync) { public void applySync(Sync returnSync) {
for (int i = 0; i < returnSync.getMessages().size(); i++) { 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 // The message has an ID
&& returnSync.getMessages().get(i).getMetadata().getState() == MessageState.RECEIVED) { if (returnSync.getMessages().get(i).getMetadata().getMessageId() != 0) {
// Update Messages in localDB to state RECEIVED
for (int j = 0; j < getChats().size(); j++) { // Messages are processes differently corresponding to their state
if (getChats().get(j).getRecipient().getID() == returnSync.getMessages().get(i).getMetadata().getRecipient()) { switch (returnSync.getMessages().get(i).getMetadata().getState()) {
for (int k = 0; k < getChats().get(j).getModel().getSize(); k++) { case SENT:
if (getChats().get(j).getModel().get(k).getMetadata().getMessageId() == returnSync.getMessages() // Update previously waiting and now sent messages that were assigned an ID by
.get(i) // the server
.getMetadata() sync.getMessages().get(i).getMetadata().setMessageId(returnSync.getMessages().get(i).getMetadata().getMessageId());
.getMessageId()) { sync.getMessages().get(i).getMetadata().setState(returnSync.getMessages().get(i).getMetadata().getState());
// Update Message in LocalDB break;
getChats().get(j).getModel().get(k).getMetadata().setState(returnSync.getMessages().get(j).getMetadata().getState()); 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.");
if (returnSync.getMessages().get(i).getMetadata().getMessageId() != 0 for (Chat chat : getChats())
&& returnSync.getMessages().get(i).getMetadata().getState() == MessageState.READ) { if (chat.getRecipient().getID() == returnSync.getMessages().get(i).getMetadata().getRecipient()) {
// Update local Messages to state READ logger.info("Chat with: " + chat.getRecipient().getID() + "was selected.");
System.out.println("Message with ID: " + returnSync.getMessages().get(i).getMetadata().getMessageId() for (int k = 0; k < chat.getModel().getSize(); k++)
+ "was initialized to be set to READ in localDB."); if (chat.getModel().get(k).getMetadata().getMessageId() == returnSync.getMessages()
for (int j = 0; j < getChats().size(); j++) { .get(i)
if (getChats().get(j).getRecipient().getID() == returnSync.getMessages().get(i).getMetadata().getRecipient()) { .getMetadata()
System.out.println("Chat with: " + getChats().get(j).getRecipient().getID() + "was selected."); .getMessageId()) {
for (int k = 0; k < getChats().get(j).getModel().getSize(); k++) { logger.info("Message with ID: " + chat.getModel().get(k).getMetadata().getMessageId() + "was selected.");
if (getChats().get(j).getModel().get(k).getMetadata().getMessageId() == returnSync.getMessages() chat.getModel().get(k).getMetadata().setState(returnSync.getMessages().get(i).getMetadata().getState());
.get(i) logger.info("Message State is now: " + chat.getModel().get(k).getMetadata().getState());
.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());
} }
} break;
}
} }
} }
} }
// Updating UserStatus of all users in LocalDB // Updating UserStatus of all users in LocalDB
for (int j = 0; j < returnSync.getUsers().size(); j++) { for (User user : returnSync.getUsers())
for (int k = 0; k < getChats().size(); k++) { for (Chat chat : getChats())
if (getChats().get(k).getRecipient().getID() == returnSync.getUsers().get(j).getID()) { if (user.getID() == chat.getRecipient().getID()) {
chat.getRecipient().setStatus(user.getStatus());
getChats().get(k).getRecipient().setStatus(returnSync.getUsers().get(j).getStatus()); logger.info(chat.getRecipient().getStatus().toString());
System.out.println(getChats().get(k).getRecipient().getStatus().toString());
} }
}
}
sync.getMessages().clear(); sync.getMessages().clear();
sync.getUsers().clear(); sync.getUsers().clear();
} }
/**
* 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); }
/** /**
* Adds the unread messages returned from the server in the latest sync to the * Adds the unread messages returned from the server in the latest sync to the
* right chats in the LocalDB. * right chats in the LocalDB.
@ -241,11 +232,11 @@ public class LocalDB {
* @since Envoy v0.1-alpha * @since Envoy v0.1-alpha
*/ */
public void addUnreadMessagesToLocalDB() { public void addUnreadMessagesToLocalDB() {
Sync unreadMessages = unreadMessagesSync; for (Message message : unreadMessagesSync.getMessages())
for (int i = 0; i < unreadMessages.getMessages().size(); i++) for (Chat chat : getChats())
for (int j = 0; j < getChats().size(); j++) if (message.getMetadata().getSender() == chat.getRecipient().getID()) {
if (getChats().get(j).getRecipient().getID() == unreadMessages.getMessages().get(i).getMetadata().getSender()) { chat.appendMessage(message);
getChats().get(j).appendMessage(unreadMessages.getMessages().get(i)); break;
} }
} }
@ -255,7 +246,7 @@ public class LocalDB {
* <br> * <br>
* Adds these messages to the {@code readMessages} {@link Sync} object. * Adds these messages to the {@code readMessages} {@link Sync} object.
* *
* @param currentChat the chat that was just opened * @param currentChat the {@link Chat} that was just opened
* @since Envoy v0.1-alpha * @since Envoy v0.1-alpha
*/ */
public void setMessagesToRead(Chat currentChat) { public void setMessagesToRead(Chat currentChat) {
@ -271,22 +262,13 @@ public class LocalDB {
* Adds all messages with state {@code WAITING} from the {@link LocalDB} to the * Adds all messages with state {@code WAITING} from the {@link LocalDB} to the
* {@link Sync} object. * {@link Sync} object.
* *
* @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
*/
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 * @since Envoy v0.1-alpha
*/ */
private void addWaitingMessagesToSync() { private void addWaitingMessagesToSync() {
for (Chat chat : getChats()) for (Chat chat : getChats())
for (int i = 0; i < chat.getModel().size(); i++) for (int i = 0; i < chat.getModel().size(); i++)
if (chat.getModel().get(i).getMetadata().getState() == MessageState.WAITING) { if (chat.getModel().get(i).getMetadata().getState() == MessageState.WAITING) {
System.out.println("Got Waiting Message"); logger.info("Got Waiting Message");
sync.getMessages().add(chat.getModel().get(i)); sync.getMessages().add(chat.getModel().get(i));
} }
} }
@ -299,8 +281,8 @@ public class LocalDB {
public void clearUnreadMessagesSync() { unreadMessagesSync.getMessages().clear(); } public void clearUnreadMessagesSync() { unreadMessagesSync.getMessages().clear(); }
/** /**
* @return all saves {@link Chat} objects that list the client user as the * @return all saved {@link Chat} objects that list the client user as the
* client * sender
* @since Envoy v0.1-alpha * @since Envoy v0.1-alpha
**/ **/
public List<Chat> getChats() { return chats; } public List<Chat> getChats() { return chats; }

View 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();
}

View 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; }
}

View 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();
}

View 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); }
}

View 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; }
}

View File

@ -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); }
}

View File

@ -1,15 +1,19 @@
package envoy.client.ui; package envoy.client.ui;
import java.awt.Color;
import java.awt.ComponentOrientation; import java.awt.ComponentOrientation;
import java.awt.Font; import java.awt.Font;
import java.awt.GridBagConstraints; import java.awt.GridBagConstraints;
import java.awt.GridBagLayout; import java.awt.GridBagLayout;
import java.awt.Insets; import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter; import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter; import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent; import java.awt.event.WindowEvent;
import java.io.IOException; import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.DefaultListModel; import javax.swing.DefaultListModel;
import javax.swing.JButton; import javax.swing.JButton;
@ -65,6 +69,8 @@ public class ChatWindow extends JFrame {
private static int space = 4; private static int space = 4;
private static final Logger logger = Logger.getLogger(ChatWindow.class.getSimpleName());
public ChatWindow(Client client, LocalDB localDB) { public ChatWindow(Client client, LocalDB localDB) {
this.client = client; this.client = client;
this.localDB = localDB; this.localDB = localDB;
@ -73,18 +79,19 @@ public class ChatWindow extends JFrame {
setBounds(100, 100, 600, 800); setBounds(100, 100, 600, 800);
setTitle("Envoy"); setTitle("Envoy");
setLocationRelativeTo(null); setLocationRelativeTo(null);
setIconImage(Toolkit.getDefaultToolkit().createImage(getClass().getClassLoader().getResource("envoy_logo.png")));
// Save chats when window closes // Save chats when window closes
addWindowListener(new WindowAdapter() { addWindowListener(new WindowAdapter() {
@Override @Override
public void windowClosing(WindowEvent e) { public void windowClosing(WindowEvent evt) {
try { try {
localDB.saveToLocalDB(); localDB.saveToLocalDB();
Settings.getInstance().save(); Settings.getInstance().save();
} catch (IOException e1) { } catch (IOException e1) {
e1.printStackTrace(); e1.printStackTrace();
System.err.println("Could not save localDB"); logger.log(Level.WARNING, "Unable to save the messages", e1);
} }
} }
}); });
@ -125,14 +132,10 @@ public class ChatWindow extends JFrame {
@Override @Override
public void keyReleased(KeyEvent e) { public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER if (e.getKeyCode() == KeyEvent.VK_ENTER
&& ((Settings.getInstance().isEnterToSend() && e.getModifiersEx() == 0) && ((Settings.getInstance().isEnterToSend() && e.getModifiersEx() == 0)
|| (e.getModifiersEx() == KeyEvent.CTRL_DOWN_MASK))) { || (e.getModifiersEx() == KeyEvent.CTRL_DOWN_MASK))) {
postMessage(messageList); postMessage(messageList);
}
} }
}); });
// Checks for changed Message // Checks for changed Message
@ -178,11 +181,10 @@ public class ChatWindow extends JFrame {
settingsButton.addActionListener((evt) -> { settingsButton.addActionListener((evt) -> {
try { try {
SettingsScreen.open(); SettingsScreen.open();
changeChatWindowColors(Settings.getInstance().getCurrentTheme()); changeChatWindowColors(Settings.getInstance().getCurrentTheme());
} catch (Exception e) {
} catch (Exception e) { SettingsScreen.open();
System.err.println("An error occured while opening the settings screen: " + e); logger.log(Level.WARNING, "An error occured while opening the settings screen", e);
e.printStackTrace(); e.printStackTrace();
} }
}); });
@ -209,11 +211,7 @@ public class ChatWindow extends JFrame {
final User user = selectedUserList.getSelectedValue(); final User user = selectedUserList.getSelectedValue();
client.setRecipient(user); client.setRecipient(user);
currentChat = localDB.getChats() currentChat = localDB.getChats().stream().filter(chat -> chat.getRecipient().getID() == user.getID()).findFirst().get();
.stream()
.filter(chat -> chat.getRecipient().getID() == user.getID())
.findFirst()
.get();
// Set all unread messages in the chat to read // Set all unread messages in the chat to read
readCurrentChat(); readCurrentChat();
@ -247,7 +245,6 @@ public class ChatWindow extends JFrame {
contentPane.revalidate(); contentPane.revalidate();
} }
/** /**
* Used to immediately reload the ChatWindow when settings were changed. * Used to immediately reload the ChatWindow when settings were changed.
* *
@ -290,18 +287,15 @@ public class ChatWindow extends JFrame {
private void postMessage(JList<Message> messageList) { private void postMessage(JList<Message> messageList) {
if (!client.hasRecipient()) { if (!client.hasRecipient()) {
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(this, "Please select a recipient!", "Cannot send message", JOptionPane.INFORMATION_MESSAGE);
"Please select a recipient!", return;
"Cannot send message",
JOptionPane.INFORMATION_MESSAGE);
} }
if (!messageEnterTextArea.getText().isEmpty()) try { if (!messageEnterTextArea.getText().isEmpty()) try {
// Create and send message object // Create and send message object
final Message message = localDB.createMessage(messageEnterTextArea.getText(), final Message message = localDB.createMessage(messageEnterTextArea.getText(), currentChat.getRecipient());
currentChat.getRecipient().getID()); currentChat.appendMessage(message);
localDB.addWaitingMessageToLocalDB(message, currentChat);
messageList.setModel(currentChat.getModel()); messageList.setModel(currentChat.getModel());
// Clear text field // Clear text field
@ -350,8 +344,7 @@ public class ChatWindow extends JFrame {
new Thread(() -> { new Thread(() -> {
// Synchronize // Synchronize
localDB.applySync( localDB.applySync(client.sendSync(client.getSender().getID(), localDB.fillSync(client.getSender().getID())));
client.sendSync(client.getSender().getID(), localDB.fillSync(client.getSender().getID())));
// Process unread messages // Process unread messages
localDB.addUnreadMessagesToLocalDB(); localDB.addUnreadMessagesToLocalDB();
@ -361,8 +354,7 @@ public class ChatWindow extends JFrame {
readCurrentChat(); readCurrentChat();
// Update UI // Update UI
SwingUtilities SwingUtilities.invokeLater(() -> { updateUserStates(); contentPane.revalidate(); contentPane.repaint(); });
.invokeLater(() -> { updateUserStates(); contentPane.revalidate(); contentPane.repaint(); });
}).start(); }).start();
}).start(); }).start();
} }

View File

@ -28,8 +28,7 @@ public class MessageListRenderer extends JLabel implements ListCellRenderer<Mess
private static final long serialVersionUID = 5164417379767181198L; private static final long serialVersionUID = 5164417379767181198L;
@Override @Override
public Component getListCellRendererComponent(JList<? extends Message> list, Message value, int index, public Component getListCellRendererComponent(JList<? extends Message> list, Message value, int index, boolean isSelected, boolean cellHasFocus) {
boolean isSelected, boolean cellHasFocus) {
if (isSelected) { if (isSelected) {
setBackground(list.getSelectionBackground()); setBackground(list.getSelectionBackground());
setForeground(list.getSelectionForeground()); setForeground(list.getSelectionForeground());
@ -64,7 +63,6 @@ public class MessageListRenderer extends JLabel implements ListCellRenderer<Mess
textColor, textColor,
text, text,
state)); state));
return this; return this;
} }

View File

@ -509,4 +509,5 @@ public class SettingsScreen extends JDialog {
colorsPanel.add(panel); colorsPanel.add(panel);
} }
} }

View File

@ -3,6 +3,8 @@ package envoy.client.ui;
import java.awt.EventQueue; import java.awt.EventQueue;
import java.io.IOException; import java.io.IOException;
import java.util.Properties; import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
@ -26,29 +28,35 @@ import envoy.exception.EnvoyException;
*/ */
public class Startup { public class Startup {
private static final Logger logger = Logger.getLogger(Startup.class.getSimpleName());
public static void main(String[] args) { public static void main(String[] args) {
logger.setLevel(Level.ALL);
Config config = Config.getInstance(); Config config = Config.getInstance();
if (args.length > 0) {
config.load(args); // Load the configuration from client.properties first
} else { ClassLoader loader = Thread.currentThread().getContextClassLoader();
ClassLoader loader = Thread.currentThread().getContextClassLoader(); try {
try { Properties configProperties = new Properties();
Properties configProperties = new Properties(); configProperties.load(loader.getResourceAsStream("client.properties"));
configProperties.load(loader.getResourceAsStream("client.properties")); config.load(configProperties);
config.load(configProperties); } catch (IOException e) {
} catch (IOException e) { e.printStackTrace();
e.printStackTrace();
}
} }
// Override configuration values with command line arguments
if (args.length > 0) config.load(args);
if (!config.isInitialized()) { 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); System.exit(1);
} }
String userName = JOptionPane.showInputDialog("Please enter your username"); String userName = JOptionPane.showInputDialog("Please enter your username");
if (userName == null || userName.isEmpty()) { 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); System.exit(1);
} }
Client client = new Client(config, userName); Client client = new Client(config, userName);
@ -66,8 +74,9 @@ public class Startup {
EventQueue.invokeLater(() -> { EventQueue.invokeLater(() -> {
try { try {
ChatWindow frame = new ChatWindow(client, localDB); ChatWindow chatWindow = new ChatWindow(client, localDB);
frame.setVisible(true); new StatusTrayIcon(chatWindow).show();
chatWindow.setVisible(true);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }

View 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;
}
}

View File

@ -28,8 +28,7 @@ public class UserListRenderer extends JLabel implements ListCellRenderer<User> {
@SuppressWarnings("incomplete-switch") @SuppressWarnings("incomplete-switch")
@Override @Override
public Component getListCellRendererComponent(JList<? extends User> list, User value, int index, boolean isSelected, public Component getListCellRendererComponent(JList<? extends User> list, User value, int index, boolean isSelected, boolean cellHasFocus) {
boolean cellHasFocus) {
if (isSelected) { if (isSelected) {
setBackground(list.getSelectionBackground()); setBackground(list.getSelectionBackground());
setForeground(list.getSelectionForeground()); setForeground(list.getSelectionForeground());
@ -41,7 +40,6 @@ public class UserListRenderer extends JLabel implements ListCellRenderer<User> {
// Enable background rendering // Enable background rendering
setOpaque(true); setOpaque(true);
final String name = value.getName(); final String name = value.getName();
final UserStatus status = value.getStatus(); final UserStatus status = value.getStatus();
@ -50,7 +48,6 @@ public class UserListRenderer extends JLabel implements ListCellRenderer<User> {
textColor = toHex( textColor = toHex(
Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getUserNameColor()); Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getUserNameColor());
switch (status) { switch (status) {
case ONLINE: case ONLINE:
setText(String.format( setText(String.format(
@ -59,7 +56,6 @@ public class UserListRenderer extends JLabel implements ListCellRenderer<User> {
textColor, textColor,
name)); name));
break; break;
case OFFLINE: case OFFLINE:
setText(String.format( setText(String.format(
"<html><p style=\"color:#fc0303\"><b><small>%s</b></small><br><p style=\"color:%s\">%s</html>", "<html><p style=\"color:#fc0303\"><b><small>%s</b></small><br><p style=\"color:%s\">%s</html>",
@ -68,9 +64,6 @@ public class UserListRenderer extends JLabel implements ListCellRenderer<User> {
name)); name));
break; break;
} }
return this; return this;
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB