Browse Source

Initial Commit

Code from initial project (Twitter API key has been removed)
master
Chris Wiegman 7 years ago
parent
commit
62399c1cde
10 changed files with 1321 additions and 1 deletions
  1. +220
    -0
      ChatSession.java
  2. +501
    -0
      Controller.java
  3. +296
    -0
      FriendsList.java
  4. +47
    -0
      GetDms.java
  5. +45
    -0
      OpenURL.java
  6. +5
    -1
      README.md
  7. +161
    -0
      Stray.java
  8. +46
    -0
      TwitterChat.java
  9. BIN
      exit.png
  10. BIN
      icon.png

+ 220
- 0
ChatSession.java View File

@@ -0,0 +1,220 @@
/**
* DC Twitter Chat
* Dan Houle and Chris Wiegman
* CS412 - Summer 2011
*
* Chat session class
*/
package TwitterChat;
import java.util.List;
import java.util.ArrayList;
import java.util.Date;
import java.util.Calendar;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import twitter4j.DirectMessage;
public class ChatSession extends JFrame implements Runnable {
private JTextField enterField; // for entering messages
private JTextArea displayArea; // for displaying messages
private String partner; //name of the person you're talking to
private Date startDate; //used as a timestamp for displaying messages
private final int MAX_MSG_SIZE = 140; //max size of a single message
private final int PREFIX_SIZE = 6; //number of in the prefix for multiple messages
// set up GUI
public ChatSession(final String name) {
super(name);
//assign the global variable
partner = name;
//removes this thread when the window is closed
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent we) {
Controller.removeClient(name);
}
});
enterField = new JTextField("Type message here");
enterField.addActionListener(
//this action listener does several things
//1) clears the JTextField after hitting ENTER
//2) determines size of message
// a) if message is <= MAX_MSG_SIZE, nothing special happens, the message is just displayed
// b) if it's larger than MAX_MSG_SIZE, it's splitup & has a prefix put in front to show the order
//3) the global timestamp is updated after the message is sent
new ActionListener() {
public void actionPerformed(ActionEvent event) {
try {
// get message from textfield
String message = event.getActionCommand();
//the message can be sent in 1 time
if(message.length() <= MAX_MSG_SIZE){
displayArea.append("\nMe>>> " + message + "\n");
Controller.sendMessage(partner, message);
displayArea.setCaretPosition( displayArea.getText().length());
}
//message needs to be broken into smaller parts
else{
//determine number of initial "chunks"
int tCt = message.length()/MAX_MSG_SIZE;
//determine the number of "chunks" with the prefixes added
int msgCt = (message.length() + (tCt * 6)) / MAX_MSG_SIZE;
//determine if there is any overlap. if there is, increase the # of "chunks"
int rest = msgCt % MAX_MSG_SIZE;
if(rest != 0) msgCt++;
//break up message & send the individual "chunks"
for(int i = 0; i < msgCt; i++){
String chunk = "";
//if it's not the last chunk of message
if(i < msgCt - 1)
chunk = message.substring(i * (MAX_MSG_SIZE - PREFIX_SIZE), (i * (MAX_MSG_SIZE - PREFIX_SIZE)) + (MAX_MSG_SIZE - PREFIX_SIZE));
//it is the last chunk of message
else if(i == msgCt - 1)
chunk = message.substring(i * (MAX_MSG_SIZE - PREFIX_SIZE), message.length());
//create & append the prefix
String pre = "(" + (i + 1) + "/" + msgCt + ") ";
String msgShown = pre + chunk;
//send & display the chunks
displayArea.append("\nMe>>> " + msgShown + "\n");
Controller.sendMessage(partner, msgShown);
displayArea.setCaretPosition( displayArea.getText().length());
}
}
//update the timestamp & reset the textArea
startDate = new Date();
enterField.setText("");
} // end try
catch (Exception ioException) {
displayMessage(ioException + "\n");
ioException.printStackTrace();
} // end catch
} // end actionPerformed
} // end inner class
); // end call to addActionListener
add(enterField, BorderLayout.NORTH);
displayArea = new JTextArea();
add(new JScrollPane(displayArea), BorderLayout.CENTER);
setSize(400, 300); // set window size
setVisible(true); // show window
} // end Client constructor
public void run() {
//start the timestamp start waitForMessages()
startDate = new Date();
waitForMessages();
}
// wait for messages to arrive from Controller, display message contents
//upon initial start, only display messages that are <= 3 hours old
public void waitForMessages() {
//current messages pulled from the Controller
List<DirectMessage> message = null;
//prior messages to check for updates
List<DirectMessage> oldMessage = new ArrayList<DirectMessage>();
//just a flag to determine initial startup
boolean start = true;
while (true) {
// receive message and display contents if new
try {
//get the messages from the person you're chatting with
message = Controller.getMessages(false, partner);
//if there really are messages
if(message != null){
//if the messages are new
if(!message.equals(oldMessage)){
// display a single message
if(message.size() == 1){
//client has just started
if(start){
if((startDate.getTime() - (message.get(0).getCreatedAt()).getTime()) <= 10800000){
displayMessage("\n" + partner + ">>> " + message.get(0).getText() + "\n");
}
}
else{
if((message.get(0).getCreatedAt()).after(startDate)){
displayMessage("\n" + partner + ">>> " + message.get(0).getText() + "\n");
}
}
}
//there are multiple messages
else if(message.size() > 1){
int numMsg = message.size();
//client has just started
if(start){
for(int i = 0; i < numMsg; i++){
if((startDate.getTime() - (message.get(i).getCreatedAt()).getTime()) <= 10800000){
displayMessage("\n" + partner + ">>> " + message.get(i).getText() + "\n");
}
}
}
else{
for(int i = 0; i < numMsg; i++){
if((message.get(i).getCreatedAt()).after(startDate)){
displayMessage("\n" + partner + ">>> " + message.get(i).getText() + "\n");
}
}
}
}
//clear oldMessage
if(oldMessage != null)
oldMessage.clear();
//make message into oldMessage
for(int i = 0; i < message.size(); i++){
oldMessage.add(message.get(i));
}
//clear message to make room for more
message.clear();
//turn the flag "off"
start = false;
//update the timestamp
startDate = new Date();
//if the chat window isn't "active" window, it'll be brought to the front
//which means the icon will start flashing
if(!isFocused()){
toFront();
}
}
}
} // end try
catch (Exception exception) {
displayMessage(exception + "\n");
exception.printStackTrace();
} // end catch
try {
Thread.sleep(500); //sleep .5 second then try again
} catch (InterruptedException ex) {
}
} // end while
} // end method waitForPackets
// manipulates displayArea in the event-dispatch thread
private void displayMessage(final String messageToDisplay) {
SwingUtilities.invokeLater(
new Runnable() {
public void run() // updates displayArea
{
displayArea.append(messageToDisplay);
} // end method run
} // end inner class
); // end call to SwingUtilities.invokeLater
} // end method displayMessage
} // end class Client

+ 501
- 0
Controller.java View File

@@ -0,0 +1,501 @@
/**
* DC Twitter Chat
* Dan Houle and Chris Wiegman
* CS412 - Summer 2011
*
* Controller class for handling communications, etc
*/


package TwitterChat;

import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.HTMLEditorKit;
import twitter4j.DirectMessage;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.User;
import twitter4j.auth.AccessToken;
import twitter4j.auth.RequestToken;

public class Controller implements Runnable {

//constants provided by Twitter
private final String TWITTER_CONSUMER_KEY = "Consumer Key";
private final String TWITTER_CONSUMER_SECRET = "Consumer Secret";
//Access token for API calls
private static AccessToken accessToken;
//The authorization pin
private static String authPin = null;
//Twitter library for API calls
private static Twitter twitter;
//Pin interaction form
private JFrame pinForm;
//List of active chat windows
private static List<String> openChats;
private static List<DirectMessage> messages;

/**
* Constructor initializes twitter communications and lists for storing chats
* and open windows
*/
public Controller() {
openChats = new ArrayList<String>(); //list of open chat windows
twitter = new TwitterFactory().getInstance(); //connect back to twitter
messages = new ArrayList<DirectMessage>(); //list of direct messages
}

/**
* Authorize user with OAth and set up other variables
*/
public void run() {

//authorize with Twitter
authorize();

//create and start the friends list
FriendsList fl = new FriendsList();
Thread friendsList = new Thread(fl);
friendsList.start();

//wait until friendslist is loaded before continuing
while (fl.getLoaded() == false) {
try {
Thread.sleep(500); //sleep for half second then try again.
} catch (InterruptedException ex) {
Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex);
}
}

//start polling for new direct messages
Thread getDms = new Thread(new GetDms());
getDms.start();
//Start the system tray icon
Thread stray = new Thread(new Stray());
stray.start();
}

/**
* Authorizes Twitter user returns true if successful
* @return boolean
*/
private boolean authorize() {

//Request token to be sent to Twitter
RequestToken requestToken;

try {

//set Twitter variables
twitter.setOAuthConsumer(TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET);

//get request token and set accesstoken to null
requestToken = twitter.getOAuthRequestToken();
accessToken = null;

//get the url to call
String authURL = requestToken.getAuthorizationURL(); //get the URL
try {
new OpenURL(authURL); //open the URL in a browser
} catch (Exception ex) {
JOptionPane.showMessageDialog(new JFrame(), ex.getMessage(), "Dialog",
JOptionPane.ERROR_MESSAGE);
System.exit(1);
}

//while we don't have a valid access token, keep trying to get one
while (accessToken == null) {

try {

if (authPin != null) { //if we have a pin try to get an access token
accessToken = twitter.getOAuthAccessToken(requestToken, authPin);
twitter.setOAuthAccessToken(accessToken);

return true;

} else { //no pin = get one from the user
if (pinForm == null) {
getPin(authURL);
} else { //if they've closed the pin form reopen it
pinForm.setVisible(true);
}
}

} catch (TwitterException te) {
if (401 == te.getStatusCode()) { //twitter is down
JOptionPane.showMessageDialog(new JFrame(), "Unable to get the access token. Twitter is down", "Dialog",
JOptionPane.ERROR_MESSAGE);
pinForm = null;
authPin = null;
} else {
JOptionPane.showMessageDialog(new JFrame(), te.getErrorMessage(), "Dialog",
JOptionPane.ERROR_MESSAGE);
return false;
}
}
}
} catch (TwitterException ex) { //twitter is down
JOptionPane.showMessageDialog(new JFrame(), ex.getMessage(), "Dialog",
JOptionPane.ERROR_MESSAGE);
System.exit(1);
}
return false;
}

/**
* Get the OAuth pin from the user
*/
private void getPin(final String authURL) {

//Form components
JEditorPane authText;
JButton submitButton;

//Create a JFrame
pinForm = new JFrame();
pinForm.setSize(400, 260);
pinForm.setLayout(new BorderLayout(5, 10));

//Use HTML to tell the user what the form is all about
String aText = "<center>"
+ "<h1>You must authenticate with Twitter</h1>"
+ "<p>Copy the PIN given to you from the Twitter Website and enter it below.</p>"
+ "<p>If the Twitter page doesn't open automatically <a href=\"" + authURL + "\">follow this link</a>."
+ "</center>";
authText = new JEditorPane(new HTMLEditorKit().getContentType(), aText);
Font font = UIManager.getFont("Label.font");
String bodyRule = "body { font-family: " + font.getFamily() + "; "
+ "font-size: " + font.getSize() + "pt; }";
((HTMLDocument) authText.getDocument()).getStyleSheet().addRule(bodyRule);
authText.addHyperlinkListener(
new HyperlinkListener() {
// if user clicked hyperlink, go to specified page

public void hyperlinkUpdate(HyperlinkEvent event) {
if (event.getEventType()
== HyperlinkEvent.EventType.ACTIVATED) {
try {
new OpenURL(authURL);
} catch (Exception ex) {
JOptionPane.showMessageDialog(new JFrame(), ex.getMessage(), "Error",
JOptionPane.ERROR_MESSAGE);
System.exit(1);
}
}
} // end method hyperlinkUpdate
} // end inner class
);
authText.setContentType("text/html");
authText.setEditable(false);
authText.setText(aText);

//Set up the input field
final JTextField pinEntry = new JTextField();
pinEntry.addActionListener(
new ActionListener() {

//close the pin window and set the pin
public void actionPerformed(ActionEvent event) {
setPin(pinEntry.getText());
pinForm.setVisible(false);

}
});

//Set up a submit button
submitButton = new JButton();
submitButton.setText("Authenticate");
submitButton.addActionListener(new ActionListener() {

//close the pin window and set the pin
public void actionPerformed(ActionEvent event) {
setPin(pinEntry.getText());
pinForm.setVisible(false);
}
});

//add components to the form and set visible
pinForm.add(authText, BorderLayout.NORTH);
pinForm.add(pinEntry, BorderLayout.CENTER);
pinForm.add(submitButton, BorderLayout.SOUTH);
pinForm.setVisible(true);

}

/**
* Sets the pin number attained from the pin form
* @param pin
*/
private void setPin(String pin) {
authPin = pin;
}

/**
* Determine if a user is logged in
* @return boolean
*/
private boolean hasAccess() {
if (accessToken == null) { //if we have an access token we are logged in
return false;
} else {
return true;
}
}

/**
* Returns list of user objects that can be direct messaged too
* @return List<User>
*/
public static List<User> getFriendsList() {
try {
//who we're followed by
long[] followers = twitter.getFollowersIDs(-1).getIDs();

//who we're following
long[] friends = twitter.getFriendsIDs(-1).getIDs();

//temp array of user ids to send to twitter
long[] temp = new long[100];

//list of users to return
List<User> mutual = new ArrayList<User>();

//keep track of temp array items
int count = 0;

for (int i = 0; i < friends.length; i++) {

boolean found = false; //stop processing when we found a match

for (int j = 0; j < followers.length && found == false; j++) {
if (friends[i] == followers[j]) {
found = true;
temp[count] = friends[i]; //add user to temp array
count++;
}

//if array is full push to list and reset array and counter
if (count == 99) {
mutual.addAll(twitter.lookupUsers(temp));
count = 0;
Arrays.fill(temp, 0);
}
}
}

//send any left in the array to the list
mutual.addAll(twitter.lookupUsers(temp));

//sort the list by twitter username
Collections.sort(mutual, new UsernameComparator());

return mutual;

} catch (TwitterException ex) { //error with twitter
JOptionPane.showMessageDialog(new JFrame(), ex.getErrorMessage(), "Dialog",
JOptionPane.ERROR_MESSAGE);
return null;
}
}

/**
* subclass for sorting user list
*/
private static class UsernameComparator implements Comparator {

/**
* Compares twitter username of 2 users
* @param o1
* @param o2
* @return int
*/
public int compare(Object o1, Object o2) {
User u1 = (User) o1;
User u2 = (User) o2;
String s1 = u1.getScreenName().toLowerCase();
String s2 = u2.getScreenName().toLowerCase();
return s1.compareTo(s2);
}
}

/**
* Add chat window to openChats list
* @param client
*/
public static synchronized void addClient(String client) {
openChats.add(client);
}

/**
* Remove chat window from list when closed
* @param client
*/
public static synchronized void removeClient(String client) {
openChats.remove(openChats.indexOf(client));
}

/**
* Determine if a chat window has already been created for a given username
* @param client
* @return boolean
*/
public static boolean checkClient(String client) {
if (openChats.indexOf(client) == -1) {
return false;
} else {
return true;
}
}

/**
* Send direct message to given recipient
* @param receiver
* @param message
*/
public static void sendMessage(String receiver, String message) {
try {
twitter.sendDirectMessage(receiver, message);
} catch (TwitterException ex) {
JOptionPane.showMessageDialog(new JFrame(), ex.getErrorMessage(), "Dialog",
JOptionPane.ERROR_MESSAGE);
}
}

/**
* Set retrieved messages into list
* @param messageList
*/
public static synchronized void setMessages(List<DirectMessage> messageList) {
messages = messageList;
}

/**
* Return list of direct messages sort true = descending, false = ascending
* @param sort
* @return List
*/
public static synchronized List<DirectMessage> getMessages(boolean sort) {
if (sort == false) {
Collections.sort(messages, new MessageAscending());
} else {
Collections.sort(messages, new MessageDescending());
}
return messages;
}

/**
* Return list of direct messages from a given username sort true = descending, false = ascending
* @param sort
* @param String
* @return List
*/
public static synchronized List<DirectMessage> getMessages(boolean sort, String client) {
List<DirectMessage> clientMessages = new ArrayList<DirectMessage>();

//parse through messages saving only those we need
Iterator itr = messages.iterator();
while (itr.hasNext()) {
DirectMessage message = (DirectMessage) itr.next();
if (message.getSenderScreenName().equals(client)) {
clientMessages.add(message);
}
}
if (sort == false) {
Collections.sort(clientMessages, new MessageAscending());
} else {
Collections.sort(clientMessages, new MessageDescending());
}
return clientMessages;
}

/**
* Compares dates to sort messages in ascending order
*/
private static class MessageAscending implements Comparator {

/**
* Compares twitter username of 2 users
* @param o1
* @param o2
* @return int
*/
public int compare(Object o1, Object o2) {
DirectMessage m1 = (DirectMessage) o1;
DirectMessage m2 = (DirectMessage) o2;
Date d1 = m1.getCreatedAt();
Date d2 = m2.getCreatedAt();
if (d1.after(d2)) {
return 1;
} else if (d1.equals(d2)) {
return 0;
} else {
return -1;
}
}
}

/**
* Compares dates to sort messages in descending order
*/
private static class MessageDescending implements Comparator {

/**
* Compares twitter username of 2 users
* @param o1
* @param o2
* @return int
*/
public int compare(Object o1, Object o2) {
DirectMessage m1 = (DirectMessage) o1;
DirectMessage m2 = (DirectMessage) o2;
Date d1 = m1.getCreatedAt();
Date d2 = m2.getCreatedAt();
if (d1.after(d2)) {
return -1;
} else if (d1.equals(d2)) {
return 0;
} else {
return 1;
}
}
}

/**
* Returns raw directmessage list from Twitter for the getDMs class
* @return
*/
public static List<DirectMessage> getRawMessages() {
List<DirectMessage> rawMessages = new ArrayList<DirectMessage>();
try {
rawMessages = twitter.getDirectMessages();
} catch (TwitterException ex) {
JOptionPane.showMessageDialog(new JFrame(), ex.getErrorMessage(), "Dialog",
JOptionPane.ERROR_MESSAGE);
}
return rawMessages;
}
}

+ 296
- 0
FriendsList.java View File

@@ -0,0 +1,296 @@
/**
* DC Twitter Chat
* Dan Houle and Chris Wiegman
* CS412 - Summer 2011
*
* Friends list class
*/


package TwitterChat;

import java.awt.Font;
import java.awt.SystemTray;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.HTMLEditorKit;
import twitter4j.DirectMessage;
import twitter4j.User;

public class FriendsList implements Runnable {

//Interface Items
private static JFrame friendsList;
private JMenuBar menubar;
private ImageIcon icon;
private JMenu file;
private JMenuItem exitItem;
private JMenu help;
private JMenuItem helpItem;
private JScrollPane fScroller;
private JEditorPane fList;
private List<User> friends;
private String html = "";
private List<DirectMessage> messages;
private Date lastMessage;
private boolean loaded;
private final static java.net.URL IMAGE_URL = TwitterChat.class.getResource("icon.png");;
private final Font DESKTOP_FONT = UIManager.getFont("Label.font");
private final String BODY_CSS = "body { font-family: " + DESKTOP_FONT.getFamily() + "; "
+ "font-size: " + DESKTOP_FONT.getSize() + "pt; }";

/**
* Constructor initializes friends list
*/
public FriendsList() {

loaded = false; //it's currently not loaded

lastMessage = new Date(Calendar.getInstance().getTimeInMillis());

//create friends list
friendsList = new JFrame();

//menubar for exit and information
menubar = new JMenuBar();
icon = new ImageIcon(getClass().getResource("exit.png"));

file = new JMenu("File");
file.setMnemonic(KeyEvent.VK_F);

exitItem = new JMenuItem("Exit", icon);
exitItem.setMnemonic(KeyEvent.VK_C);
exitItem.setToolTipText("Exit application");
exitItem.addActionListener(new ActionListener() {

public void actionPerformed(ActionEvent event) {
System.exit(0);
}
});

file.add(exitItem);

help = new JMenu("Help");

helpItem = new JMenuItem("About");
helpItem.addActionListener(new ActionListener() {

public void actionPerformed(ActionEvent event) {
aboutBox();
}
});

help.add(helpItem);

menubar.add(file);
menubar.add(help);

//temporary text until friends are loaded
String loadText = "<br /><br /><br /><br /><h1>Loading Friends List</h1>";

fList = new JEditorPane(new HTMLEditorKit().getContentType(), loadText);
((HTMLDocument) fList.getDocument()).getStyleSheet().addRule(BODY_CSS); //use default system font
fList.setEditable(false);
fList.setContentType("text/html");
fList.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
fList.setText(loadText);
fList.addHyperlinkListener(
new HyperlinkListener() {
// if user clicked hyperlink, go to specified page

public void hyperlinkUpdate(HyperlinkEvent event) {
if (event.getEventType()
== HyperlinkEvent.EventType.ACTIVATED) {
if (Controller.checkClient(event.getDescription()) == false) {
//start a new chat session and register with controller
Thread ct = new Thread(new ChatSession(event.getDescription()));
ct.start();
Controller.addClient(event.getDescription());
}
}
} // end method hyperlinkUpdate
} // end inner class
);

fScroller = new JScrollPane(fList, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);

friendsList.add(fScroller);

friendsList.setJMenuBar(menubar);
friendsList.setTitle(TwitterChat.getTitle() + " Friends List");
friendsList.setSize(300, 800);
friendsList.setLocation(10, 10);
//disable shutdown unless user specifically requests exit from menu or system tray
friendsList.addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent evt) {
formWindowClosing(evt);
}
public void windowIconified(java.awt.event.WindowEvent evt) {
formWindowIconified(evt);
}
public void windowDeiconified(java.awt.event.WindowEvent evt) {
formWindowDeiconified(evt);
}
});
}

/**
* Displays the about box when selected
*/
private void aboutBox() {
//create and populate a new JFrame
final JFrame aboutBox = new JFrame();
aboutBox.setTitle("About " + TwitterChat.getTitle());
aboutBox.setSize(400, 300);
aboutBox.setLocation(-20, 50);
aboutBox.setLocationRelativeTo(friendsList);

String aText = "<center>"
+ "<h1>" + TwitterChat.getTitle() + "</h1>\n"
+ "Version " + TwitterChat.getVersion() + "\n"
+ "<hr />"
+ "<p>By:<br />Daniel Houle<br />and<br />ChrisWiegman</p>"
+ "<hr />"
+ "<p>Southern Illinois University Carbondale<br />CS412/591 - Summer 2011</p>"
+ "</center>";
JEditorPane aboutText = new JEditorPane(new HTMLEditorKit().getContentType(), aText);
((HTMLDocument) aboutText.getDocument()).getStyleSheet().addRule(BODY_CSS); //use default system font
aboutText.setContentType("text/html");
aboutText.setEditable(false);
aboutText.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
aboutText.setText(aText);

aboutBox.add(aboutText);

aboutBox.setVisible(true);
}

public void run() {

friendsList.setVisible(true); //show the list when a thread is created

//Set mac stuff - buggy in Lion
System.setProperty("apple.laf.useScreenMenuBar", "true"); //if mac use the screen menu bar

friends = Controller.getFriendsList(); //get the friends list

//parse user object in friends list
Iterator itr = friends.iterator();
while (itr.hasNext()) {
User temp = (User) itr.next();
html = html + "<a href=\"" + temp.getScreenName() + "\"><img align=\"left\" border=\"0\" width=\"30\" height=\"30\" src=\"" + temp.getProfileImageURL() + "\">&nbsp;<strong>" + temp.getScreenName() + "</strong> (" + temp.getName() + ")</a><hr />";
}

//update display
fList.setText(html);

//set loaded flag
loaded = true;

boolean newRun = false;

//check for new messages and open new session if necessary
while (true) {
boolean newMessages = true;
messages = Controller.getMessages(newRun);
newRun = true;
Iterator mitr = messages.iterator();
while (mitr.hasNext() && newMessages == true) {

DirectMessage message = (DirectMessage) mitr.next();

if (lastMessage.before(message.getCreatedAt())) {
String sender = message.getSenderScreenName();
lastMessage = message.getCreatedAt();
if (Controller.checkClient(sender) == false) {
//start a new chat session and register with controller
Thread ct = new Thread(new ChatSession(sender));
ct.start();
Controller.addClient(sender);

}
} else {
newMessages = false;
}

}
try {
Thread.sleep(10000); //sleep 10 seconds
} catch (InterruptedException ex) {
Logger.getLogger(FriendsList.class.getName()).log(Level.SEVERE, null, ex);
}
}
}

/**
* return true if friends list has been loaded, false if not
* @return boolean
*/
public boolean getLoaded() {
return loaded;
}

/**
* Adjust visibility of friends list window
* @param vis
*/
public static void setVisible(boolean vis) {
if (vis == true) {
friendsList.setVisible(true);
} else {
friendsList.setVisible(false);
}
}
/**
* Disable standard close buttons ad only hide window
* @param evt
*/
private void formWindowIconified(java.awt.event.WindowEvent evt) {
if (SystemTray.isSupported()) { //if system tray is supported then minimize to system tray
friendsList.setVisible(false);
}
}

/**
* Disable standard close buttons ad only hide window
* @param evt
*/
private void formWindowClosing(java.awt.event.WindowEvent evt) {
if (SystemTray.isSupported()) { //if system tray is supported then close to system tray
friendsList.setVisible(false);
} else {
System.exit(0);
}
}

/**
* Disable standard close buttons ad only hide window
* @param evt
*/
private void formWindowDeiconified(java.awt.event.WindowEvent evt) {
if (SystemTray.isSupported()) { //if system tray is supported then minimize to system tray
friendsList.setVisible(false);
}
}
}

+ 47
- 0
GetDms.java View File

@@ -0,0 +1,47 @@
/**
* DC Twitter Chat
* Dan Houle and Chris Wiegman
* CS412 - Summer 2011
*
* Class to retrieve direct messages from Twitter
*/

package TwitterChat;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import twitter4j.DirectMessage;

public class GetDms implements Runnable {
private List<DirectMessage> messages;
/**
* Initialize direct message worker with controller and twitter wrapper
* @param controller
* @param twit
*/
public GetDms() {
messages = new ArrayList<DirectMessage>(); //list to store retrieved messages
}
/**
* Start 10 second loop when thread is initialized
*/
public void run() {
while (true) {
messages = Controller.getRawMessages(); //retrieve messages from twitter
Controller.setMessages(messages); //save messages to controller
try {
Thread.sleep(10000); //sleep 10 seconds then try again
} catch (InterruptedException ex) {
Logger.getLogger(GetDms.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}

+ 45
- 0
OpenURL.java View File

@@ -0,0 +1,45 @@
/**
* DC Twitter Chat
* Dan Houle and Chris Wiegman
* CS412 - Summer 2011
*
* Class to handle URL's in the user's default browser
*/

package TwitterChat;

import java.net.URI;
import java.awt.Desktop;

public class OpenURL {

/**
* Constructor opens URL in default browser
* @param URL
*/
public OpenURL(String URL) throws Exception {

//if we're not on a supported OS just quit
if (!Desktop.isDesktopSupported()) {
throw new Exception("Unsupported Desktop. Can't open authentication site.");
}

//if thre is no url to open just quit
if (URL.length() == 0) {
throw new Exception("Invalid URL received");
}

//create a desktop opject
Desktop desktop = Desktop.getDesktop();

//another check for a supported os (not sure why this is needed)
if (!desktop.isSupported(Desktop.Action.BROWSE)) {
throw new Exception("Unsupported Desktop. Can't open authentication site.");
}

//try to open the URL or throw an error message
URI uri = new URI(URL);
desktop.browse(uri);

}
}

+ 5
- 1
README.md View File

@@ -1,4 +1,8 @@
Twitter-Chat
============

Java based Twitter Chat (IM) client.
Java based Twitter Chat (IM) client.

This is an IM client using the Twitter API to treat DMs as Instant messages.

This was done as a class project with Daniel Houle in the Summer of 2011. Perhaps someone else could find it useful.

+ 161
- 0
Stray.java View File

@@ -0,0 +1,161 @@
/**
* DC Twitter Chat
* Dan Houle and Chris Wiegman
* CS412 - Summer 2011
*
* Create and handle the system tray/notification icon
*/

package TwitterChat;

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.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JOptionPane;

public class Stray implements Runnable {

private TrayIcon trayIcon;
private boolean vis = true; //helps determine default action
private String TOOLTIP = TwitterChat.getTitle();
private java.net.URL imageURL;
private static PopupMenu menu;

/**
* Constructor sets up the tray icon
*
* @param gui
*/
public Stray() {
imageURL = TwitterChat.class.getResource("icon.png");
}

public void run() {
if (SystemTray.isSupported()) { //only launch if supported

MouseListener mouseListener = new MouseListener() {

@Override
public void mouseClicked(MouseEvent e) { //show and hide the server window when left-click on the tray icon
if (vis == true) {
FriendsList.setVisible(false);
vis = false;
} else {
FriendsList.setVisible(true);
vis = true;
}
}

@Override
public void mouseEntered(MouseEvent e) { //Not used
}

@Override
public void mouseExited(MouseEvent e) {//Not used
}

@Override
public void mousePressed(MouseEvent e) { //Not used
}

@Override
public void mouseReleased(MouseEvent e) { //Not used
}
};

SystemTray tray = SystemTray.getSystemTray();
Image trayImage = Toolkit.getDefaultToolkit().getImage(imageURL);
createMenu();
trayIcon = new TrayIcon(trayImage, TOOLTIP, menu);
trayIcon.setImageAutoSize(true);
trayIcon.addMouseListener(mouseListener);
try {
tray.add(trayIcon);
} catch (AWTException e) {
System.err.println("Error starting tray: " + e);
}
}
}

/**
* creates the popup menu for the system tray
*/
private void createMenu() {
menu = new PopupMenu();
MenuItem show = new MenuItem("Show Friendslist");
MenuItem hide = new MenuItem("Hide Friendslist");
MenuItem about = new MenuItem("About");
MenuItem exit = new MenuItem("Exit");
exit.addActionListener(new exitListener());
show.addActionListener(new showListener());
hide.addActionListener(new hideListener());
about.addActionListener(new aboutListener());
menu.add(show);
menu.add(hide);
menu.addSeparator();
menu.add(about);
menu.add(exit);
}

/**
* close the server from the system tray
*/
class exitListener implements ActionListener {

@Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
}

/**
* shows server window
*/
class showListener implements ActionListener {

@Override
public void actionPerformed(ActionEvent e) {
if (vis == false) {
FriendsList.setVisible(true);
vis = true;
}
}
}

/**
* Hides server window
*/
class hideListener implements ActionListener {

@Override
public void actionPerformed(ActionEvent e) {
if (vis == true) {
FriendsList.setVisible(false);
vis = false;
}
}
}

/**
* Shows about dialog
*/
class aboutListener implements ActionListener {

@Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(null,
TwitterChat.getTitle() +
"Version " + TwitterChat.getVersion() + "\n" +
"CS412/591 - Summer 2011", "About",
JOptionPane.PLAIN_MESSAGE);
}
}
}

+ 46
- 0
TwitterChat.java View File

@@ -0,0 +1,46 @@
/**
* DC Twitter Chat
* Dan Houle and Chris Wiegman
* CS412 - Summer 2011
*
* Execution class
*/

package TwitterChat;

public class TwitterChat {

//Program variables.
private static final String APPLICATION_TITLE = "DC Twitter Chat";
private static final String APPLICATION_VERSION = "1.0";

/**
* Execute the application. No arguments are necessary
* @param args
* @throws Exception
*/
public static void main(String args[]) throws Exception {

//Start the controller
Controller twitterController = new Controller();
Thread controller = new Thread(twitterController);
controller.start();

}

/**
* Return application name
* @return
*/
public static String getTitle() {
return APPLICATION_TITLE;
}

/**
* Return application version
* @return
*/
public static String getVersion() {
return APPLICATION_VERSION;
}
}

BIN
exit.png View File

Before After
Width: 16  |  Height: 16  |  Size: 3.3 KiB

BIN
icon.png View File

Before After
Width: 100  |  Height: 100  |  Size: 4.0 KiB