Wear 上でメッセージを送受信する

MessageClient API を使用してメッセージを送信し、以下のアイテムをメッセージにアタッチすることができます。

  • 任意のペイロード(オプション)
  • メッセージのアクションを一意に識別するパス

データアイテムの場合とは異なり、ハンドヘルド アプリとウェアラブル アプリ間での同期は行われません。メッセージは一方向の通信メカニズムであり、ウェアラブルにメッセージを送信してアクティビティを開始するなどのリモート プロシージャ コール(RPC)に適しています。

1 台のハンドヘルド デバイスに対して複数台のウェアラブル デバイスを接続できます。ネットワークに接続された各デバイスはノードとみなされます。複数台のデバイスが接続されている場合、どのノードでメッセージを受信するかを考える必要があります。たとえば、ウェアラブル デバイスで音声データを受け取る音声文字変換アプリでは、リクエストを処理できる処理能力と電池容量を持つノード(ハンドヘルド デバイスなど)にメッセージを送信する必要があります。

注: 7.3.0 より前のバージョンの Google Play 開発者サービスでは、ハンドヘルド デバイスに同時に接続できるウェアラブル デバイスは 1 台のみです。接続されている複数のノードの特徴を考慮するには、既存のコードの変更が必要になる場合があります。変更を行わないと、メッセージが目的のデバイスに配信されない可能性があります。

メッセージを送信する

ウェアラブル アプリでは、音声文字変換などの機能をユーザーに提供できます。ユーザーはウェアラブル デバイスのマイクに話しかけて、音声文字変換された内容をメモに保存することができます。ウェアラブル デバイスは通常、音声文字変換のアクティビティを処理するのに必要な処理能力と電池容量を備えていないため、アプリはこの処理をより高性能の接続済みデバイスにオフロードする必要があります。

以下のセクションでは、アクティビティのリクエストを処理可能なデバイス ノードをアドバタイズする方法、要求されたニーズに対応可能なノードを見つける方法、それらのノードにメッセージを送信する方法について説明します。

ウェアラブル デバイスからハンドヘルド デバイス上でアクティビティを起動するには、 MessageClient クラスを使用してリクエストを送信します。ハンドヘルド デバイスには複数のウェアラブルを接続できるため、ウェアラブル アプリは、使用する接続ノードがアクティビティを起動する機能を備えているのか判断する必要があります。ハンドヘルド アプリでは、そのアプリが実行されているノードで特定の機能を提供していることをアドバタイズします。

ハンドヘルド アプリの機能をアドバタイズする手順は次のとおりです。

  1. プロジェクトの res/values/ ディレクトリに XML 構成ファイルを作成し、その名前を wear.xml に設定します。
  2. android_wear_capabilities という名前のリソースを wear.xml に追加します。
  3. デバイスが提供する機能を定義します。

注: 機能名は自分で定義したカスタムの文字列で、アプリ内で一意である必要があります。

次の例は、voice_transcription という名前の機能を wear.xml に追加する方法を示しています。

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

必要な機能を備えたノードを取得する

最初に CapabilityClient クラスの getCapability メソッドを呼び出すことで、必要な機能を備えたノードを検出できます。次の例は、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);
    }
    

ウェアラブル デバイスに接続したときに、必要な機能を備えたノードかどうかを検出するには、リスナーのインスタンス(具体的には、 CapabilityClient オブジェクトの OnCapabilityChangedListener)を登録します。次の例は、リスナーを登録し、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. Named or anonymous classes can also be used.
        CapabilityClient.OnCapabilityChangedListener capabilityListener =
            capabilityInfo -> { updateTranscriptionCapability(capabilityInfo); };
        Wearable.getCapabilityClient(context).addListener(
            capabilityListener,
            VOICE_TRANSCRIPTION_CAPABILITY_NAME);
    }
    

必要な機能を備えたノードを検出したら、メッセージの送信先を確認します。複数のノードを経由するメッセージ ルーティングを最小限に抑えるには、ウェアラブル デバイスの近くにあるノードを選択する必要があります。近くにあるノードとは、デバイスに直接接続されているノードのことを指します。近くにあるノードなのかどうかを識別するには、Node.isNearby() メソッドを呼び出します。

最適なノードを特定する方法の例を以下に示します。

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

メッセージを配信する

最適なノードを特定したら、 MessageClient クラスを使用してメッセージを送信します。

次の例は、音声文字変換機能を備えたノードにウェアラブル デバイスからメッセージを送信する方法を示しています。メッセージを送信する前に、ノードが使用可能かどうかを確認します。この呼び出しは同期的で、システムが配信対象のメッセージをキューに登録するまで処理がブロックされます。

注: 成功結果コードは、メッセージが配信されたことを保証するものではありません。データの信頼性が必要となるアプリの場合は、デバイス間でのデータの送信に DataItem オブジェクトまたは ChannelClient クラスを使用することをおすすめします。

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

注: Google Play 開発者サービスの非同期呼び出しや同期呼び出しの詳細、各呼び出しを使用するタイミングについては、Tasks API をご覧ください。

接続されているすべてのノードにメッセージをブロードキャストすることもできます。メッセージを送信可能な接続済みノードをすべて取得するには、次のコードを実装します。

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

メッセージを受信する

メッセージ受信の通知を受けるには、 MessageClient.OnMessageReceivedListener インターフェースを実装して、メッセージ イベントのリスナーを組み込みます。そして、addListener メソッドを使用してリスナーを登録します。次の例は、リスナーを実装して VOICE_TRANSCRIPTION_MESSAGE_PATH を確認する方法を示しています。以下の条件が true の場合、アクティビティが開始され、音声データが処理されます。

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

これは単なるスニペットであり、より詳細な実装が必要です。完全なリスナーを備えたサービスやアクティビティの実装方法については、データレイヤ イベントをリッスンするをご覧ください。