# Getting Started

### What can you do with this SDK?

Mitter Android SDK gives you access to the entire Mitter.io platform from a **User**'s perspective. The SDK does all the hard work behind the scenes while you focus on adding your business logic and getting your app ready in record time.

Currently, the Android SDK supports the following actions:

* Sign-in as a user using either **Google Sign-In** or **Auth Token**
* Create/remove channels
* Send/receive messages & timeline events
* Auto-updating your own presence and receiving other users’ presence
* Updating your user profile

### Setting up the SDK

All right, let’s get started by grabbing the SDK from **jCenter**.

#### Adding the SDK to your project

Adding the SDK to your project is pretty straightforward. Just open up your `build.gradle` file in the **app** module and paste this line within the `dependencies` block.

```groovy
implementation 'io.mitter.android:core:0.1.7'
```

Once you’re done and just a Gradle sync and sit back while Gradle completes syncing your project.

#### Let’s do some basic configuration

Now that you’ve got the SDK added, the next step is to add some initial configuration for the SDK to work.

The central point of access to everything that the API has to offer is through the `Mitter` object. To start working, you need to configure this object with your application details that you can access from Mitter Dashboard.

A very good place to initialise and configure this object is within the `onCreate()` of your app’s `Application` class.

Let’s get started by defining the `Mitter` object as a global variable in your `Application` class.

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
lateinit var mitter: Mitter
```

{% endtab %}

{% tab title="Java" %}

```java
private Mitter mitter;
```

{% endtab %}
{% endtabs %}

Now, within `onCreate()` you need to configure this object to connect with your application on the Mitter.io platform. Additionally, you can put down some extra configuration as to how the SDK should behave locally.

The first thing we need is a `UserAuth` object which will specify the user you want the SDK to log in as. The `UserAuth` object takes a User ID and a User Auth Token as its parameters.

You need to get your user’s credentials from a backend server that connects to Mitter.io to manage your application. Alternatively, you can use a federated authentication system such as Google Sign-in to get the job done, which will be discussed in a later section. For brevity, let’s continue with the former approach:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
val userAuth = UserAuth(
    userId = "089771b6-6002-43db-bdc5-81e6ef7b6ef9",
    userAuthToken = "eyJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJtaXR0ZXItaW8iLCJ1c2VyVG9rZW5JZCI6Imc3QXYzYjR4VWJleGNsTTIiLCJ1c2VydG9rZW4iOiJyMHVsa2Jmc2ZtaWY5dTVscXNwaDVobzFpNCJ9.jnvR74f_GUBiH_9Z5FEWK7fLEnerDU_gCdPZeykKrJk5X4pOlhogVDG5PdeCyraz9FXV-G1sojlovpKuti7GTA"
)
```

{% endtab %}

{% tab title="Java" %}

```java
UserAuth userAuth = new UserAuth(
    "089771b6-6002-43db-bdc5-81e6ef7b6ef9",
    "eyJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJtaXR0ZXItaW8iLCJ1c2VyVG9rZW5JZCI6Imc3QXYzYjR4VWJleGNsTTIiLCJ1c2VydG9rZW4iOiJyMHVsa2Jmc2ZtaWY5dTVscXNwaDVobzFpNCJ9.jnvR74f_GUBiH_9Z5FEWK7fLEnerDU_gCdPZeykKrJk5X4pOlhogVDG5PdeCyraz9FXV-G1sojlovpKuti7GTA"
);
```

{% endtab %}
{% endtabs %}

Here, we configure the `Mitter` object with the User ID of `089771b6-6002-43db-bdc5-81e6ef7b6ef9` and its respective auth token which is nothing but a JWT.

Once that’s in place, you can do some application & local SDK configuration with the help of the `MitterConfig` object.

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
val mitterConfig = MitterConfig(
    applicationId = "ee421c5f-7a93-4b89-bf2a-4823c6e4fe42",
    loggingLevel = LoggingLevel.FULL
)
```

{% endtab %}

{% tab title="Java" %}

```java
MitterConfig mitterConfig = new MitterConfig(
    "ee421c5f-7a93-4b89-bf2a-4823c6e4fe42",
    LoggingLevel.FULL,
    null
);
```

{% endtab %}
{% endtabs %}

Here, you just need to punch in your application ID which can be retrieved from the Mitter Dashboard. Additionally, if you want to configure how the SDK prints out logs you can do that by specifying one of either:

* `LoggingLevel.NONE` - Prints nothing
* `LoggingLevel.BASIC` - Prints only basic operation success/error messages
* `LoggingLevel.FULL` - Prints out everything including object data. Not a good idea to use this level for production usage

#### Using the SDK with containerised Mitter.io

If you're using the Mitter.io docker container, then you need to *override* the default API endpoint in the SDK, as follows:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
val mitterConfig = MitterConfig(
    applicationId = "ee421c5f-7a93-4b89-bf2a-4823c6e4fe42",
    loggingLevel = LoggingLevel.FULL,
    apiEndpoint = MitterApiEndpoint(
        "http://localhost:11901",
        "http://localhost:11901"
    )
)
```

{% endtab %}

{% tab title="Java" %}

```java
MitterConfig mitterConfig = new MitterConfig(
    "ee421c5f-7a93-4b89-bf2a-4823c6e4fe42",
    LoggingLevel.FULL,
    new MitterApiEndpoint(
        "http://localhost:11901",
        "http://localhost:11901"
    )
);
```

{% endtab %}
{% endtabs %}

Once you have got the `UserAuth` and `MitterConfig` in place, constructing the `Mitter` object is simple. You can do as follows:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
mitter = Mitter(this, mitterConfig, userAuth)
```

{% endtab %}

{% tab title="Java" %}

```java
mitter = new Mitter(this, mitterConfig, userAuth);
```

{% endtab %}
{% endtabs %}

To sum everything up, your `Application` should look something like this:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
class MessageApp : Application() {
    lateinit var mitter: Mitter

    override fun onCreate() {
        super.onCreate()

        val userAuth = UserAuth(
            userId = "089771b6-6002-43db-bdc5-81e6ef7b6ef9",
            userAuthToken = "eyJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJtaXR0ZXItaW8iLCJ1c2VyVG9rZW5JZCI6Imc3QXYzYjR4VWJleGNsTTIiLCJ1c2VydG9rZW4iOiJyMHVsa2Jmc2ZtaWY5dTVscXNwaDVobzFpNCJ9.jnvR74f_GUBiH_9Z5FEWK7fLEnerDU_gCdPZeykKrJk5X4pOlhogVDG5PdeCyraz9FXV-G1sojlovpKuti7GTA"
        )
        val mitterConfig = MitterConfig(
            applicationId = "ee421c5f-7a93-4b89-bf2a-4823c6e4fe42",
            loggingLevel = LoggingLevel.FULL
        )
        mitter = Mitter(this, mitterConfig, userAuth)
}
```

{% endtab %}

{% tab title="Java" %}

```java
class MessageApp extends Application {
    private Mitter mitter;
​
    @Override
    public void onCreate() {
        super.onCreate();
        
        UserAuth userAuth = new UserAuth(
            "089771b6-6002-43db-bdc5-81e6ef7b6ef9",
            "eyJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJtaXR0ZXItaW8iLCJ1c2VyVG9rZW5JZCI6Imc3QXYzYjR4VWJleGNsTTIiLCJ1c2VydG9rZW4iOiJyMHVsa2Jmc2ZtaWY5dTVscXNwaDVobzFpNCJ9.jnvR74f_GUBiH_9Z5FEWK7fLEnerDU_gCdPZeykKrJk5X4pOlhogVDG5PdeCyraz9FXV-G1sojlovpKuti7GTA"
        );
        MitterConfig mitterConfig = new MitterConfig(
            "ee421c5f-7a93-4b89-bf2a-4823c6e4fe42",
            LoggingLevel.FULL,
            null
        );
        mitter = new Mitter(this, mitterConfig, userAuth);
    }
}
```

{% endtab %}
{% endtabs %}

That’s it, you’re all set to use the SDK to connect with the Mitter.io platform.

#### Getting access to Mitter.io API objects

The Mitter Android SDK segregates all the APIs into three broad categories:

* Users
* Channels
* Messages

As a result, you get access to APIs that fall within these buckets through their specific objects.

This is done especially to have a structured access to all the APIs without any additional overhead.

You can easily create an object for each of these categories through the `Mitter` object that you created in the previous step.

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
var users: Mitter.Users = mitter.Users()
var channels: Mitter.Channels = mitter.Channels()
var messaging: Mitter.Messaging = mitter.Messaging()
```

{% endtab %}

{% tab title="Java" %}

```java
Mitter.Users users = mitter.new Users();
Mitter.Channels channels = mitter.new Channels();
Mitter.Messaging messaging = mitter.new Messaging();
```

{% endtab %}
{% endtabs %}

### Creating your first channel

Before you start sending out messages, you need to create a channel with some participants in it. You’re free to define your channel however you want. Nevertheless, Mitter.io provides a list of default rulesets for channels that you can use to effortlessly create a channel.

#### What are channels?

Channels are nothing but containers for your messages. Think of it as a logical grouping for your messages. It defines who sees your messages that you send out in your application.

You can learn more about [channels](https://docs.mitter.io/platform-reference-1/channels) in our reference section. For now, let’s just focus on creating some basic channels.

#### Creating a basic channel

Although the SDK has convenience methods for creating channels of all the kinds mentioned in the reference, for simplicity, we’ll focus on creating a one-to-one communication channel (also termed as a `DirectChannel`) here.

A channel needs participants to whom Mitter could deliver messages. Thankfully, creating participants is easy. Let’s define two participants for our channel:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
val john = Participant("089771b6-6002-43db-bdc5-81e6ef7b6ef9")
val lucy = Participant("473d4f4f-0dc6-480b-ae94-7042b37f09e8")
```

{% endtab %}

{% tab title="Java" %}

```java
Participant john = new Participant(
    "089771b6-6002-43db-bdc5-81e6ef7b6ef9",
    ParticipationStatus.Active
);
Participant lucy = new Participant(
    "473d4f4f-0dc6-480b-ae94-7042b37f09e8",
    ParticipationStatus.Active
);
```

{% endtab %}
{% endtabs %}

Now that we have the participants in place, we can hook them up with a new `DirectMessageChannel`.

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
channels.createDirectMessageChannel(
    listOf(john, lucy),
    object : Mitter.OnValueAvailableCallback<Identifier> {
        override fun onValueAvailable(value: Identifier) {
            Log.d("Mitter", "Created Channel ID: ${value.identifier}")
        }

        override fun onError(error: ApiError) {
            Log.d("Mitter", "Channel Creation - ApiError: $error")
        }
    }
)
```

{% endtab %}

{% tab title="Java" %}

```java
channels.createDirectMessageChannel(
    Arrays.asList(john, lucy),
    new Mitter.OnValueAvailableCallback<Identifier>() {
        @Override
        public void onValueAvailable(Identifier identifier) {
            Log.d("Mitter", "Created Channel ID: " + identifier.getIdentifier());
        }

        @Override
        public void onError(ApiError apiError) {
            Log.d("Mitter", "Channel Creation - ApiError: " + apiError);
        }
    }
);
```

{% endtab %}
{% endtabs %}

Once a channel has been created, you get a callback with the newly created channel’s identifier. You need to use this ID to send messages or get all messages on the channel.

#### Creating a channel with advanced properties

As already said, although the SDK provides you convenience methods to easily create a channel with a pre-defined ruleset, it doesn’t lock you down from customising your channel.

You can easily create a totally customised channel by using the `Channel` object and the `createChannel()` method.

A typical `Channel` model looks like this:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
val channel = Channel(
    channelId = "f8cc57d8-af38-4313-85e8-cbe62a2ebf23",
    defaultRuleSet = "io.mitter.ruleset.chats.DirectMessage",
    participation = listOf(
        ChannelParticipation(
            participant = User().setUserId("0b604184-7abd-453c-b258-7e4425b31e7f"),
            participationStatus = ParticipationStatus.Active
        ),
        ChannelParticipation(
            participant = User().setUserId("ea4d2fe1-0d8d-42f0-afd3-8d8067ccbea1"),
            participationStatus = ParticipationStatus.Active
        )
    ),
    systemChannel = false
)
```

{% endtab %}

{% tab title="Java" %}

```java
Channel channel = new Channel(
    "f8cc57d8-af38-4313-85e8-cbe62a2ebf23",
    "",
    "io.mitter.ruleset.chats.DirectMessage",
    Arrays.asList(
        new ChannelParticipation(
            new User().setUserId("0b604184-7abd-453c-b258-7e4425b31e7f"),
            ParticipationStatus.Active,
            null,
            null,
            null
        ),
        new ChannelParticipation(
            new User().setUserId("ea4d2fe1-0d8d-42f0-afd3-8d8067ccbea1"),
            ParticipationStatus.Active,
            null,
            null,
            null
        )
    ),
    false,
    new EntityMetadata(),
    new EntityProfile(IdUtils.of(channelId, Channel.class), new ArrayList<Attribute>()),
    new ArrayList<TimelineEvent>(),
    new AppliedAclList(
        new ArrayList<AppliedAcl>(),
        new ArrayList<AppliedAcl>()
    ),
    null
);
```

{% endtab %}
{% endtabs %}

After you’ve constructed your `Channel` object, you can now swiftly create a new channel by calling the `createChannel` method:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
channels.createChannel(
    channel,
    object : Mitter.OnValueAvailableCallback<Identifier> {
        override fun onValueAvailable(value: Identifier) {
            Log.d("MAC", "Created Channel ID: ${value.identifier}")
        }

        override fun onError(apiError: ApiError) {
            Log.d("MAC", "Channel Creation - ApiError: $apiError")
        }
    }
)
```

{% endtab %}

{% tab title="Java" %}

```java
channels.createChannel(
    channel,
    new Mitter.OnValueAvailableCallback<Identifier>() {
        @Override
        public void onValueAvailable(Identifier identifier) {
            Log.d("MAC", "Created Channel ID: " + identifier.getIdentifier());
        }

        @Override
        public void onError(ApiError apiError) {
            Log.d("MAC", "Channel Creation - ApiError: " + apiError);
        }
    }
);
```

{% endtab %}
{% endtabs %}

It’s very similar to the `createDirectMessageChannel()` method, except you have full control over the channel parameters, including the channel ID.

**When to use what?**

Just remember this principle when creating channels with the SDK:

* **Quick and easy** - Use `createDirectMessageChannel()` or `createGroupMessageChannel()`
* **Full customisation** - Use `createChannel()`

### Sending your first message

Like all other operations, sending messages is as easy as it gets. The SDK provides various methods of sending messages of [pre-defined types](https://docs.mitter.io/platform-reference-1/messages#payload-types) which you can learn more about in the reference section.

Nonetheless, you still get to create fully customised messages with custom payloads with the `sendMessage()` method.

#### What are messages?

Messages are the smallest unit of information in the Mitter.io platform. Don’t think of messages as just text or image messages, they’re more than that.

You can translate almost any real-world action into an act of sending a particular type of message. You can learn more about [messages](https://docs.mitter.io/platform-reference-1/messages) in the reference section.

#### Sending your first message

Let’s say **John** wants to send a text message to **Lucy** on the channel that you just created. To achieve this, a simple method call with the channel ID and the message will suffice.&#x20;

Let’s see how:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
messaging.sendTextMessage(
    channelId,
    "Hi, Lucy!",
    object : Mitter.OnValueUpdatedCallback {
        override fun onSuccess() {
            Toast.makeText(this@MainActivity, "Yay! Message sent!", Toast.LENGTH_LONG).show()
        }

        override fun onError(error: ApiError) {}
    }
)
```

{% endtab %}

{% tab title="Java" %}

```java
messaging.sendTextMessage(
    channelId,
    "Hi, Lucy!",
    new AppliedAclList(
        new ArrayList<AppliedAcl>(),
        new ArrayList<AppliedAcl>()
    ),
    new Mitter.OnValueUpdatedCallback() {
        @Override
        public void onSuccess() {
            Toast.makeText(getApplicationContext(), "Yay! Message sent!", Toast.LENGTH_LONG).show();
        }

        @Override
        public void onError(ApiError apiError) {}
    }
);
```

{% endtab %}
{% endtabs %}

That’s all you need to do get your plain text message delivered in a channel.

#### Fetching messages in a channel

Currently, the SDK provides two ways to receive messages in a channel:

* The **Pull** approach - Calling `getMessagesInChannel()`
* The **Push** approach - Listening to push messages through FCM

While the latter is more intuitive and probably the one you’ll end up using in most cases, for now, we’ll focus on the former and keep the Push approach for the next section.

Let’s say you want to fetch all the messages in a channel between John and Lucy. You can achieve that with the following piece of code:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
messaging.getMessagesInChannel(
    channelId = "f8cc57d8-af38-4313-85e8-cbe62a2ebf23",
    onValueAvailableCallback = object : Mitter.OnValueAvailableCallback<List<Message>> {
        override fun onValueAvailable(value: List<Message>) {
            //We've got the messages
            //Populate the messages in a list
        }

        override fun onError(apiError: ApiError) {}
    }
)
```

{% endtab %}

{% tab title="Java" %}

```java
messaging.getMessagesInChannel(
    "f8cc57d8-af38-4313-85e8-cbe62a2ebf23",
    new FetchMessageConfig(),
    new Mitter.OnValueAvailableCallback<List<Message>>() {
        @Override
        public void onValueAvailable(List<Message> messageList) {
            //We've got the messages
            // Populate the messages in a list
        }

        @Override
        public void onError(ApiError apiError) {}
    }
);
```

{% endtab %}
{% endtabs %}

By default, this call fetches the last **10** messages in the channel. You can raise this limit to a maximum of **50** messages by passing a `FetchMessageConfig` object to the method.

The code for the same would be:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
val fetchMessageConfig = FetchMessageConfig(25)
messaging.getMessagesInChannel(
    channelId = "f8cc57d8-af38-4313-85e8-cbe62a2ebf23",
    fetchMessageConfig = fetchMessageConfig,
    onValueAvailableCallback = object : Mitter.OnValueAvailableCallback<List<Message>> {
        override fun onValueAvailable(value: List<Message>) {
            //We've got the messages
            //Populate the messages in a list
        }

        override fun onError(apiError: ApiError) {}
    }
)
```

{% endtab %}

{% tab title="Java" %}

```java
FetchMessageConfig fetchMessageConfig = new FetchMessageConfig(25, null, null);
messaging.getMessagesInChannel(
    "f8cc57d8-af38-4313-85e8-cbe62a2ebf23",
    fetchMessageConfig,
    new Mitter.OnValueAvailableCallback<List<Message>>() {
        @Override
        public void onValueAvailable(List<Message> messageList) {
            //We've got the messages
            // Populate the messages in a list
        }

        @Override
        public void onError(ApiError apiError) {}
    }
);
```

{% endtab %}
{% endtabs %}

When you need to cross the max limit of **25** you can opt for fetching messages in a paginated way, which will be discussed in a later section of this documentation.

#### Customising your message

As you’ve already done with your channel, you can also fully customise your messages. The SDK provides a `sendMessage()` method which accepts a `Message` object totally constructed by you.

Let’s see how it works. First, we need to create a `TimelineEvent`:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
val sender = User().setUserId("0b604184-7abd-453c-b258-7e4425b31e7f")

val sentTimelineEvent = TimelineEvent(
    type = StandardTimelineEventTypeNames.Messages.SentTime,
    eventTimeMs = System.currentTimeMillis(),
    subject = sender
)
```

{% endtab %}

{% tab title="Java" %}

```java
Identifiable<User> sender = new User().setUserId("0b604184-7abd-453c-b258-7e4425b31e7f");

TimelineEvent sentTimelineEvent = new TimelineEvent(
    UUID.randomUUID().toString(),
    "",
    StandardTimelineEventTypeNames.Messages.SentTime,
    System.currentTimeMillis(),
    sender,
    null
);
```

{% endtab %}
{% endtabs %}

A `TimelineEvent` is like a categorised timestamp on your messages. Here, we’re specifying a `SentTime` event which is mandatory when creating a `Message` object. You can learn more about **Timeline Events** [over here](https://docs.mitter.io/platform-reference-1/messages#timeline-events).

Now that you have your `TimelineEvent` set, the only thing’s left is to create your customised `Message` object. Here’s how you can do that:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
val message = Message(
    messageId = "ad75460e-97c3-4785-9db7-20bcdaee93d9",
    messageType = StandardMessageType.Standard,
    payloadType = "com.acme.messages.MyCustomMessage",
    senderId = sender,
    textPayload = "Check this out!",
    timelineEvents = listOf(sentTimelineEvent),
    messageData = listOf(
        MessageDatum(
            dataType = "com.acme.data.MyCustomMessageData",
            data = jacksonObjectMapper.valueToTree(messageData)
        )
    )
)
```

{% endtab %}

{% tab title="Java" %}

```java
Message message = new Message(
    "ad75460e-97c3-4785-9db7-20bcdaee93d9",
    "",
    StandardMessageType.Standard,
    "com.acme.messages.MyCustomMessage",
    sender,
    "Check this out!",
    Arrays.asList(
        new MessageDatum(
            "com.acme.data.MyCustomMessageData",
            jacksonObjectMapper.valueToTree(messageData)
        )
    ),
    Arrays.asList(sentTimelineEvent),
    new AppliedAclList(
        new ArrayList<AppliedAcl>(),
        new ArrayList<AppliedAcl>()
    ),
    new EntityMetadata(),
    null
);
```

{% endtab %}
{% endtabs %}

The main part here is that you can supply a list of custom payloads in the `messageData` field. Use this field to define any custom buttons or additional styling information that you want to show up in the message.

For more clarity, please refer to the detailed guide on messages over [here](https://docs.mitter.io/platform-reference-1/messages).

All right, now that your `Message` object is set, it’s time that you send it. The sending part is really easy:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
messaging.sendMessage(
    channelId = channelId,
    message = message,
    onValueUpdatedCallback = object : Mitter.OnValueUpdatedCallback {
        override fun onSuccess() {
            Toast.makeText(this@MainActivity, "Yay! Message sent!", Toast.LENGTH_LONG).show()
        }

        override fun onError(apiError: ApiError) {}
    }
)
```

{% endtab %}

{% tab title="Java" %}

```java
messaging.sendMessage(
    channelId,
    message,
    new Mitter.OnValueUpdatedCallback() {
        @Override
        public void onSuccess() {
            Toast.makeText(getApplicationContext(), "Yay! Message sent!", Toast.LENGTH_LONG).show();
        }

        @Override
        public void onError(ApiError apiError) {}
    }
);
```

{% endtab %}
{% endtabs %}

And there you have it, your very own custom message with a custom payload.

#### Attaching images to your messages

Mitter.io lets you create a pre-defined type of message called `ImageMessage`. The main advantage of choosing this type is that Mitter.io automatically generates thumbnails for the attached image without any effort from your part. For more details, [see the reference](https://docs.mitter.io/platform-reference-1/messages#image-message).

Let’s see how you can create an `ImageMessage` with the SDK. It’s pretty similar to sending a text message, just with an added image file parameter:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
messaging.sendImageMessage(
    channelId = channelId,
    caption = "The London Eye",
    file = File("/storage/emulated/0/Download/london-eye.jpg"),
    onValueUpdatedCallback = object : Mitter.OnValueUpdatedCallback {
        override fun onSuccess() {
            Log.d("Mitter", "Image message sent!")
        }

        override fun onError(error: ApiError) {
            Log.d("Mitter", "Image Message Error: ${error.message}")
        }
    }
)
```

{% endtab %}

{% tab title="Java" %}

```java
messaging.sendImageMessage(
    channelId,
    "The London Eye",
    new File("/storage/emulated/0/Download/london-eye.jpg"),
    new AppliedAclList(
        new ArrayList<AppliedAcl>(),
        new ArrayList<AppliedAcl>()
    ),
    new Mitter.OnValueUpdatedCallback() {
        @Override
        public void onSuccess() {
            Log.d("Mitter", "Image message sent!");
        }

        @Override
        public void onError(ApiError apiError) {
            Log.d("Mitter", "Image Message Error: " + apiError.getMessage());
        }
    }
);
```

{% endtab %}
{% endtabs %}

Think of an `ImageMessage` as a regular image message that you see in every other messaging apps. You have an image with some text at the bottom, which is usually the caption.

You can do the same here. Just send a caption or whatever text you would prefer and a `File` object pointing to your image.

### Wrap up

In this section, you’ve learnt how to use the Mitter Android SDK to create various types of channels as well as sending different messages into those channels.

Now, we’ll be focusing on how you can set up an **FCM** push message receiver to receive messages in *real-time* in your app.
