Receiving Conversation Text Messages for an Android Chat Application

In a previous tutorial, we were able to fetch and display chat conversations between verified users. We implemented this feature by querying the messages table from the MySQL database and returning the unique usernames that sent or received at least one message from the user that logged into the Android app. The conversations were listed in a ListView.

This tutorial continues the development of our Android chat application. Here, we’ll allow the receiving of text messages within conversations.

We’ll do this by binding an OnItemClickListener to each of the items in the ListView. When the user clicks a specific conversation, the messages sent or received within this conversation will be fetched and displayed.

This project is available at GitHub at this link:

The sections covered in this tutorial are as follows:

  • Fetching Messages within a Conversation from the Server
  • Sending Requests from Android to Fetch Messages
  • MessageListViewActivity

The Android app is available at Google Play under the name HiAi Chat. You can download and try it.

Let’s get started.

Fetching Messages within a Conversation from the Server

At the current implementation stage of the project, the server doesn’t support querying the database to retrieve messages. All it can do is register a new user, verify via e-mail address, log in a registered user, and retrieve conversations.

In this section, we are going to implement a function named receive_messages() for receiving the messages sent or received in a conversation between a user logged into the Android app and another user.

Before discussing the implementation of this method, it’s important to start by editing the chat() function, which receives the request from the Android app and selects the appropriate function to handle the request.

The new implementation of the chat() function is shown below. Compared to the function implemented in the previous tutorial, there’s an additional elif that checks whether the subject field of the request is set to receive_messages, which indicates the client asks for messages within a conversation.

def chat():
    msg_received = flask.request.get_json()
    msg_subject = msg_received["subject"]

    if msg_subject == "register":
        return register(msg_received)
    elif msg_subject == "login":
        return login(msg_received)
    elif msg_subject == "verify":
        return verify(msg_received)
    elif msg_subject == "send":
        return send(msg_received)
    elif msg_subject == "receive_chats":
        return receive_chats(msg_received)
    elif msg_subject == "receive_messages":
        return receive_messages(msg_received)
    else:
        return "Invalid request."

If the subject field of the request is set to receive_messages, then the function that will be called is named receive_messages(), which is implemented in the code below. Let’s discuss the individual parts of this function.

def receive_messages(msg_received):
    receiver_username = msg_received["receiver_username"]
    sender_username = msg_received["sender_username"]

    select_query = "SELECT * FROM users where username = " + "'" + receiver_username + "'"
    db_cursor.execute(select_query)
    records = db_cursor.fetchall()
    if len(records) == 0:
        return "Invalid receiver username."

    select_query = "SELECT message, receive_date, sender_username FROM messages where (receiver_username = " + "'" + receiver_username + "' AND sender_username = " + "'" + sender_username + "') OR (receiver_username = " + "'" + sender_username + "' AND sender_username = " + "'" + receiver_username + "') ORDER BY receive_date DESC"
    db_cursor.execute(select_query)
    records = db_cursor.fetchall()
    if len(records) == 0:
        print("No messages delivered for username " + receiver_username)
        return "0"

    all_messages = {}
    for record_idx in range(len(records)):
        curr_record = records[record_idx]
        message = curr_record[0]
        receiveDate = curr_record[1]
        sender_username = curr_record[2]

        curr_message = {}
        curr_message["message"] = message
        curr_message["date"] = str(receiveDate)
        curr_message["sender_username"] = sender_username

        all_messages[str(record_idx)] = curr_message

    all_messages = json.dumps(all_messages)
    print("Sending message(s) :", all_messages)
    return all_messages

The first 2 lines within the function return the username of the 2 participants in the conversation. Note that the one returned in the receiver_username variable is the user logged into the Android app.

After that, a SELECT statement is executed to make sure the username is valid.

The previous query validates the receiver_username, but you can also extend it to work with the sender_username, as shown below:

The following code snippet fetches the messages sent or received between the 2 users whose usernames are saved in the receiver_username and sender_username variables.

The query fetches the text message and the date it was sent/received. At the end of the code, an if statement checks whether the number of records is 0. Note that it isn’t possible to find a conversation in which there are no messages, because a conversation is opened only after there’s at least one message. The if statement is added in case the database was edited by the administrator to delete messages while the server is active.

The next part in the code creates a empty dictionary in the all_messages variable and fills it with the fetched messages.

Finally, the dictionary is converted into a JSON object. This object is what’s returned by the receive_messages() function.

By editing both the chat() and receive_messages() functions, the server is now able to receive requests asking for messages within a conversation and return such messages as a JSON object. Here’s the complete code for the server:

import flask
import mysql.connector
import sys
import json
import smtplib, ssl
from email.mime.text import MIMEText
import cryptography.fernet
app = flask.Flask(__name__)
@app.route('/', methods = ['GET', 'POST'])
def chat():
    msg_received = flask.request.get_json()
    msg_subject = msg_received["subject"]
    if msg_subject == "register":
        return register(msg_received)
    elif msg_subject == "login":
        return login(msg_received)
    elif msg_subject == "verify":
        return verify(msg_received)
    elif msg_subject == "send":
        return send(msg_received)
    elif msg_subject == "receive_chats":
        return receive_chats(msg_received)
    elif msg_subject == "receive_messages":
        return receive_messages(msg_received)
    else:
        return "Invalid request."
def register(msg_received):
    first_name = msg_received["first_name"]
    lastname = msg_received["last_name"]
    username = msg_received["username"]
    email = msg_received["email"]
    select_query = "SELECT * FROM users where username = " + "'" + username + "'"
    db_cursor.execute(select_query)
    records = db_cursor.fetchall()
    if len(records) != 0:
        return "username" # "Username already exists. Please chose another username."
    select_query = "SELECT * FROM users where email = " + "'" + email + "'"
    db_cursor.execute(select_query)
    records = db_cursor.fetchall()
    if len(records) != 0:
        return "email" # "E-mail is already registered."
    send_verification_code(first_name, lastname, username, email) # Encrypt e-mail using 2 keys and send the code to the user e-mail.
    return "success"
def verify(msg_received):
    firstname = msg_received["firstname"]
    lastname = msg_received["lastname"]
    username = msg_received["username"]
    email = msg_received["email"]
    password = msg_received["password"]
    verification_code = msg_received["verification_code"]
    select_query = "SELECT * FROM users where username = " + "'" + username + "'"
    db_cursor.execute(select_query)
    records = db_cursor.fetchall()
    if len(records) != 0:
        return "Another user just used the username. Please chose another username."
    select_query = "SELECT * FROM users where email = " + "'" + email + "'"
    db_cursor.execute(select_query)
    records = db_cursor.fetchall()
    if len(records) != 0:
        return "Another user just registered using the e-mail entered."
    verification_state = verify_code(email, verification_code)
    if verification_state == True:
        insert_query = "INSERT INTO users (first_name, last_name, username, email, password, email_confirmed) VALUES (%s, %s, %s, %s, MD5(%s), %s)"
        insert_values = (firstname, lastname, username, email, password, True)
        try:
            db_cursor.execute(insert_query, insert_values)
            chat_db.commit()
            print("This e-mail", email, " is verified and user is registered successfully.")
            subject = "Welcome to HiAi"
            body = "Hi " + firstname + " " + lastname + ",nnYour account is created successfully at HiAi. You can login with your username and password into the Android app to send and receive messages. nnYour username is " + username + "nnIf you forgot your password, just contact [email protected] for resetting it. nnGood luck.nHiAi [email protected]"
            send_email(subject, body, email)
            return "success"
        except Exception as e:
            print("Error while inserting the new record :", repr(e))
            return "Error while processing the request."
    else:
        print("This e-mail", email, " verification failed.")
        return "failure"
def verify_code(email, verification_code):
    decoded_email = decrypt_text(verification_code)
    if decoded_email == email:
        return True # Email verification succeeded.
    else:
        return False # Email verification failed.
def send(msg_received):
    text_msg = msg_received["msg"]
    sender_username = msg_received["sender_username"]
    receiver_username = msg_received["receiver_username"]
    select_query = "SELECT * FROM users where username = " + "'" + receiver_username + "'"
    db_cursor.execute(select_query)
    records = db_cursor.fetchall()
    if len(records) == 0:
        return "Invalid receiver username."
    select_query = "SELECT * FROM users where username = " + "'" + sender_username + "'"
    db_cursor.execute(select_query)
    records = db_cursor.fetchall()
    if len(records) == 0:
        return "Invalid sender username."
    insert_query = "INSERT INTO messages (sender_username, receiver_username, message) VALUES (%s, %s, %s)"
    insert_values = (sender_username, receiver_username, text_msg)
    try:
        db_cursor.execute(insert_query, insert_values)
        chat_db.commit()
        print(db_cursor.rowcount, "New record inserted successfully.")
    except Exception as e:
        print("Error while inserting the new record :", repr(e))
        return "Error while processing the request."
    print("Received message from :", sender_username, "(", text_msg, ") to be sent to ", receiver_username)
    return "success"
def receive_chats(msg_received):
    receiver_username = msg_received["receiver_username"]
    select_query = "SELECT first_name, last_name FROM users where username = " + "'" + receiver_username + "'"
    db_cursor.execute(select_query)
    records = db_cursor.fetchall()
    if len(records) == 0:
        return "Invalid receiver username."
    select_query = "SELECT DISTINCT sender_username FROM messages where receiver_username = " + "'" + receiver_username + "'"
    db_cursor.execute(select_query)
    records = db_cursor.fetchall()
    if len(records) == 0:
        print("No messages delivered for username " + receiver_username)
        return "0"
    all_chats = {}
    for record_idx in range(len(records)):
        curr_record = records[record_idx]
        sender_username = curr_record[0]
        curr_chat = {}
        curr_chat["username"] = sender_username 
        all_chats[str(record_idx)] = curr_chat
    all_chats = json.dumps(all_chats)
    print("Sending Chat(s) :", all_chats)
    return all_chats
def receive_messages(msg_received):
    receiver_username = msg_received["receiver_username"]
    sender_username = msg_received["sender_username"]
    select_query = "SELECT * FROM users where username = " + "'" + receiver_username + "'"
    db_cursor.execute(select_query)
    records = db_cursor.fetchall()
    if len(records) == 0:
        return "Invalid receiver username."
    select_query = "SELECT message, receive_date, sender_username FROM messages where (receiver_username = " + "'" + receiver_username + "' AND sender_username = " + "'" + sender_username + "') OR (receiver_username = " + "'" + sender_username + "' AND sender_username = " + "'" + receiver_username + "') ORDER BY receive_date DESC"
    db_cursor.execute(select_query)
    records = db_cursor.fetchall()
    if len(records) == 0:
        print("No messages delivered for username " + receiver_username)
        return "0"
    all_messages = {}
    for record_idx in range(len(records)):
        curr_record = records[record_idx]
        message = curr_record[0]
        receiveDate = curr_record[1]
        sender_username = curr_record[2]
        curr_message = {}
        curr_message["message"] = message
        curr_message["date"] = str(receiveDate)
        curr_message["sender_username"] = sender_username
        all_messages[str(record_idx)] = curr_message
    all_messages = json.dumps(all_messages)
    print("Sending message(s) :", all_messages)
    return all_messages
def encrypt_text(text_to_encrypt):
    key_file1 = open("encryption_key1.key", "rb")
    encryption_key1 = key_file1.read()
    key_file1.close()
    
    key_file2 = open("encryption_key2.key", "rb")
    encryption_key2 = key_file2.read()
    key_file2.close()
    encoded_email = text_to_encrypt.encode()
    f = cryptography.fernet.MultiFernet([cryptography.fernet.Fernet(encryption_key1), cryptography.fernet.Fernet(encryption_key2)])
    
    encrypted_text = f.encrypt(encoded_email)
    
    return encrypted_text
def decrypt_text(text_to_decrypt):
    key_file1 = open("encryption_key1.key", "rb")
    encryption_key1 = key_file1.read()
    key_file1.close()
    
    key_file2 = open("encryption_key2.key", "rb")
    encryption_key2 = key_file2.read()
    key_file2.close()
    
    f = cryptography.fernet.MultiFernet([cryptography.fernet.Fernet(encryption_key1), cryptography.fernet.Fernet(encryption_key2)])
    try:
        decrypted_text = f.decrypt(text_to_decrypt.encode('utf-8'))
    except:
        return False # Email verification failed.
    decoded_text = decrypted_text.decode()
    return decoded_text
def send_verification_code(firstname, lastname, username, email):
    
    encrypted_email = encrypt_text(email)
    
    body = "Hi " + firstname + " " + lastname + ",nnThanks for registering for HiAi Chat System.nnYour username is " + username + ".nTo verify your account, just copy the verification code found below, return back to the Android app, paste the code, and finally click the Verify button.nnn" + encrypted_email.decode()
    subject = "HiAi Verification"
    send_email(subject, body, email)
def login(msg_received):
    username = msg_received["username"]
    password = msg_received["password"]
    select_query = "SELECT first_name, last_name FROM users where username = " + "'" + username + "' and password = " + "MD5('" + password + "')"
    db_cursor.execute(select_query)
    records = db_cursor.fetchall()
    if len(records) == 0:
        return "failure"
    else:
        return "success"
def send_email(subject, body, email):
    port = 465  # For SSL
#    smtp_server = "smtp.gmail.com"
    smtp_server = "hiai.website"
    sender_email = "[email protected]"  # Enter your address
#    sender_email = "[email protected]"  # Enter your address
    password = "..."
    msg_body = body
    
    msg = MIMEText(msg_body, 'plain', 'utf-8')
    # add in the actual person name to the message template
    msg['From'] = sender_email
    msg['To'] = email
    msg['Subject'] = subject
    context = ssl.create_default_context()
    with smtplib.SMTP_SSL(smtp_server, port, context=context) as server:
        server.login(sender_email, password)
        server.sendmail(sender_email, email, msg.as_string())
try:
    chat_db = mysql.connector.connect(host="localhost", user="root", passwd="ahmedgad", database="chat_db")
except:
    sys.exit("Error connecting to the database. Please check your inputs.")
db_cursor = chat_db.cursor()
app.run(host="0.0.0.0", port=5000, debug=True, threaded=True) # Run in development server.
# waitress.serve(app=app, host="0.0.0.0", port=5000) # Run in production server.

After building the Flask server, the next step is to edit the Android app to send requests to the server asking for messages sent or received within a conversation.

Sending Requests from Android to Fetch Messages

In the previous tutorial, we included an activity named ChatListViewActivity, which is responsible for displaying the conversations fetched from the server. The MainActivity starts it using an Intent. The implementation of this activity is shown below.

Within this activity, we create an ArrayAdapter to display the conversations in the ListView. At the current stage of implementation, no action is taken when the items in the ListView are clicked.

package gad.hiai.chat.hiaichat;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import java.util.ArrayList;
public class ChatListViewActivity extends AppCompatActivity {
    Intent intent;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_chat_list_view);
        ListView messageListView = findViewById(R.id.chatListView);
        intent = getIntent();
        ArrayList<String> receivedChats = intent.getStringArrayListExtra("receivedChats");
        ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, receivedChats);
        messageListView.setAdapter(adapter);
    }
}

In this section, we’re going to edit the above code so that an OnItemClickListener is added to each item in the ListView. When an item is clicked, the messages inside the corresponding conversation will be fetched.

The following code is added at the end of the onCreate() method so that an OnItemClickListener is added to each item in the ListView. When an item is clicked, the onItemClick() callback method is executed. Within this method, the username is fetched from the TextView, and the result is saved in the senderUsername variable.

At this time, the 2 participants in the conversation are available. The first username is that of the user that logged into the Android app, and the class variable loginUserName defined in the MainActivity holds the username of that user.

messageListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        TextView itemTextView = (TextView) view;
        String senderUsername = itemTextView.getText().toString().trim();
        JSONObject messageContent = new JSONObject();
        try {
            messageContent.put("subject", "receive_messages");
            messageContent.put("receiver_username", MainActivity.loginUserName);
            messageContent.put("sender_username", senderUsername);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), messageContent.toString());
        postRequest(MainActivity.postUrl, body, senderUsername);
    }
});

A JSON object is prepared that holds the 2 participants in the conversation. Note that the subject field is set to receive_messages, which indicates that the Android app asks the server to reply with the messages within the conversation.

The JSON object is used to create the request body, which in turn is passed to the postRequest() method. The implementation of this method is as follows:

public void postRequest(String postUrl, RequestBody postBody, final String senderUsername) {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url(postUrl)
                .post(postBody)
                .header("Accept", "application/json")
                .header("Content-Type", "application/json")
                .build();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                // Cancel the post on failure.
                call.cancel();
                Log.d("FAIL", e.getMessage());
                // In order to access the TextView inside the UI thread, the code is executed inside runOnUiThread()
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(getApplicationContext(), "Failed to Connect to Server. Please Try Again.", Toast.LENGTH_LONG).show(); // A message indicating that no messages are delivered for the user.
                    }
                });
            }
            @Override
            public void onResponse(Call call, final Response response) {
                // In order to access the TextView inside the UI thread, the code is executed inside runOnUiThread()
                try {
                    String responseString = response.body().string().trim();
                    if (responseString.equals("0")) {
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                Toast.makeText(getApplicationContext(), "Something went wrong.", Toast.LENGTH_LONG).show(); // A message indicating that no messages are delivered for the user.
                            }
                        });
                        return;
                    }
                    Intent showMessagesIntent = new Intent(getApplicationContext(), MessageListViewActivity.class);
                    ArrayList<String> receivedMessages = new ArrayList<>();
                    ArrayList<String> sendersUsernames = new ArrayList<>();
                    ArrayList<String> receiveDates = new ArrayList<>();
                    JSONObject messageContent = new JSONObject(responseString);
                    Log.d("CHATS", "Response from the server : " + messageContent);
                    try {
                        for (int i = 0; i < messageContent.length(); i++) {
                            JSONObject currMessage = messageContent.getJSONObject(i + "");
                            String textMessage = currMessage.getString("message");
                            String messageDate = currMessage.getString("date");
                            String messageSenderUsername = currMessage.getString("sender_username");
                            receivedMessages.add(textMessage);
                            sendersUsernames.add(messageSenderUsername);
                            receiveDates.add(messageDate);
                        }
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                    Log.d("MSGS", receivedMessages.toString());
                    showMessagesIntent.putExtra("conversationOwner", senderUsername);
                    showMessagesIntent.putExtra("receivedMessages", receivedMessages);
                    showMessagesIntent.putExtra("sendersUsernames", sendersUsernames);
                    showMessagesIntent.putExtra("messagesDates", receiveDates);
                    startActivity(showMessagesIntent);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

The new part of the function is the one responsible for receiving the response from the server. Remember that the server responds with the text message, date, and username of the sender. For such a purpose, there are 3 ArrayList variables created to hold this information.

These variables are named receivedMessages, receiveDates, and receiveDates. After preparing the 3 variables with the data, an Intent is created that starts an activity named MessageListViewActivity. The 3 variables are passed to this activity as extras.

The MessageListViewActivity is responsible for creating a ListView for displaying the messages within the conversation. As such, MainActivity starts a ChatListViewActivity for displaying the conversations.

Also, ChatListViewActivity starts a MessageListViewActivity for displaying messages within a specific conversation. The next figure helps show the relationship between these 3 activities:

Before discussing the MessageListViewActivity, here’s the complete code of the ChatListViewActivity activity.

package gad.hiai.chat.hiaichat;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.util.ArrayList;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class ChatListViewActivity extends AppCompatActivity {
    Intent intent;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_chat_list_view);
        ListView messageListView = findViewById(R.id.chatListView);
        intent = getIntent();
        ArrayList<String> receivedChats = intent.getStringArrayListExtra("receivedChats");
        ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, receivedChats);
        messageListView.setAdapter(adapter);
        messageListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                TextView itemTextView = (TextView) view;
                String senderUsername = itemTextView.getText().toString().trim();
//                Toast.makeText(getApplicationContext(), "Sender username " + senderUsername, Toast.LENGTH_SHORT).show();
                JSONObject messageContent = new JSONObject();
                try {
                    messageContent.put("subject", "receive_messages");
                    messageContent.put("receiver_username", MainActivity.loginUserName);
                    messageContent.put("sender_username", senderUsername);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
                RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), messageContent.toString());
                postRequest(MainActivity.postUrl, body, senderUsername);
            }
        });
    }
    public void postRequest(String postUrl, RequestBody postBody, final String senderUsername) {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url(postUrl)
                .post(postBody)
                .header("Accept", "application/json")
                .header("Content-Type", "application/json")
                .build();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                // Cancel the post on failure.
                call.cancel();
                Log.d("FAIL", e.getMessage());
                // In order to access the TextView inside the UI thread, the code is executed inside runOnUiThread()
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(getApplicationContext(), "Failed to Connect to Server. Please Try Again.", Toast.LENGTH_LONG).show(); // A message indicating that no messages are delivered for the user.
                    }
                });
            }
            @Override
            public void onResponse(Call call, final Response response) {
                // In order to access the TextView inside the UI thread, the code is executed inside runOnUiThread()
                try {
                    String responseString = response.body().string().trim();
                    if (responseString.equals("0")) {
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                Toast.makeText(getApplicationContext(), "Something went wrong.", Toast.LENGTH_LONG).show(); // A message indicating that no messages are delivered for the user.
                            }
                        });
                        return;
                    }
                    Intent showMessagesIntent = new Intent(getApplicationContext(), MessageListViewActivity.class);
                    ArrayList<String> receivedMessages = new ArrayList<>();
                    ArrayList<String> sendersUsernames = new ArrayList<>();
                    ArrayList<String> receiveDates = new ArrayList<>();
                    JSONObject messageContent = new JSONObject(responseString);
                    Log.d("CHATS", "Response from the server : " + messageContent);
                    try {
                        for (int i = 0; i < messageContent.length(); i++) {
                            JSONObject currMessage = messageContent.getJSONObject(i + "");
                            String textMessage = currMessage.getString("message");
                            String messageDate = currMessage.getString("date");
                            String messageSenderUsername = currMessage.getString("sender_username");
                            receivedMessages.add(textMessage);
                            sendersUsernames.add(messageSenderUsername);
                            receiveDates.add(messageDate);
                        }
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                    Log.d("MSGS", receivedMessages.toString());
                    showMessagesIntent.putExtra("conversationOwner", senderUsername);
                    showMessagesIntent.putExtra("receivedMessages", receivedMessages);
                    showMessagesIntent.putExtra("sendersUsernames", sendersUsernames);
                    showMessagesIntent.putExtra("messagesDates", receiveDates);
                    startActivity(showMessagesIntent);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

MessageListViewActivity

The MessageListViewActivity class is responsible for creating a ListView in which the messages are displayed. The layout of this activity is given in the next code. We use a simple ListView for displaying the messages.

<?xml version="1.0" encoding="utf-8"?>
<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"
    tools:context=".MessageListViewActivity">
    <ListView
        android:id="@+id/messageListView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

In addition to the previous layout file, there’s another layout file created for specifying the layout of each item in the list. These items hold the messages. Here is the layout of each item—there are 3 TextView elements for holding the text message, sender, and date.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="2dp"
    android:orientation="vertical"
    android:paddingStart="2dp"
    android:paddingEnd="2dp">
    <TextView
        android:id="@+id/messageView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:textAppearance="@style/TextAppearance.AppCompat.Medium"
        android:textColor="#000000"
        android:textStyle="bold" />
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <TextView
            android:id="@+id/senderView"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:textAppearance="@style/TextAppearance.AppCompat.Small"
            android:textColor="#888888"
            android:textStyle="bold" />
        <TextView
            android:id="@+id/dateView"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:textAppearance="@style/TextAppearance.AppCompat.Small"
            android:textColor="#888888"
            android:textStyle="bold" />
    </LinearLayout>
</LinearLayout>

After preparing the layout of the message item, the next step is to implement the activity itself, which is given in the following code.

At the beginning of the onCreate() method, the ListView is returned into the messageListView variable. After that, the 3 extras sent inside the Intent are fetched. Then an instance of the ArrayAdapter class is created that fills the message layout by the data.

For each message to be displayed, the getView() method is implicitly called. Within it, the 3 TextView elements are filled with the appropriate data. Finally, the adapter is associated with the ListView.

package gad.hiai.chat.hiaichat;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import java.util.ArrayList;
public class MessageListViewActivity extends AppCompatActivity {
    String replyReceiverUsername;
    MainActivity mainActivity = new MainActivity();
    private Context messageListViewActivityContext;
    private ListView messageListView;
    private int numMessages;
    ArrayList<String> receivedMessages;
    ArrayList<String> sendersUsernames;
    ArrayList<String> messagesDates;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_message_list_view);
        messageListViewActivityContext = this;
        messageListView = findViewById(R.id.messageListView);
        Intent intent = getIntent();
        final String conversationOwner;
        conversationOwner = intent.getStringExtra("conversationOwner");
        receivedMessages = intent.getStringArrayListExtra("receivedMessages");
        sendersUsernames = intent.getStringArrayListExtra("sendersUsernames");
        messagesDates = intent.getStringArrayListExtra("messagesDates");
        numMessages = receivedMessages.size();
        replyReceiverUsername = conversationOwner;
        ArrayAdapter adapter = new ArrayAdapter(messageListViewActivityContext, R.layout.message_list_item, R.id.messageView, receivedMessages) {
            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                View view = super.getView(position, convertView, parent);
                TextView messageView = view.findViewById(R.id.messageView);
                TextView senderView = view.findViewById(R.id.senderView);
                TextView dateView = view.findViewById(R.id.dateView);
                messageView.setText(receivedMessages.get(position));
                senderView.setText(sendersUsernames.get(position));
                dateView.setText(messagesDates.get(position));
                if (conversationOwner.equals(sendersUsernames.get(position))) {
                    view.setBackgroundColor(Color.parseColor("#FFFFFF"));
                } else {
                    view.setBackgroundColor(Color.parseColor("#d8e3eb"));
                }
                return view;
            }
        };
        messageListView.setAdapter(adapter);
    }
}

At this time, both the client and the server are able to negotiate successfully to allow accessing the messages within a conversation. Before running the project, the new activity MessageListViewActivity must be registered inside the Manifest file according to the following line of code:

After running the application, here’s its main screen:

After clicking on the Show Your Conversations button, all conversations will be fetched from the server and displayed according to the next figure. Note that users have to log in:

Finally, click on any of these conversations, and you’ll get the messages within it, as shown in the next figure (when clicking on the middle item, ahmedgad).

Conclusion

This tutorial enabled our Android chat application to receive messages within a given conversation. At first, the Flask server is edited to fetch the messages from the MySQL database.

Then a new activity named MessageListViewActivity is created that displays the messages received from the server.

Receiving instant notifications for new messages is a known feature in essentially all chat applications. This is why, in the next tutorial, we’ll work on sending push notifications for all new messages.

Fritz

Our team has been at the forefront of Artificial Intelligence and Machine Learning research for more than 15 years and we're using our collective intelligence to help others learn, understand and grow using these new technologies in ethical and sustainable ways.

Comments 0 Responses

Leave a Reply

Your email address will not be published. Required fields are marked *

wix banner square