# Message Customization

Chat SDK uses [ChatKit](https://github.com/stfalcon-studio/ChatKit/blob/master/docs/COMPONENT_MESSAGES_LIST.md) for the message view so I would recommend that you look at their documentation before trying to add custom message types to Chat SDK. Chat SDK really just exposes the ChatKit custom message API.

### Override Text Message Display

In this example, we will override the outcoming text message so that we can append "Hello World!" to every text message.

To do this, the first step is to make a new class that extends the `OutcomingTextMessageViewHolder`.

```java
public class CustomOutcomingTextMessageViewHolder extends OutcomingTextMessageViewHolder {
    public CustomOutcomingTextMessageViewHolder(View itemView, Object payload) {
        super(itemView, payload);
    }

    @Override
    public void onBind(MessageHolder message) {
        super.onBind(message);
        text.setText(text.getText() + " Hello World");
    }

}
```

The `onBind` method is what binds the `MessageHolder` data model to the `MesageViewHolder`.

We then need to make a new subclass of the `TextMessageHandler` class so that we can register the new view holder with Chat SDK.

```java
public class CustomTextMessageHandler extends TextMessageHandler {

    @Override
    public void onBindMessageHolders(Context context, MessageHolders holders) {
        holders.setIncomingTextConfig(IncomingTextMessageViewHolder.class, R.layout.view_holder_incoming_text_message, getAvatarClickPayload(context))
                .setOutcomingTextConfig(CustomOutcomingTextMessageViewHolder.class, R.layout.view_holder_outcoming_text_message, getAvatarClickPayload(context));
    }

}
```

This is where we are registering the view holder with ChatKit. We can also specify custom layout files.

Finally, we need to tell Chat SDK to use this message handler:

```java
ChatSDKUI.shared().getMessageCustomizer().addMessageHandler(new CustomTextMessageHandler());
```

#### Creating a Custom Message Type

Chat SDK supports completely custom message types. To do this, we need to setup a number of classes.

```java
public class ExampleSnapMessageHandler extends CustomMessageHandler {

    public static int SnapMessageType = 919;

    @Override
    public List<Byte> getTypes() {
        return types(SnapMessageType);
    }

    @Override
    public boolean hasContentFor(MessageHolder holder) {
        return holder.getClass().equals(SnapMessageHolder.class);
    }

    @Override
    public void onBindMessageHolders(Context context, MessageHolders holders) {
        holders.registerContentType(
                (byte) SnapMessageType,
                IncomingSnapMessageViewHolder.class,
                sdk.chat.ui.R.layout.view_holder_incoming_image_message,
                OutcomingSnapMessageViewHolder.class,
                sdk.chat.ui.R.layout.view_holder_outcoming_image_message,
                ChatSDKUI.shared().getMessageCustomizer());
    }
}
```

This is an example of a custom message type.&#x20;

* `getTypes()` - Which message types we support
* `hasContentFor` - Is this view holder of the correct type?
* `onBindMessageHolders` Here we register our custom types with ChatKit. We can define outcoming and incoming view holders and also define custom XML files.&#x20;

```java
public class SnapMessageHolder extends MessageHolder implements MessageContentType {

    public String customValue;

    public SnapMessageHolder(Message message) {
        super(message);
        customValue = (String) message.valueForKey("Key");
    }
}
```

The message holder exposes properties from the message to the view holders.

```java
public class IncomingSnapMessageViewHolder extends BaseIncomingTextMessageViewHolder<SnapMessageHolder> {

    TextView username;

    public IncomingSnapMessageViewHolder(View itemView, Object payload) {
        super(itemView, payload);
        username = itemView.findViewById(R.id.userName);
    }

    @Override
    public void onBind(SnapMessageHolder message) {
        super.onBind(message);

        String value = message.customValue;
        username.setText(value);

    }
}
```

The view holder binds the message holder to the view. It has access to the view and can get properties from the XML layout file using `itemView.findViewById().`&#x20;

The final step would be to send the custom message. 

```java
Disposable d = MessageSendRig.create(new MessageType(ExampleSnapMessageHandler.SnapMessageType), thread, message -> {
    // Set custom Data here
    message.setValueForKey("Value", "Key");
}).run().subscribe(() -> {
    // Success
}, throwable -> {
    // Failure
});
```

For another real world example you can look ad the `ImageMessageHandler`.

#### Image Message Handler

Chat SDK supports completely custom message types. To do this, we need to extend the `CustomMessageHandler` class. We can look at the image message handler as an example:

```java
public class ImageMessageHandler extends CustomMessageHandler {

    @Override
    public List<Byte> getTypes() {
        return types(MessageType.Image, MessageType.Location);
    }

    @Override
    public boolean hasContentFor(MessageHolder holder) {
        return holder.getClass().equals(ImageMessageHolder.class);
    }

    @Override
    public void onBindMessageHolders(Context context, MessageHolders holders) {
        holders.registerContentType(
                (byte) MessageType.Image,
                IncomingImageMessageViewHolder.class,
                getAvatarClickPayload(context),
                R.layout.view_holder_incoming_image_message,
                OutcomingImageMessageViewHolder.class,
                getAvatarClickPayload(context),
                R.layout.view_holder_outcoming_image_message,
                ChatSDKUI.shared().getMessageCustomizer());
    }

    @Override
    public void onClick(ChatActivity activity, View rootView, Message message) {
        if (message.getSender().isMe() && message.getMessageStatus() == MessageSendStatus.Failed) {
            super.onClick(activity, rootView, message);
        } else if (message.typeIs(MessageType.Image)) {
            ImageMessageOnClickHandler.onClick(activity, rootView, message.stringForKey(Keys.ImageUrl));
        }
        else if (message.typeIs(MessageType.Location)) {
            double longitude = message.doubleForKey(Keys.MessageLongitude);
            double latitude = message.doubleForKey(Keys.MessageLatitude);

            Location location = new Location(ChatSDK.getString(R.string.app_name));
            location.setLatitude(latitude);
            location.setLongitude(longitude);

            LocationMessageOnClickHandler.onClick(activity, location);
        }
    }

    @Override
    public MessageHolder onNewMessageHolder(Message message) {
        if (message.getMessageType().is(MessageType.Image, MessageType.Location)) {
            return new ImageMessageHolder(message);
        }
        return null;
    }

}
```

* The `onBindMessageHolder` allows us to register our content type with ChatKit
* The `onClick` method is called if the user clicks the message
* The `onNewMessageHolder` method is called to provide a new instance of the message holder
* The `hasContentFor` method lets the system know when this custom message type should be used

To register this with the framework, you can use the following:

```
ChatSDKUI.shared().getMessageCustomizer().addMessageHandler(new ImageMessageHandler());
```

#### Summary

This may seem complicated, if that's the case, I would recommend thoroughly reviewing the ChatKit documentation. This guide just explains how to access the ChatKit API from within Chat SDK. It doesn't go through the ChatSDK API in detail.
