Bluetooth Example 2
 
 

 

Third example: A chat application for mobile phones

Purpose: One of the best training camps for client-server applications is the programming of a chat system. In our example two mobile phones are connected via Bluetooth and each user can enter simultaneously lines of text. By pressing an OK button the text line is sent to the partner and displayed in a scrollable text area.

Despite the two nodes are equivalent, the client-server model still applies: When one node starts, it first enters the client mode and looks for a partner's server node. If this search fails, the node starts as server waiting for a client to connect. Thus we write a class BtChatClient that works as client and a class BtChatServer that works as server. The main application BtChatGidlet is responsible to select the client or server mode.

// BtChatClient.java

import
 ch.aplu.gidlet.*;
import
 java.io.*;
import
 ch.aplu.bluetooth.*;
import
 javax.microedition.lcdui.*;

public
 class BtChat
{

// --------------- Inner class Executor ----------------
private
 class Executor extends Thread
{
  
public void run()
  
{
    isr 
= new InputStreamReader(bc.getInputStream());
    osw 
= new OutputStreamWriter(bc.getOutputStream());

    
try
    
{
      
while (true)
      
{
        
int ch;
        
String line = "";
        
while ((ch = isr.read()) != -1)
        
{
          line 
+= (char)ch;
          
if (ch == '\n')
          
{
            
say(line);
            line 
= "";
          
}
        
}
        
// Server connection closed
        
disconnect();
        bcg.
startServer();
      
}
    
}
    
catch (IOException ex)
    
{
      
// bc.disconnect() called
    
}
    
if (isVerbose)
      VerboseWriter.out.
println("Executor terminated");
  
}
}
// --------------- End of inner class ------------------

  
private String serverName;
  
private final String serviceName = "ChatServer";
  
private final boolean isVerbose = false;

  
private BtChatGidlet bcg;
  
private BluetoothClient bc;
  
private InputStreamReader isr;
  
private OutputStreamWriter osw;
  
private MForm form;
  
private TextField tf;
  
public TextArea ta;

  
public BtChatClient(BtChatGidlet bcg, String serverName)
  
{
    
this.bcg = bcg;
    
this.serverName = serverName;
    
if (isVerbose)
      VerboseWriter.
init("e:/debug/BtChatClient.txt");
    form 
= new MForm("BtChat Client");
    tf 
= form.addInputField("Enter line to send:""");
    ta 
= new TextArea("Response:", 5, 20);
    form.
append(ta);
    form.
addOkButton();
    form.
show();
  
}

  
public boolean connect()
  
{
    
say("Connecting to '" + serverName + "'..");
    bc 
= new BluetoothClient(serverName, serviceName);
    bc.
setVerbose(isVerbose);
    
if (!bc.connect())
    
{
      VerboseWriter.
close();
      
return false;
    
}
    
new Executor().start();
    VerboseWriter.
close();
    
return true;
  
}

  
public void sendLine()
  
{
    
String line = tf.getString();
    tf.
setString("");
    
try
    
{
      osw.
write(line, 0, line.length());
      osw.
write('\r');
      osw.
write('\n');
      osw.
flush();
    
}
    
catch (Exception ex)
    
{
      
// osw may be null
    
}
  
}

  
public void say(String line)
  
{
    ta.
append(line);
  
}

  
public void disconnect()
  
{
    
if (bc != null)
      bc.
disconnect();
  
}
}


Discussion: The program is much the same as BtClient in the introductory demonstration. We append a <cr><lf> to each line, so that the application is compatible with a J2SE version running on a desktop PC. The helper class TextArea from the Gidlet distribution is used to display an upscrolling number of lines automatically wrapped at word boundaries.

The chat server is largely inspired from BtServer in the introductory demonstration:

// BtChatServer.java

import
 java.io.*;
import
 javax.bluetooth.*;
import
 javax.microedition.lcdui.*;
import
 ch.aplu.gidlet.*;
import
 ch.aplu.bluetooth.*;

public
 class BtChatServer implements BtListener
{
  
private final String serviceName = "ChatServer";
  
private final boolean isVerbose = false;

  
private BluetoothServer bs;
  
private InputStreamReader isr;
  
private OutputStreamWriter osw;
  
private MForm form;
  
private TextField tf;
  
private TextArea ta;

  
public BtChatServer()
  
{
    
if (isVerbose)
      VerboseWriter.
init("e:/debug/BtChatServer.txt");
    form 
= new MForm("BtChat Server");
    tf 
= form.addInputField("Enter line to send:""");
    ta 
= new TextArea("Response:", 5, 20);
    form.
append(ta);
    form.
addOkButton();
    form.
show();
    bs 
= new BluetoothServer(serviceName, this, isVerbose);
    
say("Running as server");
    
say("Wait for connection..");
  
}

  
public void sendLine()
  
{
    
String line = tf.getString();
    tf.
setString("");
    
try
    
{
      osw.
write(line, 0, line.length());
      osw.
write('\r');
      osw.
write('\n');
      osw.
flush();
    
}
    
catch (Exception ex)
    
{
      
// osw may be null
    
}
  
}

  
public void notifyConnection(RemoteDevice rd, 
                               
InputStream is, OutputStream os)
  
{
    isr 
= new InputStreamReader(is);
    osw 
= new OutputStreamWriter(os);

    
try
    
{
      
say("Connected to '" + rd.getFriendlyName(false+ "'");
    
}
    
catch (Exception ex)
    
{
      
say("Exception in getFriendlyName()");
      
return;
    
}

    
try
    
{
      
while(true)
      
{
        
int ch;
        
String line = "";
        
while ((ch = isr.read()) != -1)
        
{
          line 
+= (char)ch;
          
if (ch == '\n')
          
{
            
say(line);
            line 
= "";
          
}
        
}
        
throw new IOException();
      
}
    
}
    
catch (IOException ex)
    
{
      
say("Connection lost");
      
say("Wait for a client..");
    
}
    bs.
close();
  
}

  
public void say(String line)
  
{
    ta.
append(line);
  
}

  
public void close()
  
{
    
if (bs != null)
      bs.
close();
  
}

  
public void cancel()
  
{
    
if (bs != null)
      bs.
cancel();
  
}
}

The application program has the duty to start a client or a server in a consistent way. It first asks for the partner's Bluetooth name. Then the client is started. If the partner is found, the client runs as long as the connection is established. If the connection gets lost, a server is automatically started that waits for a client to connect.

// BtChatGidlet.java

import javax.microedition.lcdui.*;
import ch.aplu.gidlet.*;
import ch.aplu.bluetooth.*;

public class BtChatGidlet extends Gidlet
{

// ------------- Inner class MyCommandListener -------------
private class MyCommandListener implements CommandListener
{
  public void commandAction(Command c, Displayable d)
  {
    if (== serverCommand)
    {
      isServer = true;
      wakeUp();
    }

    if (== okCommand)
    {
      btName = askForm.getText(tf);
      if (btName.trim().length() != 0)
      {
        StringStore store = new StringStore("chatstore");
        store.write(btName);
        wakeUp();
      }
      else
        si.setText("Illegal entry");
    }
  }
}
// ------------- End of inner class ------------------------

  private BtChatClient bcc = null;
  private BtChatServer bcs = null;
  private String btName;
  private MForm askForm;
  private StringItem si;
  private TextField tf;
  private Command okCommand = new Command("OK", Command.OK, 1);
  private Command serverCommand = 
    new Command("Server", Command.EXIT, 1);
  private boolean isServer = false;

  public void main()
  {
    askBtName();
    if (isServer)
      startServer();
    else
    {
      bcc = new BtChatClient(this, btName);
      if (!bcc.connect())
      {
        bcc = null;
        startServer();
      }
      else
        bcc.say("Connected");
    }
  }

  public void doOk()
  {
    if (bcc != null)
      bcc.sendLine();
    if (bcs != null)
      bcs.sendLine();
  }

  public void doExit()
  {
    if (bcc != null)
      bcc.disconnect();
    if (bcs != null)
    {
      bcs.close();
      bcs.cancel();
    }
    VerboseWriter.close();
    super.doExit();
  }

  public void startServer()
  {
    bcs = new BtChatServer();
  }

  private void askBtName()
  {
    String prompt = "Enter Bluetooth Name";
    String title = "BtChat (www.aplu.ch)";
    askForm = new MForm(title);
    askForm.addCommand(okCommand);
    askForm.removeExitButton();
    askForm.addCommand(serverCommand);
    askForm.setCommandListener(new MyCommandListener());
    tf = askForm.addInputField(prompt, "");
    si = askForm.addOutputField();
    StringStore store = new StringStore("chatstore");
    String content = store.read();
    if (content != null)
      tf.setString(content);
    askForm.show();
    putSleep();
  }
}


Download JAR/JAD files for installation on mobile phone. (You can install it directly on your mobile phone from http://www.aplu.ch/g.)

Discussion: This application has already a commercial touch and can be useful to communicate in a small area where voice communication is prohibited. Unfortunately some mobile phones has a very short range for Bluetooth connections.