# Using the UI Framework

The Mitter.io Core package provides easy access to the platform from your Android project. However, getting from zero to seeing chat messages appear in your app requires a bit of UI setup which is mostly boilerplate code.

The UI framework package aims to reduce the time taken to get a basic chat app running to the minimum by providing a lightweight framework using which you can add your custom UI elements like chat bubbles and more to your app without filling out too much boilerplate.

## Installation

Adding the UI framework package is similar to adding any library in Android. Just add the following line to your `build.gradle` file and sync your dependencies:

{% code title="build.gradle" %}

```groovy
implementation 'io.mitter.android:uiframework:0.1.5'
```

{% endcode %}

## Adding a ChannelWindow

Currently, the UI framework provides a `ChannelWindow` view which is a very thin wrapper on Android’s `RecyclerView`. The difference between `RecyclerView` and `ChannelWindow` is that the latter provides additional features like pagination out of the box.

You can add a `ChannelWindow` view to your `Activity` or `Fragment` XML layout where you want the messages to be displayed.

{% code title="activity\_chat.xml" %}

```markup
<io.mitter.android.ui.views.ChannelWindow
	android:id="@+id/channelWindow"
	android:layout_width="match_parent"
	android:layout_height="wrap_content"  
	app:layoutManager="android.support.v7.widget.LinearLayoutManager" />

```

{% endcode %}

## Creating producers

A `ChannelWindow` needs to be connected to a `ChannelWindowManager` to work. A `ChannelWindowManager` creates an internal `RecyclerView` adapter from a list of producers and displays your messages on the `ChannelWindow`.

### What are producers?

Producers are just simple view producers. You use a producer to define and create a single row of your channel list. In simple terms, this is where you control the look and feel of your message bubbles.

### Define a producer

To define a new producer, you need to implement the `ChannelWindowElementProducer` interface. This interface has 3 methods:

* `canProduceFor()` - Here, you need to write a condition which will match this view for a message type
* `getViewId()` - Here, you need to specify a layout ID which you want your message to be rendered on
* `produceView()` - This is where you get a `RecyclerView.ViewHolder` and your `Message`. You need to bind your message data to the UI using the supplied `ViewHolder`

A simple text message producer would look similar to the one below:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="TextMessageProducer.kt" %}

```kotlin
class TextMessageProducer(
    val mitter: Mitter
) : ChannelWindowElementProducer {
    override fun canProduceFor(element: Any): Boolean {
        return element is Message && element.payloadType == StandardPayloadTypeNames.TextMessage
    }

    override fun getViewId(): Int = R.layout.item_text_message

    override fun produceView(holder: RecyclerView.ViewHolder, element: Any) {
        val message = element as Message

        holder.itemView.text.text = message.textPayload
        holder.itemView.senderName.text = message.senderId.domainId()
        holder.itemView.textMessageWrapper.gravity = when (mitter.getUserId()) {
            message.senderId.domainId() -> Gravity.END
            else -> Gravity.START
        }
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="TextMessageProducer.java" %}

```java
public class TextMessageProducer implements ChannelWindowElementProducer {
    private Mitter mitter;

    public TextMessageProducer(Mitter mitter) {
        this.mitter = mitter;
    }

    @Override
    public void produceView(@NotNull RecyclerView.ViewHolder holder, @NotNull Object o) {
        Message message = (Message) o;

        TextView messageText = holder.itemView.findViewById(R.id.text);
        TextView senderNameText = holder.itemView.findViewById(R.id.senderName);
        LinearLayout textMessageWrapperLayout = holder.itemView.findViewById(R.id.textMessageWrapper);

        messageText.setText(message.getTextPayload());
        senderNameText.setText(message.getSenderId().domainId());
        if (mitter.getUserId().equals(message.getSenderId().domainId())) {
            textMessageWrapperLayout.setGravity(Gravity.END);
        } else {
            textMessageWrapperLayout.setGravity(Gravity.START);
        }
    }

    @Override
    public boolean canProduceFor(@NotNull Object o) {
        return o instanceof Message && ((Message) o).getPayloadType().equals(StandardPayloadTypeNames.TextMessage);
    }

    @Override
    public int getViewId() {
        return R.layout.item_text_message;
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

Writing a producer is quite similar to writing a `RecyclerView` adapter. A producer, however, abstracts away boilerplate code and helps you write smaller and separated view binding logic.

You can create as many producers as you want depending on your incoming message types. Some common examples would be:

* TextMessageProducer
* ImageMessageProducer
* EmojiMessageProducer

And so on.

## Setting up a manager

As already discussed in the previous section, a `ChannelWindow` needs a `ChannelWindowManager` to work. A `ChannelWindowManager` in turn needs one or more producers to function.

Now that you’ve already created a producer, you can setup your manager by passing your producer and a channel ID for which you want the messages to be loaded on the app screen.

Before you can initialise a manager, you need to have an instance of `MitterUi`. You can define an instance of `MitterUi` inside your activity where you want to display your message list (or `ChannelWindow`).

{% tabs %}
{% tab title="Kotlin" %}
{% code title="ChatActivity.kt" %}

```kotlin
lateinit var mitterUi: MitterUi

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_channel_window_test)

        mitterUi = MitterUi(
            (application as MessageApp).mitter
        )
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="ChatActivity.java" %}

```java
private MitterUi mitterUi;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mitterUi =  new MitterUi(
        ((MessageApp) getApplication()).mitter
    );
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

> Here `MessageApp` refers to the global `Application` class for this project where the `Mitter` instance is defined. Change this to wherever you’ve defined your `Mitter` instance in your project, as shown in the previous articles of this guide.

Once you have that, you can easily get an instance of `ChannelWindowManager` as follows:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="ChatActivity.kt" %}

```kotlin
val channelWindowManager = ChannelWindowManager(
            mitterUi,
            "channel-id",
            ChannelWindowConfig(producers = listOf(
                TextMessageProducer(mitterUi.mitter),
                StringProducer()
            )
)
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="ChatActivity.java" %}

```java
List<ChannelWindowElementProducer> producers = new ArrayList<>();
producers.add(new TextMessageProducer(mitterUi.getMitter()));

PaginationConfig paginationConfig = new PaginationConfig();
ChannelWindowManager channelWindowManager = new ChannelWindowManager(
    mitterUi,
    "channel-id",
    new ChannelWindowConfig(
        producers,
        paginationConfig
    )
);
```

{% endcode %}
{% endtab %}
{% endtabs %}

After that, you just need to hook your manager to your `ChannelWindow` view:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="ChatActivity.kt" %}

```kotlin
channelWindowManager.attach(channelWindow)
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="ChatActivity.java" %}

```java
ChannelWindow channelWindow = findViewById(R.id.channelWindow);
channelWindowManager.attach(channelWindow);
```

{% endcode %}
{% endtab %}
{% endtabs %}

If you now run your app, you should be able to get messages populated on your device’s screen for the channel ID you mentioned over here. This assumes that you have properly setup `Mitter` with your application credentials as shown in the previous articles.

## Paginating messages

By default, the `ChannelWindow` will load **10** messages at a single go in the specified channel every time you scroll up. You can change this behaviour and also attach a callback to get notified when pagination happens by using the `PaginationConfig` parameter in the `ChannelWindowManager`.

Get started by defining your `PaginationConfig` first:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="ChatActivity.kt" %}

```kotlin
val paginationConfig = PaginationConfig(
            itemsPerPage = 20,
            pageFetchCallback = object : PageFetchCallback {
                override fun onStart() {
                    Log.d("MitterUI", "Loading more messages")
                }

                override fun onEnd() {
                    Log.d("MitterUI", "Successfully loaded more messages")
                }
            }
)
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="ChatActivity.java" %}

```java
PaginationConfig paginationConfig = new PaginationConfig(
    20,
    3,
    new PageFetchCallback() {
        @Override
        public void onStart() {
            Log.d("MitterUI", "Loading more messages");
        }

        @Override
        public void onEnd() {
            Log.d("MitterUI", "Successfully loaded new messages");
        }
    }
);
```

{% endcode %}
{% endtab %}
{% endtabs %}

Once you have your `PaginationConfig` configured to your needs, you can pass it to your `ChannelWindowConfig` instance that you defined inside your `ChannelWindowManager` instance in the previous step to override the default pagination behaviour.

{% tabs %}
{% tab title="Kotlin" %}
{% code title="ChatActivity.kt" %}

```kotlin
val channelWindowManager = ChannelWindowManager(
            mitterUi,
            "channel-id",
            ChannelWindowConfig(
                producers = listOf(
                    TextMessageProducer(mitterUi.mitter),
                    StringProducer()
                ),
                paginationConfig = paginationConfig
            )
)
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code title="ChatActivity.java" %}

```java
ChannelWindowManager channelWindowManager = new ChannelWindowManager(
    mitterUi,
    "channel-id",
    new ChannelWindowConfig(
        producers,
        paginationConfig
    )
);
```

{% endcode %}
{% endtab %}
{% endtabs %}

Now, your manager will load up to **20** messages at a time instead of **10** and also you’ll start getting callbacks as the user starts scrolling up to fetch more messages.

This callback is useful when you want to show up a loader while fetching previous messages.

## Wrap up

That’s all you need to and can do with the UI framework right now. It’s in a beta stage. Your valuable feedbacks will allow us to improve the UI framework experience in the future releases on this package.
