Cómo enviar y recibir mensajes en Wear

Para enviar mensajes, debes usar la API de MessageClient y adjuntar los siguientes elementos al mensaje:

  • Una carga útil arbitraria opcional
  • Una ruta de acceso que identifica exclusivamente la acción del mensaje.

A diferencia de lo que ocurre en el caso de los elementos de datos, no se produce ninguna sincronización entre las apps para dispositivos de mano y para wearables. Los mensajes son un mecanismo de comunicación unidireccional útil para las llamadas de procedimientos remotos (RPC), como el envío de un mensaje al wearable a fin de iniciar una actividad.

Se pueden conectar varios wearables al dispositivo de mano de un usuario. Cada dispositivo conectado en la red se considera un nodo.

Como tiene varios dispositivos conectados, debes considerar los nodos que reciben los mensajes. Por ejemplo, en una app de transcripción de voz que recibe datos de voz en el dispositivo wearable, envía el mensaje a un nodo con la potencia de procesamiento y la capacidad de batería para procesar la solicitud, como un dispositivo de mano.

Nota: Cuando especifiques los detalles de tu mensaje, considera la posibilidad de que haya varios nodos conectados. Asegúrate de que el mensaje se entregue a los dispositivos o nodos previstos.

Consulta los siguientes ejemplos de apps para ver ejemplos de uso:

Cómo enviar un mensaje

Una app para wearables puede proporcionar funciones como la transcripción de voz a los usuarios. Los usuarios pueden hablar al micrófono de su dispositivo wearable y guardar la transcripción de lo que dicen en una nota. Como un dispositivo wearable generalmente no tiene la potencia de procesamiento ni la capacidad de batería necesarias para controlar la actividad de transcripción de voz, la app debe descargar este trabajo en un dispositivo conectado con mayor capacidad.

En las siguientes secciones, se muestra cómo anunciar nodos de dispositivos que pueden procesar solicitudes de actividad, identificar los nodos capaces de satisfacer una necesidad solicitada y enviar mensajes a esos nodos.

Si vas a iniciar una actividad en un dispositivo de mano desde un wearable, usa la clase MessageClient para enviar la solicitud. Como se pueden conectar varios wearables al dispositivo de mano, la app para wearables debe determinar que hay un nodo conectado capaz de iniciar la actividad. En tu app para dispositivos de mano, anuncia que el nodo en el que se ejecuta proporciona funciones específicas.

Para anunciar las funciones de tu app para dispositivos de mano, haz lo siguiente:

  1. Crea un archivo de configuración XML en el directorio res/values/ de tu proyecto y asígnale el nombre wear.xml.
  2. Agrega un recurso llamado android_wear_capabilities a wear.xml.
  3. Define las funciones que brinda el dispositivo.

Nota: Las funciones son cadenas personalizadas que defines y deben ser únicas dentro de tu app.

En el siguiente ejemplo, se muestra cómo agregar una función llamada voice_transcription a wear.xml:

<resources xmlns:tools="http://schemas.android.com/tools"
           tools:keep="@array/android_wear_capabilities">
    <string-array name="android_wear_capabilities">
        <item>voice_transcription</item>
    </string-array>
</resources>

Cómo obtener nodos con las funciones requeridas

Inicialmente, puedes detectar los nodos capaces con una llamada al método getCapability de la clase CapabilityClient. Para usar este método, la app para Wear OS y la app para teléfonos deben tener el mismo ID de aplicación. En el siguiente ejemplo, se muestra cómo obtener de forma manual los resultados de los nodos disponibles con la función voice_transcription:

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);
}

Para detectar nodos capaces a medida que se conectan al dispositivo wearable, registra una instancia de un objeto de escucha, específicamente un OnCapabilityChangedListener de un objeto CapabilityClient. En el siguiente ejemplo, se muestra cómo registrar el objeto de escucha y obtener un resultado con nodos accesibles que tengan la función voice_transcription:

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. You can use named or anonymous classes.
    CapabilityClient.OnCapabilityChangedListener capabilityListener =
        capabilityInfo -> { updateTranscriptionCapability(capabilityInfo); };
    Wearable.getCapabilityClient(context).addListener(
        capabilityListener,
        VOICE_TRANSCRIPTION_CAPABILITY_NAME);
}

Después de detectar los nodos capaces, determina adónde enviar el mensaje. Elige un nodo que esté cerca de tu dispositivo wearable para minimizar el enrutamiento de mensajes a través de varios nodos. Se entiende por nodo cercano a aquel que está directamente conectado al dispositivo. Para determinar si hay un nodo cercano (por ejemplo, conectado por Bluetooth), llama al método Node.isNearby(). Si hay más de uno, elige uno de manera arbitraria. Del mismo modo, si no hay ningún nodo capaz cerca, elige uno de manera arbitraria.

En el siguiente ejemplo, se muestra cómo podrías determinar cuál es el mejor nodo para usar:

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;
}

Cómo entregar el mensaje

Cuando hayas identificado el nodo que se usará, envía el mensaje usando la clase MessageClient.

En el siguiente ejemplo, se muestra cómo enviar un mensaje desde un dispositivo wearable al nodo capaz de realizar la transcripción. Esta llamada es síncrona y bloquea el procesamiento hasta que el sistema pone en cola el mensaje para su entrega.

Nota: Un código de resultado exitoso no garantiza que se haya entregado el mensaje. Si tu app requiere confiabilidad de datos, puedes usar objetos DataItem o la clase ChannelClient para enviar datos entre dispositivos.

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
    }
}

Nota: Para obtener más información sobre las llamadas síncronas y asíncronas a los Servicios de Google Play y cuándo usarlas, consulta la API de Tasks.

También puedes transmitir mensajes a todos los nodos conectados. Para recuperar todos los nodos conectados a los que puedes enviar mensajes, implementa el siguiente código:

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;
}

Cómo recibir un mensaje

Si quieres recibir notificaciones de mensajes recibidos, implementa la interfaz de MessageClient.OnMessageReceivedListener para proporcionar un objeto de escucha de eventos de mensaje. Luego, registra ese objeto de escucha con el método addListener. En el siguiente ejemplo, se muestra cómo implementar el objeto de escucha para comprobar la VOICE_TRANSCRIPTION_MESSAGE_PATH. Si la condición es true, inicia una actividad para procesar los datos de voz.

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);
    }
}

Este código requiere más detalles sobre la implementación. Obtén información para implementar una actividad o un servicio completo de objeto de escucha en Cómo detectar eventos de Data Layer.