Simple chat application using Firebase Android

    Through "Getting started with Firebase Android" post, you've got an overview and account authentication mechanism of Firebase, a mobile-backend-as-a-service which developed by Google now. With it, we do not need to care about building a backend system.

    Today, in this tutorial, I will present Firebase real-time database feature and show you how to leverage Firebase UI to create a group chat app you can share with your friends. It's going to be a very simple app with just one chat room, which is open to all users. This app will depend on Firebase authentication to manage user registration and sign in. It will also use Firebase's real-time database to store the group chat messages.
    DEMO VIDEO:

Android Studio project configuration

    Firebase now has integrated on Android Studio, so after starting a new project, to configure the project to use the Firebase platform, open the Firebase Assistant window by clicking on Tools -> Firebase, you will see this panel on right side:
    Click on "Log an Analytics event" and after that, click at "Connect to Firebase" button, your default browser will be launched and now, please login by your Google account and you will be redirect to Firebase console page:
    Make sure that you select "Create new project" in this screen (because your project is starting to develop). Once the connection is established, back to Android Studio, you will see this result:
    You've connected to Firebase and now, click at "Add Analytics to your app" button (at entry (2)), the Android Studio project is not only integrated with Firebase Analytics, it is also ready to use all other Firebase services.
    Adding Firebase UI dependency to your app/build.gradle (required) and in this project, I use some widgets of Android Support Library, so I add it's dependency, too:
compile 'com.android.support:design:23.4.0'
compile 'com.firebaseui:firebase-ui:0.6.0'

Layouts definition

    Firstly, we need create a layout for our main activity. It's simple like this:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin">

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:src="@drawable/ic_send_black_24dp"
        android:tint="@android:color/white"
        app:fabSize="mini" />

    <android.support.design.widget.TextInputLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_toLeftOf="@id/fab">

        <EditText
            android:id="@+id/input"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Input" />
    </android.support.design.widget.TextInputLayout>

    <ListView
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@id/fab"
        android:layout_alignParentTop="true"
        android:layout_marginBottom="16dp"
        android:divider="@android:color/transparent"
        android:dividerHeight="16dp"
        android:stackFromBottom="true"
        android:transcriptMode="alwaysScroll" />
</RelativeLayout>
    As you can see, this layout included:
  • A ListView which displays all chat messages.
  • A TextInputLayout which allows user type message.
  • A FloatingActionButton to push message to Firebase database and display to the ListView then.
    Now, we must create layout for each chat message, I will 2 layouts corresponding to in/out messages. Each layout has 3 TextViews which display message time, message user and message content:
item_in_message.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/bubble_in">

    <TextView
        android:id="@+id/message_user"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:text="fdsfsdf"
        android:textStyle="normal|bold" />

    <TextView
        android:id="@+id/message_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/message_user"
        android:layout_marginTop="5dp"
        android:text="dsfsdfds"
        android:textAppearance="@style/TextAppearance.AppCompat.Body1"
        android:textColor="@android:color/white"
        android:textSize="18sp" />

    <TextView
        android:id="@+id/message_time"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/message_text"
        android:text="sdfsdfsd" />
</RelativeLayout>
item_out_message.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:background="@drawable/bubble_out">

        <TextView
            android:id="@+id/message_user"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:text="fdsfsdf"
            android:textStyle="normal|bold" />

        <TextView
            android:id="@+id/message_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/message_user"
            android:textColor="@android:color/white"
            android:layout_marginTop="5dp"
            android:text="dsfsdfds"
            android:textAppearance="@style/TextAppearance.AppCompat.Body1"
            android:textSize="18sp" />

        <TextView
            android:id="@+id/message_time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/message_text"
            android:text="sdfsdfsd" />
    </RelativeLayout>
</RelativeLayout>
    NOTE: bubble_out and bubble_in are 9-patch images to create a bubble chat layout. Please read "Designing bubble chat UI" post to learn how to create it.
    We'll have this output result later:

Firebase Authenticating configuration

   In this project, only registered user can post messages. In order to allows user signup and login, please got to Firebase console page, choose your project and select "Authentication" entry on the left panel. In "Sign-In Method" tab, enabling "Email/Password"as a sign-in provider:
    Feel free to enable OAuth 2.0 sign-in providers as well. Moreover, FirebaseUI v0.6.0 seamlessly supports only Google Sign-In, Facebook Login, Twitter Auth,...:

Handle user sign in and sign up in code

    With FirebaseUI, creating those screens takes a lot less code than you might imagine. Firstly, you must check whether current Firebase user is null, you must request to opens Firebase sign-in activity. And if the current user is not null, show all old messages of this chat room to ListView. Add this code to onCreate() method of MainActivity:
        //find views by Ids
        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        final EditText input = (EditText) findViewById(R.id.input);
        listView = (ListView) findViewById(R.id.list);

        if (FirebaseAuth.getInstance().getCurrentUser() == null) {
            // Start sign in/sign up activity
            startActivityForResult(AuthUI.getInstance()
                    .createSignInIntentBuilder()
                    .build(), SIGN_IN_REQUEST_CODE);
        } else {
            // User is already signed in, show list of messages
            showAllOldMessages();
        }
    The SIGN_IN_REQUEST_CODE is an int constant.
    Now, you must override onActivityResult() to get sign-in or sign-up result. If the result's code is RESULT_OK, it means the user has signed in successfully. If so, you must display all old messages, otherwise, call finish() to close the app:
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == SIGN_IN_REQUEST_CODE) {
            if (resultCode == RESULT_OK) {
                Toast.makeText(this, "Signed in successful!", Toast.LENGTH_LONG).show();
                showAllOldMessages();
            } else {
                Toast.makeText(this, "Sign in failed, please try again later", Toast.LENGTH_LONG).show();
                finish();
            }
        }
    }

Handle Sign-out action

    Just call signOut() method of AuthUI class, you can logout user. Add this code to your MainActivity to create an option menu and handle user click "Log out" button:
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.menu_sign_out) {
            AuthUI.getInstance().signOut(this)
                    .addOnCompleteListener(new OnCompleteListener() {
                        @Override
                        public void onComplete(@NonNull Task task) {
                            Toast.makeText(MainActivity.this, "You have logged out!", Toast.LENGTH_SHORT).show();
                            finish();
                        }
                    });
        }
        return true;
    }
And this is the menu (xml) file:
res/menu/main.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@+id/menu_sign_out"
        app:showAsAction="always"
        android:icon="@drawable/logout"
        android:title="Sign out" />
</menu>

Creating messages adapter

    In order to store the chat messages in the Firebase real-time database, you must create a model for them. It will be simple like this:
ChatMessage.java
package info.devexchanges.firebasechatapplication;

import java.util.Date;

public class ChatMessage {
    private String messageText;
    private String messageUser;
    private String messageUserId;
    private long messageTime;

    public ChatMessage(String messageText, String messageUser, String messageUserId) {
        this.messageText = messageText;
        this.messageUser = messageUser;
        messageTime = new Date().getTime();
        this.messageUserId = messageUserId;
    }

    public ChatMessage(){

    }

    public String getMessageUserId() {
        return messageUserId;
    }

    public void setMessageUserId(String messageUserId) {
        this.messageUserId = messageUserId;
    }

    public String getMessageText() {
        return messageText;
    }

    public void setMessageText(String messageText) {
        this.messageText = messageText;
    }

    public String getMessageUser() {
        return messageUser;
    }

    public void setMessageUser(String messageUser) {
        this.messageUser = messageUser;
    }

    public long getMessageTime() {
        return messageTime;
    }

    public void setMessageTime(long messageTime) {
        this.messageTime = messageTime;
    }
}
    FirebaseUI has a very handy class called FirebaseListAdapter, so we need write a subclass of it to make a custom ListView adapter. The most important method that you must override is populateView() which use for used to populate the views of each list item:

package info.devexchanges.firebasechatapplication;

import android.text.format.DateFormat;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.firebase.ui.database.FirebaseListAdapter;
import com.google.firebase.database.DatabaseReference;

public class MessageAdapter extends FirebaseListAdapter<ChatMessage> {

    private MainActivity activity;

    public MessageAdapter(MainActivity activity, Class<ChatMessage> modelClass, int modelLayout, DatabaseReference ref) {
        super(activity, modelClass, modelLayout, ref);
        this.activity = activity;
    }

    @Override
    protected void populateView(View v, ChatMessage model, int position) {
        TextView messageText = (TextView) v.findViewById(R.id.message_text);
        TextView messageUser = (TextView) v.findViewById(R.id.message_user);
        TextView messageTime = (TextView) v.findViewById(R.id.message_time);

        messageText.setText(model.getMessageText());
        messageUser.setText(model.getMessageUser());

        // Format the date before showing it
        messageTime.setText(DateFormat.format("dd-MM-yyyy (HH:mm:ss)", model.getMessageTime()));
    }
}
    The most important part of building this adapter is creating 2 layouts corresponding to 2 types of messages (in and out). So you must override getView(), getViewType() and getViewTypeCount() to complete this adapter class:
    @Override
    public View getView(int position, View view, ViewGroup viewGroup) {
        ChatMessage chatMessage = getItem(position);
        if (chatMessage.getMessageUserId().equals(activity.getLoggedInUserName()))
            view = activity.getLayoutInflater().inflate(R.layout.item_out_message, viewGroup, false);
        else
            view = activity.getLayoutInflater().inflate(R.layout.item_in_message, viewGroup, false);

        //generating view
        populateView(view, chatMessage, position);

        return view;
    }

    @Override
    public int getViewTypeCount() {
        // return the total number of view types. this value should never change
        // at runtime
        return 2;
    }

    @Override
    public int getItemViewType(int position) {
        // return a value between 0 and (getViewTypeCount - 1)
        return position % 2;
    }
    Back to the MainActivity code, rewrite showAllOldMessages() method to set ListView adapter and load all messages from server:
private String loggedInUserName = "";
private void showAllOldMessages() {
        loggedInUserName = FirebaseAuth.getInstance().getCurrentUser().getUid();
        Log.d("Main", "user id: " + loggedInUserName);

        adapter = new MessageAdapter(this, ChatMessage.class, R.layout.item_in_message,
                FirebaseDatabase.getInstance().getReference());
        listView.setAdapter(adapter);
    }

public String getLoggedInUserName() {
        return loggedInUserName;
    }

Post a Chat Message

    When click on the FloatingActionButton, the EditText content will be posted to Firebase server. Add this code in onCreate():
fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (input.getText().toString().trim().equals("")) {
                    Toast.makeText(MainActivity.this, "Please enter some texts!", Toast.LENGTH_SHORT).show();
                } else {
                    FirebaseDatabase.getInstance()
                            .getReference()
                            .push()
                            .setValue(new ChatMessage(input.getText().toString(),
                                    FirebaseAuth.getInstance().getCurrentUser().getDisplayName(),
                                    FirebaseAuth.getInstance().getCurrentUser().getUid())
                            );
                    input.setText("");
                }
            }
        });
    Data in the Firebase real-time database is always stored as key-value pairs. However, if you observe the code above, you'll see that we're calling setValue() without specifying any key. That's allowed only because the call to the setValue() method is preceded by a call to the push() method, which automatically generates a new key.

Running application

    When running the app, when user has not logged in yet, the signup screen of Firebase was launched, you will put your email here:
    If your mail is not available in Firebase database, "Create an account" page will displayed, you must enter your name and password here:
     Otherwise, if your entered mail is available on the database, you only need input your password at "Sign in" screen:
    After Firebase complete authenticating your information, the app "main screen" will be displayed - you are entered the "chat room" and all old messages will be loaded:
    If you go to your app page on Firebase console, choose "Database" entry, you'll see this result:

Conclusions

    With Firebase real-time database, we can build a simple chat application without any single line of server-side code. Through this post, I hope you understanding the real-time database feature of Firebase. Further, please read these posts to learn more about Firebase:
Simple Bluetooth chat application in Android

Simple Bluetooth chat application in Android

    Firstly, take a glance at Bluetooth technology defining on Wikipedia:
Bluetooth is a wireless technology standard for exchanging data over short distances (using short-wavelength UHF radio waves in the ISM band from 2.4 to 2.485 GHz[4]) from fixed and mobile devices, and building personal area networks (PANs). Invented by telecom vendor Ericsson in 1994,[5] it was originally conceived as a wireless alternative to RS-232 data cables. It can connect several devices, overcoming problems of synchronization.
    According to this, we can "build" a local are network (LAN) by connecting devices over Bluetooth. The Android platform includes support for the Bluetooth network stack, which allows a device to wirelessly exchange data with other Bluetooth devices. The application framework provides access to the Bluetooth functionality through the Android Bluetooth APIs. These APIs let applications wirelessly connect to other Bluetooth devices, enabling point-to-point and multipoint wireless features so we absolutely able to transferring data to other devices in the network circle.
    Now, in this tutorial, I will note some important works to make a simple chat application which allows two Android devices to carry out two-way text chat over Bluetooth. If you only need full application code, please go to end of this post!

Requesting Bluetooth permissions

    In order to use Bluetooth service, please add BLUETOOTH permission to your AndroidManifest.xml. Moreover, because we need to discover available devices nearby later, BLUETOOTH_ADMIN permission should be required, too:
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

Checking if device supports Bluetooth

    Now to check whether Bluetooth is supported on device or not, we use object of BluetoothAdapter class. If getDefaultAdapter() return null, your device not supports Bluetooth. This is the "check code":
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if (bluetoothAdapter == null) {
            Toast.makeText(this, "Bluetooth is not available!", Toast.LENGTH_SHORT).show();
            finish(); //automatic close app if Bluetooth service is not available!
        }

Check if Bluetooth is Enabled

    The 2nd important works is check if your device is enabled Bluetooth. If not, request to turn it on:
if (!bluetoothAdapter.isEnabled()) {
            Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableIntent, REQUEST_ENABLE_BLUETOOTH);
        }
    You should put this code in onStart() to ensure that your app always check the connection when it launched! The "enabling request" dialog may be like this:

Discovering Bluetooth devices

    In android, available devices is not discoverable by default. To scanning them, use startDiscovery() method of BluetoothAdapter class. The activity which starts scanning must register a BroadCastReceiver with BluetoothDevice.ACTION_FOUND action. After completing discovery, system will broadcast BluetoothDevice.ACTION_FOUND Intent. This Intent contains extra fields EXTRA_DEVICE and EXTRA_CLASS, representing a BluetoothDevice and a BluetoothClass, respectively. In this application, I will add detected devices to an ArrayAdapter and show by ListView:
if (bluetoothAdapter.isDiscovering()) {
            bluetoothAdapter.cancelDiscovery();
        }
        bluetoothAdapter.startDiscovery();

        // Register for broadcasts when a device is discovered
        IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
        registerReceiver(discoveryFinishReceiver, filter);

        // Register for broadcasts when discovery has finished
        filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        registerReceiver(discoveryFinishReceiver, filter);
The BroadcastReceiver variable seem like this:
private final BroadcastReceiver discoveryFinishReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();

            if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
                    discoveredDevicesAdapter.add(device.getName() + "\n" + device.getAddress());
                }
            } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
                if (discoveredDevicesAdapter.getCount() == 0) {
                    discoveredDevicesAdapter.add(getString(R.string.none_found));
                }
            }
        }
    };

Listing paired devices

    Moreover, your devices can be connected to some other devices before, so you can listing them by call getBondedDevices():
 bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();

        // If there are paired devices, add each one to the ArrayAdapter
        if (pairedDevices.size() > 0) {
            for (BluetoothDevice device : pairedDevices) {
                pairedDevicesAdapter.add(device.getName() + "\n" + device.getAddress());
            }
        } else {
            pairedDevicesAdapter.add(getString(R.string.none_paired));
        }
    In this sample application, I show a Dialog which contains 2 ListViews of paired devices and discovered devices and this result look like this:

Connecting to a device

    To connect two devices, we must implement server side and client side mechanism. One device shall open the server socket and another should initiate the connection (the remain device is a client device).
    With connection as server:
  • Initializing an instance of BluetoothServerSocket by calling the listenUsingRfcommWithServiceRecord() method.
  • Listening for connection requests by calling accept()
  • Release server socket by calling close()
private class AcceptThread extends Thread {
        private final BluetoothServerSocket serverSocket;

        public AcceptThread() {
            BluetoothServerSocket tmp = null;
            try {
                tmp = bluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord(APP_NAME, MY_UUID);
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            serverSocket = tmp;
        }

        public void run() {
            setName("AcceptThread");
            BluetoothSocket socket;
            while (state != STATE_CONNECTED) {
                try {
                    socket = serverSocket.accept();
                } catch (IOException e) {
                    break;
                }

                // If a connection was accepted
                if (socket != null) {
                    synchronized (ChatController.this) {
                        switch (state) {
                            case STATE_LISTEN:
                            case STATE_CONNECTING:
                                // start the connected thread.
                                connected(socket, socket.getRemoteDevice());
                                break;
                            case STATE_NONE:
                            case STATE_CONNECTED:
                                // Either not ready or already connected. Terminate
                                // new socket.
                                try {
                                    socket.close();
                                } catch (IOException e) {
                                }
                                break;
                        }
                    }
                }
            }
        }
    As connected as a client:
  • Create an instance of BluetoothSocket by calling createRfcommSocketToServiceRecord(UUID) on BluetoothDevice object.
  • Initializing the connection by calling connect().
private class ConnectThread extends Thread {
        private final BluetoothSocket socket;
        private final BluetoothDevice device;

        public ConnectThread(BluetoothDevice device) {
            this.device = device;
            BluetoothSocket tmp = null;
            try {
                tmp = device.createInsecureRfcommSocketToServiceRecord(MY_UUID);
            } catch (IOException e) {
                e.printStackTrace();
            }
            socket = tmp;
        }

        public void run() {
            setName("ConnectThread");

            // Always cancel discovery because it will slow down a connection
            bluetoothAdapter.cancelDiscovery();

            // Make a connection to the BluetoothSocket
            try {
                socket.connect();
            } catch (IOException e) {
                try {
                    socket.close();
                } catch (IOException e2) {
                }
                connectionFailed();
                return;
            }

            // Reset the ConnectThread because we're done
            synchronized (ChatController.this) {
                connectThread = null;
            }

            // Start the connected thread
            connected(socket, device);
        }

        public void cancel() {
            try {
                socket.close();
            } catch (IOException e) {
            }
        }
    }
    This is definition of My_UUID constant:
private static final UUID MY_UUID = UUID.fromString("8ce255c0-200a-11e0-ac64-0800200c9a66");

Read and write data (text messages)

    Of course, after establishing connection successfully, we'll do the most important work of a chat application: send/receive text messages. Now, each device has a connected BluetoothSocket, both of them can read and write data to the streams using read(byte[]) and write(byte[]):
private class ReadWriteThread extends Thread {
        private final BluetoothSocket bluetoothSocket;
        private final InputStream inputStream;
        private final OutputStream outputStream;

        public ReadWriteThread(BluetoothSocket socket) {
            this.bluetoothSocket = socket;
            InputStream tmpIn = null;
            OutputStream tmpOut = null;

            try {
                tmpIn = socket.getInputStream();
                tmpOut = socket.getOutputStream();
            } catch (IOException e) {
            }

            inputStream = tmpIn;
            outputStream = tmpOut;
        }

        public void run() {
            byte[] buffer = new byte[1024];
            int bytes;

            // Keep listening to the InputStream
            while (true) {
                try {
                    // Read from the InputStream
                    bytes = inputStream.read(buffer);

                    // Send the obtained bytes to the UI Activity
                    handler.obtainMessage(MainActivity.MESSAGE_READ, bytes, -1,
                            buffer).sendToTarget();
                } catch (IOException e) {
                    connectionLost();
                    // Start the service over to restart listening mode
                    ChatController.this.start();
                    break;
                }
            }
        }

        // write to OutputStream
        public void write(byte[] buffer) {
            try {
                outputStream.write(buffer);
                handler.obtainMessage(MainActivity.MESSAGE_WRITE, -1, -1,
                        buffer).sendToTarget();
            } catch (IOException e) {
            }
        }

        public void cancel() {
            try {
                bluetoothSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
NOTE: In my sample project, I put AcceptThread, ConnectThread and ReadWriteThread classes into a class named ChatController.

Application output

    Some screenshots of this simple chat app:
When you have connected to a dive
Sending/receiving messages with connected device
When user close app on other device, the connection was lost

Conclusions

    I have presented some important works in developing a simple bluetooth chat application, hope this is helpful with your work! To make a better chat messages interface, please a glance in "Design Chat bubble UI" post. Moreover, visit these official document pages to deep understanding about Bluetooth connection in Android: