Using Firebase Database to create a chat application in Android : Part VI

in utopian-io •  7 years ago  (edited)

What will I learn?

  • Defining the structure of our chat messages in firebase database
  • Creating an adapter to inflate two layouts for single recycler view

Requirement

  • A PC/laptop with any Operating system such as Linux, Mac OSX, Windows OS
  • Preinstalled Android Studio, Android SDK and JDK

Note: This tutorial is performed in Android Studio on the laptop with Windows 10 Home, 64 bit OS

Difficulty

Anybody with basic knowledge of Android can grasp the tutorial

Tutorial Content

In the previous tutorial we learned to send the user list to our fragment from our MainActivity, we learned to reuse the same fragment and we displayed the list of total user from our firebase database n our “Users” tab fragment. Today we will be learning to create chat activity and chat viewholder to let our users give chat interface. Before diving into creating an interface for our application let us first see how will we be creating a database for storing our messages. It will look something like this.

Here we have a main node called chat_room which will contain the list of chat rooms that will be created as users start chatting. In order to create a chat room, we will be requiring the id of sender and receiver so that we can store the message between them two uniquely. A user can send the message to multiple users so in order to extract the message between only two of them we have to mix the id’s of sender and receiver and push the message uniquely under the key. Here we have made the chat room id by adding ‘_’ between senderId and receiverId. Here room id can be created in two ways that are:
• senderId_receiverId
• receiverId_senderId

While creating chat room in this order we have to check if the room id already exists or not. If the room id exists in any of the way (senderId_receiverId or receiverId_senderId) then we have to push the message inside this roomId and if the roomId doesn’t exist then we will be creating a new roomId and start pushing in that particular id. While pushing each message we will be pushing message in ‘message’ key receiver email in ‘receiver’ key receiver user id in ‘receiverUid’ sender email in ‘sender’ key, sender user id in ‘senderUid’ key and finally, the message sent date and time in ‘timestapm’ key. We have to push each message uniquely so that the message doesn’t overwrite so each message has been bushed under a unique key. I think you have grasped the concept of how our database looks like so now let us create chatting interface.

We will be creating all the attributes that we discussed above in a class named Chat.

So Chat.java will look like this:

public class Chat {
    public String sender;
    public String receiver;
    public String senderUid;
    public String receiverUid;
    public String message;
    public long timestamp;

    public Chat(){
        
    }
    
    public Chat(String sender, String receiver, String senderUid, String receiverUid, String message, long timestamp) {
        this.sender = sender;
        this.receiver = receiver;
        this.senderUid = senderUid;
        this.receiverUid = receiverUid;
        this.message = message;
        this.timestamp = timestamp;
    }
}

In order to provide chatting interface to our user let us create a new activity and name it ChatActivity.java

Remove the FloatingActionButton from activity_chat.xml and open content_chat.xml. and write the following code.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.programminghub.simplechat.ChatActivity"
    tools:showIn="@layout/activity_chat">


        <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:app="http://schemas.android.com/apk/res-auto"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            >

            <FrameLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_above="@+id/send_message_layout">

                <android.support.v7.widget.RecyclerView
                    android:id="@+id/recycler_view_chat"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"

                    app:layoutManager="LinearLayoutManager" />

                <TextView
                    android:id="@+id/no_chats_avilable"
                    android:visibility="invisible"
                    android:padding="@dimen/padding_avg"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center"
                    android:text="@string/no_chats_available"
                    android:textColor="@color/black"
                    android:textSize="@dimen/text_size_extra_large"/>
            </FrameLayout>

            <LinearLayout
                android:layout_alignParentBottom="true"
                android:orientation="horizontal"
                android:layout_width="match_parent"
                android:id="@+id/send_message_layout"
                android:layout_height="wrap_content">

                <EditText
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:inputType="textPersonName"
                    android:ems="10"
                    android:id="@+id/edit_text_message"
                    android:hint="Type Message to Send" />

                <ImageView
                    android:layout_width="50dp"
                    android:layout_height="50dp"
                    android:id="@+id/image_message_send"
                    android:src="@drawable/gallery"
                    android:layout_weight="0.1" />

                <ImageButton
                    android:layout_width="50dp"
                    android:layout_height="50dp"
                    android:id="@+id/text_message_send"
                    android:src="@drawable/send"
                    android:layout_weight="0.1" />
            </LinearLayout>

        </RelativeLayout>


</LinearLayout>

It consists of a RecyclerView to display the list of chats between the user and a Textview which will be visible if the two uses haven’t chatted yet. It also contains an EditText to allow the user to send the message, ImageView to allow the user to select the image to send and finally an ImageButton to send the message when a user presses the send button.

As we have a RecyclerView in order to display the chats of the user we will need chat adapter and view to display the individual chat message. Making ChatAdapter is a bit complicated in this scenario as we have to display the message sent by us on the right-hand side and we have to display the message received on the left-hand side. So we will be needing two layouts to display messages at left and right.

Now let us make a new class and name it ChatAdapter

Now we will be needing two layouts to display the message on the left and right side. So let us create a layout.

We will name the layout item_chat_mine.xml. This layout will be used to display message sent by us.
Now let us make another layout named item_chat_other.xml which will be used to display message received.

Now open item_chat_mine.xml and make the view like this

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="?android:attr/selectableItemBackground"
    android:clickable="true"
    android:gravity="center_vertical"
    android:orientation="horizontal"
    android:paddingBottom="@dimen/padding_small"
    android:paddingEnd="@dimen/padding_avg"
    android:paddingStart="@dimen/padding_avg"
    android:paddingTop="@dimen/padding_small">

    <TextView
        android:id="@+id/text_view_chat_message"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="@dimen/margin_huge"
        android:layout_toStartOf="@+id/text_view_user_alphabet"
        android:background="@drawable/chat_rounded_rect_bg"
        android:padding="10dp"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:textColor="@color/grey_100"
        tools:text="Noice man !"
        android:layout_marginLeft="@dimen/margin_huge"
        android:layout_toLeftOf="@+id/text_view_user_alphabet" />

    <TextView
        android:id="@+id/text_view_user_alphabet"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentEnd="true"
        android:layout_marginStart="@dimen/margin_avg"
        android:background="@drawable/circle_accent"
        android:gravity="center"
        android:maxEms="1"
        android:textAllCaps="true"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:textColor="@color/grey_100"
        android:textStyle="bold"
        tools:text="a"
        android:layout_alignParentRight="true"
        android:layout_marginLeft="@dimen/margin_avg" />


    <TextView
        android:layout_below="@+id/text_view_chat_message"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:text="Medium Text"
        android:id="@+id/senderMsgTime"
        android:layout_gravity="right" />

</RelativeLayout>

Here we have used three TextView to display the message, message sent date and the alphabet of the sender. Here we have aligned layout gravity to the right so that the message sent by us will appear at the right-hand side of the view.
Now open item_chat_other.xml and make a view like this:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="?android:attr/selectableItemBackground"
    android:clickable="true"
    android:orientation="horizontal"
    android:paddingBottom="@dimen/padding_small"
    android:paddingEnd="@dimen/padding_avg"
    android:paddingStart="@dimen/padding_avg"
    android:paddingTop="@dimen/padding_small">

    <TextView
        android:id="@+id/text_view_user_alphabet"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/circle_accent"
        android:gravity="center"
        android:maxEms="1"
        android:textAllCaps="true"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:textColor="@color/grey_100"
        android:textStyle="bold"
        tools:text="a" />

    <android.support.v4.widget.Space
        android:layout_width="@dimen/space_avg"
        android:layout_height="wrap_content"
        android:id="@+id/space"
        android:layout_toRightOf="@+id/text_view_user_alphabet"/>

    <TextView
        android:layout_toRightOf="@+id/space"
        android:id="@+id/text_view_chat_message"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="@dimen/margin_huge"
        android:background="@drawable/chat_rounded_rect_bg"
        android:padding="10dp"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:textColor="@color/grey_100"
        tools:text="Hello mate ! \ntest"
        android:layout_marginRight="@dimen/margin_huge" />
    
    <TextView
        android:layout_below="@+id/text_view_chat_message"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"

        android:textAppearance="?android:attr/textAppearanceMedium"
        android:text="Medium Text"
        android:id="@+id/receiverMsgTime"
        android:layout_gravity="right" />
</RelativeLayout>

Here also we have three TextView to display the sender message, sender message sent time and sender first alphabet. Here we have aligned the layout to the left-hand side so that the message sent by the sender will appear on the left-hand side.
Now as we have made the view its time to write the corresponding java class so open ChatAdapter.java

In another adapter, we hade made only one ViewHolder to hold our view but in this adapter, we will require two Viewholder named MyChatViewHolder and OtherChatVIewHolder. MyChatViewHolder inflates item_chat_mine.xml layout and displays message sent by us and OtherChatViewHolder inflate item_chat_oter.xml layout and displays message sent by the sender. The ViewHolder’s will look something like this:

private static class MyChatViewHolder extends RecyclerView.ViewHolder {
    private TextView txtChatMessage, txtUserAlphabet;
    private TextView senderMsgTime;

    public MyChatViewHolder(View itemView) {
        super(itemView);
        txtChatMessage = (TextView) itemView.findViewById(R.id.text_view_chat_message);
        txtUserAlphabet = (TextView) itemView.findViewById(R.id.text_view_user_alphabet);
        senderMsgTime=(TextView) itemView.findViewById(R.id.senderMsgTime);
    }
}

private static class OtherChatViewHolder extends RecyclerView.ViewHolder {
    private TextView txtChatMessage, txtUserAlphabet;
    private TextView receiverMsgTime;




    public OtherChatViewHolder(View itemView) {
        super(itemView);
        txtChatMessage = (TextView) itemView.findViewById(R.id.text_view_chat_message);
        txtUserAlphabet = (TextView) itemView.findViewById(R.id.text_view_user_alphabet);
        receiverMsgTime=(TextView) itemView.findViewById(R.id.receiverMsgTime);


    }

We should also implement getItemViewType() method which decides which view is to be inflated. Here VIEW_TYPE_MINE and VIEW_TYPE_OTHER are simply Integer values we have defined. If the message is sent to me then it will return VIEW_TYPE_ME and if the message is received then VIEW_TYPE_OTHER is returned.

@Override
public int getItemViewType(int position) {
    if (TextUtils.equals(mChats.get(position).senderUid,
        FirebaseAuth.getInstance().getCurrentUser().getUid())
){
        return VIEW_TYPE_ME;
    } else {
        return VIEW_TYPE_OTHER;
    }
}

Here mChats is the list of chat message and if the senderUid equal the userId of the currently logged in user then that particular message will be the message sent by me(logged in user) which will then return VIEW_TYPE_ME and it will display message in right-hand side and will inflate item_chat_mine.xml and if the senderUid is not equal to the userId of currently logged in user then that message will be message sent by other user so it will return VIEW_TYPE_OTHER which will display message on left-hand side and it will inflate item_chat_other.xml.
Our onCreateViewHolder will look something like this.

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
    RecyclerView.ViewHolder viewHolder = null;
    switch (viewType) {
        case VIEW_TYPE_ME:
            View viewChatMine = layoutInflater.inflate(R.layout.item_chat_mine, parent, false);
            viewHolder = new MyChatViewHolder(viewChatMine);
            break;
        case VIEW_TYPE_OTHER:
            View viewChatOther = layoutInflater.inflate(R.layout.item_chat_other, parent, false);
            viewHolder = new OtherChatViewHolder(viewChatOther);
            break;
    }
    return viewHolder;
}

Here we have implemented a switch case. If the viewType is VIEW_TYPE_ME then it inflates item_chat_mine and if the viewType is VIEW_CHAT_OTHER then we will be inflating the item_chat_other. And our onBindViewHolder will look like this:

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    if (TextUtils.equals(mChats.get(position).senderUid,
            FirebaseAuth.getInstance().getCurrentUser().getUid())) {
        configureMyChatViewHolder((MyChatViewHolder) holder, position);
    } else {
        configureOtherChatViewHolder((OtherChatViewHolder) holder, position);
    }
}

Here if the senderUid is equal to the userId of the of the user logged in then we have called a method called configureMyChatViewHolder() which will configure our view that is set values to our respective view and if the userId doesn’t matches then configureOtherChatViewHolder() method is called which will set value to it’s view accordingly.

private void configureMyChatViewHolder(final MyChatViewHolder myChatViewHolder, int position) {
    Chat chat = mChats.get(position);
    SimpleDateFormat sfd = new SimpleDateFormat("hh:mm a");
    String date=sfd.format(new Date(chat.timestamp).getTime());
    myChatViewHolder.senderMsgTime.setText(date);


}

private void configureOtherChatViewHolder(final OtherChatViewHolder otherChatViewHolder, int position) {
    final Chat chat = mChats.get(position);
    SimpleDateFormat sfd = new SimpleDateFormat("hh:mm a");
    String date=sfd.format(new Date(chat.timestamp).getTime());
    otherChatViewHolder.receiverMsgTime.setText(date);

}

Our final ChatAdapter.java looks like this:

public class ChatAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private static final int VIEW_TYPE_ME = 1;
    private static final int VIEW_TYPE_OTHER = 2;
    List<Chat> mChats;

    public ChatAdapter(List<Chat> mChats) {
        this.mChats = mChats;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
        RecyclerView.ViewHolder viewHolder = null;
        switch (viewType) {
            case VIEW_TYPE_ME:
                View viewChatMine = layoutInflater.inflate(R.layout.item_chat_mine, parent, false);
                viewHolder = new MyChatViewHolder(viewChatMine);
                break;
            case VIEW_TYPE_OTHER:
                View viewChatOther = layoutInflater.inflate(R.layout.item_chat_other, parent, false);
                viewHolder = new OtherChatViewHolder(viewChatOther);
                break;
        }
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (TextUtils.equals(mChats.get(position).senderUid,
                FirebaseAuth.getInstance().getCurrentUser().getUid())) {
            configureMyChatViewHolder((MyChatViewHolder) holder, position);
        } else {
            configureOtherChatViewHolder((OtherChatViewHolder) holder, position);
        }
    }


    private void configureMyChatViewHolder(final MyChatViewHolder myChatViewHolder, int position) {
        Chat chat = mChats.get(position);
        SimpleDateFormat sfd = new SimpleDateFormat("hh:mm a");
        String date=sfd.format(new Date(chat.timestamp).getTime());
        myChatViewHolder.senderMsgTime.setText(date);


    }

    private void configureOtherChatViewHolder(final OtherChatViewHolder otherChatViewHolder, int position) {
        final Chat chat = mChats.get(position);
        SimpleDateFormat sfd = new SimpleDateFormat("hh:mm a");
        String date=sfd.format(new Date(chat.timestamp).getTime());
        otherChatViewHolder.receiverMsgTime.setText(date);

    }

    @Override
    public int getItemCount() {
        return 0;
    }


    @Override
    public int getItemViewType(int position) {
        if (TextUtils.equals(mChats.get(position).senderUid,
                FirebaseAuth.getInstance().getCurrentUser().getUid())) {
            return VIEW_TYPE_ME;
        } else {
            return VIEW_TYPE_OTHER;
        }
    }

    private static class MyChatViewHolder extends RecyclerView.ViewHolder {
        private TextView txtChatMessage, txtUserAlphabet;
        private TextView senderMsgTime;

        public MyChatViewHolder(View itemView) {
            super(itemView);
            txtChatMessage = (TextView) itemView.findViewById(R.id.text_view_chat_message);
            txtUserAlphabet = (TextView) itemView.findViewById(R.id.text_view_user_alphabet);
            senderMsgTime=(TextView) itemView.findViewById(R.id.senderMsgTime);
        }
    }

    private static class OtherChatViewHolder extends RecyclerView.ViewHolder {
        private TextView txtChatMessage, txtUserAlphabet;
        private TextView receiverMsgTime;




        public OtherChatViewHolder(View itemView) {
            super(itemView);
            txtChatMessage = (TextView) itemView.findViewById(R.id.text_view_chat_message);
            txtUserAlphabet = (TextView) itemView.findViewById(R.id.text_view_user_alphabet);
            receiverMsgTime=(TextView) itemView.findViewById(R.id.receiverMsgTime);


        }
    }
}

We successfully created a recycler view for our showing chats and we also created a single adapter for displaying two view types of inflating two different layouts in single recycler view. We also described the structure of our chat message in firebase database. In the next session, we will be sending the message to a particular user.

All above codes are available on my Github. Click here to download.

Curriculam

Using firebase Database to create a chat application in Android : Part I

Using Firebase Database to create a chat application in Android : Part II

Using Firebase Database to create a chat application in Android : Part III

Using Firebase Database to create a chat application in Android : Part IV

Using Firebase Database to create a chat application in Android : Part V



Posted on Utopian.io - Rewarding Open Source Contributors

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!
Sort Order:  

Your contribution cannot be approved because it does not refer to or relate to an open-source repository. See here for a definition of "open-source." Neither firebase itself nor android SDK for firebase has the github repository with the open source license.

You can contact us on Discord.
[utopian-moderator]

This tutorial series not only covers usases of firebase but also makes an android application completely. Regarding this, github.com/android might help us to adjust the repository for this tutorial. Please look at the link or else android is open source it's android open source project mirror located at github.com/aosp-mirror may help. Rereview it. Thanks

When will be the next session i.e. sending messages

@murthyky as this series got github issues and couldn't be continued on utopian.io, it got delayed. I'm working to complete this series in one more part. Thank you for involving in this series. Sorry for the inconvenience. Follow @programminghub to get my posts in your feed. Next part will be posted soon.

Hey @dakeshi, I just gave you a tip for your hard work on moderation. Upvote this comment to support the utopian moderators and increase your future rewards!

Your post had been curated by the @buildawhale & @upmyvote team and mentioned here:

https://steemit.com/curation/@buildawhale/buildawhale-curation-digest-04-04-18

Keep up the good work and original content, everyone appreciates it!