Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Implementing a Recent Conversation List in Android

Tech 1

To manage recent chats in an Android instant messaging application, an Activity is required to listen for incoming message broadcasts, update the dataset, and refresh the ListView. The implementation involves a main Activity, a custom adapter for UI binding, and a model class for conversation data.

The ConversationListActivity registers a local broadcast receiver. When a new message arrives, the receiver extracts the account ID, message body, and timestamp. It removes any existing conversation entries for the same user to prevent duplicate rows in the list, appends the new chat snippet, and updates the adapter. Clicking a list item routes the user to the actual chat interface.

public class ConversationListActivity extends Activity {
    private ListView conversationListView;
    private List<ChatSnippet> conversationList = new ArrayList<>();
    private String[] incomingPayload;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_conversations);

        IntentFilter filter = new IntentFilter("com.app.messenger.NEW_MESSAGE");
        registerReceiver(new MessageUpdateReceiver(), filter);

        conversationListView = findViewById(R.id.conversation_list);
        conversationListView.setOnItemClickListener((parent, view, position, id) -> {
            Intent chatIntent = new Intent(ConversationListActivity.this, ChatDetailActivity.class);
            chatIntent.putExtra("userId", Integer.parseInt(incomingPayload[0]));
            chatIntent.putExtra("username", "");
            startActivity(chatIntent);
        });
    }

    private class MessageUpdateReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            incomingPayload = intent.getStringArrayExtra("payload");
            int targetAccountId = Integer.parseInt(incomingPayload[0]);
            String messageBody = incomingPayload[1];
            String timestamp = incomingPayload[2];

            Toast.makeText(context, "New message from " + targetAccountId + ": " + messageBody, Toast.LENGTH_SHORT).show();

            // Remove existing conversation entries for this user to avoid duplicates
            conversationList.removeIf(snippet -> snippet.getAccountId() == targetAccountId);

            conversationList.add(new ChatSnippet(5, targetAccountId, String.valueOf(targetAccountId), messageBody, timestamp, false));
            conversationListView.setAdapter(new ChatSnippetAdapter(ConversationListActivity.this, conversationList));
            unregisterReceiver(this);
        }
    }
}

The ChatSnippetAdapter extends BaseAdapter to map the ChatSnippet model to the list item layout. It uses the ViewHolder pattern to optimize performence by avoiding repeated findViewById calls during scrolling.

public class ChatSnippetAdapter extends BaseAdapter {
    private final Context mContext;
    private final List<ChatSnippet> mSnippets;
    private final LayoutInflater mInflater;

    public ChatSnippetAdapter(Context context, List<ChatSnippet> snippets) {
        this.mContext = context;
        this.mSnippets = snippets;
        this.mInflater = LayoutInflater.from(context);
    }

    @Override
    public int getCount() {
        return mSnippets.size();
    }

    @Override
    public Object getItem(int position) {
        return mSnippets.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.item_conversation, parent, false);
            holder = new ViewHolder();
            holder.avatarImg = convertView.findViewById(R.id.img_user_avatar);
            holder.nicknameTxt = convertView.findViewById(R.id.txt_username);
            holder.messageTxt = convertView.findViewById(R.id.txt_last_message);
            holder.readStatusImg = convertView.findViewById(R.id.img_read_indicator);
            holder.timeTxt = convertView.findViewById(R.id.txt_message_time);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        ChatSnippet snippet = mSnippets.get(position);
        holder.nicknameTxt.setText(snippet.getNick());
        holder.messageTxt.setText(snippet.getContent());
        holder.readStatusImg.setImageResource(R.drawable.ic_unread_indicator);
        holder.timeTxt.setText(snippet.getTime());

        return convertView;
    }

    static class ViewHolder {
        ImageView avatarImg;
        TextView nicknameTxt;
        TextView messageTxt;
        ImageView readStatusImg;
        TextView timeTxt;
    }
}

The ChatSnippet model acts as a data container holding properties like unique identifier, account ID, nickname, last message content, timestamp, and read status.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.