Getting Started

Welcome to the reference section for Mitter’s Android SDK. In this section, you'll mostly find a detailed explanation of how the SDK works and how to harness it best.

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.

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.

lateinit var mitter: Mitter

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:

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

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.

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

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:

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

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

mitter = Mitter(this, mitterConfig, userAuth)

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

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)
}

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.

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

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 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:

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

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

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")
        }
    }
)

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:

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
)

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

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")
        }
    }
)

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 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 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.

Let’s see how:

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) {}
    }
)

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:

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) {}
    }
)

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:

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) {}
    }
)

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:

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

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

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.

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:

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)
        )
    )
)

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.

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

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) {}
    }
)

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.

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:

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}")
        }
    }
)

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.

Last updated