Save the date! Android Dev Summit is coming to Mountain View, CA on November 7-8, 2018.

Send and receive messages on Wear

You send messages using the MessageClient API and attach the following items to the message:

  • An arbitrary payload (optional)
  • A path that uniquely identifies the message's action

Unlike in the case of data items, no syncing occurs between the handheld and wearable apps. Messages are a one-way communication mechanism that's good for remote procedure calls (RPC), such as sending a message to the wearable to start an activity.

Multiple wearable devices can be connected to a user’s handheld device. Each connected device in the network is considered a node. With multiple connected devices, you must consider which nodes receive the messages. For example, in a voice transcription app that receives voice data on the wearable device, you should send the message to a node with the processing power and battery capacity to handle the request, such as a handheld device.

Note: With versions of Google Play services prior to 7.3.0, only one wearable device could be connected to a handheld device at a time. You may need to update your existing code to take the multiple connected nodes feature into consideration. If you don’t implement the changes, your messages may not get delivered to intended devices.

Refer to the following related resources:

Send a message

A wearable app can provide functionality for users such as voice transcription. Users can speak into their wearable device's microphone, and have a transcription saved to a note. Since a wearable device typically does not have the processing power and battery capacity required to handle the voice transcription activity, the app should offload this work to a more capable, connected device.

The following sections show you how to advertise device nodes that can process activity requests, discover the nodes capable of fulfilling a requested need, and send messages to those nodes.

To launch an activity on a handheld device from a wearable device, use the MessageClient class to send the request. Since multiple wearables can be connected to the handheld device, the wearable app needs to determine that a connected node is capable of launching the activity. In your handheld app, advertise that the node it runs on provides specific capabilities.

To advertise the capabilities of your handheld app:

  1. Create an XML configuration file in the res/values/ directory of your project and name it wear.xml.
  2. Add a resource named android_wear_capabilities to wear.xml.
  3. Define capabilities that the device provides.

Note: Capabilities are custom strings that you define and must be unique within your app.

The following example shows how to add a capability named voice_transcription to wear.xml:

<resources>
    <string-array name="android_wear_capabilities">
        <item>voice_transcription</item>
    </string-array>
</resources>

Retrieve the nodes with the required capabilities

Initially, you can detect the capable nodes by calling the getCapability method of the CapabilityClient class. The following example shows how to manually retrieve the results of reachable nodes with the voice_transcription capability:

Kotlin

private const val VOICE_TRANSCRIPTION_CAPABILITY_NAME = "voice_transcription"
...
private fun setupVoiceTranscription() {
    val capabilityInfo: CapabilityInfo = Tasks.await(
            Wearable.getCapabilityClient(context)
                    .getCapability(
                            VOICE_TRANSCRIPTION_CAPABILITY_NAME,
                            CapabilityClient.FILTER_REACHABLE
                    )
    )
    // capabilityInfo has the reachable nodes with the transcription capability
    updateTranscriptionCapability(capabilityInfo)
}

Java

private static final String
    VOICE_TRANSCRIPTION_CAPABILITY_NAME = "voice_transcription";
    ...
private void setupVoiceTranscription() {
    CapabilityInfo capabilityInfo = Tasks.await(
        Wearable.getCapabilityClient(context).getCapability(
            VOICE_TRANSCRIPTION_CAPABILITY_NAME, CapabilityClient.FILTER_REACHABLE));
    // capabilityInfo has the reachable nodes with the transcription capability
    updateTranscriptionCapability(capabilityInfo);
}

To detect capable nodes as they connect to the wearable device, register an instance of a listener, specifically an OnCapabilityChangedListener of a CapabilityClient object. The following example shows how to register the listener and retrieve the results of reachable nodes with the voice_transcription capability:

Kotlin

private fun setupVoiceTranscription() {
    updateTranscriptionCapability(capabilityInfo).also { capabilityListener ->
        Wearable.getCapabilityClient(context).addListener(
                capabilityListener,
                VOICE_TRANSCRIPTION_CAPABILITY_NAME
        )
    }
}

Java

private void setupVoiceTranscription() {
    ...
    // This example uses a Java 8 Lambda. Named or anonymous classes can also be used.
    CapabilityClient.OnCapabilityChangedListener capabilityListener =
        capabilityInfo -> { updateTranscriptionCapability(capabilityInfo); };
    Wearable.getCapabilityClient(context).addListener(
        capabilityListener,
        VOICE_TRANSCRIPTION_CAPABILITY_NAME);
}

After detecting the capable nodes, determine where to send the message. You should pick a node that is in close proximity to your wearable device to minimize message routing through multiple nodes. A nearby node is defined as one that is directly connected to the device. To determine if a node is nearby, call the Node.isNearby() method.

The following example shows how you might determine the best node to use:

Kotlin

private var transcriptionNodeId: String? = null

private fun updateTranscriptionCapability(capabilityInfo: CapabilityInfo) {
    transcriptionNodeId = pickBestNodeId(capabilityInfo.nodes)
}

private fun pickBestNodeId(nodes: Set<Node>): String? {
    // Find a nearby node or pick one arbitrarily
    return nodes.firstOrNull { it.isNearby }?.id ?: nodes.firstOrNull()?.id
}

Java

private String transcriptionNodeId = null;

private void updateTranscriptionCapability(CapabilityInfo capabilityInfo) {
    Set<Node> connectedNodes = capabilityInfo.getNodes();

    transcriptionNodeId = pickBestNodeId(connectedNodes);
}

private String pickBestNodeId(Set<Node> nodes) {
    String bestNodeId = null;
    // Find a nearby node or pick one arbitrarily
    for (Node node : nodes) {
        if (node.isNearby()) {
            return node.getId();
         }
         bestNodeId = node.getId();
    }
    return bestNodeId;
}

Deliver the message

Once you've identified the best node to use, send the message using the MessageClient class.

The example below shows how to send a message to the transcription-capable node from a wearable device. Verify that the node is available before you attempt to send the message. This call is synchronous and blocks processing until the system queues the message for delivery.

Note: A successful result code does not guarantee delivery of the message. If your app requires data reliability, consider using DataItem objects or the ChannelClient class to send data between devices.

Kotlin

const val VOICE_TRANSCRIPTION_MESSAGE_PATH = "/voice_transcription"
...
private fun requestTranscription(voiceData: ByteArray) {
    transcriptionNodeId?.also { nodeId ->
        val sendTask: Task<*> = Wearable.getMessageClient(context).sendMessage(
                nodeId,
                VOICE_TRANSCRIPTION_MESSAGE_PATH,
                voiceData
        ).apply {
            addOnSuccessListener { ... }
            addOnFailureListener { ... }
        }
    }
}

Java

public static final String VOICE_TRANSCRIPTION_MESSAGE_PATH = "/voice_transcription";
private void requestTranscription(byte[] voiceData) {
    if (transcriptionNodeId != null) {
        Task<Integer> sendTask =
            Wearable.getMessageClient(context).sendMessage(
              transcriptionNodeId, VOICE_TRANSCRIPTION_MESSAGE_PATH, voiceData);
         // You can add success and/or failure listeners,
         // Or you can call Tasks.await() and catch ExecutionException
         sendTask.addOnSuccessListener(...);
         sendTask.addOnFailureListener(...);
    } else {
        // Unable to retrieve node with transcription capability
    }
}

Note: To learn more about asynchronous and synchronous calls to Google Play services and when to use each, see the Tasks API.

You can also broadcast messages to all connected nodes. To retrieve all of the connected nodes that you can send messages to, implement the following code:

Kotlin

private fun getNodes(): Collection<String> {
    return Tasks.await(Wearable.getNodeClient(context).connectedNodes).map { it.id }
}

Java

private Collection<String> getNodes() {
    HashSet <String>results = new HashSet<String>();
    List<Node> nodes =
        Tasks.await(Wearable.getNodeClient(context).getConnectedNodes());
    for (Node node : nodes.getNodes()) {
        results.add(node.getId());
    }
    return results;
}

Receive a message

To be notified of received messages, implement the MessageClient.OnMessageReceivedListener interface to provide a listener for message events. Then, register the listener with the addListener method. This example shows how you might implement the listener to check the VOICE_TRANSCRIPTION_MESSAGE_PATH. If this condition is true, start an activity to process the voice data.

Kotlin

fun onMessageReceived(messageEvent: MessageEvent) {
    if (messageEvent.path == VOICE_TRANSCRIPTION_MESSAGE_PATH) {
        val startIntent = Intent(this, MainActivity::class.java).apply {
            addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            putExtra("VOICE_DATA", messageEvent.data)
        }
        startActivity(this, startIntent)
    }
}

Java

@Override
public void onMessageReceived(MessageEvent messageEvent) {
    if (messageEvent.getPath().equals(VOICE_TRANSCRIPTION_MESSAGE_PATH)) {
        Intent startIntent = new Intent(this, MainActivity.class);
        startIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startIntent.putExtra("VOICE_DATA", messageEvent.getData());
        startActivity(this, startIntent);
    }
}

This is just a snippet that requires more implementation details. Learn about how to implement a full listener service or activity in Listening for Data Layer Events.