Messages

Message objects represent individual messages. They belong to a conversation and consist of one or more pieces of content, known as Message Parts.

Push notification previews and sounds are set when you create each message. Each message keeps track of its delivery and read state for every participant in a conversation.

Creating a message

Message objects are initialized by calling layerClient.newMessage(). This method takes a list of message parts:

Message message = layerClient.newMessage(Arrays.asList(messagePart));

Message Parts

A message is comprised of one or more parts. You may use a multi-part message to send a photo along with a description, for example, or to send a message with a location.

You can send any type of data through Layer. To support this, Message Parts consist of a data blob for the value
and a MIME type string for the type. Each Message Part can be up to 2GB. The MIME type string simply describes the type of content.

Note

Message Parts that are 2KB or smaller are sent inline (the content is sent with the Message Part instance). Larger Message Parts have their content uploaded to dedicated storage using the Rich Content APIs.

// Creates a message part with a string of "Hi!..." and text/plain MIME type.
String messageText = "Hi! How are you";
MessagePart messagePart = layerClient.newMessagePart("text/plain", messageText.getBytes());

// Creates a message part with an image
Bitmap imageBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.back_icon);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
imageBitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
byte[] imageData = stream.toByteArray();
MessagePart messagePart = layerClient.newMessagePart("image/jpeg", imageData);

MessagePart also declares a convenience method for creating a plain text (text/plain MIME type) message part:

String messageText = "Hi! How are you";
MessagePart messagePart = layerClient.newMessagePart(messageText);

Your application can declare additional MIME types that it wishes to support; the following example demonstrates sending location data:

// Creates a HashMap with latitude and longitude
HashMap location = new HashMap<String, String>();
location.put("lat", "25.43567");
location.put("lon", "123.54383");

// Convert the location to data
ByteArrayOutputStream locationData = new ByteArrayOutputStream();
ObjectOutputStream outputStream = new ObjectOutputStream(locationData);
outputStream.writeObject(location);

MessagePart locationPart = layerClient.newMessagePart("text/location", locationData.toByteArray());

Note

While Layer does not place any restrictions on the MIME Type, Google and Apple dictate that the MIME Type string MUST conform to a */* convention. If the MIME Type does not contain a forward slash (/) you may have issues sending messages. For a comprehensive list of MIME Type values check out the IANA’s official registry of media types.

Sending a message

// Sends the specified message
conversation.send(message);

// Send the message, and get notified of upload progress.
// This is useful for large messages (images, videos, etc).
// See below for more details
conversation.send(message, new LayerProgressListener() {
    public void onProgressStart(MessagePart messagePart, Operation operation) {
    }
    public void onProgressUpdate(MessagePart messagePart, Operation operation, long l) {
    }
    public void onProgressComplete(MessagePart messagePart, Operation operation) {
    }
    public void onProgressError(MessagePart messagePart, Operation operation, Throwable throwable) {
    }
});

If Layer has a current network connection, the message will immediately be sent off of the device. Otherwise it will remain enqueued locally until the SDK re-establishes a network connection. At that point, the SDK will automatically send the message.

You can check if a Message object has been sent via the isSent() method, which returns a boolean telling your application if the message was successfully sent from your device and synchronized with the Layer service.

Confirming delivery

The simplest way to confirm that a message has been delivered is to visit the Logs page of the Developer Dashboard. If a message was successfully sent, you’ll see a log like this:

May 02 2015 2:34:27pm Sync: User <USER_IDENTIFIER> created a message in conversation <CONVERSATION_IDENTIFIER>.

Receiving messages

The Layer SDK automatically receives incoming messages — you don’t need to force a sync or set up a polling system.

When displaying the message, you can get the sender’s Identity object, which contains profile data such as avatar URL and display name.

// The sender's Identity object
Identity sender = msg.getSender();

You will also need to check the message’s MIME type (sent when it was sent) to determine how to decode and present the message contents:

List<MessagePart> parts = message.getMessageParts();
for (MessagePart part : parts) {
    switch (part.getMimeType()) {
        case "text/plain":
            String textMsg = new String(part.getData());
            break;

        case "image/jpeg":
            Bitmap imageMsg = BitmapFactory.decodeByteArray(part.getData(), 0, part.getData().length);
            break;
    }
}

By default, LayerKit automatically downloads content for message parts whose content size is less than 2KB. However, you can change this setting:

// Autodownload all message parts whose content size is less than 100KB
layerClient.setAutoDownloadSizeThreshold(1024 * 100);

You can also configure the types of content that should be automatically downloaded, regardless of their content size.

// Autodownload all JPEGs (MIME type image/jpeg)
layerClient.setAutoDownloadMimeTypes(Arrays.asList("image/jpeg"));

Finally, you can manually download message part content — often this is used when a message is scrolled into view, or when the user taps on a preview/placeholder.

This method takes a LayerProgressListener that reports progress of the download transfer:

public void downloadMessagePart(MessagePart part){
    // You can add whatever conditions make sense.
    // In this case, we only start the download if the
    // MessagePart is ready (not DOWNLOADING or COMPLETE)
    if (part.getTransferStatus() == MessagePart.TransferStatus.READY_FOR_DOWNLOAD) {

        // Start the download with a ProgressListener
        part.download(new LayerProgressListener() {
            @Override
            public void onProgressStart(MessagePart messagePart, Operation operation) {

            }

            @Override
            public void onProgressUpdate(MessagePart messagePart, Operation operation, long l) {
                // You can calculate the percentage complete based on the size of the Message Part
                float pctComplete = bytes / part.getSize();

                // Use this to update any GUI elements associated with this Message Part
                System.out.println(operation.toString() + " Percent Complete: " + pctComplete);
            }

            @Override
            public void onProgressComplete(MessagePart messagePart, Operation operation) {
                // Handle whatever MIME types you are expecting
                if(messagePart.getMimeType().equals("image/jpg")){
                    byte[] myData = messagePart.getData();
                    Bitmap image = BitmapFactory.decodeByteArray(myData, 0, myData.length);
                    // Display image in UI
                }
            }

            @Override
            public void onProgressError(MessagePart messagePart, Operation operation, Throwable throwable) {

            }
        });
    }
}

Disk space management

You can limit the amount of space Layer will use for saving data. If locally cached content exceeds this limit, then content will be deleted, starting with the least-recently accessed.

// Sets the local disk space Layer is allowed to use (in bytes).
// Passing in a value of 0 will remove any limit (this is the default behavior)
layerClient.setDiskCapacity(1024 * 1024 * 50); //Sets the limit to 50 MB

// Returns the amount of disk capacity (in bytes) Layer is set to use
long layerCapacity = layerClient.getDiskCapacity();

// Check Layer's actual usage (in bytes)
long usage = layerClient.getDiskUtilization();

// You can always choose to delete a Message Part
// This only applies locally; the server is not affected)
layerClient.deleteLocalMessageData(messagePart);

Recipient status

Layer keeps track of the recipient status of each message, for each participant in a conversation. Note that messages within channels do not track recipient status; only Conversation rather than Channel messages track this.

There are four possible statuses:

  • Message.RecipientStatus.PENDING: The message is waiting to be synced with Layer
  • Message.RecipientStatus.SENT: The message has successfully reached Layer servers and is waiting to be synchronized with recipient devices
  • Message.RecipientStatus.DELIVERED: The message has arrived on the recipient’s devices
  • Message.RecipientStatus.READ: The recipient’s device has marked the message as read

Your app can check recipient statuses by calling the getRecipientStatus property on Message objects:

// Get the status for a specific participant
Message.RecipientStatus status = message.getRecipientStatus(participant);

// Get statuses for all participants
Map<Identity, Message.RecipientStatus> statuses = message.getRecipientStatus();

Layer automatically updates the recipient status to sent, delivered, or invalid. The only update your app can perform is to mark a message as read:

message.markAsRead();

Your app can also mark all the messages in a conversation as read:

conversation.markAllMessagesAsRead();

Note that Messages within Channels do not yet support marking messages as read.

Channels Announcements