TTClient.java :  » Messenger » tigertalk » client » Java Open Source

Java Open Source » Messenger » tigertalk 
tigertalk » client » TTClient.java
package client;

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.net.*;
import java.io.*;
import msgs.*;
import java.util.*;
import org.jdesktop.jdic.tray.*;

import javax.sound.sampled.*;

import java.util.*;

/**
 * Brings up the TigerTalk client GUI window, allows for login and network selection
 * Team 9: Andrew Hayworth, Brian Parrella, Ryan Kortmann, Nina Papa
 * @author Andrew Hayworth
 */

// Had to comment out tray code last minute -- something broke!! :-(

public class TTClient implements TTConstants {
  protected static boolean soundOk = true;
  private JComboBox jcb;
  private String[] ntwks = { "TigerTalk", "AOL Instant Messenger" };
  private JFrame blWindow;
  private JTextField uNameField, hostnameField;
  private JPasswordField pwdField;
  private JLabel uNameLabel, pwdLabel, hostnameLabel;
  private JPanel jp1, jp2, jp3, jp4, jp5, jp6;
  private JButton login, cancel;
  private int p1, p2;
  
  /**
   * logout menu item, we need to access this from a few places
   */
  protected static JMenuItem miLogout;

  /**
   * System tray icon
   */
  //protected static org.jdesktop.jdic.tray.TrayIcon trayIcon; // use the fully qualified package name as not to confuse JDK's newer than 5
  
  /**
   * Server ip address
   */
  protected static String server;
  
  /**
   * Our username
   */
  protected static String name;
  
  /**
   * Tigertalk login authentication key
   */
  protected static byte[] key;
  
  /**
   * Our IP address
   */
  protected static String ip;
  
  /**
   * Our password. 
   */
  protected static String pwd;
  
  /**
   * Buddylist data
   */
  protected static String[] blData;
  
  /**
   * Status of each buddy in the list
   */
  protected static HashMap<String, Integer> blDataStatus;
  
  /**
   * Boolean to determine if the TTRunningClient should update it's buddylist
   */
  protected static boolean updateBLNeeded = false;
  
  /**
   * Running list of all the group chats
   */
  protected static HashMap<String, Chat> chats;
  
  /**
   * Running list of the private messages
   */
  protected static HashMap<String, PMChat> pmChats;
  
  /**
   * Determines if we were in the middle of updating our profile
   */
  protected static boolean updatingProfile = false;
  
  /**
   * Locations of our sound files
   */
  protected static URL loginSound, logoutSound, msgInSound, msgOutSound;
  
  // this is a work-around table of os'es that tend to report that their external ip address is localhost...
  private ArrayList<String> ipWorkaroundTable;

  public static void main(String[] args) {
    new TTClient();
  }
  
  // forcing one to read my gui code is forbidden by the geneva convention
  public TTClient() {
    
    System.out.println("Running on " + System.getProperty("os.name"));
    
    // Some OS X specific gui niceness here
    if (System.getProperty("os.name").startsWith("Mac")) {
      try {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        System.setProperty("apple.laf.useScreenMenuBar", "true");
        System.setProperty("com.apple.mrj.application.apple.menu.about.name", "TigerTalk");
        System.setProperty("com.apple.mrj.application.growbox.intrudes", "false");
        
      }
      catch (Exception e) {e.printStackTrace();} // this is ok, because on exception it just defaults to swing l&f
    }
    
    ipWorkaroundTable = createWorkaroundTable();
    loginSound = getClass().getResource("sound/login.wav");
    logoutSound = getClass().getResource("sound/logout.wav");
    msgInSound = getClass().getResource("sound/msgin.wav");
    msgOutSound = getClass().getResource("sound/msgout.wav");

    pmChats = new HashMap<String, PMChat>();
    chats = new HashMap<String, Chat>();
    
    // where we're connecting on. Server can be run on different ports but the client, by default, cannot.
    p1 = 6666;
    p2 = 6667;
    
    blDataStatus = new HashMap<String, Integer>();

    blWindow = new JFrame();
    uNameLabel = new JLabel("Username");
    uNameField = new JTextField(10);
    pwdLabel = new JLabel("Password");
    pwdField = new JPasswordField(10);
    hostnameField = new JTextField(10);
    hostnameField.setText("localhost");
    hostnameLabel = new JLabel("Server");
    login = new JButton("Login");
    login.setMnemonic('L');
    cancel = new JButton("Cancel");
    cancel.setMnemonic('C');
    jcb = new JComboBox(ntwks);

    blWindow.setLayout(new BoxLayout(blWindow.getContentPane(), BoxLayout.Y_AXIS));
    
    jp2 = new JPanel();
    jp3 = new JPanel();
    jp4 = new JPanel();
    jp5 = new JPanel();
    jp6 = new JPanel();
    
    jp2.setMaximumSize(new Dimension(210,20));
    jp3.setMaximumSize(new Dimension(210,20));
    jp4.setMaximumSize(new Dimension(210,20));
    jp5.setMaximumSize(new Dimension(210,20));
    jp6.setMaximumSize(new Dimension(210,20));

    jp2.add(uNameLabel);
    jp2.add(uNameField);
    jp3.add(pwdLabel);
    jp3.add(pwdField);
    jp4.add(hostnameLabel);
    jp4.add(hostnameField);
    jp5.add(jcb);
    jp6.add(login);
    jp6.add(cancel);
    blWindow.add(Box.createVerticalGlue());
    blWindow.add(jp2);
    blWindow.add(jp3);
    blWindow.add(jp4);
    blWindow.add(jp5);
    blWindow.add(jp6);
    blWindow.add(Box.createVerticalGlue());

    blWindow.setSize(250, 600);

    blWindow.setLocationRelativeTo(null);
    blWindow.setTitle("TigerTalk v1.0 -- Login");
    blWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    blWindow.setVisible(true);
    
    createTrayIcon();
    
    ///////////
    //// HERE BE KEYLISTENERS
    ///////////
    login.addKeyListener(new KeyAdapter() {
      public void keyReleased(KeyEvent ke) {
        if (ke.getKeyCode() == KeyEvent.VK_ENTER) {
          login.doClick();
        }
      }
    });
    
    jcb.addKeyListener(new KeyAdapter() {
      public void keyReleased(KeyEvent ke) {
        if (ke.getKeyCode() == KeyEvent.VK_ENTER) {
          login.doClick();
        }
      }
    });
    
    cancel.addKeyListener(new KeyAdapter() {
      public void keyReleased(KeyEvent ke) {
        if (ke.getKeyCode() == KeyEvent.VK_ENTER) {
          cancel.doClick();
        }
      }
    });
    
    uNameField.addKeyListener(new KeyAdapter() {
      public void keyReleased(KeyEvent ke) {
        if (ke.getKeyCode() == KeyEvent.VK_ENTER) {
          login.doClick();
        }
      }
    });
    
    pwdField.addKeyListener(new KeyAdapter() {
      public void keyReleased(KeyEvent ke) {
        if (ke.getKeyCode() == KeyEvent.VK_ENTER) {
          login.doClick();
        }
      }
    });
    
    hostnameField.addKeyListener(new KeyAdapter() {
      public void keyReleased(KeyEvent ke) {
        if (ke.getKeyCode() == KeyEvent.VK_ENTER) {
          login.doClick();
        }
      }
    });
    
    // sets the hostname field to aol or localhost depending on the selection in jcb
    // hackish, but it works :)
    jcb.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent ae) {
        if (jcb.getSelectedItem().toString().equals("AOL Instant Messenger")) {
          jp4.setVisible(false);
          hostnameField.setText("aol");
        }
        else {
          jp4.setVisible(true);
          hostnameField.setText("localhost");
        }
      }
    });
    
    // this starts the login process
    login.addActionListener( new ActionListener() {
      public void actionPerformed(ActionEvent ae) {
        try {
          if (hostnameField.getText().equals("aol")) {
            String p = "";
            for (char c : pwdField.getPassword()) {
              p += c;
            }
            // if we're trying to do aol, gtfo of this class and start AIM
            (new AIM(uNameField.getText(), p)).start();
            blWindow.dispose();
            return;
          }
          // otherwise, try to raise a ttserver
          login();
          
          // provided we logged in, start our processing queues
          startServerListener();
          startServerSender();
          startMsgParser();
          
          PMSender ps = new PMSender(1234);
          ps.start();
          
          PMListener pl = new PMListener(1234);
          pl.start();
          
          PMParser pmp = new PMParser();
          pmp.start();
          
          blWindow.dispose();
          // start the main show!
          (new TTRunningClient(blData)).start();
        }
        catch (LoginException e) {
          JOptionPane.showMessageDialog(null, e.getMessage(), "Login failed!", JOptionPane.ERROR_MESSAGE);
        }
      }

    });
    
    cancel.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent ae) {
        System.exit(0);
      }
    });
    
  }
  // This exists solely that I can have a nice table of OS'es that we need to hack the ip address for...
  private ArrayList<String> createWorkaroundTable() {
    ArrayList<String> a = new ArrayList<String>();
    // essentially, we only need to add the unices, as java returns correct IP addr info for NT platforms and OS X.
    // i'm not sure if it's a bug or feature that we're working around, but it is sure annoying.
    a.add("Linux");
    a.add("Solaris");
    a.add("SunOS");
    a.add("MPE/iX");
    a.add("HP-UX");
    a.add("HP UX");
    a.add("AIX");
    a.add("FreeBSD");
    a.add("Irix");
    a.add("Digital Unix");
    return a;
  }

  // some convience methods for starting listeners ...
  // originally created with the hopes that they would do more complex setup, but they never really did.
  protected void startServerSender() {
    (new ServerSender(p1)).start();
  }

  protected void startServerListener() {
    (new ServerListener(p2)).start();
    
  }
  
  private void startMsgParser() {
    (new MsgParser()).start();
    
  }

  // our private login method. handles getting the ip address, logging into the server, raising exceptions on failure and 
  // setting up our initial environment
  private void login() throws LoginException{
    try {
      server = hostnameField.getText();
      
      // open a socket and server socket to talk to TigerTalk
      Socket sock = new Socket(server, p1);
      ServerSocket ssock = new ServerSocket(p2);
      
      // try to get the ip address ....if we're on a bad os :(
      if (ipWorkaroundTable.contains(System.getProperty("os.name"))) {
        System.out.println("Running on a unix, trying to get proper IP address info..");
        // this is really braindamaged.... but its the easiest way...
        Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces();
        while (e.hasMoreElements()) {
          NetworkInterface n = (NetworkInterface) e.nextElement();
          // Apple, I truly hate thee. Why must thou ship JDK 1.5? 
          // Thou eliminatest many convenient methods from mine aresenal...
          //if (!(n.isLoopback()) && n.isUp()) {
          Enumeration<InetAddress> ei = n.getInetAddresses();
          while (ei.hasMoreElements()) {
            InetAddress i = (InetAddress) ei.nextElement();    
            if (!(i.isLoopbackAddress())) {
              if (!(Character.isDigit(i.getHostAddress().charAt(0)))) System.out.println("IPv6 skipped");
              else {
                ip = i.getHostAddress();
                System.out.println("Detected IP address: " + ip);
              }
            }
          }
        }
      }
      // yay, we're not on a bad os, this is simple now!
      else {
        InetAddress inetAddr = InetAddress.getLocalHost();
        ip = inetAddr.getHostAddress();
        System.out.println("Detected IP address: " + ip);
      }

      String p = "";
      for (char c : pwdField.getPassword()) {
        p += c;
      }
      
      // set up our login request message
      ClientMsg cm = new ClientMsg(ip, uNameField.getText(), p);
      cm.setAction(Actions.LOGIN);
      ObjectOutputStream oos = new ObjectOutputStream(sock.getOutputStream());
      oos.writeObject(cm);  //sendit
      oos.flush();
      oos.close();
      
      // wait for a response
      sock = ssock.accept();
      ObjectInputStream ois = new ObjectInputStream(sock.getInputStream());
      cm = (ClientMsg) ois.readObject();
      ois.close();
      sock.close();
      ssock.close();
      
      // if no go, throw a login exception back up to the actionlistener on the login button, and let them try again
      if (cm.getResponse() == Responses.DENY) {
        throw new LoginException("Username or password incorrect.");  
      }
      
      // otherwise, setup our initial variables
      name = cm.getName();
      key = cm.getClientKey();
      pwd = cm.getPwd();
      String[] s = { "" };
      blData = s;
      blDataStatus.put("", 0);
    }
    catch (IOException e) {
      throw new LoginException("Unable to contact server.");
    }
    catch (ClassNotFoundException e) {
      e.printStackTrace();
    }
  }
  
  /**
   * Plays the login sound
   */
  public static void playLogin() {
    if (soundOk) {
      try {
        Clip soundClip = AudioSystem.getClip();
        AudioInputStream ais = AudioSystem.getAudioInputStream(TTClient.loginSound);
        soundClip.open(ais);
        soundClip.start();
      }
      catch (Exception e) { e.printStackTrace(); }
    }
  }
  
  /**
   * Plays the logout sound
   */
  public static void playLogout() {
    if (soundOk) {
      try {
        Clip soundClip = AudioSystem.getClip();
        AudioInputStream ais = AudioSystem.getAudioInputStream(TTClient.logoutSound);
        soundClip.open(ais);
        soundClip.start();
      }
      catch (Exception e) { e.printStackTrace(); }
    }
  }
  
  /**
   * Plays the message in sound
   */
  public static void playMsgIn() {
    if (soundOk) {
      try { 
        Clip soundClip = AudioSystem.getClip();
        AudioInputStream ais = AudioSystem.getAudioInputStream(TTClient.msgInSound);
        soundClip.open(ais);
        soundClip.start();
      }
      catch (Exception e) { e.printStackTrace(); }
    }
  }
  
  /**
   * Plays the message out sound
   */
  public static void playMsgOut() {
    if (soundOk) {
      try {
        Clip soundClip = AudioSystem.getClip();
        AudioInputStream ais = AudioSystem.getAudioInputStream(TTClient.msgOutSound);
        soundClip.open(ais);
        soundClip.start();
      }
      catch (Exception e) { e.printStackTrace(); }
    }
  }
  
  /** 
   * This creates our nifty little tray icon!
   */
  public void createTrayIcon()
  {
       
        JPopupMenu pmMenu = new JPopupMenu();
        
        JMenuItem miExit = new JMenuItem("Exit");
        JMenuItem miAbout = new JMenuItem("About");
        miLogout = new JMenuItem("Logout");
        miLogout.setEnabled(false);
        
        pmMenu.add(miAbout);
        pmMenu.addSeparator();
        pmMenu.add(miLogout);
        pmMenu.add(miExit);
        
        // will this work in a jar??
        Image iconImage = Toolkit.getDefaultToolkit().getImage(getClass().getResource("images/tigertalk.png"));
        ImageIcon iic = new ImageIcon(iconImage);
        
        // Have to package this into an icon, or we can't use the JDIC
        //trayIcon = new TrayIcon((Icon)iic, "TigerTalk v1.0", pmMenu);
       // trayIcon = new org.jdesktop.jdic.tray.TrayIcon((Icon)iic, "TigerTalk v1.0", pmMenu);
       // trayIcon.setIconAutoSize(true);
        
        
        // kills things off cleanly if they try to close the program from this menu
        miExit.addActionListener(new ActionListener()
        {
          public void actionPerformed(ActionEvent ae)
          {
            if (name != null ) {
              System.out.println("Shutting down!");
              ServerSender.killServerSender();
            ServerListener.killServerListener();
            MsgParser.killMsgParser();
            PMListener.killPMListener();
            PMSender.killPMSender();
            PMParser.killPMParser();
            }
            System.exit(0);
          }
        });
        
        miAbout.addActionListener(new ActionListener()
        {
          public void actionPerformed(ActionEvent ae)
          {
            //JDIC doesn't support this on the mac yet :'-(
            if (System.getProperty("os.name").startsWith("Mac")) {
              JOptionPane.showMessageDialog(null, "TigerTalk v1.0", "About TigerTalk", JOptionPane.INFORMATION_MESSAGE);
            }
            else {
        //      trayIcon.displayMessage("About TigerTalk v1.0", "219 Final Project\nMay 21st, 2009\nInfo box or this kind of message box for the about screen???", org.jdesktop.jdic.tray.TrayIcon.INFO_MESSAGE_TYPE);
            }
            //JOptionPane.showMessageDialog(null, "TigerTalk v1.0", "About TigerTalk", JOptionPane.INFORMATION_MESSAGE);
          }
        });
        
        
        // using full classnames. Here's the reason:
        // JDK 6 has native, fullfeatured classes for SystemTray and TrayIcon. Work wonderfully!
        // ... but OS X ships JDK5. Which has neither class. In any way shape or form.
        // So, we found an old library that the JDK 6 features were *based* from. It works rather well, and is cross platform 
        // even on an alpha build, which we are using, it is stable.
        // however, to avoid confusing a JDK6 compiler with identical classnames, we're fully specifying them here.
        // It's the same situation as in java.util.Timer vs javax.swing.Timer -- just ambiguous
     //   org.jdesktop.jdic.tray.SystemTray tray = org.jdesktop.jdic.tray.SystemTray.getDefaultSystemTray();
        try
        {
    //        tray.addTrayIcon(trayIcon);
        }
        catch (Exception e)
        {
            System.out.println("TrayIcon could not be added.");
        }
        
  }

  /**
   * @return the soundOk
   */
  public static boolean isSoundOk() {
    return soundOk;
  }

  /**
   * @param soundOk the soundOk to set
   */
  public static void setSoundOk(boolean soundOk) {
    TTClient.soundOk = soundOk;
  }
  
  
}

/**
 * This class just allows us to barf up an error to the main gui when logging in
 * @author Andrew Hayworth
 */
class LoginException extends Exception {
  
  public LoginException() {
    super();
  }
  
  public LoginException(String cause) {
    super(cause);
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.