tencent cloud

Feedback

Android&iOS&Windows&Mac

Last updated: 2024-03-12 16:27:15

    Feature Description

    The addSimpleMsgListener API is used to listen for and receive text and custom messages, with the callback defined in the V2TIMSimpleMsgListener protocol.
    The addAdvancedMsgListener API is used to listen for and receive all types of messages (text, custom, and rich media messages), with the callback defined in the V2TIMAdvancedMsgListener protocol.

    Setting a Message Listener

    The SDK provides the V2TIMSimpleMsgListener simple message listener and the V2TIMAdvancedMsgListener advanced message listener. They differ in that:
    1. The simple message listener can only receive text and custom messages. You can use it if your business only requires these two types of messages.
    2. The advanced message listener can receive all types of messages. You can use it if your business also requires rich media, merged, and other messages.
    Note:
    1. addSimpleMsgListener and addAdvancedMsgListener are exclusive. Do not use both of them; otherwise, unpredictable logic bugs will occur.
    2. A message listener must be added first to receive the following types of messages.

    Simple message listener

    Adding a listener

    The receiver calls the addSimpleMsgListener (Android / iOS and macOSWindows) to add the simple message listener. We recommend it be called early, such as after the chat page is initialized, to ensure timely message receiving in the application.
    Sample code:
    Android
    iOS and macOS
    Windows
    V2TIMManager.getInstance().addSimpleMsgListener(simpleMsgListener);
    // `self` is id<V2TIMSignalingListener>
    [[V2TIMManager sharedInstance] addSimpleMsgListener:self];
    class SimpleMsgListener final : public V2TIMSimpleMsgListener {
    // Members ...
    };
    
    // Note that `simpleMsgListener` should not be released before the IM SDK is uninitialized,
    // otherwise the message callback cannot be called.
    SimpleMsgListener simpleMsgListener;
    V2TIMManager::GetInstance()->AddSimpleMsgListener(&simpleMsgListener);

    Listener callback event

    After adding the simple message listener, the receiver can receive different types of messages in the callback of V2TIMSimpleMsgListener (Android / iOS and macOSWindows), as described below:
    Android
    iOS and macOS
    Windows
    public abstract class V2TIMSimpleMsgListener {
    // Received the one-to-one text message
    public void onRecvC2CTextMessage(String msgID, V2TIMUserInfo sender, String text) {}
    
    // Received the custom one-to-one (signaling) message
    public void onRecvC2CCustomMessage(String msgID, V2TIMUserInfo sender, byte[] customData) {}
    
    // Received the group text message
    public void onRecvGroupTextMessage(String msgID, String groupID, V2TIMGroupMemberInfo sender, String text) {}
    
    // Received the custom group (signaling) message
    public void onRecvGroupCustomMessage(String msgID, String groupID, V2TIMGroupMemberInfo sender, byte[] customData) {}
    }
    /// Basic message callback of the IM SDK
    @protocol V2TIMSimpleMsgListener <NSObject>
    @optional
    
    /// Received a one-to-one text message
    - (void)onRecvC2CTextMessage:(NSString *)msgID sender:(V2TIMUserInfo *)info text:(NSString *)text;
    
    /// Received a custom one-to-one (signaling) message
    - (void)onRecvC2CCustomMessage:(NSString *)msgID sender:(V2TIMUserInfo *)info customData:(NSData *)data;
    
    /// Received a group text message
    - (void)onRecvGroupTextMessage:(NSString *)msgID groupID:(NSString *)groupID sender:(V2TIMGroupMemberInfo *)info text:(NSString *)text;
    
    /// Received a custom group (signaling) message
    - (void)onRecvGroupCustomMessage:(NSString *)msgID groupID:(NSString *)groupID sender:(V2TIMGroupMemberInfo *)info customData:(NSData *)data;
    @end
    class SimpleMsgListener final : public V2TIMSimpleMsgListener {
    public:
    SimpleMsgListener() = default;
    ~SimpleMsgListener() override = default;
    
    // Received the one-to-one text message
    void OnRecvC2CTextMessage(const V2TIMString& msgID, const V2TIMUserFullInfo& sender,
    const V2TIMString& text) override {}
    
    // Received the custom one-to-one (signaling) message
    void OnRecvC2CCustomMessage(const V2TIMString& msgID, const V2TIMUserFullInfo& sender,
    const V2TIMBuffer& customData) override {}
    
    // Received the group text message
    void OnRecvGroupTextMessage(const V2TIMString& msgID, const V2TIMString& groupID,
    const V2TIMGroupMemberFullInfo& sender, const V2TIMString& text) override {}
    
    // Received the custom group (signaling) message
    void OnRecvGroupCustomMessage(const V2TIMString& msgID, const V2TIMString& groupID,
    const V2TIMGroupMemberFullInfo& sender,
    const V2TIMBuffer& customData) override {}
    };

    Removing a listener

    To stop receiving messages, the receiver can call removeSimpleMsgListener (Android / iOS and macOSWindows) to remove the simple message listener.
    Sample code:
    Android
    iOS and macOS
    Windows
    V2TIMManager.getInstance().removeSimpleMsgListener(simpleMsgListener);
    // `self` is id<V2TIMSignalingListener>
    [[V2TIMManager sharedInstance] removeSimpleMsgListener:self];
    class SimpleMsgListener final : public V2TIMSimpleMsgListener {
    // Members ...
    };
    
    // `simpleMsgListener` is the instance of SimpleMsgListener
    V2TIMManager::GetInstance()->RemoveSimpleMsgListener(&simpleMsgListener);

    Advanced message listener

    Adding a listener

    The receiver calls the addAdvancedMsgListener (Android / iOS and macOSWindows) to add the advanced message listener. We recommend it be called early, such as after the chat page is initialized, to ensure timely message receiving in the application.
    Sample code:
    Android
    iOS and macOS
    Windows
    V2TIMManager.getMessageManager().addAdvancedMsgListener(advancedMsgListener);
    // `self` is id<V2TIMAdvancedMsgListener>
    [[V2TIMManager sharedInstance] addAdvancedMsgListener:self];
    class AdvancedMsgListener final : public V2TIMAdvancedMsgListener {
    // Members ...
    };
    
    // Note that `advancedMsgListener` should not be released before the IM SDK is uninitialized,
    // otherwise the message callback cannot be called.
    AdvancedMsgListener advancedMsgListener;
    V2TIMManager::GetInstance()->GetMessageManager()->AddAdvancedMsgListener(&advancedMsgListener);

    Listener callback event

    After adding the advanced message listener, the receiver can receive different types of messages in the callback of V2TIMAdvancedMsgListener (Android / iOS and macOSWindows), as described below:
    Android
    iOS and macOS
    Windows
    public abstract class V2TIMAdvancedMsgListener {
    // Received a new message
    public void onRecvNewMessage(V2TIMMessage msg) {}
    
    // one-to-one message read notification (a notification is received when the receiver calls `markC2CMessageAsRead`)
    public void onRecvC2CReadReceipt(List<V2TIMMessageReceipt> receiptList) {}
    
    // Message read receipt notification (if read receipts are supported, the notification is received when the receiver calls `sendMessageReadReceipts`)
    public void onRecvMessageReadReceipts(List<V2TIMMessageReceipt> receiptList) {}
    
    // Received a message recall notification
    public void onRecvMessageRevoked(String msgID) {}
    
    // The message content is modified.
    public void onRecvMessageModified(V2TIMMessage msg) {}
    }
    /// Advanced message listener
    @protocol V2TIMAdvancedMsgListener <NSObject>
    @optional
    /// Received a new message
    - (void)onRecvNewMessage:(V2TIMMessage *)msg;
    
    /// Message read receipt notification (if read receipts are supported, the notification is received when the receiver calls `sendMessageReadReceipts`)
    - (void)onRecvMessageReadReceipts:(NSArray<V2TIMMessageReceipt *> *)receiptList;
    
    /// One-to-one message read notification (a notification is received when the receiver calls `markC2CMessageAsRead`)
    - (void)onRecvC2CReadReceipt:(NSArray<V2TIMMessageReceipt *> *)receiptList;
    
    /// Received a message recall notification
    - (void)onRecvMessageRevoked:(NSString *)msgID;
    
    /// The message content is modified
    - (void)onRecvMessageModified:(V2TIMMessage *)msg;
    @end
    class AdvancedMsgListener final : public V2TIMAdvancedMsgListener {
    public:
    AdvancedMsgListener() = default;
    ~AdvancedMsgListener() override = default;
    
    // Received a new message
    void OnRecvNewMessage(const V2TIMMessage& message) override {}
    
    // one-to-one message read notification (a notification is received when the receiver calls `markC2CMessageAsRead`)
    void OnRecvC2CReadReceipt(const V2TIMMessageReceiptVector& receiptList) override {}
    
    // Message read receipt notification (if read receipts are supported, the notification is received when the receiver calls `sendMessageReadReceipts`)
    void OnRecvMessageReadReceipts(const V2TIMMessageReceiptVector& receiptList) override {}
    
    // Received a message recall notification
    void OnRecvMessageRevoked(const V2TIMString& messageID) override {}
    
    // The message content is modified.
    void OnRecvMessageModified(const V2TIMMessage& message) override {}
    };

    Removing a listener

    To stop receiving messages, the receiver can call removeAdvancedMsgListener (Android / iOS and macOSWindows) to remove the advanced message listener.
    Sample code:
    Android
    iOS and macOS
    Windows
    V2TIMManager.getMessageManager().removeAdvancedMsgListener(advancedMsgListener);
    // `self` is id<V2TIMAdvancedMsgListener>
    [[V2TIMManager sharedInstance] removeAdvancedMsgListener:self];
    class AdvancedMsgListener final : public V2TIMAdvancedMsgListener {
    // Members ...
    };
    
    // `advancedMsgListener` is the instance of AdvancedMsgListener
    V2TIMManager::GetInstance()->GetMessageManager()->RemoveAdvancedMsgListener(&advancedMsgListener);

    Receiving a Text Message

    Receiving a message with the simple message listener

    One-to-one text message

    The receiver can receive a one-to-one text message by using the simple message listener in the following steps:
    1. Call addSimpleMsgListener to set the event listener.
    2. Listen for the onRecvC2CTextMessage callback (Android / iOS and macOSWindows) to receive text messages.
    3. To stop receiving messages, call removeSimpleMsgListener to remove the listener. This step is optional and can be performed as needed.
    Sample code:
    Android
    iOS and macOS
    Windows
    // Set the event listener
    V2TIMManager.getInstance().addSimpleMsgListener(simpleMsgListener);
    
    // Receive the one-to-one text message
    /**
    * Received the one-to-one text message
    *
    * @param msgID Unique message ID
    * @param sender Sender information
    * @param text The sent content
    */
    public void onRecvC2CTextMessage(String msgID, V2TIMUserInfo sender, String text) {
    // Parse the message and display it on the UI
    }
    // Set the event listener
    [[V2TIMManager sharedInstance] addSimpleMsgListener:self];
    
    /// Receive the one-to-one text message
    /// @param msgID Message ID
    /// @param info Sender information
    /// @param text Text content
    - (void)onRecvC2CTextMessage:(NSString *)msgID sender:(V2TIMUserInfo *)info text:(NSString *)text {
    // Parse the message and display it on the UI
    }
    class SimpleMsgListener final : public V2TIMSimpleMsgListener {
    public:
    /**
    * Receive the one-to-one text message
    *
    * @param msgID Unique message ID
    * @param sender Sender information
    * @param text The sent content
    */
    void OnRecvC2CTextMessage(const V2TIMString& msgID, const V2TIMUserFullInfo& sender,
    const V2TIMString& text) override {
    // Parse the message and display it on the UI
    std::cout << "text:" << std::string{text.CString(), text.Size()} << std::endl;
    }
    // Other members ...
    };
    
    // Note that `simpleMsgListener` should not be released before the IM SDK is uninitialized,
    // otherwise the message callback cannot be called.
    SimpleMsgListener simpleMsgListener;
    V2TIMManager::GetInstance()->AddSimpleMsgListener(&simpleMsgListener);

    Group text message

    The receiver can receive a group text message by using the simple message listener in the following steps:
    1. Call addSimpleMsgListener to set the event listener.
    2. Listen for the onRecvGroupTextMessage callback (Android / iOS and macOSWindows) to receive text messages.
    3. To stop receiving messages, call removeSimpleMsgListener to remove the listener. This step is optional and can be performed as needed.
    Sample code:
    Android
    iOS and macOS
    Windows
    // Set the event listener
    V2TIMManager.getInstance().addSimpleMsgListener(simpleMsgListener);
    
    // Receive the group text message
    /**
    * Received the group text message
    *
    * @param msgID Unique message ID
    * @param groupID Group ID
    * @param sender The group member information of the sender
    * @param text The sent content
    */
    public void onRecvGroupTextMessage(String msgID, String groupID, V2TIMGroupMemberInfo sender, String text) {
    // Parse the message and display it on the UI
    }
    // Set the event listener
    [[V2TIMManager sharedInstance] addSimpleMsgListener:self];
    
    /// Receive the group text message
    /// @param msgID Message ID
    /// @param groupID Group ID
    /// @param info Sender information
    /// @param text Text content
    - (void)onRecvGroupTextMessage:(NSString *)msgID groupID:(NSString *)groupID sender:(V2TIMGroupMemberInfo *)info text:(NSString *)text {
    // Parse the message and display it on the UI
    }
    class SimpleMsgListener final : public V2TIMSimpleMsgListener {
    public:
    /**
    * Received the group text message
    *
    * @param msgID Unique message ID
    * @param groupID Group ID
    * @param sender The group member information of the sender
    * @param text The sent content
    */
    void OnRecvGroupTextMessage(const V2TIMString& msgID, const V2TIMString& groupID,
    const V2TIMGroupMemberFullInfo& sender, const V2TIMString& text) override {
    // Parse the message and display it on the UI
    std::cout << "text:" << std::string{text.CString(), text.Size()} << std::endl;
    }
    // Other members ...
    };
    
    // Note that `simpleMsgListener` should not be released before the IM SDK is uninitialized,
    // otherwise the message callback cannot be called.
    SimpleMsgListener simpleMsgListener;
    V2TIMManager::GetInstance()->AddSimpleMsgListener(&simpleMsgListener);

    Receiving a message with the advanced message listener

    The receiver can receive a one-to-one or group text message by using the advanced message listener in the following steps:
    1. Call addAdvancedMsgListener to set the event listener.
    2. Listen for the onRecvNewMessage callback (Android / iOS and macOSWindows) to receive text messages.
    3. To stop receiving messages, call removeAdvancedMsgListener to remove the listener. This step is optional and can be performed as needed.
    Sample code:
    Android
    iOS and macOS
    Windows
    // Set the event listener
    V2TIMManager.getMessageManager().addAdvancedMsgListener(advancedMsgListener);
    
    /**
    * Received a new message
    * @param msg Message
    */
    public void onRecvNewMessage(V2TIMMessage msg) {
    // Parse the `groupID` and `userID`
    String groupID = msg.getGroupID();
    String userID = msg.getUserID();
    
    // Determine whether it's a one-to-one chat or group chat
    // If `groupID` is not empty, the message is from a group chat; if `userID` is not empty, the message is from a one-to-one chat.
    
    // Parse the text message in `msg`
    if (msg.getElemType() == V2TIMMessage.V2TIM_ELEM_TYPE_TEXT) {
    V2TIMTextElem textElem = msg.getTextElem();
    String text = textElem.getText();
    Log.i("onRecvNewMessage", "text:" + text);
    }
    }
    // Set the event listener
    [[V2TIMManager sharedInstance] addAdvancedMsgListener:self];
    
    /// Receive the message
    /// @param msg Message object
    - (void)onRecvNewMessage:(V2TIMMessage *)msg {
    // Parse the `groupID` and `userID`
    NSString *groupID = msg.groupID;
    NSString *userID = msg.userID;
    
    // Determine whether it's a one-to-one chat or group chat
    // If `groupID` is not empty, the message is from a group chat; if `userID` is not empty, the message is from a one-to-one chat.
    
    // Parse the text message in `msg`
    if (msg.elemType == V2TIM_ELEM_TYPE_TEXT) {
    V2TIMTextElem *textElem = msg.textElem;
    NSString *text = textElem.text;
    NSLog(@"onRecvNewMessage, text: %@", text);
    }
    }
    class AdvancedMsgListener final : public V2TIMAdvancedMsgListener {
    public:
    /**
    * Received a new message
    *
    * @param message Message
    */
    void OnRecvNewMessage(const V2TIMMessage& message) override {
    // Parse the `groupID` and `userID`
    V2TIMString groupID = message.groupID;
    V2TIMString userID = message.userID;
    
    // Determine whether it's a one-to-one chat or group chat
    // If `groupID` is not empty, the message is from a group chat;
    // if `userID` is not empty, the message is from a one-to-one chat.
    // Parse the text message in `message`
    if (message.elemList.Size() == 1) {
    V2TIMElem* elem = message.elemList[0];
    if (elem->elemType == V2TIMElemType::V2TIM_ELEM_TYPE_TEXT) {
    auto textElem = static_cast<V2TIMTextElem*>(elem);
    V2TIMString text = textElem->text;
    // Parse the message and display it on the UI
    std::cout << "text:" << std::string{text.CString(), text.Size()} << std::endl;
    }
    }
    }
    // Other members ...
    };
    
    // Note that `advancedMsgListener` should not be released before the IM SDK is uninitialized,
    // otherwise the message callback cannot be called.
    AdvancedMsgListener advancedMsgListener;
    V2TIMManager::GetInstance()->GetMessageManager()->AddAdvancedMsgListener(&advancedMsgListener);

    Receiving a Custom Message

    Receiving a message with the simple message listener

    Custom one-to-one message

    The receiver can receive a custom one-to-one message by using the simple message listener in the following steps:
    1. Call addSimpleMsgListener to set the event listener.
    2. Listen for the onRecvC2CCustomMessage callback (Android / iOS and macOSWindows) to receive custom one-to-one messages.
    3. To stop receiving messages, call removeSimpleMsgListener to remove the listener. This step is optional and can be performed as needed.
    Sample code:
    Android
    iOS and macOS
    Windows
    /**
    * Receive the custom one-to-one message
    * @param msgID Message ID
    * @param sender Sender information
    * @param customData The sent content
    */
    public void onRecvC2CCustomMessage(String msgID, V2TIMUserInfo sender, byte[] customData) {
    Log.i("onRecvC2CCustomMessage", "msgID:" + msgID + ", from:" + sender.getNickName() + ", content:" + new String(customData));
    }
    /// Receive the custom one-to-one message
    /// @param msgID Message ID
    /// @param info Sender information
    /// @param data The binary content of the custom message
    - (void)onRecvC2CCustomMessage:(NSString *)msgID sender:(V2TIMUserInfo *)info customData:(NSData *)data {
    NSLog(@"onRecvC2CCustomMessage, msgID: %@, sender: %@, customData: %@", msgID, info, data);
    }
    class SimpleMsgListener final : public V2TIMSimpleMsgListener {
    public:
    /**
    * Receive the custom one-to-one message
    *
    * @param msgID Message ID
    * @param sender Sender information
    * @param customData The sent content
    */
    void OnRecvC2CCustomMessage(const V2TIMString& msgID, const V2TIMUserFullInfo& sender,
    const V2TIMBuffer& customData) override {
    // Parse the message and display it on the UI
    std::cout << "customData:"
    << std::string{reinterpret_cast<const char*>(customData.Data()), customData.Size()}
    << std::endl;
    }
    // Other members ...
    };
    
    // Note that `simpleMsgListener` should not be released before the IM SDK is uninitialized,
    // otherwise the message callback cannot be called.
    SimpleMsgListener simpleMsgListener;
    V2TIMManager::GetInstance()->AddSimpleMsgListener(&simpleMsgListener);

    Custom group message

    The receiver can receive a custom group message by using the simple message listener in the following steps:
    1. Call addSimpleMsgListener to set the event listener.
    2. Listen for the onRecvGroupCustomMessage callback (Android / iOS and macOSWindows) to receive custom group messages.
    3. To stop receiving messages, call removeSimpleMsgListener to remove the listener. This step is optional and can be performed as needed.
    Android
    iOS and macOS
    Windows
    /**
    * Receive the custom group message
    * @param msgID Message ID
    * @param groupID Group ID
    * @param sender The group member information of the sender
    * @param customData The sent content
    */
    public void onRecvGroupCustomMessage(String msgID, String groupID, V2TIMGroupMemberInfo sender, byte[] customData) {
    Log.i("onRecvGroupCustomMessage", "msgID:" + msgID + ", groupID:" + groupID + ", from:" + sender.getNickName() + ", content:" + new String(customData));
    }
    /// Receive the custom group message
    /// @param msgID Message ID
    /// @param groupID Group ID
    /// @param info Sender information
    /// @param text The binary content of the custom message
    - (void)onRecvGroupCustomMessage:(NSString *)msgID groupID:(NSString *)groupID sender:(V2TIMGroupMemberInfo *)info customData:(NSData *)data {
    NSLog(@"onRecvGroupCustomMessage, msgID: %@, groupID: %@, sender: %@, customData: %@", msgID, groupID, info, data);
    }
    class SimpleMsgListener final : public V2TIMSimpleMsgListener {
    public:
    /**
    * Receive the custom group message
    *
    * @param msgID Message ID
    * @param groupID Group ID
    * @param sender The group member information of the sender
    * @param customData The sent content
    */
    void OnRecvGroupCustomMessage(const V2TIMString& msgID, const V2TIMString& groupID,
    const V2TIMGroupMemberFullInfo& sender,
    const V2TIMBuffer& customData) override {
    // Parse the message and display it on the UI
    std::cout << "customData:"
    << std::string{reinterpret_cast<const char*>(customData.Data()), customData.Size()}
    << std::endl;
    }
    // Other members ...
    };
    
    // Note that `simpleMsgListener` should not be released before the IM SDK is uninitialized,
    // otherwise the message callback cannot be called.
    SimpleMsgListener simpleMsgListener;
    V2TIMManager::GetInstance()->AddSimpleMsgListener(&simpleMsgListener);

    Receiving a message with the advanced message listener

    The receiver can receive a custom one-to-one or group message by using the advanced message listener in the following steps:
    1. Call addAdvancedMsgListener to set the event listener.
    2. Listen for the onRecvNewMessage callback (Android / iOS and macOSWindows) to receive custom messages.
    3. To stop receiving messages, call removeAdvancedMsgListener to remove the listener. This step is optional and can be performed as needed.
    Sample code:
    Android
    iOS and macOS
    Windows
    // Set the event listener
    V2TIMManager.getMessageManager().addAdvancedMsgListener(v2TIMAdvancedMsgListener);
    
    // Receive the message
    public void onRecvNewMessage(V2TIMMessage msg) {
    // Parse the `groupID` and `userID`
    String groupID = msg.getGroupID();
    String userID = msg.getUserID();
    
    // Determine whether it's a one-to-one chat or group chat
    // If `groupID` is not empty, the message is from a group chat; if `userID` is not empty, the message is from a one-to-one chat.
    
    // Parse the custom message in `msg`
    if (msg.getElemType() == V2TIMMessage.V2TIM_ELEM_TYPE_CUSTOM) {
    V2TIMCustomElem customElem = msg.getCustomElem();
    String data = new String(customElem.getData());
    Log.i("onRecvNewMessage", "customData:" + data);
    }
    }
    // Set the event listener
    [[V2TIMManager sharedInstance] addAdvancedMsgListener:self];
    
    /// Receive the message
    /// @param msg Message object
    - (void)onRecvNewMessage:(V2TIMMessage *)msg {
    // Parse the `groupID` and `userID`
    NSString *groupID = msg.groupID;
    NSString *userID = msg.userID;
    
    // Determine whether it's a one-to-one chat or group chat
    // If `groupID` is not empty, the message is from a group chat; if `userID` is not empty, the message is from a one-to-one chat.
    
    // Parse the custom message in `msg`
    if (msg.elemType == V2TIM_ELEM_TYPE_CUSTOM) {
    V2TIMCustomElem *customElem = msg.customElem;
    NSData *customData = customElem.data;
    NSLog(@"onRecvNewMessage, customData: %@", customData);
    }
    }
    class AdvancedMsgListener final : public V2TIMAdvancedMsgListener {
    public:
    /**
    * Receive the message
    *
    * @param message Message
    */
    void OnRecvNewMessage(const V2TIMMessage& message) override {
    // Parse the `groupID` and `userID`
    V2TIMString groupID = message.groupID;
    V2TIMString userID = message.userID;
    
    // Determine whether it's a one-to-one chat or group chat
    // If `groupID` is not empty, the message is from a group chat;
    // if `userID` is not empty, the message is from a one-to-one chat.
    
    // Parse the custom message in `message`
    if (message.elemList.Size() == 1) {
    V2TIMElem* elem = message.elemList[0];
    if (elem->elemType == V2TIMElemType::V2TIM_ELEM_TYPE_CUSTOM) {
    auto customElem = static_cast<V2TIMCustomElem*>(elem);
    V2TIMBuffer data = customElem->data;
    // Parse the message and display it on the UI
    std::cout << "data:"
    << std::string{reinterpret_cast<const char*>(data.Data()), data.Size()}
    << std::endl;
    }
    }
    }
    // Other members ...
    };
    
    // Note that `advancedMsgListener` should not be released before the IM SDK is uninitialized,
    // otherwise the message callback cannot be called.
    AdvancedMsgListener advancedMsgListener;
    V2TIMManager::GetInstance()->GetMessageManager()->AddAdvancedMsgListener(&advancedMsgListener);

    Receiving a Rich Media Message

    The receiver can receive a rich media message only by using the advanced message listener in the following steps:
    1. Call the addAdvancedMsgListener API to set the advanced message listener.
    2. Listen for the onRecvNewMessage callback (Android / iOS and macOSWindows) to receive the V2TIMMessage message.
    3. Parse the elemType attribute in the V2TIMMessage message and then parse the message again according to the type to get the specific content of the elements in the message.
    4. To stop receiving messages, call removeAdvancedMsgListener to remove the listener. This step is optional and can be performed as needed.

    Image message

    The image in an image message can be in three formats: original, large, and thumbnail. The latter two are automatically generated by the SDK during message sending and can be ignored.
    Large image: A large image is an image obtained after the original image is proportionally compressed. After the compression, the height or width (whichever is shorter) is equal to 720 pixels.
    Thumbnail: A thumbnail is an image obtained after the original image is proportionally compressed. After the compression, the height or width (whichever is shorter) is equal to 198 pixels.
    After the image message is received, we recommend you call the SDK's downloadImage (Android / iOS and macOSWindows) to download the image and then render it to the UI layer.
    To avoid repeated download and save resources, we recommend you set the uuid attribute value of the V2TIMImage object to the image download path to identify the image.
    The following sample code demonstrates how to parse the image content from V2TIMMessage:
    Android
    iOS and macOS
    Windows
    public void onRecvNewMessage(V2TIMMessage msg) {
    if (msg.getElemType() == V2TIMMessage.V2TIM_ELEM_TYPE_IMAGE) {
    // Image message
    V2TIMImageElem v2TIMImageElem = msg.getImageElem();
    // The image in an image message can be in three formats: original, large, and thumbnail. The latter two are automatically generated by the SDK during message sending and can be ignored.
    // Large image: A large image is an image obtained after the original image is proportionally compressed. After the compression, the height or width (whichever is shorter) is equal to 720 pixels.
    // Thumbnail: A thumbnail is an image obtained after the original image is proportionally compressed. After the compression, the height or width (whichever is shorter) is equal to 198 pixels.
    List<V2TIMImageElem.V2TIMImage> imageList = v2TIMImageElem.getImageList();
    for (V2TIMImageElem.V2TIMImage v2TIMImage : imageList) {
    // Image ID, which is an internal ID and can be used as an external cache key
    String uuid = v2TIMImage.getUUID();
    // Image types, encompassing three distinct varieties: V2TIM_IMAGE_TYPE_ORIGIN (Original), V2TIM_IMAGE_TYPE_THUMB (Thumbnail), and V2TIM_IMAGE_TYPE_LARGE (Large).
    int imageType = v2TIMImage.getType();
    // Image size (bytes)
    int size = v2TIMImage.getSize();
    // Image width
    int width = v2TIMImage.getWidth();
    // Image height
    int height = v2TIMImage.getHeight();
    // Set the image download path `imagePath`. Here, `uuid` can be used as an identifier to avoid repeated download.
    String imagePath = "/sdcard/im/image/" + "myUserID" + uuid;
    File imageFile = new File(imagePath);
    // Determine whether there is a downloaded image file in `imagePath`
    if (!imageFile.exists()) {
    // Download the image
    v2TIMImage.downloadImage(imagePath, new V2TIMDownloadCallback() {
    @Override
    public void onProgress(V2TIMElem.V2ProgressInfo progressInfo) {
    // Callback for download progress. `v2ProgressInfo.getCurrentSize()` indicates the downloaded file size, and `v2ProgressInfo.getTotalSize()` indicates the total file size.
    }
    @Override
    public void onError(int code, String desc) {
    // Download failed
    }
    @Override
    public void onSuccess() {
    // Downloaded
    }
    });
    } else {
    // The image already exists.
    }
    }
    }
    }
    - (void)onRecvNewMessage:(V2TIMMessage *)msg {
    if (msg.elemType == V2TIM_ELEM_TYPE_IMAGE) {
    V2TIMImageElem *imageElem = msg.imageElem;
    // The list of original images, large images, and thumbnails
    NSArray<V2TIMImage *> *imageList = imageElem.imageList;
    for (V2TIMImage *timImage in imageList) {
    // Image ID, which is an internal ID and can be used as an external cache key
    NSString *uuid = timImage.uuid;
    // Image type
    V2TIMImageType type = timImage.type;
    // Image size (bytes)
    int size = timImage.size;
    // Image width
    int width = timImage.width;
    // Image height
    int height = timImage.height;
    // Set the image download path `imagePath`. Here, `uuid` can be used as an identifier to avoid repeated download.
    NSString *imagePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat: @"testImage%@", timImage.uuid]];
    // Determine whether there is a downloaded image file in `imagePath`
    if (![[NSFileManager defaultManager] fileExistsAtPath:imagePath]) {
    // Download the image
    [timImage downloadImage:imagePath progress:^(NSInteger curSize, NSInteger totalSize) {
    // Download progress
    NSLog(@"Image download progress: curSize: %lu,totalSize:%lu",curSize,totalSize);
    } succ:^{
    // Downloaded successfully
    NSLog(@"Image downloaded");
    } fail:^(int code, NSString *msg) {
    // Download failed
    NSLog(@"Failed to download the image: code: %d,msg:%@",code,msg);
    }];
    } else {
    // The image already exists.
    }
    NSLog(@"Image information: uuid:%@, type:%ld, size:%d, width:%d, height:%d", uuid, (long)type, size, width, height);
    }
    }
    }
    class DownloadCallback final : public V2TIMDownloadCallback {
    public:
    using SuccessCallback = std::function<void()>;
    using ErrorCallback = std::function<void(int, const V2TIMString&)>;
    using DownLoadProgressCallback = std::function<void(uint64_t, uint64_t)>;
    
    DownloadCallback() = default;
    ~DownloadCallback() override = default;
    
    void SetCallback(SuccessCallback success_callback, ErrorCallback error_callback,
    DownLoadProgressCallback download_progress_callback) {
    success_callback_ = std::move(success_callback);
    error_callback_ = std::move(error_callback);
    download_progress_callback_ = std::move(download_progress_callback);
    }
    void OnSuccess() override {
    if (success_callback_) {
    success_callback_();
    }
    }
    void OnError(int error_code, const V2TIMString& error_message) override {
    if (error_callback_) {
    error_callback_(error_code, error_message);
    }
    }
    void OnDownLoadProgress(uint64_t currentSize, uint64_t totalSize) override {
    if (download_progress_callback_) {
    download_progress_callback_(currentSize, totalSize);
    }
    }
    
    private:
    SuccessCallback success_callback_;
    ErrorCallback error_callback_;
    DownLoadProgressCallback download_progress_callback_;
    };
    
    class AdvancedMsgListener final : public V2TIMAdvancedMsgListener {
    public:
    void OnRecvNewMessage(const V2TIMMessage& message) override {
    if (message.elemList.Size() == 1) {
    V2TIMElem* elem = message.elemList[0];
    if (elem->elemType == V2TIMElemType::V2TIM_ELEM_TYPE_IMAGE) {
    // Image message
    auto imageElem = static_cast<V2TIMImageElem*>(elem);
    // The image in an image message can be in three formats: original, large, and thumbnail.
    // The latter two are automatically generated by the SDK during message sending and can be ignored.
    // Large image: A large image is an image obtained after the original image is proportionally compressed.
    // After the compression, the height or width (whichever is shorter) is equal to 720 pixels.
    // Thumbnail: A thumbnail is an image obtained after the original image is proportionally compressed.
    // After the compression, the height or width (whichever is shorter) is equal to 198 pixels.
    V2TIMImageVector imageList = imageElem->imageList;
    for (size_t i = 0; i < imageList.Size(); ++i) {
    V2TIMImage& image = imageList[i];
    // Image ID, which is an internal ID and can be used as an external cache key
    V2TIMString uuid = image.uuid;
    // Image type
    V2TIMImageType type = image.type;
    // Image size (bytes) when type == V2TIMImageType::V2TIM_IMAGE_TYPE_ORIGIN
    uint64_t size = image.size;
    // Image width
    uint32_t width = image.width;
    // Image height
    uint32_t height = image.height;
    // Set the image download path `imagePath`.
    // Here, `uuid` can be used as an identifier to avoid repeated download.
    std::filesystem::path imagePath = u8"./File/Image/"s + uuid.CString();
    // Determine whether there is a downloaded image file in `imagePath`
    if (!std::filesystem::exists(imagePath)) {
    std::filesystem::create_directories(imagePath.parent_path());
    auto callback = new DownloadCallback{};
    callback->SetCallback(
    [=]() {
    // Downloaded
    delete callback;
    },
    [=](int error_code, const V2TIMString& error_message) {
    // Download failed
    delete callback;
    },
    [=](uint64_t currentSize, uint64_t totalSize) {
    // Callback for download progress.
    // `currentSize` indicates the downloaded file size,
    // and `totalSize` indicates the total file size.
    });
    image.DownloadImage(imagePath.string().c_str(), callback);
    } else {
    // The image already exists.
    }
    }
    }
    }
    }
    // Other members ...
    };
    
    // Note that `advancedMsgListener` should not be released before the IM SDK is uninitialized,
    // otherwise the message callback cannot be called.
    AdvancedMsgListener advancedMsgListener;
    V2TIMManager::GetInstance()->GetMessageManager()->AddAdvancedMsgListener(&advancedMsgListener);

    Video message

    After a video message is received, a video preview is displayed on the chat page, and the video is played back after the user clicks the message. Two steps are required:
    1. Download the video screenshot. We recommend you call the SDK's downloadSnapshot (Android / iOS and macOSWindows) for download.
    2. Download the video. We recommend you call the SDK's downloadVideo (Android / iOS and macOSWindows) for download.
    To avoid repeated download and save resources, we recommend you set the videoUUID attribute value of the V2TIMVideoElem object to the video download path to identify the video.
    The following sample code demonstrates how to parse the video content from V2TIMMessage:
    Android
    iOS and macOS
    Windows
    public void onRecvNewMessage(V2TIMMessage msg) {
    if (msg.getElemType() == V2TIMMessage.V2TIM_ELEM_TYPE_VIDEO) {
    // Video message
    V2TIMVideoElem v2TIMVideoElem = msg.getVideoElem();
    // Video screenshot ID, which is an internal ID and can be used as an external cache key
    String snapshotUUID = v2TIMVideoElem.getSnapshotUUID();
    // Video screenshot file size
    int snapshotSize = v2TIMVideoElem.getSnapshotSize();
    // Video screenshot width
    int snapshotWidth = v2TIMVideoElem.getSnapshotWidth();
    // Video screenshot height
    int snapshotHeight = v2TIMVideoElem.getSnapshotHeight();
    // Video ID, which is an internal ID and can be used as an external cache key
    String videoUUID = v2TIMVideoElem.getVideoUUID();
    // Video file size
    int videoSize = v2TIMVideoElem.getVideoSize();
    // Video duration
    int duration = v2TIMVideoElem.getDuration();
    // Set the video screenshot file path. Here, `uuid` can be used as an identifier to avoid repeated download.
    String snapshotPath = "/sdcard/im/snapshot/" + "myUserID" + snapshotUUID;
    File snapshotFile = new File(snapshotPath);
    if (!snapshotFile.exists()) {
    v2TIMVideoElem.downloadSnapshot(snapshotPath, new V2TIMDownloadCallback() {
    @Override
    public void onProgress(V2TIMElem.V2ProgressInfo progressInfo) {
    // Callback for download progress. `v2ProgressInfo.getCurrentSize()` indicates the downloaded file size, and `v2ProgressInfo.getTotalSize()` indicates the total file size.
    }
    @Override
    public void onError(int code, String desc) {
    // Download failed
    }
    @Override
    public void onSuccess() {
    // Downloaded
    }
    });
    } else {
    // The file already exists.
    }
    
    // Set the video file path. Here, `uuid` can be used as an identifier to avoid repeated download.
    String videoPath = "/sdcard/im/video/" + "myUserID" + videoUUID;
    File videoFile = new File(videoPath);
    if (!videoFile.exists()) {
    v2TIMVideoElem.downloadVideo(videoPath, new V2TIMDownloadCallback() {
    @Override
    public void onProgress(V2TIMElem.V2ProgressInfo progressInfo) {
    // Callback for download progress. `v2ProgressInfo.getCurrentSize()` indicates the downloaded file size, and `v2ProgressInfo.getTotalSize()` indicates the total file size.
    }
    @Override
    public void onError(int code, String desc) {
    // Download failed
    }
    @Override
    public void onSuccess() {
    // Downloaded
    }
    });
    } else {
    // The file already exists.
    }
    }
    }
    - (void)onRecvNewMessage:(V2TIMMessage *)msg {
    if (msg.elemType == V2TIM_ELEM_TYPE_VIDEO) {
    V2TIMVideoElem *videoElem = msg.videoElem;
    // Video screenshot ID, which is an internal ID and can be used as an external cache key
    NSString *snapshotUUID = videoElem.snapshotUUID;
    // Video screenshot file size
    int snapshotSize = videoElem.snapshotSize;
    // Video screenshot width
    int snapshotWidth = videoElem.snapshotWidth;
    // Video screenshot height
    int snapshotHeight = videoElem.snapshotHeight;
    // Video ID, which is an internal ID and can be used as an external cache key
    NSString *videoUUID = videoElem.videoUUID;
    // Video file size
    int videoSize = videoElem.videoSize;
    // Video duration
    int duration = videoElem.duration;
    // Set the video screenshot file path. Here, `uuid` can be used as an identifier to avoid repeated download.
    NSString *snapshotPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat: @"testVideoSnapshot%@",snapshotUUID]];
    if (![[NSFileManager defaultManager] fileExistsAtPath:snapshotPath]) {
    // Download the video screenshot
    [videoElem downloadSnapshot:snapshotPath progress:^(NSInteger curSize, NSInteger totalSize) {
    // Download progress
    NSLog(@"%@", [NSString stringWithFormat:@"Video screenshot download progress: curSize: %lu,totalSize:%lu",curSize,totalSize]);
    } succ:^{
    // Downloaded successfully
    NSLog(@"Video screenshot downloaded");
    } fail:^(int code, NSString *msg) {
    // Download failed
    NSLog(@"%@", [NSString stringWithFormat:@"Failed to download the video screenshot: code: %d,msg:%@",code,msg]);
    }];
    } else {
    // The video screenshot already exists.
    }
    NSLog(@"Video screenshot information: snapshotUUID:%@, snapshotSize:%d, snapshotWidth:%d, snapshotWidth:%d, snapshotPath:%@", snapshotUUID, snapshotSize, snapshotWidth, snapshotHeight, snapshotPath);
    
    // Set the video file path. Here, `uuid` can be used as an identifier to avoid repeated download.
    NSString *videoPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat: @"testVideo%@",videoUUID]];
    if (![[NSFileManager defaultManager] fileExistsAtPath:videoPath]) {
    // Download the video
    [videoElem downloadVideo:videoPath progress:^(NSInteger curSize, NSInteger totalSize) {
    // Download progress
    NSLog(@"%@", [NSString stringWithFormat:@"Video download progress: curSize: %lu,totalSize:%lu",curSize,totalSize]);
    } succ:^{
    // Downloaded successfully
    NSLog(@"Video downloaded");
    } fail:^(int code, NSString *msg) {
    // Download failed
    NSLog(@"%@", [NSString stringWithFormat:@"Failed to download the video: code: %d,msg:%@",code,msg]);
    }];
    } else {
    // The video already exists.
    }
    NSLog(@"Video information: videoUUID:%@, videoSize:%d, duration:%d, videoPath:%@", videoUUID, videoSize, duration, videoPath);
    }
    }
    class DownloadCallback final : public V2TIMDownloadCallback {
    public:
    using SuccessCallback = std::function<void()>;
    using ErrorCallback = std::function<void(int, const V2TIMString&)>;
    using DownLoadProgressCallback = std::function<void(uint64_t, uint64_t)>;
    
    DownloadCallback() = default;
    ~DownloadCallback() override = default;
    
    void SetCallback(SuccessCallback success_callback, ErrorCallback error_callback,
    DownLoadProgressCallback download_progress_callback) {
    success_callback_ = std::move(success_callback);
    error_callback_ = std::move(error_callback);
    download_progress_callback_ = std::move(download_progress_callback);
    }
    void OnSuccess() override {
    if (success_callback_) {
    success_callback_();
    }
    }
    void OnError(int error_code, const V2TIMString& error_message) override {
    if (error_callback_) {
    error_callback_(error_code, error_message);
    }
    }
    void OnDownLoadProgress(uint64_t currentSize, uint64_t totalSize) override {
    if (download_progress_callback_) {
    download_progress_callback_(currentSize, totalSize);
    }
    }
    
    private:
    SuccessCallback success_callback_;
    ErrorCallback error_callback_;
    DownLoadProgressCallback download_progress_callback_;
    };
    
    class AdvancedMsgListener final : public V2TIMAdvancedMsgListener {
    public:
    void OnRecvNewMessage(const V2TIMMessage& message) override {
    if (message.elemList.Size() == 1) {
    V2TIMElem* elem = message.elemList[0];
    if (elem->elemType == V2TIMElemType::V2TIM_ELEM_TYPE_VIDEO) {
    // Video message
    auto videoElem = static_cast<V2TIMVideoElem*>(elem);
    // Video ID, which is an internal ID and can be used as an external cache key
    V2TIMString videoUUID = videoElem->videoUUID;
    // Video file size
    uint64_t videoSize = videoElem->videoSize;
    // Video type
    V2TIMString videoType = videoElem->videoType;
    // Video duration
    uint32_t duration = videoElem->duration;
    // Video screenshot ID, which is an internal ID and can be used as an external cache key
    V2TIMString snapshotUUID = videoElem->snapshotUUID;
    // Video screenshot file size
    uint64_t snapshotSize = videoElem->snapshotSize;
    // Video screenshot width
    uint32_t snapshotWidth = videoElem->snapshotWidth;
    // Video screenshot height
    uint32_t snapshotHeight = videoElem->snapshotHeight;
    
    // Set the video file path. Here, `uuid` can be used as an identifier to avoid repeated download.
    std::filesystem::path videoPath = u8"./File/Video/"s + videoUUID.CString();
    if (!std::filesystem::exists(videoPath)) {
    std::filesystem::create_directories(videoPath.parent_path());
    auto callback = new DownloadCallback{};
    callback->SetCallback(
    [=]() {
    // Downloaded
    delete callback;
    },
    [=](int error_code, const V2TIMString& error_message) {
    // Download failed
    delete callback;
    },
    [=](uint64_t currentSize, uint64_t totalSize) {
    // Callback for download progress.
    // `currentSize` indicates the downloaded file size,
    // and `totalSize` indicates the total file size.
    });
    videoElem->DownloadVideo(videoPath.string().c_str(), callback);
    } else {
    // The file already exists.
    }
    
    // Set the video screenshot file path. Here, `uuid` can be used as an identifier to avoid repeated download.
    std::filesystem::path snapshotPath = u8"./File/Snapshot/"s + snapshotUUID.CString();
    if (!std::filesystem::exists(snapshotPath)) {
    std::filesystem::create_directories(snapshotPath.parent_path());
    auto callback = new DownloadCallback{};
    callback->SetCallback(
    [=]() {
    // Downloaded
    delete callback;
    },
    [=](int error_code, const V2TIMString& error_message) {
    // Download failed
    delete callback;
    },
    [=](uint64_t currentSize, uint64_t totalSize) {
    // Callback for download progress.
    // `currentSize` indicates the downloaded file size,
    // and `totalSize` indicates the total file size.
    });
    videoElem->DownloadSnapshot(snapshotPath.string().c_str(), callback);
    } else {
    // The file already exists.
    }
    }
    }
    }
    // Other members ...
    };
    
    // Note that `advancedMsgListener` should not be released before the IM SDK is uninitialized,
    // otherwise the message callback cannot be called.
    AdvancedMsgListener advancedMsgListener;
    V2TIMManager::GetInstance()->GetMessageManager()->AddAdvancedMsgListener(&advancedMsgListener);

    Audio message

    After the audio message is received, we recommend you call the SDK's downloadSound (Android / iOS and macOSWindows) to download the audio and then play it back.
    To avoid repeated download and save resources, we recommend you set the uuid attribute value of the V2TIMSoundElem object to the audio download path to identify the audio.
    The following sample code demonstrates how to parse the audio content from V2TIMMessage:
    Android
    iOS and macOS
    Windows
    public void onRecvNewMessage(V2TIMMessage msg) {
    if (msg.getElemType() == V2TIMMessage.V2TIM_ELEM_TYPE_SOUND) {
    // Audio message
    V2TIMSoundElem v2TIMSoundElem = msg.getSoundElem();
    // Audio ID, which is an internal ID and can be used as an external cache key
    String uuid = v2TIMSoundElem.getUUID();
    // Audio file size
    int dataSize = v2TIMSoundElem.getDataSize();
    // Audio duration
    int duration = v2TIMSoundElem.getDuration();
    // Set the audio file path `soundPath`. Here, `uuid` can be used as an identifier to avoid repeated download
    String soundPath = "/sdcard/im/sound/" + "myUserID" + uuid;
    File imageFile = new File(soundPath);
    // Determine whether there is a downloaded audio file in `soundPath`
    if (!imageFile.exists()) {
    v2TIMSoundElem.downloadSound(soundPath, new V2TIMDownloadCallback() {
    @Override
    public void onProgress(V2TIMElem.V2ProgressInfo progressInfo) {
    // Callback for download progress. `v2ProgressInfo.getCurrentSize()` indicates the downloaded file size, and `v2ProgressInfo.getTotalSize()` indicates the total file size.
    }
    @Override
    public void onError(int code, String desc) {
    // Download failed
    }
    @Override
    public void onSuccess() {
    // Downloaded
    }
    });
    } else {
    // The file already exists.
    }
    }
    }
    - (void)onRecvNewMessage:(V2TIMMessage *)msg {
    if (msg.elemType == V2TIM_ELEM_TYPE_SOUND) {
    V2TIMSoundElem *soundElem = msg.soundElem;
    // Audio ID, which is an internal ID and can be used as an external cache key
    NSString *uuid = soundElem.uuid;
    // Audio file size
    int dataSize = soundElem.dataSize;
    // Audio duration
    int duration = soundElem.duration;
    // Set the audio file path `soundPath`. Here, `uuid` can be used as an identifier to avoid repeated download
    NSString *soundPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat: @"testSound%@",uuid]];
    // Determine whether there is a downloaded audio file in `soundPath`
    if (![[NSFileManager defaultManager] fileExistsAtPath:soundPath]) {
    // Download the audio
    [soundElem downloadSound:soundPath progress:^(NSInteger curSize, NSInteger totalSize) {
    // Download progress
    NSLog(@"Audio download progress: curSize: %lu,totalSize:%lu",curSize,totalSize);
    } succ:^{
    // Downloaded successfully
    NSLog(@"Audio downloaded");
    } fail:^(int code, NSString *msg) {
    // Download failed
    NSLog(@"Failed to download the audio: code: %d,msg:%@",code,msg);
    }];
    } else {
    // The audio already exists.
    }
    NSLog(@"Audio information: uuid:%@, dataSize:%d, duration:%d, soundPath:%@", uuid, dataSize, duration, soundPath);
    }
    }
    class DownloadCallback final : public V2TIMDownloadCallback {
    public:
    using SuccessCallback = std::function<void()>;
    using ErrorCallback = std::function<void(int, const V2TIMString&)>;
    using DownLoadProgressCallback = std::function<void(uint64_t, uint64_t)>;
    
    DownloadCallback() = default;
    ~DownloadCallback() override = default;
    
    void SetCallback(SuccessCallback success_callback, ErrorCallback error_callback,
    DownLoadProgressCallback download_progress_callback) {
    success_callback_ = std::move(success_callback);
    error_callback_ = std::move(error_callback);
    download_progress_callback_ = std::move(download_progress_callback);
    }
    void OnSuccess() override {
    if (success_callback_) {
    success_callback_();
    }
    }
    void OnError(int error_code, const V2TIMString& error_message) override {
    if (error_callback_) {
    error_callback_(error_code, error_message);
    }
    }
    void OnDownLoadProgress(uint64_t currentSize, uint64_t totalSize) override {
    if (download_progress_callback_) {
    download_progress_callback_(currentSize, totalSize);
    }
    }
    
    private:
    SuccessCallback success_callback_;
    ErrorCallback error_callback_;
    DownLoadProgressCallback download_progress_callback_;
    };
    
    class AdvancedMsgListener final : public V2TIMAdvancedMsgListener {
    public:
    void OnRecvNewMessage(const V2TIMMessage& message) override {
    if (message.elemList.Size() == 1) {
    V2TIMElem* elem = message.elemList[0];
    if (elem->elemType == V2TIMElemType::V2TIM_ELEM_TYPE_SOUND) {
    // Audio message
    auto soundElem = static_cast<V2TIMSoundElem*>(elem);
    // Audio ID, which is an internal ID and can be used as an external cache key
    V2TIMString uuid = soundElem->uuid;
    // Audio file size
    uint64_t dataSize = soundElem->dataSize;
    // Audio duration(seconds)
    uint32_t duration = soundElem->duration;
    // Set the audio file path `soundPath`. Here, `uuid` can be used as an identifier to avoid repeated download
    std::filesystem::path soundPath = u8"./File/Sound/"s + uuid.CString();
    // Determine whether there is a downloaded audio file in `soundPath`
    if (!std::filesystem::exists(soundPath)) {
    std::filesystem::create_directories(soundPath.parent_path());
    auto callback = new DownloadCallback{};
    callback->SetCallback(
    [=]() {
    // Downloaded
    delete callback;
    },
    [=](int error_code, const V2TIMString& error_message) {
    // Download failed
    delete callback;
    },
    [=](uint64_t currentSize, uint64_t totalSize) {
    // Callback for download progress.
    // `currentSize` indicates the downloaded file size,
    // and `totalSize` indicates the total file size.
    });
    soundElem->DownloadSound(soundPath.string().c_str(), callback);
    } else {
    // The file already exists.
    }
    }
    }
    }
    // Other members ...
    };
    
    // Note that `advancedMsgListener` should not be released before the IM SDK is uninitialized,
    // otherwise the message callback cannot be called.
    AdvancedMsgListener advancedMsgListener;
    V2TIMManager::GetInstance()->GetMessageManager()->AddAdvancedMsgListener(&advancedMsgListener);

    File message

    After the file message is received, we recommend you call the SDK's downloadFile (Android / iOS and macOSWindows) to download the file and then display it.
    To avoid repeated download and save resources, we recommend you set the uuid attribute value of the V2TIMFileElem object to the file download path to identify the file.
    The following sample code demonstrates how to parse the file content from V2TIMMessage:
    Android
    iOS and macOS
    Windows
    public void onRecvNewMessage(V2TIMMessage msg) {
    if (msg.getElemType() == V2TIMMessage.V2TIM_ELEM_TYPE_FILE) {
    // File message
    V2TIMFileElem v2TIMFileElem = msg.getFileElem();
    // File ID, which is an internal ID and can be used as an external cache key
    String uuid = v2TIMFileElem.getUUID();
    // Filename
    String fileName = v2TIMFileElem.getFileName();
    // File size
    int fileSize = v2TIMFileElem.getFileSize();
    // Set the file path. Here, `uuid` can be used as an identifier to avoid repeated download.
    String filePath = "/sdcard/im/file/" + "myUserID" + uuid;
    File file = new File(filePath);
    if (!file.exists()) {
    v2TIMFileElem.downloadFile(filePath, new V2TIMDownloadCallback() {
    @Override
    public void onProgress(V2TIMElem.V2ProgressInfo progressInfo) {
    // Callback for download progress. `v2ProgressInfo.getCurrentSize()` indicates the downloaded file size, and `v2ProgressInfo.getTotalSize()` indicates the total file size.
    }
    @Override
    public void onError(int code, String desc) {
    // Download failed
    }
    @Override
    public void onSuccess() {
    // Downloaded
    }
    });
    } else {
    // The file already exists.
    }
    }
    }
    - (void)onRecvNewMessage:(V2TIMMessage *)msg {
    if (msg.elemType == V2TIM_ELEM_TYPE_FILE) {
    V2TIMFileElem *fileElem = msg.fileElem;
    // File ID, which is an internal ID and can be used as an external cache key
    NSString *uuid = fileElem.uuid;
    // Filename
    NSString *filename = fileElem.filename;
    // File size
    int fileSize = fileElem.fileSize;
    // Set the file path. Here, `uuid` can be used as an identifier to avoid repeated download.
    NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat: @"testFile%@",uuid]];
    if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
    // Download the file
    [fileElem downloadFile:filePath progress:^(NSInteger curSize, NSInteger totalSize) {
    // Download progress
    NSLog(@"%@", [NSString stringWithFormat:@"File download progress: curSize: %lu,totalSize:%lu",curSize,totalSize]);
    } succ:^{
    // Downloaded successfully
    NSLog(@"File downloaded");
    } fail:^(int code, NSString *msg) {
    // Download failed
    NSLog(@"%@", [NSString stringWithFormat:@"Failed to download the file: code: %d,msg:%@",code,msg]);
    }];
    } else {
    // The file already exists.
    }
    NSLog(@"File information: uuid:%@, filename:%@, fileSize:%d, filePath:%@", uuid, filename, fileSize, filePath);
    }
    }
    class DownloadCallback final : public V2TIMDownloadCallback {
    public:
    using SuccessCallback = std::function<void()>;
    using ErrorCallback = std::function<void(int, const V2TIMString&)>;
    using DownLoadProgressCallback = std::function<void(uint64_t, uint64_t)>;
    
    DownloadCallback() = default;
    ~DownloadCallback() override = default;
    
    void SetCallback(SuccessCallback success_callback, ErrorCallback error_callback,
    DownLoadProgressCallback download_progress_callback) {
    success_callback_ = std::move(success_callback);
    error_callback_ = std::move(error_callback);
    download_progress_callback_ = std::move(download_progress_callback);
    }
    void OnSuccess() override {
    if (success_callback_) {
    success_callback_();
    }
    }
    void OnError(int error_code, const V2TIMString& error_message) override {
    if (error_callback_) {
    error_callback_(error_code, error_message);
    }
    }
    void OnDownLoadProgress(uint64_t currentSize, uint64_t totalSize) override {
    if (download_progress_callback_) {
    download_progress_callback_(currentSize, totalSize);
    }
    }
    
    private:
    SuccessCallback success_callback_;
    ErrorCallback error_callback_;
    DownLoadProgressCallback download_progress_callback_;
    };
    
    class AdvancedMsgListener final : public V2TIMAdvancedMsgListener {
    public:
    void OnRecvNewMessage(const V2TIMMessage& message) override {
    if (message.elemList.Size() == 1) {
    V2TIMElem* elem = message.elemList[0];
    if (elem->elemType == V2TIMElemType::V2TIM_ELEM_TYPE_FILE) {
    // File message
    auto fileElem = static_cast<V2TIMFileElem*>(elem);
    // File ID, which is an internal ID and can be used as an external cache key
    V2TIMString uuid = fileElem->uuid;
    // Filename
    V2TIMString filename = fileElem->filename;
    // File size
    uint64_t fileSize = fileElem->fileSize;
    // Set the file path. Here, `uuid` can be used as an identifier to avoid repeated download.
    std::filesystem::path filePath = u8"./File/File/"s + uuid.CString();
    if (!std::filesystem::exists(filePath)) {
    std::filesystem::create_directories(filePath.parent_path());
    auto callback = new DownloadCallback{};
    callback->SetCallback(
    [=]() {
    // Downloaded
    delete callback;
    },
    [=](int error_code, const V2TIMString& error_message) {
    // Download failed
    delete callback;
    },
    [=](uint64_t currentSize, uint64_t totalSize) {
    // Callback for download progress.
    // `currentSize` indicates the downloaded file size,
    // and `totalSize` indicates the total file size.
    });
    fileElem->DownloadFile(filePath.string().c_str(), callback);
    } else {
    // The file already exists.
    }
    }
    }
    }
    // Other members ...
    };
    
    // Note that `advancedMsgListener` should not be released before the IM SDK is uninitialized,
    // otherwise the message callback cannot be called.
    AdvancedMsgListener advancedMsgListener;
    V2TIMManager::GetInstance()->GetMessageManager()->AddAdvancedMsgListener(&advancedMsgListener);

    Location message

    After receiving the location message, the receiver can parse the latitude and longitude information directly from V2TIMLocationElem. The following sample code demonstrates how to parse the location content from V2TIMMessage:
    Android
    iOS and macOS
    Windows
    public void onRecvNewMessage(V2TIMMessage msg) {
    if (msg.getElemType() == V2TIMMessage.V2TIM_ELEM_TYPE_LOCATION) {
    // Geographical location message
    V2TIMLocationElem v2TIMLocationElem = msg.getLocationElem();
    // Geographical location information description
    String desc = v2TIMLocationElem.getDesc();
    // Longitude
    double longitude = v2TIMLocationElem.getLongitude();
    // Latitude
    double latitude = v2TIMLocationElem.getLatitude();
    }
    }
    - (void)onRecvNewMessage:(V2TIMMessage *)msg {
    if (msg.elemType == V2TIM_ELEM_TYPE_LOCATION) {
    V2TIMLocationElem *locationElem = msg.locationElem;
    // Geographical location information description
    NSString *desc = locationElem.desc;
    // Longitude
    double longitude = locationElem.longitude;
    // Latitude
    double latitude = locationElem.latitude;
    NSLog(@"Geographical location information: desc: %@, longitude:%f, latitude:%f", desc, longitude, latitude);
    }
    }
    class AdvancedMsgListener final : public V2TIMAdvancedMsgListener {
    public:
    void OnRecvNewMessage(const V2TIMMessage& message) override {
    if (message.elemList.Size() == 1) {
    V2TIMElem* elem = message.elemList[0];
    if (elem->elemType == V2TIMElemType::V2TIM_ELEM_TYPE_LOCATION) {
    // Geographical location message
    auto locationElem = static_cast<V2TIMLocationElem*>(elem);
    // Geographical location information description
    V2TIMString desc = locationElem->desc;
    // Longitude
    double longitude = locationElem->longitude;
    // Latitude
    double latitude = locationElem->latitude;
    }
    }
    }
    // Other members ...
    };
    
    // Note that `advancedMsgListener` should not be released before the IM SDK is uninitialized,
    // otherwise the message callback cannot be called.
    AdvancedMsgListener advancedMsgListener;
    V2TIMManager::GetInstance()->GetMessageManager()->AddAdvancedMsgListener(&advancedMsgListener);

    Emoji message

    The SDK provides a passthrough channel only for emoji messages. For message content fields, see the definition of V2TIMFaceElem (Android / iOS and macOSWindows). Here, index and data can be customized.
    For example, the sender can set index to 1 and data to x12345 to indicate the smile emoji. The receiver parses the received emoji message as 1 and x12345 and displays the message as the smile emoji according to the preset rules.
    The following sample code demonstrates how to parse the emoji content from V2TIMMessage:
    Android
    iOS and macOS
    Windows
    public void onRecvNewMessage(V2TIMMessage msg) {
    if (msg.getElemType() == V2TIMMessage.V2TIM_ELEM_TYPE_FACE) {
    // Emoji message
    V2TIMFaceElem v2TIMFaceElem = msg.getFaceElem();
    // Emoji location
    int index = v2TIMFaceElem.getIndex();
    // Custom emoji data
    byte[] data = v2TIMFaceElem.getData();
    }
    }
    - (void)onRecvNewMessage:(V2TIMMessage *)msg {
    if (msg.elemType == V2TIM_ELEM_TYPE_FACE) {
    V2TIMFaceElem *faceElem = msg.faceElem;
    // Emoji location
    int index = faceElem.index;
    // Custom emoji data
    NSData *data = faceElem.data;
    NSLog(@"Emoji information: index: %d, data: %@", index, data);
    }
    }
    class AdvancedMsgListener final : public V2TIMAdvancedMsgListener {
    public:
    void OnRecvNewMessage(const V2TIMMessage& message) override {
    if (message.elemList.Size() == 1) {
    V2TIMElem* elem = message.elemList[0];
    if (elem->elemType == V2TIMElemType::V2TIM_ELEM_TYPE_FACE) {
    // Emoji message
    auto faceElem = static_cast<V2TIMFaceElem*>(elem);
    // Emoji location
    uint32_t index = faceElem->index;
    // Custom emoji data
    V2TIMBuffer data = faceElem->data;
    }
    }
    }
    // Other members ...
    };
    
    // Note that `advancedMsgListener` should not be released before the IM SDK is uninitialized,
    // otherwise the message callback cannot be called.
    AdvancedMsgListener advancedMsgListener;
    V2TIMManager::GetInstance()->GetMessageManager()->AddAdvancedMsgListener(&advancedMsgListener);

    Receiving a Broadcast Message

    You can only use the Broadcast Message of Audio-Video Group to send broadcast messages to all AVChatRooms under the same AppID, and all online group members in the AVChatRooms can receive this message. The SDK cannot send broadcast messages, it can only receive messages from the REST API. The receiving of messages is still in the onRecvNewMessage notification mentioned earlier. After receiving a message, you can determine whether the message is a broadcast message based on the isBroadcastMessage property.
    Note
    1. To use this feature, you need to purchase the Premium edition.
    2. This feature is available only in SDK enhanced edition v6.5.2803 or later.
    3. Only AVChatRoom supports broadcast message, other types of group are temporarily not supported.
    Android
    iOS & Mac
    Windows
    public void onRecvNewMessage(V2TIMMessage msg) {
    if (msg.isBroadcastMessage) {
    // Received the broadcast message
    }
    }
    - (void)onRecvNewMessage:(V2TIMMessage *)msg {
    if (msg.isBroadcastMessage) {
    // Received the broadcast message
    }
    }
    class AdvancedMsgListener final : public V2TIMAdvancedMsgListener {
    public:
    void OnRecvNewMessage(const V2TIMMessage& message) override {
    if (message.isBroadcastMessage) {
    // Received the broadcast message
    }
    }
    // Other members ...
    };
    
    // Note that `advancedMsgListener` should not be released before the IM SDK is uninitialized,
    // otherwise the message callback cannot be called.
    AdvancedMsgListener advancedMsgListener;
    V2TIMManager::GetInstance()->GetMessageManager()->AddAdvancedMsgListener(&advancedMsgListener);

    Receiving a Message Containing Multiple Element Objects

    1. Parse the first element object through the Message object.
    2. Get the next element object through the nextElem method of the first element object. If the next object exists, an element object instance is returned; otherwise, nil or null is returned.
    Sample code:
    Android
    iOS and macOS
    Windows
    @Override
    public void onRecvNewMessage(V2TIMMessage msg) {
    // View the first element
    int elemType = msg.getElemType();
    if (elemType == V2TIMMessage.V2TIM_ELEM_TYPE_TEXT) {
    // Text message
    V2TIMTextElem v2TIMTextElem = msg.getTextElem();
    String text = v2TIMTextElem.getText();
    // Check whether `v2TIMTextElem` is followed by more elements
    V2TIMElem elem = v2TIMTextElem.getNextElem();
    while (elem != null) {
    // Identify the element type. Here, `V2TIMCustomElem` is used as an example.
    if (elem instanceof V2TIMCustomElem) {
    V2TIMCustomElem customElem = (V2TIMCustomElem) elem;
    byte[] data = customElem.getData();
    }
    // Further check whether the current element is followed by more elements
    elem = elem.getNextElem();
    }
    // If the element is `null`, all the elements have been parsed.
    }
    }
    - (void)onRecvNewMessage:(V2TIMMessage *)msg {
    // View the first element
    if (msg.elemType == V2TIM_ELEM_TYPE_TEXT) {
    V2TIMTextElem *textElem = msg.textElem;
    NSString *text = textElem.text;
    NSLog(@"Text information: %@", text);
    // Check whether `textElem` is followed by more elements
    V2TIMElem *elem = textElem.nextElem;
    while (elem != nil) {
    // Identify the element type
    if ([elem isKindOfClass:[V2TIMCustomElem class]]) {
    V2TIMCustomElem *customElem = (V2TIMCustomElem *)elem;
    NSData *customData = customElem.data;
    NSLog(@"Custom information: %@",customData);
    }
    // Further check whether the current element is followed by more elements
    elem = elem.nextElem;
    }
    // If the element is `nil`, all the elements have been parsed.
    }
    }
    class AdvancedMsgListener final : public V2TIMAdvancedMsgListener {
    public:
    void OnRecvNewMessage(const V2TIMMessage& message) override {
    // View the first element
    for (size_t i = 0; i < message.elemList.Size(); ++i) {
    V2TIMElem* elem = message.elemList[i];
    switch (elem->elemType) {
    case V2TIMElemType::V2TIM_ELEM_TYPE_NONE: {
    // Unknown message
    } break;
    case V2TIMElemType::V2TIM_ELEM_TYPE_TEXT: {
    // Text message
    auto textElem = static_cast<V2TIMTextElem*>(elem);
    } break;
    case V2TIMElemType::V2TIM_ELEM_TYPE_CUSTOM: {
    // Custom message
    auto customElem = static_cast<V2TIMCustomElem*>(elem);
    } break;
    case V2TIMElemType::V2TIM_ELEM_TYPE_IMAGE: {
    // Image message
    auto imageElem = static_cast<V2TIMImageElem*>(elem);
    } break;
    case V2TIMElemType::V2TIM_ELEM_TYPE_SOUND: {
    // Audio message
    auto soundElem = static_cast<V2TIMSoundElem*>(elem);
    } break;
    case V2TIMElemType::V2TIM_ELEM_TYPE_VIDEO: {
    // Video message
    auto videoElem = static_cast<V2TIMVideoElem*>(elem);
    } break;
    case V2TIMElemType::V2TIM_ELEM_TYPE_FILE: {
    // File message
    auto fileElem = static_cast<V2TIMFileElem*>(elem);
    } break;
    case V2TIMElemType::V2TIM_ELEM_TYPE_LOCATION: {
    // Geographical location message
    auto locationElem = static_cast<V2TIMLocationElem*>(elem);
    } break;
    case V2TIMElemType::V2TIM_ELEM_TYPE_FACE: {
    // Emoji message
    auto faceElem = static_cast<V2TIMFaceElem*>(elem);
    } break;
    case V2TIMElemType::V2TIM_ELEM_TYPE_GROUP_TIPS: {
    // Group @ message
    auto mergerElem = static_cast<V2TIMMergerElem*>(elem);
    } break;
    case V2TIMElemType::V2TIM_ELEM_TYPE_MERGER: {
    // Merged message
    auto groupTipsElem = static_cast<V2TIMGroupTipsElem*>(elem);
    } break;
    default: {
    } break;
    }
    }
    }
    // Other members ...
    };
    
    // Note that `advancedMsgListener` should not be released before the IM SDK is uninitialized,
    // otherwise the message callback cannot be called.
    AdvancedMsgListener advancedMsgListener;
    V2TIMManager::GetInstance()->GetMessageManager()->AddAdvancedMsgListener(&advancedMsgListener);
    
    Contact Us

    Contact our sales team or business advisors to help your business.

    Technical Support

    Open a ticket if you're looking for further assistance. Our Ticket is 7x24 avaliable.

    7x24 Phone Support