Messaging service

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

Depending on the client you initialised ComapiClient or RxComapiClient you will have access to callbacks or reactive profile APIs. Reactive version returns Observables you need to subscribe to. For callback version pass callback as the last parameter and the request will be performed in the background delivering result in UI thread.

data consistency

In order to manage concurrent updates of a conversation from many devices/websites use ETag. When obtaining conversation details from Comapi services you can find an ETag in ComapiResult object. It describes what version of server data you got. When you want to update this data pass the same ETag with the new details. If the server data will change before your next update the services will not allow such modification until you obtain the recent version of the data and pass correct ETag with the next update. This way 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 Comapi 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 Comapi 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 convesartion

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 the 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 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 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 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 conversationId from ComapiResult result.

// 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 is through

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, some custom metadata, add more message parts with e.g. Base64 encoded images etc.

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 conversationId from ComapiResult result.

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

Introduced in v1.0.2.

Conversation events - sending a message and updating a message status can be obtained providing conversation event id to start from (and going forward) 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 will 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();

See socket events for details.

query messages

List of messages in a conversation can be obtained providing conversation event id to start from (and going backwards) and an upper limit of messages you are interested in. This interface allows you to get messages from the end of the conversation one page at a time. This will allow you to 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 id higher then 'from' - so for messages obtained e.g. in previous 'paging calls'. This events need to be applied to this 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

Introduced in v1.0.2.

To send an information 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

Introduced in v1.1.1

To 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 will contain the full url to the file in the cloud. You can for e.g. send a message with a message Part containing this url pointing to an attachment data.