Presence and Timeline Events

Okay, now that you’ve laid a solid foundation for sending and receiving messages in your app, it’s time to step up the game and enrich your app with a lot more features Mitter.io has to offer.

Setting user presence

User presence is a vital ingredient of any solid messaging platform. Mitter.io is no exception. The SDK gives you multiple ways to update your user’s presence without the hassle.

What is a presence?

Presence, as the name suggests, is the current availability of a user. It is usually a single word which tells whether the user is currently available to chat or is not available at the moment.

You can learn more about presence and how Mitter.io handles them in the reference section.

Exploring the bundled presence types

The Mitter Android SDK ships with a couple of default presence types which you can use to get started:

  • Online

  • Away

  • Sleeping

  • Missing

  • Offline

Each of these presence types has a timeToLive field which indicates the time in seconds up to which a presence is valid. After the time has elapsed, the user presence automatically shifts to the next presence in line (if any).

Updating your user’s presence

The SDK allows you to update the current user’s presence in two ways:

  • Update the presence manually

  • Tell the SDK to keep updating the presence automatically

Regardless of which approach you choose, you need a Presence object, to begin with. Therefore, let’s build one:

val presence = PresenceBuilder()
    .startWith(StandardUserPresenceTypes.online(15))
    .then(StandardUserPresenceTypes.away(30))
    .then(StandardUserPresenceTypes.sleeping(60))
    .then(StandardUserPresenceTypes.offline())
    .build()

Here, we’re using the handy PresenceBuilder to build a cascading presence in a fluent manner. In a nutshell, the user’s presence begins with Online then shifts to Away after 15 seconds and so on.

Now that you have the Presence object, you can choose either the manual approach or the automatic one for updating. It’s recommended that you use the automatic approach as it’s less management work for you.

Update presence automatically

To start updating the current user’s presence automatically, all you need to do is call the startAutoUpdateCurrentUserPresence() method on the Users object:

users.startAutoUpdateCurrentUserPresence(presence)

What’s happening behind the scenes is that the SDK periodically polls on your behalf to update the current user’s presence as Online. Once the user exits your app, it stops polling and as a result, the presence starts expiring to the next status as discussed earlier.

Note: Here, the second argument value is the polling interval (in secs). Therefore, this example sets polling every 5 seconds.

Update presence manually

If you prefer not to use the automatic approach, the SDK also allows you to update the current user’s presence in a single shot.

To update the current user’s presence you need to call the setCurrentUserPresence() method on the Users object. Something like this:

users.setCurrentUserPresence(
    presence,
    object : Mitter.OnValueUpdatedCallback {
        override fun onSuccess() {
            Log.d("Mitter", "Presence updated!")
        }

        override fun onError(error: ApiError) {
            Log.d("Mitter", "User Presence - ApiError: $error")
        }
    }
)

Note: This is a one-shot update call. If you don’t make this call periodically, the current user’s presence will shift to the next expiring presence after the timeToLive has elapsed, even if your app is open.

Fetching another user’s presence

Just as you to seamlessly set the current user’s presence, you can easily fetch any user’s presence by calling the getUserPresence() method on the Users object.

Just do the following:

users.getUserPresence(
    userId = "debd7e00-1c3e-463c-92d1-dbb0e9e0e4ba",
    onValueAvailableCallback = object : Mitter.OnValueAvailableCallback<Presence> {
        override fun onValueAvailable(value: Presence) {
            Log.d("Mitter", "Presence: ${value.type}")
        }

        override fun onError(apiError: ApiError) {
            Log.d("Mitter", "User Presence - ApiError: $error")
        }
    }
)

Unlike setting a presence, the SDK doesn’t currently provide an automatic presence fetching mechanism. This means that you’ll have to poll this call with a short time interval let’s say every 5 seconds, to continuously stay in sync with the other user’s presence.

A good place for doing this would be your chat screen. You can call this method every 5 or 10 seconds on the chat screen for every other participant in the chat except the current user.

That’s all you need to know about handling presence for users. Now, we’ll move on to another interesting topic which is dealing with timeline events.

Adding timeline events

In the previous sections, we’ve talked a lot about timeline events. You’ve also noticed that you need to add a SentTime timeline event to every message you sent.

You must be wondering, how are the useful exactly and if there are other types of timeline events that you can use. Good news, there are some other timeline events that you can use out the box while always having the ability to spin up a custom event of your own.

What are timeline events?

Think of timeline events as categorised timestamp for all activities by a user. Learn more here.

Exploring the standard timeline events

Although you can create any custom type of timeline events, Mitter.io provides a set of event types out of the box. Currently, there are 4 standard event types shipped by Mitter.io:

  • mitter.mtet.SentTime - The time at which the message was sent

  • mitter.mtet.ReceivedTime - The time at which the message was received by the server

  • mitter.mtet.DeliveredTime - The time at which the message was delivered to the user

  • mitter.mtet.ReadTime - The time at which the message was read by the user

While you can any of these standard events from the SDK, the SDK provides convenience methods for the mitter.mtet.DeliveredTime and mitter.mtet.ReadTime events where you don’t have to set these types explicitly. You’ll see how.

Adding a standard timeline event to a message

Taking an use case of adding a read receipt to a message, this can easily be done by calling the addReadTimelineEvent() method on the Messaging object. Here’s a quick demo:

messaging.addReadTimelineEvent(
    channelId = "b8bcb84d-8ad2-4f8a-816e-92772b2b8055",
    messageIds = listOf(
        "89a3de83-769e-4b62-8fee-b50b197c09b4",
        "8e5ea0fe-82e1-4b12-b6ca-5f33d965c0fa"
    )
)

In this example, we’re adding a read timeline event to 2 messages at once without explicitly setting the timeline event type and putting in any timestamp. The SDK does the job for you.

You can, also, attach an optional callback to this method as the last parameter onValueUpdatedCallback, if you want to be notified after the operation has succeeded.

Adding a customised timeline event to a message

When you grow out the standard use cases, you might want to use the addTimelineEvent() method to add a timeline event to your messages. This method accepts a TimelineEvent object which you need to construct on your own, fully customised to your needs.

Preparing the TimelineEvent object

Before sending out any event, you need to construct a TimelineEvent object with some required parameters, as follows:

val timelineEvent = TimelineEvent(
    type = "com.acme.events.GiftCardOpened",
    eventTimeMs = System.currentTimeMillis(),
    subject = User().setUserId("3764e344-5a53-4c02-8bfa-2b3bc11e7c0e")
)

Here, the type is the type of the event that you’re sending, eventTimeMs is the timestamp you want to associate with the event and the subject is the user with whom this event should be associated.

For example, if you’re sending a read time type of event, the subject would the receiving user who has read the message.

Sending the event

Now that you have the event prepared with your necessary customisations, it’s time to actually send it out for Mitter.io to deliver it to the concerned user(s).

You can do the same by calling the addTimelineEvent() method and supplying the TimelineEvent object that you created in the previous step. This is how the code looks:

messaging.addTimelineEvent(
    channelId = "b8bcb84d-8ad2-4f8a-816e-92772b2b8055",
    messageIds = listOf(
        "89a3de83-769e-4b62-8fee-b50b197c09b4",
        "8e5ea0fe-82e1-4b12-b6ca-5f33d965c0fa"
    ),
    timelineEvent = timelineEvent
)

As you can notice, it’s almost the same as the addReadTimelineEvent() method with the exception that it accepts a TimelineEvent object without assuming anything on it’s own.

This is how you can have full control over what events you want to send out.

Wrap up

This section has dealt with some advanced properties of Mitter.io and how you can leverage them to work the way you want.

In the next section, you’ll learn how to update your user’s profile and adding certain locators that’ll help you find any user in your application.

Last updated