Layer Messaging Experiences

Layer’s XDK provides a framework for building and rendering rich and interactive Messaging Experiences. Each message that is sent/received by an application built using Layer consists of a collection of Message Parts that have MIME Types that define what type of message it is. Each type of message can have its own custom rendering, interactions and operations. Layer’s XDK provides the tools for providing these Message Types and rendering them; it also comes with a small library of these built in.

The tools for building these Messaging Experiences are:

  • MessageModelManager: Handles mapping of Message Models to MIME types and instantiating the models
  • MessageModel: Represents a specific type of Message such as an Image Message, exposing properties, methods and operations for interacting with that type of message
  • Message View: A rendering for a specific type of Message Type Model
  • MessageContainer: A container that lays out the Message Type View with respect to other relevant information or widgets related to the Message

Examples of these include:

  • Text Message Model + Text Message View combine to create the Text Message
  • Receipt Message Model + Receipt Message View combine to create the Receipt Message

The Message Model Manager

Every Message that is rendered within the Message List is created via the MessageModelManager. This handles all needed dependency injection into the created MessageModel object. These MessageModels are then loaded into a RecyclerView and displayed in their specified MessageContainer.

What is a Message Model?

Up to this point in the documentation, Messages and Message Parts were treated as abstract concepts that took a custom MIME Type such as text/plain, some body such as hello world, and what you do with these is entirely unspecified. As part of providing a system of Messaging Experiences, the definitions and expectations of what goes in the MessageParts has become a lot more specific; these MessageParts are expected to match a specific format and set of rules. The following Message represents a Carousel of Maps; this example is presented here so that you can see a hint of what Messages and Message Parts underlying this will look like. You will not need to really work with Messages and Message Parts directly until you start creating Custom Messaging Experiences.

[
  {
    "mime_type": "application/vnd.layer.carousel+json; role=root; node-id=6307b011-e0e9-4bb5-8b97-99309e49cbfc",
    "body": ""
  },
  {
    "mime_type": "application/vnd.layer.location+json; role=carousel-item; parent-node-id=6307b011-e0e9-4bb5-8b97-99309e49cbfc",
    "body": "{
        "title": "Layer HQ 2017",
        "street": "655 4th st",
        "city": "San Francisco",
        "administrative_area": "CA",
        "postal_code": "91447"
    }""
  },
  {
    "mime_type": "application/vnd.layer.location+json; role=carousel-item; parent-node-id=6307b011-e0e9-4bb5-8b97-99309e49cbfc",
    "body": "{
        "title": "Near Layer HQ 2017",
        "street": "650 4th st",
        "city": "San Francisco",
        "administrative_area": "CA",
        "postal_code": "91447"
    }"
  },
  {
    "mime_type": "application/vnd.layer.location+json; role=carousel-item; parent-node-id=6307b011-e0e9-4bb5-8b97-99309e49cbfc",
    "body": "{
        "title": "Less Near Layer HQ 2017",
        "street": "645 4th st",
        "city": "San Francisco",
        "administrative_area": "CA",
        "postal_code": "91447"
    }"
  }
]

So… thats a lot of raw data, all of it buried in JSON strings, and all of it spread across a bunch of Message Parts and MIME Types. Rather than have that as what developers interact with to create unique Messaging Experiences, we instead provide a MessageModel that abstracts all of the above data into a simple set of methods, properties and operations.

A MessageModel provides the following services:

  1. Understands and abstracts all of the complexities of the Message and its Message Parts into simple APIs that can be used by developers
  2. Manage MessageModel Actions
  3. Maps values of the model to a Title, Description and Footer (for Message Type Views that use Message View Containers)
  4. Provide named actions to be triggered if a user clicks/selects a Message
  5. Defines resource IDs for the following layouts:

A MessageModel is used in the Message List. This list uses a view holder that binds a message model to its view. When these view holders are created, the entire view hierarchy of the message is instantiated (based on the full MIME type tree). This view hierarchy will contain one or more MessageContainer. The Message View](https://developer.android.com/reference/android/view/View.html) defined in the [MessageModel may define additional MessageContainers to achieve this nested layout.

Developers using prebuilt Layer Messages should only care about how to use a Model to create Messages of various types. To learn more about how to create new Models, see Customizing Messaging Experiences.

Message Metadata

There are metadata objects (for example LocationMessageMetadata) that represent the data in a given MessagePart. These are primarily used for serializing/de-serializing JSON into POJOs. These are primarily used for display purposes but can be used for creating message parts as well.

MessageModel Actions

All Models will have the following properties:

Name Required Type Description
action No Action Contains a description of what happens when the user clicks/selects a Message rendered from this Model
action.event No String A string naming the event to trigger. Events are handled by registering an ActionHandler with the ActionHandlerRegistry
action.data No JsonObject Note that data commonly comes from the Model directly; a Link Message has a url, a File Message has a source; but suppose you wanted to override that behavior… or to have an open-url action on a Text Message? You can put a url into model.action.data and this url will take precedence over any other url for the open-url action.

MessageModel Action Events

All Action Events come with the following properties:

Name Type Description
data JsonObject Custom Data provided to the operation. This Custom Data comes from either the model’s action.data when the Message is tapped by the user, or from an Action Button’s data property when an Action Button is tapped by the user.
model MessageModel The model representing the Message/Sub-Message that was selected (or whose Action Buttons were selected by the user)
rootModel MessageModel If the model property was a Sub-Model (i.e. a Carousel Item within a Carousel Message), then this provides the top level MessageModel that the model exists within. rootModel might be a Carousel Model, while model is a simple Link Model.

Events are triggered by an ActionHandler callback. These are registered via ActionHandlerRegistry.register().

To override the existing open-url action:

private static class BetterUrlHandler extends ActionHandler {

	public BetterUrlHandler(LayerClient layerClient) {
        super(layerClient, "open-url");
    }

    @Override
    public void performAction(@NonNull Context context, @Nullable JsonObject data) {
    	Intent intent = new Intent(context, WebBrowserActivity.class);
    	intent.putExtra("data", data.toString());
    	context.startActivity(intent);
    }
}

ActionHandlerRegistry.register(new BetterUrlHandler(layerClient));
Registering a New Action Handler

If defining a custom action for your application’s messages, you should register an Action Handler to process any message that has that action. This example assumes a custom Message Type Model has been defined that contains a bill property and a processBill method.

private static class BillHandler extends ActionHandler {

	public BillHandler(LayerClient layerClient) {
        super(layerClient, "kill-bill");
    }

    @Override
    public void performAction(@NonNull Context context, @Nullable JsonObject data) {  
		String bill = data.get("bill").getAsString();
    }
}

ActionHandlerRegistry.register(new BillHandler(layerClient));

A ChoiceHandler can also be registered for choice buttons to gain access to the model and root model objects.

MessageModel Custom Data

All MessageModel will have the following properties:

Name Required Type Description
customData No Object Arbitrary data that won’t affect rendering or actions of the message but does affect understanding of the message

A simple example of where customData might be used is to add a Product ID, or SKU Number to a Model that does not have those concepts built into it. While this Product ID would not affect how its rendered, it may be used for use cases such as:

  • Your server may have a webhook that detects messages of a specific type, and needs a Product ID (or other custom data) to properly log the event or kick off a process on behalf of the users
  • When the user selects the message triggering some custom action within the browser, this allows for the action to have more information about the message. This is generally a better way to associate custom information about a message than action.data as it allows your messages to standardize on a set of expected properties; those that are built into the model, and those that you add to the model.

Text Messages

A Text Message has a few uses:

  • A standard conversational message containing messages users type to one another
  • A framed and titled Message for quoted text. For example, a quote from a product manual or a product page might be rendered, and attribution given as to where it came from so that it does not look like something that the sender just typed in.
Text Message with Details
Text Message without Details

The above illustrates the difference between a Text Message that is rendered as a conversational message, and a Text Message that renders quoted text with a title and footer.

A Text Message can be instantiated using:

// Create the contents
String mimeType = MessagePartUtils.getAsRoleRoot(TextMessageModel.ROOT_MIME_TYPE);
JsonObject contents = new JsonObject();
contents.addProperty("text", Starting at the SIM card slot, stick the plastic opening tool in between the back panel and the phone. Slide the opener tool gently around the phone until you hear clicks and visibly see the panel separate from the body of the phone. Continue this process on each edge of the phone, until the only thing holding the back panel to the body of the phone is the fingerprint scanner.);
contents.addProperty("title", "Step 2 of Nexus 5x Battery Replacement");
contents.addProperty("author", "ifixit");
MessagePart root = getLayerClient().newMessagePart(mimeType, contents.toString().getBytes());

// Send a message
PushNotificationPayload payload = new PushNotificationPayload.Builder()
        .text("New message from " + sender)
        .build();
Message message = layerClient.newMessage(root);

Alternatively, use a RichTextSender registered on a ComposeBar.

The Text Model supports the following properties:

Name Required Type Description
text Yes String This is the core data rendered by the Core UI for this Message
title No String Title for the Text Message
subtitle No String Rendered below the title
author No String Rendered below the subtitle

Note

If text is provided, but no title, subtitle or author then this Text Message will render as a regular chat bubble rather than look like a quoted message with metadata shown below it.

A Link Message has a few uses:

  • A simple way to send a link to users
  • A framed summary of a web page, article or other online content consisting of a picture, title, description and the url itself.

Note

URLs sent within a Text Message will be hyperlinked; this means that links can be sent as part of a Text Message. The Link Message provides a richer experience, and clicking anywhere on the message will open the link, and Link Messages will not truncate the rendering of the URL the way that a Text Message will do.

Example of a Link Message without any Details
Example of a Link Message showing an Article with Details

A Link Message can be instantiated using:

Only displaying link messages on Android is supported at this time. Support for sending link messages on Android will come in a future release.

The Link Model supports the following properties:

Name Required Type Description
url Yes String The URL to open; used by the open-url action, and by the URL Enrichment Integration (optional)
title No String Title for the Link Message
description No String Rendered below the title
author No String Rendered below the description
imageUrl No String URL to an image to render over the title

Note

If url is provided, but no title, description, author or imageUrl then this Link Message will render as a conversational message and show the url value directly.

An optional URL Enrichment Integration Service will be made available to customers that looks for any Link Message that contains only the URL, and updates the Message to have a title, description, author and imageUrl based on data it reads off of the web page loaded from that URL.

File Messages

A File Message has a few uses:

  • Allow a user to drag and drop one or more files from their desktop and send them as Messages that recipients can open
  • Allow an application to send a reference to a file in its library, with accompanying descriptions

A File Message can be instantiated using:

MessageSender messageSender = getMessageSender();
FileMessageComposer composer = FileMessageComposer();

// Send the raw Image
Message message = composer.buildFileMessage(context, layerClient, uriPath);
messageSender.send(message);

Alternatively, use a FileSender registered on a ComposeBar. Only sending simple file messages on Android is supported at this time. Support for sending additional file properties on Android will come in a future release.

The File Model supports the following properties:

Name Required Type Description
source No File/Blob A File or Blob object
sourceUrl No String URL to a remote file (should not be used if source is in use)
title No String Title for the File Message
author No String Rendered below the title
size No Long Represents the size of the file in number of Bytes

Note

  • source or sourceUrl must be provided for this to be a valid File Message.
  • Do not use both source and sourceUrl.

Image Messages

An Image Message has a few uses:

  • Allow a user to drag and drop one or more Images from their desktop and send them as Messages that just show the image and nothing more
  • Allow an application to send images with titles, subtitles and other annotations. For example, one might send a product picture with the name and description of the product associated with it.
Basic Image Message
Image with Details

A Image Message can be instantiated using:

MessageSender messageSender = getMessageSender();
ImageMessageComposer composer = getImageMessageComposer();

// Send the raw Image
Message message = composer.newImageMessage(new File(mPhotoFilePath.get()));
messageSender.send(message);

Alternatively, use a GallerySender or CameraSender registered on a ComposeBar. Only sending simple image messages on Android is supported at this time. Support for sending additional image properties on Android will come in a future release.

The Image Model supports the following properties:

Name Required Type Description
source no Image/Blob A File or Blob object representing a known Image format
sourceUrl No String URL to a remote Image (do not use this if source is in use)
title No String Title for the Image Message
subtitle No String Rendered below the title
author No String Rendered below the subtitle
width No Integer Width in pixels of the source image
height No Integer Height in pixels of the source image

Note

  • source or sourceUrl must be provided for this to be a valid Image Message.
  • Do not use both source and sourceUrl.
  • If using sourceUrl, you should provide a width and height value. width and height will be added automatically if providing an File/Blob representing an Image.

Location Messages

A Location Message has a few uses:

  • Show a map of a location; perhaps a restaurant to meet at? A house to sell? Or just your current position in the world
  • Show an address, for example, as a sub-message-view of a Receipt Message

Just the map:

Just a Map
Map with Details
Map with Address

A Location Message can be instantiated using:

MessageSender messageSender = getMessageSender();

// Send the address
LocationMessageMetadata metadata = new LocationMessageMetadata();
metadata.mCity = "San Francisco";
metadata.mTitle = "Layer Inc";
metadata.mPostalCode = "94107";
metadata.mAdministrativeArea = "CA";
metadata.mStreet1 = "655 4th St";
String mimeType = MessagePartUtils.getAsRoleRoot(LocationMessageModel.ROOT_MIME_TYPE);
MessagePart part = layerClient.newMessagePart(
	mimeType, 
	messageSender.mGson.toJson(metadata).getBytes());
Message message = layerClient.newMessage(part);
messageSender.send(message);

// Send the lat/long
LocationMessageMetadata metadata = new LocationMessageMetadata();
metadata.mTitle = "Layer Inc";
metadata.mLatitude = 37.7734858;
metadata.mLongitude = -122.3916087;
String mimeType = MessagePartUtils.getAsRoleRoot(LocationMessageModel.ROOT_MIME_TYPE);
MessagePart part = layerClient.newMessagePart(
	mimeType,
	messageSender.mGson.toJson(metadata).getBytes());
Message message = layerClient.newMessage(part);
messageSender.send(message);

Alternatively, use a CurrentLocationSender registered on a ComposeBar.

The Location Model supports the following properties:

Name Required Type Description
latitude No Double Use lat/long and/or street address
longitude No Double Use lat/long and/or street address
zoom No Integer Zoom level from 1 - 18; defaults to 16; see Google Maps API
title No String Title for the Location Message
description No String Rendered below the title. If present, will be rendered instead of an Address.
street1 No String String address line 1
street2 No String String address line 2
city No String City that the location is within
administrativeArea No String Name of the state or comparable unit of region
postalCode No String Zipcode or some regionally comparable concept
country No String Country code or name

Note

  • To only show the map, and no other information, include latitude and longitude, and leave out titles, addresses and descriptions.
  • If you want to render an Address instead of the description, leave the description empty.

Status Messages

A Status Message is a Message within the Message List that:

  • Does not have an avatar, nor indicate any sender
  • Is centered rather than right or left aligned as though it came from one or the other participant
  • Does not have a timestamp
  • Supports only textual information
A simple Status Message

A Status Message can be instantiated using:

Only displaying status messages on Android is supported at this time. Support for sending status messages on Android will come in a future release.

The Status Model supports the following properties:

Name Required Type Description
text Yes String This is the core data rendered by the Core UI for this Message

The Carousel Message presents an arbitrary collection of sub-message-views in a left-right scrolling view.

While one can create a Carousel that contains a mix of Image Messages, Text Messages, Product Messages, Button Messages, etc… for best results, the Caroursel items:

  1. Should all be of the same type
  2. Should all contain similar sizes and quantities of content
Carousel of File Messages

A Carousel Message can be created using:

Only displaying carousel messages on Android is supported at this time. Support for sending carousel messages on Android will come in a future release.

Its important to understand that a Carousel Message’s action.event and action.data will override all of the Carousel Item’s default actions. In the above example, the default action on each FileModel is open-file. We can change that as follows:

Only displaying carousel messages on Android is supported at this time. Support for sending carousel messages on Android will come in a future release.

When the user clicks on ANY of the Messages specified in items, it will invoke the delete-file action on the Carousel Item, and will NOT trigger an open-file action. NOTE: If any of the items specifies its own action.event, this will not be overwritten; only default values for action.event are overwritten.

Futher, you can add custom action data:

Only displaying carousel messages on Android is supported at this time. Support for sending carousel messages on Android will come in a future release.

If your custom delete-file event handler checks for the evt.detail.data.confirm_deletion it will know whether to directly delete the file or ask for confirmation. action.data will be mixed into any existing action.data on each Carousel Item, and will not overwrite any action.data values explicitly entered for a given sub-message within the carousel; this means that individual Carousel Items could have their own value for evt.detail.data.confirm_deletion.

The Carousel Model supports the following properties:

Name Required Type Description
items Yes List<MessageModel> List of Message Models representing the Items the Carousel will scroll through

Choice Messages

The Choice Message presents the user with a set of choices to make, and when the user has made their selection, it updates the message’s state for all participants in the Conversation.

Note

This Message, unlike others, depends upon a Message Response Integration Service which you will need to install.

Choice with No Selection
Choice with Selection and allowReselect: false

A Choice Message, configured to not allow Reselection:

Only displaying choice messages on Android is supported at this time. Support for sending choice messages on Android will come in a future release.

The above Choice Message will ask the user the specified question, and provide the specified choices for the user to choose from. When the recipient selects an answer, the following actions will be performed:

  1. A Response Message is sent indicating what the user selected, and providing a textual description of that selection
  2. A Response Message is rendered for all participants, representing that Response Message, and informing all users of the change in the state of the Message. It might say something like “User X has selected Are we using Imperial or Metric units?”.
  3. The Message’s local state is promptly updated for the user who made the selection, providing quick feedback to the user that the selection was accepted.
  4. The Message Response Integration Service receives the Response Message, and updates the original Choice Message with the user’s Selection
  5. All other participants receive the update to the Choice Message, and now all users see the selection.

The choices array is designed to have an id so that edits to the text values may eventually be supported, while leaving the id values unchanged. This would leave the user’s selections intact, while allowing for tweaks to the text.

The Buttons Model supports the following properties:

Name Required Type Description
label N String A question to ask the user
choices Y List<ChoiceMetadata> List of choices for the user to pick from
choices.text Y String Text to show the user within a selection button
choices.id Y String ID that identifies the selection and distinguishes it from the other choices
allowReselect N Boolean Default is false; if false then selections may not be changed once made
allowDeselect N Boolean Default is false; if false then selections may not be selected again to deselect them. If true then allowReselect will be set to true
allowMultiselect N Boolean Default is false; if false then only a single selection is allowed at a time. If this is true, users may select multiple choices, and allowReselect and allowDeselect are set to true.
responseName N String Name that will uniquely identify this response within the Message and differentiate it from other types of responses
enabledFor Yes String Identity ID allowed to select these buttons
customResponseData No JsonObject Name value pairs to add to the Message Response data. Allows you to add some context to the Response Messages as well as to the data added to the Choice Model once the Message has been updated
title N String The default title of Choose One can be replaced by providing your own title

Note

  • allowReselect is enforced client side, not server side, and should not be treated as securely enforced at this time.
  • Failure to set a responseName to a unique name could cause different message interactions to overwrite one another. This should not matter for simple messages, but for complicated messages with multiple types of state and interactions it can easily cause errors.
  • Default responseName is selection.

Button Messages

Most of the messages described up above are minimally interactive. Except for the Choice Message, the only action they provide is that if you click/select them, they try to open something or trigger an event. The Buttons Message allows you to take any other Message and add Actions to it.

Product Message wrapped in a Button Message. Button Message provides all buttons shown above.

Only displaying button messages on Android is supported at this time. Support for sending button messages on Android will come in a future release.

For a button with an event of open-url, one of the following will occur:

  • Your button’s data.url will be opened if present
  • Your Button Model’s contentModel.url will be opened if present
  • No action will occur

Typically, your event will either be open-url (or other Layer provided event) or a custom action that will be triggered on the DOM for your event handler to listen for.

The Buttons Model supports the following properties:

Name Required Type Description
contentModel No MessageModel A Message Model for a a sub-message-model that will have buttons attached to it; however, buttons can be sent without any content.
buttons Yes List<ButtonMetadata> List of objects describing the buttons to add
buttons.type Yes String Currently “action” and “choice” are the only supported values
buttons.text Yes String Label for the Button; used for type=action buttons only
buttons.event No String Event to trigger when the button is clicked
buttons.data No JsonObject Custom parameters to provide with your buttons.event
buttons.choices No List<ChoiceMetadata> Choices that the user can chose from using the ChoiceMetadata

Button Message with Choice Buttons

The Choice Buttons within the Button Message enables an app to persist a user’s selection and share it with all participants. These selections are designed for a single user only to perform the selection, and for that selection to become a permanent part of that Message. Only a single state name can be set by a given Button Set; you can not set multiple types of state in a single Button Set.

Button Message that does not wrap another message, and which provides 3 choices

Only displaying button messages with choices on Android is supported at this time. Support for sending button messages with choices on Android will come in a future release.

The following properties can be used with the ButtonModel constructor to setup your Choice Buttons:

Name Required Type Description
choices.text Yes String Button text; emoji characters are accepted as well such as “\uD83D\uDC4D”
choices.tooltip No String Button tooltip
choices.id Yes String Value that will be recorded if this Choice is selected
data.responseName No String Unique name associated with this response; needed if more than one Choice or Response may be associated with this message
data.allowReselect No Boolean Allow users to change their selection once made
data.allowDeselect No Boolean Allow user to deselect a previous selection requires allowReselect
data.allowMultiselect No Boolean Default is false; if false then only a single selection is allowed at a time. If this is true, users may select multiple choices, and allowReselect and allowDeselect are implicitly set to true.
data.enabledFor Yes String Identity ID allowed to select these buttons
data.customResponseData No JsonObject Name value pairs to add to the Message Response data. Allows you to add some context to the Response Messages as well as to the data added to the Choice Model once the Message has been updated

Note

  • Failure to set a data.responseName to a unique name could cause different Message Response interactions to overwrite one another.
  • Default responseName is selection.
  • A Button Message may contain both Action buttons and Choice Buttons

Product Messages

The Product Message represents a product for sale. Unlike many other informational Messaging Experiences, this does not depend upon the Standard Message Container which provides a standard layout of information. There is a lot of custom information about a product that needs to be shown. Typically this Message would be used to allow a user to purchase, bookmark or perhaps return a Product. Such actions are attached using the Buttons Message.

Standard Product Message

Only displaying product messages on Android is supported at this time. Support for sending product messages on Android will come in a future release.

The Product Model supports the following properties:

Name Required Type Description
name N String Name of your product
brand N String Brand of your product
description N String One or more lines of product description
price N Float Cost per unit of this product
quantity N Integer Number of units of the product; defaults to 1
currency N String ISO 4217 currency codes; default is USD
url N String URL to open if user clicks for more information
imageUrls N List<String> List of URLs to a product pictures. Currently only the first is rendered.
options N List<ChoiceModels> List of Choice models representing options for this product (clothing size, CPU speed, etc…). If type='label' then this Product Message simply shows the user what options are selected, but does not allow changing them. (Only label supported at this time)

Note

Currently, only a ChoiceModel with type='label' is supported. Future versions may allow type values that allow users to select a choice and update the state of the Message.

Receipt Messages

The Receipt Message is a Message that renders:

  • a list of Products
  • a shipping address
  • a total price
  • payment info
Standard Receipt Message

Only displaying receipt messages on Android is supported at this time. Support for sending receipt messages on Android will come in a future release.

The Receipt Model supports the following properties:

Name Required Type Description
billingAddressModel N Location Model A billing address; not rendered in the basic Receipt Message View
shippingAddressModel Y Location Model The shipping address; rendered in the basic Receipt Message View
currency N String ISO 4217 currency codes; default is USD
paymentMethod Y String Arbirary string, often of the form “Visa ****-XXXX”
order N OrderMetadata Object describing the Order
order.number N String Order Number / Order ID
summary Y SummaryMetadata Summary of the order
summary.productCost N Double The sum of all costs for all products (not currently rendered)
summary.totalTax N Double Total taxes for the purchase (not currently rendered)
summary.shippingCost N Double Shipping charges for the purchase (not currently rendered)
summary.totalCost N Double Sum of all costs; this is always rendered
summary.subtitle N String Summary of order (Not currently rendered)
items Y List<ProductModel> List of items that are to be displayed as part of the purchase
Misc Components Custom Message Types