Message receipts

Updated

Introduces how to use the Agora Chat SDK to implement message receipt functionalities in one-to-one chats and chat groups.

The Chat SDK provides the message read receipt feature that allows the user, after sending a message, to know whether the message is read. The feature is available to both one-to-one chats and group chats.

  • Message delivery receipt: Available only to one-to-one chats.
  • Message read receipt: Available to both one-to-one chats and group chats.

Understand the tech

The Chat SDK uses ChatManager to provide message receipt. The following are the core methods:

  • ChatOptions.setRequireAck: Enables message read receipt.
  • ChatOptions.setRequireDeliveryAck: Enables message delivery receipt.
  • ackConversationRead: Sends a conversation read receipt.
  • ackMessageRead: Sends a message read receipt.
  • ackGroupMessageRead: Sends a message read receipt for group chat.

The logic for implementing these receipts are as follows:

  • Message delivery receipts

    1. The message sender enables delivery receipts by setting ChatOptions.setRequireDeliveryAck as true.
    2. After the recipient receives the message, the SDK automatically sends a delivery receipt to the sender.
    3. The sender receives the delivery receipt by listening for onMessageDelivered.
  • Conversation and message read receipts

    1. The message sender enables read receipt by setting ChatOptions.setRequireAck as true.
    2. After reading the message, the recipient calls ackConversationRead or ackMessageRead to send a conversation or message read receipt.
    3. The sender receives the conversation or message receipt by listening for onConversationRead or onMessageRead.

Prerequisites

Before proceeding, ensure that you meet the following requirements:

  • You have integrated the Chat SDK, initialized the SDK and implemented the functionality of registering accounts and login. For details, see Chat SDK quickstart.
  • You understand the API call frequency limits as described in Limitations.
  • Message read receipts for chat groups are not enabled by default. To use this feature, contact support@agora.io.

Implementation

This section introduces how to implement message delivery and read receipts in your chat app.

Message delivery receipts

To send a message delivery receipt, take the following steps:

  1. The message sender sets setRequireDeliveryAck in ChatOptions as true before sending the message:

    ChatOptions chatOptions = new ChatOptions();
    chatOptions.setRequireDeliveryAck(true);
    ...
    ChatClient.getInstance().init(mContext, chatOptions);
  2. Once the recipient receives the message, the SDK triggers onMessageDelivered on the message sender's client, notifying the message sender that the message has been delivered to the recipient.

    // Add a message listener to listen for the receipt message.
    MessageListener msgListener = new MessageListener() {
        // Occurs when the message is received.
        @Override
        public void onMessageReceived(List messages) {
        }
        // Occurs when the message delivery receipt is received
        @Override
        public void onMessageDelivered(List message) {
        }
    };
    // Register a message listener.
    ChatClient.getInstance().chatManager().addMessageListener(msgListener);
    // Remove the message listener when it is not used.
    ChatClient.getInstance().chatManager().removeMessageListener(msgListener);

Conversation and message read receipts

In both one-to-one chats and group chats, you can use message read receipts to notify the message sender that the message has been read. To minimize the method call for message read receipts, the SDK also supports conversation read receipts in one-to-one chats.

One-to-one chats

In one-to-one chats, the SDK supports sending both the conversation read receipts and message read receipts. Agora recommends using conversation read receipts if the new message arrives when the message recipient has not entered the conversation UI.

  • Conversation read receipts

    Follow the steps to implement conversation read receipts in one-to-one chats.

    1. When a user enters the conversation UI, check whether the conversation contains unread messages. If yes, call ackConversationRead to send a conversation read receipt.

      // The message receiver calls ackConversationRead to send the conversation read receipt.
      // This is an asynchronous method.
      try {
          ChatClient.getInstance().chatManager().ackConversationRead(conversationId);
      } catch (ChatException e) {
          e.printStackTrace();
      }
    2. The message sender listens for message events and receives the conversation read receipt in onConversationRead.

      // The message sender calls addConversationListener to listen for conversation events.
      ChatClient.getInstance().chatManager().addConversationListener(new ConversationListener() {
                  ...
                  @Override
                  // Occurs when the all the messages in the conversation is read.
                  public void onConversationRead(String from, String to) {
                      // Add follow-up logics such as poping up a notification.
                  }
              });

    In use-cases where a user is logged in multiple devices, if the user sends a conversation read receipt from one device, the server sets the count of unread messages in the conversation as 0, and all the other devices receive onConversationRead.

  • Message read receipts

    To implement the message read receipt, take the following steps:

    1. Send a conversation read receipt when the recipient enters the conversation.

      // The message receiver calls ackConversationRead to send the conversation read receipt.
      try {
          ChatClient.getInstance().chatManager().ackConversationRead(conversationId);
      }catch (ChatException e) {
          e.printStackTrace();
      }
    2. When a new message arrives, send the message read receipt and add proper handling logics for the different message types.

      ChatClient.getInstance().chatManager().addMessageListener(new MessageListener() {
          ......
      
          @Override
          // Occurs when the specified message is received.
          public void onMessageReceived(List messages) {
              ......
              // Send the message read receipt.
              sendReadAck(message);
              ......
          }
          ......
      });
      // Send the message read receipt.
      public void sendReadAck(ChatMessage message) {
          // For messages in one-to-one chat
          if(message.direct() == ChatMessage.Direct.RECEIVE
                  undefined message.getChatType() == ChatMessage.ChatType.Chat) {
              ChatMessage.Type type = message.getType();
              // For voice, video, and file messages, you need to send the receipt after clicking the files.
              if(type == ChatMessage.Type.VIDEO || type == ChatMessage.Type.VOICE || type == ChatMessage.Type.FILE) {
                  return;
              }
              try {
                  // Call ackMessageRead to send the message read receipt.
                  ChatClient.getInstance().chatManager().ackMessageRead(message.getFrom(), message.getMsgId());
              } catch (ChatException e) {
                  e.printStackTrace();
              }
          }
      }
    3. The message sender listens for the message receipt:

      // The message sender calls addMessageListener to listen for message events.
      ChatClient.getInstance().chatManager().addMessageListener(new MessageListener() {
          ......
          @Override
          // Occurs when the specified message is read.
          public void onMessageRead(List messages) {
              // Add follow-up logics such as poping up a notification.
          }
          ......
      });

Chat groups

For a group chat, group members can determine whether to require message read receipts when sending a message. If yes, after a group member reads the message, the SDK sends a read receipt. In a group chat, the number of message read receipts that are sent for the message refers to the number of group members that have read this message.

The following table shows the restrictions of this feature:

Feature RestrictionDefaultDescription
Enabling the functionDisabledTo use this feature, contact support@agora.io to enable it.
PermissionAll group membersBy default, all group members can request read receipts when sending a message. You can contact support@agora.io to grant the permission only to the group owner and administrators.
Number of days before read receipts cannot be returned after the message is sent3 daysThe server no longer records the group members that read the message three days after it is sent, nor sends the read receipts.
Chat group size200 membersThis feature is available only to groups with up to 200 members. If the upper limit is exceeded, no read receipts are returned for the message sent within the group. To increase the upper limit of group member count, you can contact support@agora.io.
View the number of read receipts returned for a group messageMessage senderBy default, only the message sender can view the number of read receipts returned for a group message (or the number of group members that have returned the read receipts). To allow all group members to view the count, you can contact support@agora.io.

Follow the steps to implement read receipts for a chat group message:

  1. When sending a message, a group member can set whether to require a message read receipt.

    // Set setIsNeedGroupAck as true when sending the group message
    ChatMessage message = ChatMessage.createTextSendMessage(content, to);
    message.setIsNeedGroupAck(true);
  2. After the group member reads the chat group message, call ackGroupMessageRead from the group member's client to send a message read receipt.

    // Send the group message read receipt.
    public void sendAckMessage(ChatMessage message) {
            if (!validateMessage(message)) {
                return;
            }
    
            if (message.isAcked()) {
                return;
            }
    
            // May a user login from multiple devices, so do not need to send the ack msg.
            if (ChatClient.getInstance().getCurrentUser().equalsIgnoreCase(message.getFrom())) {
                return;
            }
    
            try {
                if (message.isNeedGroupAck() && !message.isUnread()) {
                    String to = message.conversationId(); // do not use getFrom() here
                    String msgId = message.getMsgId();
                    ChatClient.getInstance().chatManager().ackGroupMessageRead(to, msgId, ((TextMessageBody)message.getBody()).getMessage());
                    message.setUnread(false);
                    EMLog.i(TAG, "Send the group ack cmd-type message.");
                }
            } catch (Exception e) {
                EMLog.d(TAG, e.getMessage());
            }
        }
  3. The message sender listens for the message read receipt.

    // Occurs when the group message is read.
    void onGroupMessageRead(List groupReadAcks) {
        // Add follow-up notifications
    }
  4. The message sender can get the detailed information of the read receipt using asyncFetchGroupReadAcks.

    // msgId: The message ID.
    // pageSize: The page size. The value range is [1,50].
    // startAckId: The starting receipt ID for query. Set it as null for the first call of the method and the SDK retrieves from the latest receipt.
    * @return The message receipt list and a cursor.
    */
    ChatClient.getInstance().chatManager().asyncFetchGroupReadAcks(msgId, pageSize, startAckId, new ValueCallBack>() {
       @Override
       public void onSuccess(CursorResult value) {// Succeeded in getting the details of the read receipt.
       }
       @Override
       public void onError(int error, String errorMsg) {
        // Failed to get the details of the read receipt.
       }
    });