Monday, August 18, 2014

Simple Android Chat Application, client side.

It's client side of Simple Android Chat Application, for the server side, refer last post "Implement simple Android Chat Application, server side".


MainActivity.java
package com.example.androidchatclient;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;

import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends ActionBarActivity {
 
 static final int SocketServerPORT = 8080;
 
 LinearLayout loginPanel, chatPanel;

 EditText editTextUserName, editTextAddress;
 Button buttonConnect;
 TextView chatMsg, textPort;
 
 EditText editTextSay;
 Button buttonSend;
 Button buttonDisconnect;

 String msgLog = "";
 
 ChatClientThread chatClientThread = null;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  loginPanel = (LinearLayout)findViewById(R.id.loginpanel);
  chatPanel = (LinearLayout)findViewById(R.id.chatpanel);

  editTextUserName = (EditText) findViewById(R.id.username);
  editTextAddress = (EditText) findViewById(R.id.address);
  textPort = (TextView) findViewById(R.id.port);
  textPort.setText("port: " + SocketServerPORT);
  buttonConnect = (Button) findViewById(R.id.connect);
  buttonDisconnect = (Button) findViewById(R.id.disconnect);
  chatMsg = (TextView) findViewById(R.id.chatmsg);

  buttonConnect.setOnClickListener(buttonConnectOnClickListener);
  buttonDisconnect.setOnClickListener(buttonDisconnectOnClickListener);
  
  editTextSay = (EditText)findViewById(R.id.say);
  buttonSend = (Button)findViewById(R.id.send);
  
  buttonSend.setOnClickListener(buttonSendOnClickListener);
 }
 
 OnClickListener buttonDisconnectOnClickListener = new OnClickListener() {

  @Override
  public void onClick(View v) {
   if(chatClientThread==null){
    return;
   }
   chatClientThread.disconnect();
  }
  
 };
 
 OnClickListener buttonSendOnClickListener = new OnClickListener() {

  @Override
  public void onClick(View v) {
   if (editTextSay.getText().toString().equals("")) {
    return;
   }
   
   if(chatClientThread==null){
    return;
   }
   
   chatClientThread.sendMsg(editTextSay.getText().toString() + "\n");
  }
  
 };

 OnClickListener buttonConnectOnClickListener = new OnClickListener() {

  @Override
  public void onClick(View v) {
   String textUserName = editTextUserName.getText().toString();
   if (textUserName.equals("")) {
    Toast.makeText(MainActivity.this, "Enter User Name",
      Toast.LENGTH_LONG).show();
    return;
   }

   String textAddress = editTextAddress.getText().toString();
   if (textAddress.equals("")) {
    Toast.makeText(MainActivity.this, "Enter Addresse",
      Toast.LENGTH_LONG).show();
    return;
   }
   
   msgLog = "";
   chatMsg.setText(msgLog);
   loginPanel.setVisibility(View.GONE);
   chatPanel.setVisibility(View.VISIBLE);

   chatClientThread = new ChatClientThread(
     textUserName, textAddress, SocketServerPORT);
   chatClientThread.start();
  }

 };

 private class ChatClientThread extends Thread {

  String name;
  String dstAddress;
  int dstPort;
  
  String msgToSend = "";
  boolean goOut = false;

  ChatClientThread(String name, String address, int port) {
   this.name = name;
   dstAddress = address;
   dstPort = port;
  }

  @Override
  public void run() {
   Socket socket = null;
   DataOutputStream dataOutputStream = null;
   DataInputStream dataInputStream = null;

   try {
    socket = new Socket(dstAddress, dstPort);
    dataOutputStream = new DataOutputStream(
      socket.getOutputStream());
    dataInputStream = new DataInputStream(socket.getInputStream());
    dataOutputStream.writeUTF(name);
    dataOutputStream.flush();

    while (!goOut) {
     if (dataInputStream.available() > 0) {
      msgLog += dataInputStream.readUTF();

      MainActivity.this.runOnUiThread(new Runnable() {

       @Override
       public void run() {
        chatMsg.setText(msgLog);
       }
      });
     }
     
     if(!msgToSend.equals("")){
      dataOutputStream.writeUTF(msgToSend);
      dataOutputStream.flush();
      msgToSend = "";
     }
    }

   } catch (UnknownHostException e) {
    e.printStackTrace();
    final String eString = e.toString();
    MainActivity.this.runOnUiThread(new Runnable() {

     @Override
     public void run() {
      Toast.makeText(MainActivity.this, eString, Toast.LENGTH_LONG).show();
     }
     
    });
   } catch (IOException e) {
    e.printStackTrace();
    final String eString = e.toString();
    MainActivity.this.runOnUiThread(new Runnable() {

     @Override
     public void run() {
      Toast.makeText(MainActivity.this, eString, Toast.LENGTH_LONG).show();
     }
     
    });
   } finally {
    if (socket != null) {
     try {
      socket.close();
     } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     }
    }

    if (dataOutputStream != null) {
     try {
      dataOutputStream.close();
     } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     }
    }

    if (dataInputStream != null) {
     try {
      dataInputStream.close();
     } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     }
    }

    MainActivity.this.runOnUiThread(new Runnable() {

     @Override
     public void run() {
      loginPanel.setVisibility(View.VISIBLE);
      chatPanel.setVisibility(View.GONE);
     }
     
    });
   }

  }
  
  private void sendMsg(String msg){
   msgToSend = msg;
  }
  
  private void disconnect(){
   goOut = true;
  }
 }

}

/res/layout/activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Char Client"
        android:textStyle="bold" />

    <LinearLayout
        android:id="@+id/loginpanel"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:visibility="visible" >

        <EditText
            android:id="@+id/username"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="User name" />

        <EditText
            android:id="@+id/address"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="dstAddress" />

        <TextView
            android:id="@+id/port"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <Button
            android:id="@+id/connect"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Connect..." />
    </LinearLayout>

 <LinearLayout
        android:id="@+id/chatpanel"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:visibility="gone" >
        
        <EditText
            android:id="@+id/say"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Say something" />
     
        <Button
            android:id="@+id/send"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Send" />
        
        <Button
            android:id="@+id/disconnect"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Disconnect" />

        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent" >

            <TextView
                android:id="@+id/chatmsg"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
        </ScrollView>
    </LinearLayout>

</LinearLayout>

Permission of "android.permission.INTERNET" is needed in AndroidManifest.xml.

download filesDownload the files.

26 comments:

Unknown said...

So good

shubham said...

sir i am new to to wifi api ..i could not download the files attached .so i copy pasted the code and edited the manifest file .and copied the appcompat file to the project .but it is stopping on startup saying in logcat NoClassDefFound..Please guide me

Ajay Kumar Dutta said...

Can't connect to server from another device, am i need to port forward or something else?

Unknown said...

I am not sure but I think when client disconnect from the application, Toast that writes "removed" does not seem immediately.It is seen after a new client connects and sends a message.How can i handle this? Thanks for the answers.

Anonymous said...

Hi good sir, how come you don't get a "CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views" error?
It seems that you are calling chatMsg.setText from within the clientChatThread, but chatMsg was made in the MainActivity thread. Intuitively I would do this as well but I've been getting this error, do you have any work-arounds to this error? Thanks1

Erik said...

hello Anonymous,

To prevent "CalledFromWrongThreadException...":

MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
chatMsg.setText(msgLog);
}
});

This will create a Runnable to run in ui thread.

Unknown said...

Sir how we can chat with individual in different Router

Unknown said...

thank for this application ,but i want know if i can create other ChatRoom or GroupChat in this app

Erik said...

I think it is depends on you.

Unknown said...

sir
what is the username and destAddress to connect

Erik said...

hello raju mahato,
username is any name you choice.
destAddress is the IP address of the server, it will be shown in Server side app.

Unknown said...

tks tks tks

kartzy said...

Firstly, Thats a very nice code :)
Can you plz guide me as to also implement small files transfer in this application?? I tried going through a lot of stuff like xmpp etc but is there something more simpler?

Unknown said...

Dear I need such type of communication as between client and server as in
http://android-er.blogspot.com/2014/08/implement-simple-android-chat.html
presented but using 3G/4G services.

What you want to suggest me, type of hint/method to do to achieve this.

Unknown said...

Is it Tcp application?

Unknown said...

how to prevent java.net.socketException

Unknown said...

awesome & loved it, tutorial is working finely

Unknown said...

Hi, this is great, but may i ask how to make the text to not repeat if i send, i mean i want to replace the current text with the new one sent from server or also client,

i believe we need to change something here;

while (!goOut) {
if (dataInputStream.available() >= 0) {
msgLog += dataInputStream.readUTF();

MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
chatMsg.setText(msgLog);
}
});
}

}

Erik said...

Hello Ezra Alvaro Manuel,

It's a old exercise, i also forgot the details.

The point is what's new or old. For human understanding, client enter text "abc" then send, then enter "cde" then send. "abc" is old, "cde" is new.

What the machine view (in my understanding), it's "a", "b", "c", "d", "e", "f". It don't know when you click the send button. And the code if(dataInputStream.available() > 0){} will not handle it. May be it will receive "a", "bcd", "ef", or "abcd", "e", "f", no guarantee.

So I will suggest to insert some signature (such as "NL"(new line), "CR"(carriage return)) in the end of the text in client side. Such that the service side can check the signature to clear msgLog.

hope can help.

Anonymous said...

HY WHERE CAN I RECEIVE THE MESSAGES LIKE SHOWN ATT THE TABLETTE

Erik said...

hello Anonymous,

Refer "Implement simple Android Chat Application, server side (http://android-er.blogspot.com/2014/08/implement-simple-android-chat.html)"

Unknown said...

I need permission to download the files. Can I please get dot compliance permission?

Erik said...

Hello Isha Sinha,

??? Already set public, no permission needed.

Or you try to download here (https://sites.google.com/site/androidexercise2/download/AndroidChatClient_20140818a.zip) to by-pass the adv. page.

Anonymous said...

Hello,

I can't receive data from server? I am using an another server, python server.

Can you give any clue? Thanks

Unknown said...

awesome.. so useful. helped it alot. thank u

Unknown said...

awesome work.will u please help me I want to send messages from the server also.is it possible???