Messaging service

service = client.service().messaging();

Depending on the client you initialised, ComapiClient or RxComapiClient , you have access to callbacks or reactive profile APIs.

The reactive version returns Observables you need to subscribe to. For the callback version, pass callback as the last parameter and the request is performed in the background, delivering results in the UI thread.

Data consistency

In order to manage concurrent updates of a conversation from many devices or websites, use ETag.

When obtaining conversation details from our services you can find an ETag in the ComapiResult object. It describes what version of the server data you received.

When you want to update this data, pass the same ETag with the new details. If the server data changes before your next update, the services don’t allow the modification until you obtain the most recent version of the data and pass the correct ETag with the next update. This means you can keep consistency of the data across many devices.


Messaging service API

Create conversation

ConversationCreate conversation = ConversationCreate.builder()
   // Unique conversation identifier.
   .setId("1234")
   // Description
   .setDescription("This is my first conversation")
   // Name 
   .setName("Awesome chat")
   // Is this conversation visible to users not being participants.
   .setPublic(false)
   /* Sets what permissions 'owner' and 'participant' will have in this conversation
      You can set if they will can: add new participants, remove participants, send messages.
      By default 'owner' and 'participant' can send messages, can add participants, cannot remove participants.
      Here we added CanRemoveParticipants permission for the 'owner'. */
   .setRoles(new Roles(Role.builder().setCanRemoveParticipants().build(), new Role()))
   .build();

Then pass this object to our services:

service.createConversation(conversation,
   new Callback<ComapiResult<ConversationDetails>>(){/* implement */});
rxService.createConversation(conversation)
   .subscribe(new Observer<ComapiResult<ConversationDetails>>(){/* implement */});

Update conversation

ConversationUpdate update = ConversationUpdate.builder()
                .setDescription("New description")
                .setName("Different name")
                .build();

Then pass this object to our services:

service.updateConversation(conversationId, update, eTag,
   new Callback<ComapiResult<ConversationDetails>>(){/* implement */});
rxService.updateConversation(conversationId, update, eTag)
   .subscribe(new Observer<ComapiResult<ConversationDetails>>(){/* implement */});

Delete conversation

service.deleteConversation(conversationId, eTag,
   new Callback<ComapiResult<Void>>(){/* implement */});
rxService.deleteConversation(conversationId, eTag)
   .subscribe(new Observer<ComapiResult<Void>>(){/* implement */});

Get conversation

To obtain conversation details call:

service.getConversation(conversationId,
  new Callback<ComapiResult<ConversationDetails>>(){/* implement */});
rxService.getConversation(conversationId)
   .subscribe(new Observer<ComapiResult<ConversationDetails>>(){/* implement */});
// is the conversation public or private
isPublic();

// unique identification number
getId();

// name of the conversation
getName();

// description of the conversation
getDescription();

// privileges of owner and participants in this conversation
getRoles();

Query conversations

You can also obtain a list of all conversations:

service.getConversations(isPublic,
   new Callback<ComapiResult<List<Conversation>>>(){/* implement */});
rxService.getConversations(isPublic)
   .subscribe(new Observer<ComapiResult<List<Conversation>>>(){/* implement */});
// is the conversation public or private
isPublic();

// unique identification number
getId();

// name of the conversation
getName();

// description of the conversation
getDescription();

// privileges of owner and participants in this conversation
getRoles();

// eTag to compare if local version of the data is the same as the one the server side
getETag();

// number of participant in conversation
getParticipantCount();

// latest event id of sent message in the conversation. If null there are no messages in the conversation
getLatestSentEventId();

Remove conversation participants

Provide list of profile ids to be removed from a conversation:

service.removeParticipants(conversationId, ids,
   new Callback<ComapiResult<Void>>(){/* implement */});
rxService.removeParticipants(conversationId, ids)
   .subscribe(new Observer<ComapiResult<Void>>(){/* implement */});

Query conversation participants

Query all participants for a particular conversation:

service.getParticipants(conversationId, 
   new Callback<ComapiResult<List<Participant>>>(){/* implement */});
rxService.getParticipants(conversationId)
   .subscribe(new Observer<ComapiResult<List<Participant>>>(){/* implement */});

Add participants to conversation

Add new participants to a conversation:

service.addParticipants(conversationId, participants,
   new Callback<ComapiResult<Void>>(){/* implement */});
rxService.addParticipants(conversationId, participants)
   .subscribe(new Observer<ComapiResult<Void>>(){/* implement */});

Send message in conversation

service.sendMessage(conversationId, body,
   new Callback<ComapiResult<MessageSentResponse>>(){/* implement */});
rxService.sendMessage(conversationId, body)
   .subscribe(new Observer<ComapiResult<MessageSentResponse>>(){/* implement */});

You can update the message status later using the conversationId from the ComapiResult.

// globaly unique message identifier
getId();

// unique for conversation event identifier, monotonically increasing, can be used to order messages
getEventId();

The more advanced way of constructing a new message:

Map<String, Object> data = new HashMap<>();
data.put("key","value");

Map<String, Object> fcm = new HashMap<>();
fcm.put("data", data);
fcm.put("notification", "{ \"title\":\"Message\", \"body\":\"Hi!\" } ");

Map<String, Object> apns = new HashMap<>();
apns.put("alert", "Hi!");

Part part = Part.builder()
               .setData("Hi")
               .setName("body")
               .setSize("Hi".length())
               .setType("text/plain")
               .build();

MessageToSend message = MessageToSend.builder()
   .setAlert(fcm, apns)
   .setMetadata(data)
   .addPart(part)
   .build();

In this way, you can set details of FCM/APNS notifications to be sent to participants’ devices, send custom metadata, and add more message parts, for example, Base64 encoded images.

Then call:

service.sendMessage(conversationId, message, 
   new Callback<ComapiResult<MessageSentResponse>>(){/* implement */});
rxService.sendMessage(conversationId, message)
   .subscribe(new Observer<ComapiResult<MessageSentResponse>>(){/* implement */});

You can update the message status later using the conversationId from ComapiResult.

Update message status

MessageStatusUpdate update = MessageStatusUpdate.builder()
   .addMessageId("id")
   .setStatus(MessageStatus.delivered)
   .build();

List<MessageStatusUpdate> updates = new ArrayList<>();
updates.add();

Then pass the list with delivered and read statuses to the service:

service.updateMessageStatus(conversationId, updates,
   new Callback<ComapiResult<Void>>(){/* implement */});
rxService.updateMessageStatus(conversationId, updates)
   .subscribe(new Observer<ComapiResult<Void>>(){/* implement */});

Query conversation events

Sending a message and updating a message status can be obtained by providing a conversation event ID and an upper limit of events you are interested in.

service.queryConversationEvents(conversationId, from, limit,
   new Callback<ComapiResult<ConversationEventsResponse>>(){/* implement */});
rxService.queryConversationEvents(conversationId, from, limit)
   .subscribe(new Observer<ComapiResult<ConversationEventsResponse>>(){
      /* implement */});

In response, you get:

/**
 * Gets Events in the order in which they were received.
 * This collection can contain following conversation events:
 * MessageSentEvent - new message received in the conversation
 * MessageDeliveredEvent - message status marked 'delivered'
 * MessageReadEvent - message status marked 'read'
 * Cast elements of this collection to one of these.
 */
getEventsInOrder();

// Parsed message sent events
getMessageSent();

// Parsed message status 'delivered' events
getMessageDelivered();

// Parsed message status 'read' events
getMessageRead();

Learn more in Listen to events.

Query messages

To obtain a list of messages in a conversation you must provide a conversation event ID and an upper limit of messages you are interested in. This allows you to get messages from the end of the conversation one page at a time. You can implement the classic pull down interface to load more messages.

service.queryMessages(conversationId, from, limit, 
   new Callback<ComapiResult<MessagesQueryResponse>>(){/* implement */});
rxService.queryMessages(conversationId, from, limit)
   .subscribe(new Observer<ComapiResult<MessagesQueryResponse>>(){/* implement */});

The result is a list of messages and orphaned events associated with them. The orphaned events are the events updating messages for conversation event IDs higher than the provided from value, so, for example, messages obtained in previous paging calls. These events need to be applied to the newer messages.

// Messages conforming to the query.
getMessages();

/* Latest event id in this conversation that 
was taken into account constructing the query result. */
getLatestEventId();

/* Earliest event id in this conversation that 
was taken into account constructing the query result. */
getEarliestEventId();

// Events updating messages for conversation event id later/higher then 'from'.
getOrphanedEvents();

Getter messagesQueryResponse.getMessages() returns list of MessageReceived:

// Message unique identifier.
getMessageId();

/* Unique, monotonically increasing number of event 
of this message and conversation. */
getSentEventId();

// Message sender.
getFromWhom();

/* Message sender defined internally on server 
(shouldn't be visible inside the app). */
getSentBy();

// When the message was sent.
getSentOn();

// Conversation unique identifier
getConversationId();

// Key is the profile identifier the value is either 'delivered' or 'read'
getStatusUpdate();

Getter messagesQueryResponse.getOrphanedEvents() returns list of OrphanedEvent:

// Conversation event id.
getConversationEventId()

// Event name.
getName();

// Unique event identifier.
getEventId()

// Id of the updated message.
getMessageId();

// Id of the conversation for which message was updated.
getConversationId();

// Profile id of the user that changed the message status.
getProfileId();

// Gets time when the message status changed.
getTimestamp();

// True if this event is of type message delivered.
isEventTypeDelivered()

// True if this event is of type message read.
isEventTypeRead();

Send user is typing event

To send notification to conversation participants that the user started or finished typing a new message call:

service.isTyping(conversationId, isTyping, 
   new Callback<ComapiResult<Void>>(){/* implement */});
rxService.isTyping(conversationId, isTyping)
   .subscribe(new Observer<ComapiResult<Void>>(){/* implement */});

Upload content data

service.uploadContent(folder, contentData,
   new Callback<ComapiResult<UploadContentResponse>>(){/* implement */});
rxService.uploadContent(folder, contentData)
   .subscribe(new Observer<ComapiResult<UploadContentResponse>>(){
     /* implement */});

The content data can be created based on a file, byte array, and base64 string.

contentData = ContentData.create(file, type, name);
contentData = ContentData.create(byte[], type, name);
contentData = ContentData.create(base64, type, name);

The UploadContentResponse contains the full URL to the file in the cloud. You can, for example, send a message with a message Part containing this URL pointing to an attachment.