---
source_url: https://www.pubnub.com/docs/chat/community-supported/android/data-components
title: Data components for PubNub Chat Components for Android
updated_at: 2026-05-20T11:04:16.040Z
---

> Documentation Index
> For a curated overview of PubNub documentation, see: https://www.pubnub.com/docs/llms.txt
> For the full list of all documentation pages, see: https://www.pubnub.com/docs/llms-full.txt


# Data components for PubNub Chat Components for Android

Data components are responsible for managing data between the [UI Components](https://www.pubnub.com/docs/chat/community-supported/android/ui-components) and the persistence layer. They also interact with the PubNub service by sending and receiving messages and signals.

Available components include:

* [Channel Data](#channel)
* [Member Data](#member)
* [Message Data](#message) (with [Message Reactions data](#message-reactions))

## Overview

PubNub Chat Components for Android utilize several layers of configurable data to drive the functionality found in the UI Components. The goal of the data layer is to provide a simple and robust foundation that can be used across any chat implementation. The two main forms of data used by the components are [persistent](#persistent-data) and [network](#network-data) objects.

### Persistent data

All the data is stored as entities in a local database using Room, an abstraction layer over SQLite. Room provides the following benefits:

* Compile-time verification of SQL queries.
* Convenience annotations that minimize repetitive and error-prone boilerplate code.
* Streamlined database migration paths.

You can find the default implementation of the database in [ChatProvider](https://www.pubnub.com/docs/chat/community-supported/android/chat-provider#configuration). It contains data entities, data access objects, and database implementation. Additionally, ChatProvider uses the repository design pattern that allows for separation of concerns by abstracting the data persistence logic.

To learn more about Room, refer to the [official Android docs](https://developer.android.com/jetpack/androidx/releases/room).

### Network data

Network objects are most commonly used when interacting with PubNub APIs. Those objects exist only at the network layer and need to be converted with [Data Mappers](#data-mappers) to be used in the application.

### UI data

The UI Components don't use persistent or network data but extra UI data objects are created for them instead. These UI data objects contain only user-readable information. For example, to display a timetoken, it must be converted to a date string first.

### Data mappers

The role of Data Mappers is to convert one data type to another to allow developers to swap between Persistent, Network, and UI data objects. For example, when the `NetworkMessage` is received by `MessageService`, the data is converted to `DBMessage` and stored in the database. Similarly to the previous example, when the user sends a message, the `MessageUi.Data` object is converted to `DBMessage` and stored in the local database. At the same time, the object is converted to `NetworkMessagePayload` and published in PubNub.

### View models

The `ViewModel` class is designed to store and manage UI-related data by communicating with services and repositories.

For more information on view models, refer to the [official Android docs](https://developer.android.com/topic/libraries/architecture/viewmodel).

### Services

The role of services is to communicate with PubNub APIs, add, get, or update objects and store them using local repositories. The default implementation of services exists in the [ChatProvider](https://www.pubnub.com/docs/chat/community-supported/android/chat-provider) composition tree.

### Data flow

The typical data flow through the components revolves around storing incoming data in the Room database instance and updating that data automatically in UI Components if the data matches the UI Components population query.

![Component Data Flow](https://www.pubnub.com/assets/images/android-components-data-flow-cfb4d98c37f6fcdb1808aa2207044fc0.gif)

1. The PubNub APIs and events return network data that can be stored directly in the Room database using the repositories declared in ChatProvider.
2. The UI Components use specific view models to connect with the database (persistent data) through repositories and Data Access Object instances. If the returned type of the ViewModel method is declared as Flow, it automatically updates the component's data source if the new matching data is added, updated, or removed.
3. The UI Components provide actions that can be configured to call PubNub APIs through DataProvider or update existing objects and store the result.

### Data payloads

PubNub Chat Components for Android enforce you to use the unified JSON schemas (data models) for such common entity types as [users](#users), [channels](#channels), and [messages](#messages) when transferring data over the PubNub network. These common data payloads allow you to create cross-platform applications and ensure that such applications developed in different technologies (Swift, Kotlin, or JavaScript) communicate with one another properly, avoiding any unexpected errors.

:::tip Validate your payload
To avoid any parsing errors, validate the data payload you prepared for users, channels, and messages in an [online validator](https://www.jsonschemavalidator.net).
:::

#### Users

Follow this schema for the user data in your apps:

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "User",
  "type": "object",
  "description": "User of a chat application",
  "properties": {
    "id": {
      "description": "Unique identifier for a user",
      "type": "string"
    },
    "name": {
      "description": "Name of the user that you can display in the UI",
      "type": "string"
    },
    "email": {
      "description": "User's email address",
      "type": "string"
    },
    "externalId": {
      "description": "Reference to user identifier from an external system",
      "type": "string"
    },
    "profileUrl": {
      "description": "URL to a user avatar that you can display in the UI",
      "type": "string"
    },
    "type": {
      "description": "Type of the user",
      "type": "string",
      "examples": ["default", "admin", "moderator"],
      "default": "default"
    },
    "status": {
      "description": "Status of the user",
      "type": "string",
      "examples": ["default", "deleted"]
    },
    "custom": {
      "description": "Any additional payload information",
      "type": "object",
      "properties": {
        "description": {
          "description": "Description, job title, or some other information that you can display in the UI",
          "type": "string"
        }
      }
    },
    "eTag": {
      "description": "Identifier used for cache invalidation",
      "type": "string"
    },
    "updated": {
      "description": "ISO8601 date string of when the user has been last updated",
      "type": "string"
    },
  },
  "required": ["id", "type"]
}
```

Example:

```json
{
  "id": "some-user-id",
  "name": "Jane Doe",
  "email": "jane.doe@example.com",
  "externalId": "some-external-user-id",
  "profileUrl": "https://randomuser.me/api/portraits/men/1.jpg",
  "type": "default",
  "status": "default",
  "custom": {
    "description": "Office Assistant",
  },
  "eTag": "AYGyoY3gre71eA",
  "updated": "2020-09-23T09:23:34.598494Z"
}
```

#### Channels

Follow this schema for the channel data in your apps:

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Channel",
  "description": "Channel in the chat application",
  "type": "object",
  "properties": {
    "id": {
      "description": "Unique identifier for a channel",
      "type": "string"
    },
    "name": {
      "description": "Name of the channel that you can display in the UI",
      "type": "string"
    },
    "description": {
      "description": "Description of the channel that you can display in the UI",
      "type": "string"
    },
    "type": {
      "description": "Type of the channel",
      "type": "string",
      "examples": ["default", "group", "direct"],
      "default": "default"
    },
    "status": {
      "description": "Status of the channel",
      "type": "string",
      "examples": ["default", "deleted"]
    },
    "custom": {
      "description": "Any additional payload information",
      "type": "object",
      "properties": {
        "profileUrl": {
          "description": "URL to a channel avatar that you can display in the UI",
          "type": "string"
        }
      }
    },
    "eTag": {
      "description": "Identifier used for cache invalidation",
      "type": "string"
    },
    "updated": {
      "description": "ISO8601 date string of when the channel has been last updated",
      "type": "string"
    },
  },
  "required": ["id", "type"]
}
```

Example:

```json
{
  "id": "some-channel-id",
  "name": "Off-topic",
  "description": "Off-topic channel for random chatter and fun",
  "type": "default",
  "status": "default",
  "custom": {
    "profileUrl": "https://www.gravatar.com/avatar/149e60f311749f2a7c6515f7b34?s=256&d=identicon"
  },
  "eTag": "AbOx6N+6vu3zoAE",
  "updated": "2020-09-23T09:23:37.175764Z"
}
```

#### Messages

Follow this schema for the message data in your apps:

:::note Supported message types
PubNub Chat Components for Android support only text message data types.
:::

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Message Content",
  "description": "Message content in the chat application",
  "type": "object",
  "properties": {
    "id": {
      "description": "Unique identifier for a message. Use the UUID v4 algorithm.",
      "type": "string"
    },
    "text": {
      "description": "Text of the message that you can display in the UI",
      "type": "string"
    },
    "contentType": {
      "description": "If a message contains any extra content, this field describes its type. Currently, PubNub Chat Components support only text messages.",
      "type": "string",
      "examples": ["externalUrl", "imageUrl"]
    },
    "content": {
      "description": "Extra content for the message. It can contain any feature-specific data like URLs to external images.",
      "type": "object"
    },
    "createdAt": {
      "description": "ISO8601 date string of when the message was created",
      "type": "string"
    },
    "custom": {
      "description": "Any additional payload information",
      "type": "object"
    }
  },
  "required": ["id", "text", "createdAt"]
}
```

Example:

```json
{
  "id": "6da72b98-e211-4724-aad4-e0fb9f08999f",
  "text": "Let's sync on the topic in this thread.",
  "contentType": "none",
  "content": {},
  "custom": {},
  "createdAt": "2022-05-10T14:48:00.000Z"
}
```

## Channel

A [channel](https://www.pubnub.com/docs/general/channels/overview) is commonly viewed as a collection of associated users with a specific purpose or goal. A channel could be created for direct communication between two or more users, a group of users, or other use case-specific scenarios.

### Persistent data

The default Persistent Object Model inside the Chat Components defines an `Entity` named `DBChannel`.

If a custom Persistent Object Model is used, then an `Entity` must implement the `Channel` interface before it can be used by the Chat Components framework.

To return all the members from a selected channel, the `DBChannelWithMembers` object is implemented.

#### Default channel entity

The `DBChannel` entity is defined with the following attributes:

| Name | Type | Description |
| --- | --- | --- |
| `id` | `ChannelId` | Unique identifier for the object. Used as a primary key. |
| `name` | `String?` | Name of the channel that can be displayed in the UI. |
| `description` | `String?` | Channel details that can be displayed alongside the name. |
| `type` | `String` | Functional type of the channel. The default value is set to `"default"`. |
| `status` | `String?` | Status of the channel. |
| `custom` | `Any?` | Any additional custom payload (as a key-value pair) that can be stored with the channel. |
| `profileUrl` | `String?` | URL to an image that can be used to visually represent the channel. |
| `eTag` | `String?` | Caching value that changes whenever the remote object changes. It isn't used in PubNub Chat Components for Android. |
| `updated` | `String?` | Last time the remote object was changed. It isn't used in PubNub Chat Components for Android. |

The `CustomDataMap` typealias is a map which stores additional key-value properties.

```kotlin
typealias CustomDataMap = HashMap<String, Any>
```

#### Custom channel entity

To create a custom Persistent Object Model, you must implement the `Channel` interface.

The following properties are required:

```kotlin
interface Channel {
    val id: ChannelId
    val name: String?
    val description: String?
    val type: String
    val status: String?
    val custom: Any?
    val profileUrl: String?
    val eTag: String?
    val updated: String?
}
```

#### Relationships

The `DBChannelWithMembers` entity contains the following attributes:

| Name | Type | Description |
| --- | --- | --- |
| `channel` | `DBChannel` | Entity of the channel object. |
| `members` | `List<DBMember>` | List of members with membership in a selected channel. |

#### Repository

PubNub Chat Components for Android use a default channel repository named `DefaultChannelRepository`.

If you want to use a custom Persistent Object Model, create a custom `ViewModel` in which your `Repository` implements the `ChannelRepository` interface before it can be used by the Chat Components framework.

##### Default channel repository

The `DefaultChannelRepository` implementation is defined with the following attributes:

| Name | Type | Description |
| --- | --- | --- |
| `channelDao` | `ChannelDao<DBChannel, DBChannelWithMembers>` | Room's data access object (DAO) for the channel entity. |

The `ChannelDao` interface uses both `DBChannel` and `DBChannelWithMembers` types. The first one is used for CRUD operations (Create, Read, Update, Delete) and the second one defines the type of the returned object.

##### Custom channel repository

To create a custom channel repository, implement the `ChannelRepository` interface.

These are the required methods:

* get() Used to get one Channel object with specified id from the database. It returns null when the object doesn't exist. 1suspend fun get(id: ChannelId): OUT?
* getAll() Returns the paginated source of channels. 1fun getAll(2 id: UserId? = null,3 filter: Query? = null,4 vararg sorted: Sorted = emptyArray(),5): PagingSource<Int, OUT> NameTypeDescriptionidUserId?If specified, this method returns only channels the user is a member of.filterQuery?Query filter for the database.sortedSortedArray of sorted keys and directions. Example: 1val repository: DefaultChannelRepository = LocalChannelRepository.current2 3val channels = remember {4 repository.getAll(5 id = "my-user-id",6 filter = Query("type LIKE ?", "group"),7 sorted = arrayOf(Sorted("type", Sorted.Direction.ASC)),8 )9}
* getList() Returns the list of all channels. 1suspend fun getList(): List<OUT>
* insertOrUpdate() Sets or updates a channel object in the database. 1suspend fun insertOrUpdate(vararg channel: IN)
* remove() Removes the channel object with the specified ID. 1suspend fun remove(id: ChannelId)
* size() Returns the number of all channels. 1suspend fun size(): Long

### Network data

The `NetworkChannelMetadata` class is used to communicate with PubNub APIs to send and receive [channel metadata](https://www.pubnub.com/docs/chat/sdks/channels/channel-metadata).

| Name | Type | Description |
| --- | --- | --- |
| `id` | `String` | Unique identifier for the object. |
| `name` | `String?` | Name of the channel. |
| `description` | `String?` | Channel details that can be displayed alongside the name. |
| `type` | `String` | Functional type of the channel. The default value is set to `"default"`. |
| `status` | `String?` | Status of the channel. |
| `custom` | `Any?` | Custom key value pairs that can be stored with the channel. |
| `profileUrl` | `String?` | URL to an image that can be used to visually represent the channel. |
| `eTag` | `String?` | Caching value that changes whenever the remote object changes. It isn't used in PubNub Chat Components for Android. |
| `updated` | `String?` | Last time the remote object was changed. It isn't used in PubNub Chat Components for Android. |

#### Custom channel data

`NetworkChannelMetadata` contains the `custom` property. You can use it to store a custom object. In the default implementation, `NetworkChannelMapper` is implemented to parse the custom object to `CustomDataMap`.

### Service

PubNub Chat Components for Android use a default service named `DefaultChannelServiceImpl`. This service is responsible for synchronizing PubNub APIs network channel data and storing it in the local database.

You can obtain the instance or override it using `LocalChannelService` in `CompositionLocalProvider`. If you want to use a custom service, you need to create a class that implements the `ChannelService` interface.

#### Default channel service

The `DefaultChannelService` implementation is defined with the following attributes:

| Name | Type | Description |
| --- | --- | --- |
| `pubNub` | `PubNub` | PubNub instance. |
| `logger` | `Logger` | Instance of the logging class. |

#### Custom channel service

To create a custom channel service, implement the `ChannelService` interface and pass it as a parameter to the composition tree. You can do it by wrapping `ChatProvider` with `CompositionLocalProvider`.

```kotlin
CompositionLocalProvider(LocalChannelService provides MyChannelServiceImpl){
  ChatProvider(…)
}
```

These are the required methods:

* bind() Subscribes to the channels and sets listeners for channel events. 1fun bind(vararg channels: String)
* unbind() Removes the listeners and unsubscribes from channels. 1fun unbind()

## Member

The [users](https://www.pubnub.com/docs/general/metadata/users-metadata) that are associated with a channel are also known as its members. A user might have many channel memberships and a channel might have multiple members.

### Persistent data

The default Persistent Object Model inside the Chat Components defines an `Entity` named `DBMember`.

If a custom Persistent Object Model is used, then an `Entity` must implement the `Member` interface before it can be used by the Chat Components framework.

#### Default member entity

The `DBMember` entity is defined with the following attributes:

| Name | Type | Description |
| --- | --- | --- |
| `id` | `UserId` | A unique identifier for the object. Used as a primary key. |
| `name` | `String` | Name of the user. |
| `email` | `String?` | Email address of the user. It isn't used in PubNub Chat Components for Android. |
| `externalId` | `String?` | A unique external identifier for the user. It isn't used in PubNub Chat Components for Android. |
| `profileUrl` | `String?` | Image that can be used to visually represent the user. |
| `type` | `String` | Functional type of the user. The default value is set to `"default"`. |
| `status` | `String?` | Status of the user. |
| `custom` | `Any?` | Custom object to store a description of the user. |
| `eTag` | `String?` | Caching value that changes whenever the remote object changes. It isn't used in PubNub Chat Components for Android. |
| `updated` | `String?` | Last time the remote object was changed. It isn't used in PubNub Chat Components for Android. |

#### Custom member entity

To create a custom Persistent Object Model, you must implement the `Member` interface.

The following properties are required:

```kotlin
interface Member {
    val id: UserId
    val name: String
    val email: String?
    val externalId: String?
    val profileUrl: String?
    val type: String
    val status: String?
    val custom: Any?
    val eTag: String?
    val updated: String?
}
```

#### Relationships

The `DBMemberWithChannels` entity contains the following attributes:

| Name | Type | Description |
| --- | --- | --- |
| `member` | `DBMember` | Entity of a member object. |
| `channels` | `List<DBChannel>` | List of channels with membership. |

#### Repository

PubNub Chat Components for Android use a default member repository named `DefaultMemberRepository`.

If a custom Persistent Object Model is used, then a `Repository` must implement the `MemberRepository` interface before it can be used by the Chat Components framework. Also, you must implement a custom view model.

##### Default member repository

The `DefaultMemberRepository` implementation is defined with the following attributes:

| Name | Type | Description |
| --- | --- | --- |
| `memberDao` | `MemberDao<DBMember, DBMemberWithChannels>` | Room DAO for the member entity. |

The `MemberDao` interface uses both `DBMember` and `DBMemberWithChannels` types. The first one is used for CRUD operations (Create, Read, Update, Delete) and the second one defines the type of the returned object.

##### Custom member repository

To create a custom member repository, you must implement a `MemberRepository` interface.

The following methods are required:

* get() Used to get one member object with specified id from the database. Returns null when the object doesn't exist. 1suspend fun get(id: UserId): OUT?
* getAll() Returns paginated source of members. Possible arguments are: 1fun getAll(2 id: ChannelId? = null,3 filter: Query? = null,4 vararg sorted: Sorted = emptyArray(),5): PagingSource<Int, OUT> NameTypeDescriptionidChannelId?If specified, returns all members of the channel with the specified ID.filterQuery?Query filter for the database.sortedSortedArray of sorted keys and directions. Example: 1val repository: DefaultMemberRepository = LocalMemberRepository.current2 3val members = remember {4 repository.getAll(5 id = "channel-id",6 filter = Query("name LIKE ?", "Android"),7 sorted = arrayOf(Sorted("name", Sorted.Direction.ASC)),8 )9}
* getList() If id is specified, it returns the list of members of the specified channel. Otherwise, it returns the list of all members. 1suspend fun getList(id: ChannelId? = null): List<OUT>
* insertOrUpdate() Sets or updates a member object in the database. 1suspend fun insertOrUpdate(vararg member: IN)
* remove() Removes the member object with the specified ID. 1suspend fun remove(id: UserId)
* size() Returns the number of all members. 1suspend fun size(): Long

### Network data

The `NetworkMember` class is used to communicate with PubNub APIs to send and receive [user metadata](https://www.pubnub.com/docs/general/metadata/users-metadata).

| Name | Type | Description |
| --- | --- | --- |
| `id` | `UserId` | A unique identifier for the object. Used as a primary key. |
| `name` | `String` | Name of the user. |
| `email` | `String?` | Email address of the user. It isn't used in PubNub Chat Components for Android. |
| `externalId` | `String?` | A unique external identifier for the user. It isn't used in PubNub Chat Components for Android. |
| `profileUrl` | `String?` | Image that can be used to visually represent the user. |
| `type` | `String` | Functional type of the user. The default value is set to `"default"`. |
| `status` | `String?` | Status of the user. |
| `custom` | `Any?` | Custom object to store a description of the user. |
| `eTag` | `String?` | Caching value that changes whenever the remote object changes. It isn't used in PubNub Chat Components for Android. |
| `updated` | `String?` | Last time the remote object was changed. It isn't used in PubNub Chat Components for Android. |

#### Custom member data

The `NetworkMember` contains the `custom` property. You can use it to store a custom object.

## Message

A [message](https://www.pubnub.com/docs/general/messages/publish) is the dynamic content that a user sends to other users inside a channel.

### Persistent data

The default Persistent Object Model inside the Chat Components defines an `Entity` named `DBMessage`.

If a custom Persistent Object Model is used, then an `Entity` must implement the `Message` interface before it can be used by the Chat Components framework.

#### Default message entity

The `DBMessage` entity is defined with the following attributes:

| Name | Type | Description |
| --- | --- | --- |
| `id` | `String` | Unique identifier for the object. Used as a primary key. |
| `text` | `String` | Text of the message. |
| `contentType` | `String?` | If a message contains any extra content, this field describes its type. Currently, PubNub Chat Components support only text messages. |
| `content` | `Any?` | Extra content for the message like feature-specific data like URLs to external images. |
| `createdAt` | `String` | ISO8601 date string of when the message was created. |
| `custom` | `Any?` | Custom key value pairs that can be stored with the message. |
| `publisher` | `UserId` | ID of the message publisher. |
| `channel` | `ChannelId` | ID of the channel the message is published on. |
| `timetoken` | `Timetoken` | Time when a message was created. |
| `published` | `Timetoken` | Time when a message was published. |
| `isSent` | `Boolean` | Information if a message was sent successfully. |
| `exception` | `String?` | Error details when an exception is thrown. |

#### Custom message entity

To create a custom Persistent Object Model, you must implement the `Message` interface.

The following properties are required:

```kotlin
interface Message {
    val id: MessageId
    val text: String
    val contentType: String?
    val content: Any?
    val createdAt: String
    val custom: Any?
}
```

#### Repository

PubNub Chat Components for Android use a default message repository named `DefaultMessageRepository`.

If you want to use a custom Persistent Object Model, a `Repository` must implement the `MessageRepository` interface before it can be used by the Chat Components framework. Also, you must implement a custom view model.

##### Default message repository

The `DefaultMessageRepository` implementation is defined with the following attributes:

| Name | Type | Description |
| --- | --- | --- |
| `messageDao` | `MessageDao<DBMessage, DBMessage>` | Room DAO for the message entity. |

The `MessageDao` interface uses the `DBMessage` type for CRUD operations (Create, Read, Update, Delete) and to define the type of the returned object.

##### Custom message repository

To create a custom message repository, implement the `MessageRepository` interface.

The following methods are required:

* get() Used to get one message object with a specified id from the database. Returns null when the object doesn't exist. 1suspend fun get(id: MessageId): OUT?
* getList() Returns the list of all messages from a specified id. 1suspend fun getList(2 id: ChannelId,3 count: Int,4 before: Boolean,5 timestamp: Timetoken,6): List<OUT> These are the expected arguments: NameTypeDescriptionidChannelIdSpecifies a channel to return messages from.countIntSpecifies the number of messages to return.beforeBooleanRetrieves history messages before or after a specified timestamp.timestampTimetokenGets messages before or after a given time, based on the value in the before field.
* getAll() Returns a paginated source of channels. 1fun getAll(2 id: ChannelId? = null,3 contentType: String? = null,4 filter: Query? = null,5 vararg sorted: Sorted = emptyArray(),6): PagingSource<Int, OUT> Possible arguments are: NameTypeDescriptionidChannelId?If specified, it returns only messages from a specified ID.contentTypeString?If a message contains any extra content, this field describes its type. Currently, PubNub Chat Components support only text messages.filterQuery?Query filter for the database.sortedArray<Sorted>Array of sorted keys and directions. Example: 1val repository: DefaultMessageRepository = LocalMessageRepository.current2 3val messages = remember {4 repository.getAll(5 id = "my-channel-id",6 contentType = "default",7 filter = Query("publisher LIKE ?", "my-userId"),8 sorted = arrayOf(Sorted("contentType", Sorted.Direction.ASC)),9 )10}
* getLast() Returns the last message from a given channel. 1suspend fun getLast(channelId: String): OUT?
* getLastByChannel() Returns a flowable list of most recent messages (in a specified number) from a given channel. 1fun getLastByChannel(id: ChannelId, count: Long): Flow<List<OUT>> NameTypeDescriptionidStringChannels to return messages from a specified ID.countLongNumber of messages to return.
* has() Checks if there is a message with a specified ID in the database. 1suspend fun has(id: MessageId): Boolean
* hasMoreBefore() Returns true when any message with older timestamp than specified exists in the database for a given channel. 1suspend fun hasMoreBefore(id: ChannelId, timestamp: Timetoken): Boolean
* hasMoreAfter() Returns true when any message with a newer timestamp than specified exists in a database for a given channel. 1suspend fun hasMoreAfter(id: ChannelId, timestamp: Timetoken): Boolean
* remove() Removes a message object from the database. 1suspend fun remove(message: IN)
* removeAll() Removes all messages from a given channel. 1suspend fun removeAll(id: ChannelId)
* insertOrUpdate() Sets or updates a message object in the database. 1suspend fun insertOrUpdate(vararg message: IN)
* setSent() Sets the message sent status. 1suspend fun setSent(2 id: MessageId,3 timestamp: Timetoken? = null,4)
* setSendingError() Sets the message error status. 1suspend fun setSendingError(2 id: MessageId,3 exception: String? = null,4 timestamp: Timetoken? = null,5)
* getLastTimestamp() Returns the last known timestamp of the message in the channel. 1suspend fun getLastTimestamp(id: ChannelId): Timetoken

### Network data

The `NetworkMessage` class is used to communicate with PubNub APIs. The `message` field is mapped to the `NetworkMessagePayload` class:

| Name | Type | Description |
| --- | --- | --- |
| `id` | `String` | Unique identifier for the object. Used as a primary key. |
| `text` | `String` | Text of the message. |
| `contentType` | `String?` | If a message contains any extra content, this field describes its type. Currently, PubNub Chat Components support only text messages. |
| `content` | `Any?` | Extra content for the message like feature-specific data like URLs to external images. |
| `createdAt` | `String` | ISO8601 date string of when the message was created. |
| `custom` | `Any?` | Custom key value pairs that can be stored with the message. |

#### Custom message data

The `NetworkMessagePayload` contains the `custom` property. This one might be used to store custom object. In default implementation, the `NetworkMessageMapper` and `NetworkMessageHistoryMapper` are used to map it.

### Service

PubNub Chat Components for Android use a default service named `DefaultMessageService`. This service is responsible for synchronizing PubNub APIs network message data and storing it in the local database.

You can obtain the instance or override it using `LocalMessageService` in `CompositionLocalProvider`. If you want to use a custom service, you need to create a class which implements the `MessageService` interface.

#### Default message service

The `DefaultMessageService` implementation is defined with the following attributes:

| Name | Type | Description |
| --- | --- | --- |
| `pubNub` | `PubNub` | PubNub instance. |
| `userId` | `userId` | ID of the current user. |
| `messageRepository` | `MessageRepository<DBMessage, DBMessageWithActions>` | The `messageRepository` implementation responsible for CRUD operations (Create, Read, Update, Delete) on message objects in the database. |
| `messageActionRepository` | `MessageActionRepository<DBMessageAction>` | The `messageActionRepository` implementation responsible for CRUD operations (Create, Read, Update, Delete) on message action objects in the database. |
| `networkMapper` | `NetworkMessageMapper` | Network object to database object mapper. |
| `networkHistoryMapper` | `NetworkMessageHistoryMapper` | Network history object to database object mapper. |
| `messageActionHistoryMapper` | `NetworkMessageActionHistoryMapper` | Network message action history object to database object mapper. |
| `logger` | `Logger` | Instance of the logging class. |
| `coroutineScope` | `CoroutineScope` | Scope for new coroutines. |
| `dispatcher` | `CoroutineDispatcher` | Coroutine dispatcher implementation. |

#### Custom message service

To create a custom member service, implement the `MessageService` interface and pass it as a parameter to the composition tree. You can do it by wrapping `ChatProvider` with `CompositionLocalProvider`:

```kotlin
CompositionLocalProvider(LocalMessageService provides MyMessageServiceImpl){
  ChatProvider(…)
  }
```

These are the required methods:

* bind() Sets listeners for message events. 1fun bind()
* unbind() Removes the listeners. 1fun unbind()
* send() Sends a message to a selected channel. 1suspend fun send(2 channelId: ChannelId,3 message: NetworkMessagePayload,4 meta: Any? = null,5 store: Boolean = true,6 onSuccess: (String, Timetoken) -> Unit = { _: String, _: Timetoken -> },7 onError: (Exception) -> Unit = {},8) These are the expected arguments: NameTypeDescriptionidChannelIdSpecifies a channel to send a message to.messageDataMessage data object to send.metaAny?Additional metadata to send.storeBooleanThe flag denoting to store a message in history.onSuccess(String, Timetoken) -> UnitCallback to invoke when a message is sent successfully.onError(Exception) -> UnitCallback to invoke when there is an error while sending a message.
* fetchAll() Pulls the messages from the PubNub Message Persistence API. 1suspend fun fetchAll(2 id: ChannelId,3 start: Long?,4 end: Long?,5 count: Int,6 withActions: Boolean = false,7 withUUID: Boolean = false,8): NetworkHistorySyncResult? These are the expected arguments: NameTypeDescriptionidChannelIdSpecifies a channel to return history messages from.startLong?Delimits the start of a time slice (exclusive) to pull messages from.endLong?Delimits the end of a time slice (inclusive) to pull messages from.countIntSpecifies the number of historical messages to return.withActionsBooleanRetrieves history messages with message reactions.withUUIDBooleanIncludes a publisher User ID with every history message.

This method returns the `NetworkHistorySyncResult` object containing such information about received messages as the smallest and largest timetoken, last page, and message count.

```kotlin
data class NetworkHistorySyncResult(
  val minTimetoken: Timetoken?,
  val maxTimetoken: Timetoken?,
  val page: PNBoundedPage?,
  val messageCount: Int,
)
```

### Message Reactions

A [message reaction](https://www.pubnub.com/docs/chat/community-supported/android/message-reactions) is an emoji that a user adds to a message to react to it.

#### Persistent data

The default Persistent Object Model inside the Chat Components defines an `Entity` named `DBMessageAction`.

If a custom Persistent Object Model is used, then an `Entity` must implement the `MessageAction` interface before it can be used by the Chat Components framework.

##### DBMessageWithActions

`DBMessageWithActions` is the relation between a message and all its actions. Both are stored independently but merged together to one data object when returned from the database. They are related through the `DBMessage::timetoken` and `DBMessageAction::messageTimestamp` entires.

```kotlin
data class DBMessageWithActions(
    @Embedded
    val message: DBMessage,

    @Relation(
        parentColumn = "timetoken",
        entityColumn = "messageTimestamp"
    )
    val actions: List<DBMessageAction>,
) : Message by message
```

##### Default message reaction entity

The `DBMessageAction` entity is defined with the following attributes:

| Name | Type | Description |
| --- | --- | --- |
| `channel` | `ChannelId` | ID of the channel on which you add a message reaction. |
| `user` | `UserId` | ID of the user who selects a reaction. |
| `messageTimestamp` | `Timetoken` | Time when a message to which you react was published. |
| `published` | `Timetoken` | Time when a reaction was added. |
| `type` | `String` | The `"reaction"` type of the action you made. |
| `value` | `String` | Reaction's value. |

##### Custom message reaction entity

To create a custom Persistent Object Model, you must implement the `MessageAction` interface.

The following properties are required:

```kotlin
interface MessageAction {
    val id: String
    val channel: ChannelId
    val user: UserId
    val messageTimestamp: Timetoken
    val published: Timetoken
    val type: String
    val value: String
}
```

##### Repository

PubNub Chat Components for Android use a default message reaction repository named `DefaultMessageActionRepository`.

If you want to use a custom Persistent Object Model, a `Repository` must implement the `MessageActionRepository` interface before it can be used by the Chat Components framework. Also, you must implement a custom view model.

###### Default message reaction repository

The `DefaultMessageActionRepository` implementation is defined with the following attributes:

| Name | Type | Description |
| --- | --- | --- |
| `messageActionDao` | `MessageActionDao<DBMessageAction>` | Room DAO for the message entity. |

The `MessageActionDao` interface uses the `DBMessageAction` type for CRUD operations (Create, Read, Update, Delete) and to define the type of the returned object.

###### Custom message reaction repository

To create a custom message repository, implement the `MessageActionRepository` interface.

The following methods are required:

* get() Returns a message action for the provided parameters. 1suspend fun get(2 user: UserId,3 channel: ChannelId,4 messageTimetoken: Timetoken,5 type: String,6 value: String,7): DB? These are the expected arguments: NameTypeDescriptionuserUserIdSpecifies a user who added the reaction.channelChannelIdSpecifies the channel on which the reaction was added.messageTimetokenTimetokenRetrieves information on the time when the message a given user reacted to was sent.typeStringGets information on the action type ("reaction").valueStringGets information on the reaction value, like "\uD83D\uDC4D" which stands for "thumbs up".
* has() Checks if a given message reaction exists in the database. 1suspend fun has(id: String): Boolean NameTypeDescriptionidStringID of a message reaction to check.
* remove() Removes a given message reaction from the database. 1suspend fun remove(vararg action: DB) NameTypeDescriptionvararg actionDBDBMessageAction object to remove.
* insertOrUpdate() Sets or updates a given DBMessageAction object in the database. 1suspend fun insertOrUpdate(vararg action: DB) NameTypeDescriptionvararg actionDBDBMessageAction object to add or update.
* getLastTimetoken() Returns the last known timestamp of the message reaction in the channel. 1suspend fun getLastTimetoken(channel: ChannelId): Timetoken NameTypeDescriptionchannelChannelIdChannel where a message reaction was added.

#### Network data

The `PNMessageAction` class is used to communicate with PubNub APIs to send or receive message actions (inluding reactions).

| Name | Type | Description |
| --- | --- | --- |
| `type` | `String` | Message action type (`"reaction"` for message reactions). |
| `value` | `String` | Message reaction value, like `"\uD83D\uDC4D"` which stands for "thumbs up". |
| `messageTimetoken` | `Timetoken` | Time when the message a given user reacted to was sent. |

#### Service

PubNub Chat Components for Android use a default reaction service named `DefaultMessageReactionService`. This service is responsible for synchronizing PubNub APIs network message reaction data and storing it in the local database.

You can obtain the instance or override it using `LocalMessageReactionService` in `CompositionLocalProvider`. If you want to use a custom service, you need to create a class which implements the `MessageReactionService` interface.

##### Default message reaction service

The `DefaultMessageReactionService` implementation is defined with the following attributes:

| Name | Type | Description |
| --- | --- | --- |
| `userId` | `UserId` | ID of the current user. |
| `actionService` | `ActionService` | Generic implementation of message actions. It's not solely restricted to message reactions but could also be used to implement read receipts or channel invitations. It listens for incoming message actions, adding, removing, and fetching actions from PubNub API. |
| `messageActionRepository` | `MessageActionRepository<DBMessageAction>` | The `MessageRepository` implementation responsible for CRUD operations (Create, Read, Update, Delete) on message actions objects in the database. |
| `mapper` | `Mapper<PNMessageActionResult, DBMessageAction>` | Network history object to database object mapper. |
| `logger` | `Logger` | Instance of the logging class. |
| `coroutineScope` | `CoroutineScope` | Scope for new coroutines. |
| `dispatcher` | `CoroutineDispatcher` | Coroutine dispatcher implementation. |

##### Custom message reaction service

To create a custom message reaction service, implement the `MessageReactionService` interface and pass it as a parameter to the composition tree. You can do it by wrapping `ChatProvider` with `CompositionLocalProvider`:

```kotlin
CompositionLocalProvider(LocalMessageReactionService provides MyMessageReactionService){
  ChatProvider(…)
}
```

These are the required methods:

* bind() Sets listeners for the message reactions. 1fun bind(2 types: Array<String> = arrayOf("reaction")3 ) NameTypeDescriptiontypesArray<String> = arrayOf("reaction")Refers to the accepted message action type ("reaction") that will be stored in the database.
* unbind() Stops listening for message reactions. 1fun bind()
* synchronize() Synchronizes message reactions for the provided channel. 1fun synchronize(2 channel: ChannelId,3 lastTimetoken: Timetoken? = null4 ) NameTypeDescriptionchannelChannelIdID of the channel to synchronize.lastTimetokenTimetoken? = nullLast synchronization timestamp.
* add() Adds a specific message reaction to the PubNub API and the local repository. 1suspend fun add(2 channel: ChannelId,3 messageTimetoken: Timetoken,4 type: String,5 value: String6 ) NameTypeDescriptionchannelChannelIdID of the channel where the message reaction was added.messageTimetokenTimetokenTime when the message a given user reacted to was sent.typeStringMessage action type ("reaction").valueStringMessage reaction value.
* remove() Removes a specific message reaction from the PubNub API and the local repository. 1suspend fun remove(2 channel: ChannelId,3 messageTimetoken: Timetoken,4 published: Timetoken,5 type: String,6 value: String7 ) NameTypeDescriptionchannelChannelIdID of the channel where the message reaction was added.messageTimetokenTimetokenTime when the message a given user reacted to was sent.publishedTimetokenTime when the message reaction was added.typeStringMessage action type ("reaction").valueStringMessage reaction value.