-1

I've been reading how to use the Capability API and Message API between the watch and phone on the official site: https://developer.android.com/training/wearables/data-layer/messages.html I have no difficulty sending messages from the watch to the phone.

i do have trouble, however, sending messages from the phone to the watch using the method shown below. I'm not sure why because it seems like I implemented the API using the same methods (I even developed a more abstract class to simplify API calls). My phone still can't seem to find the watch using the capability filter.

Is this a known issue, or is there something wrong with my implementation?

MOBILE/values/wear.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="CAPABILITY_MOBILE">MOBILE</string>
<string name="CAPABILITY_ANDROID_WEAR">WEARABLE</string>
<string-array name="android_wear_capabilities">
    <item>@string/CAPABILITY_MOBILE</item>
</string-array>
</resources>

WEAR/values/wear.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="CAPABILITY_MOBILE">MOBILE</string>
<string name="CAPABILITY_ANDROID_WEAR">WEARABLE</string>
<string-array name="android_wear_capabilities">
    <item>@string/CAPABILITY_ANDROID_WEAR</item>
</string-array>
</resources>

Method to Send Message to Wear

gapi = new GoogleApiClient.Builder(getApplicationContext())
                .addApi(Drive.API)
                .addApi(Games.API)
                .addApi(Plus.API)
                .addApiIfAvailable(Wearable.API)
                .addApiIfAvailable(Fitness.HISTORY_API)
                .addScope(Drive.SCOPE_FILE)
                .addScope(Games.SCOPE_GAMES)
                .addScope(Plus.SCOPE_PLUS_LOGIN)
                .addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
                    @Override
                    public void onConnected(Bundle bundle) {
                        final int action_watch = intent.getIntExtra(ACTION_WATCH, ACTION_NULL);
                        //For any action, open Home.class on watch
                        if(action_watch != ACTION_NULL) {
                            final ConnectionUtils.NodeManager nodeManager =
                                    new ConnectionUtils.NodeManager(gapi, getString(R.string.CAPABILITY_ANDROID_WEAR), MessageListener.NOTIFICATION_ACTIONS_PATH);
                            Log.d(TAG, "Start looking for nodes");
                            nodeManager.setNodeListener(new ConnectionUtils.NodeListener() {
                                @Override
                                public void onNodeFound() {
                                    nodeManager.sendMessage(action_watch+"");
                                    Log.d(TAG, "Sending "+action_watch+" to node");
                                }
                            });
                        } else {
                            Log.d(TAG, "No watch action requested");
                        }
                    }

                    @Override
                    public void onConnectionSuspended(int i) {

                    }
                })
                .addOnConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener() {
                    @Override
                    public void onConnectionFailed(ConnectionResult connectionResult) {
                        Log.d(TAG, "Error occurred");
                    }
                })
                .build();
        gapi.connect();

In which I'm using a custom class, NodeManager, to automatically handle capability filters and node management.

package com.felkertech.n.virtualpets.Utils;

import android.util.Log;

import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.PendingResult;
import com.google.android.gms.common.api.Result;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.wearable.CapabilityApi;
import com.google.android.gms.wearable.CapabilityInfo;
import com.google.android.gms.wearable.DataApi;
import com.google.android.gms.wearable.MessageApi;
import com.google.android.gms.wearable.Node;
import com.google.android.gms.wearable.NodeApi;
import com.google.android.gms.wearable.PutDataMapRequest;
import com.google.android.gms.wearable.PutDataRequest;
import com.google.android.gms.wearable.Wearable;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

public class ConnectionUtils {

    public static class NodeManager {
        private String transcriptionNodeId = null;
        private GoogleApiClient mGoogleApiClient;
        private String CAPABILITY;
        private String PATH;
        private NodeListener listener;
        private int nodesPinged = 0;
        public NodeManager(final GoogleApiClient mGoogleApiClient, String CAPABILITY, String PATH) {
            this.mGoogleApiClient = mGoogleApiClient;
            this.CAPABILITY = CAPABILITY;
            this.PATH = PATH;
            setupNode();
        }
        public void setNodeListener(NodeListener nodeListener) {
            listener = nodeListener;
        }
        private void setupNode() {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    CapabilityApi.GetCapabilityResult result =
                        Wearable.CapabilityApi.getCapability(
                                mGoogleApiClient, CAPABILITY,
                                CapabilityApi.FILTER_REACHABLE).await();
                    updateTranscriptionCapability(result.getCapability());

                    CapabilityApi.CapabilityListener capabilityListener =
                        new CapabilityApi.CapabilityListener() {
                            @Override
                            public void onCapabilityChanged(CapabilityInfo capabilityInfo) {
                                    updateTranscriptionCapability(capabilityInfo);
                            }
                        };

                    Wearable.CapabilityApi.addCapabilityListener(
                        mGoogleApiClient,
                        capabilityListener,
                        CAPABILITY);
                }
            }).start();
        }
        private void updateTranscriptionCapability(CapabilityInfo capabilityInfo) {
            if(capabilityInfo == null)
                return; //TODO Send this in update
            Set<Node> connectedNodes = capabilityInfo.getNodes();

            transcriptionNodeId = pickBestNodeId(connectedNodes);
            if(listener != null) {
                listener.onNodeFound();
                if(nodesPinged == 0)
                    listener.onFirstNodeFound();
                nodesPinged++;
            }
        }
        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;
        }
        public void sendMessage(String msg) {
            byte[] voiceData = msg.getBytes();
            if (transcriptionNodeId != null) {
                Wearable.MessageApi.sendMessage(mGoogleApiClient, transcriptionNodeId,
                    PATH, voiceData).setResultCallback(
                        new ResultCallback() {
                            @Override
                            public void onResult(Result result) {
                                MessageApi.SendMessageResult sendMessageResult = (MessageApi.SendMessageResult) result;
                                if (!sendMessageResult.getStatus().isSuccess()) {
                                // Failed to send message
                                }
                            }
                        }
                );
            } else {
                // Unable to retrieve node with transcription capability
            }
        }
        private Collection<String> getNodes() {
            HashSet<String> results = new HashSet<String>();
            NodeApi.GetConnectedNodesResult nodes =
                Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).await();
            for (Node node : nodes.getNodes()) {
                results.add(node.getId());
            }
            return results;
        }
    }

    public static void sendLaunchCommand(final GoogleApiClient mGoogleApiClient) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).await();
                for(Node node : nodes.getNodes()) {
                    Log.i("test", "pinging to: " + node.getDisplayName());
                    MessageApi.SendMessageResult result = Wearable.MessageApi.sendMessage(mGoogleApiClient, node.getId(), "/messageapi/launcher", "Hello World".getBytes()).await();
                    if(!result.getStatus().isSuccess()){
                        Log.e("test", "error");
                    } else {
                        Log.i("test", "success!! sent to: " + node.getDisplayName());
                    }
                }
            }
        }).start();
    }
    public static void sendData(GoogleApiClient mGoogleApiClient, String name, String type, Object value) {
        PutDataMapRequest dataMap = PutDataMapRequest.create("/dataitem/data");
        if(type.equals("boolean"))
            dataMap.getDataMap().putBoolean(name, (Boolean) value);
        if(type.equals("int"))
            dataMap.getDataMap().putInt(name, (Integer) value);
        PutDataRequest request = dataMap.asPutDataRequest();
        PendingResult<DataApi.DataItemResult> pendingResult =
                Wearable.DataApi.putDataItem(mGoogleApiClient, request);
        pendingResult.setResultCallback(new     ResultCallback<DataApi.DataItemResult>() {
            @Override
            public void onResult(DataApi.DataItemResult dataItemResult) {
                Log.d("TAG", "onResult: " + dataItemResult.getStatus().toString());
            }
        });
    }
    public interface NodeListener {
        void onNodeFound();
        void onFirstNodeFound();
    }

}

Nick Felker
  • 11,536
  • 1
  • 21
  • 35

1 Answers1

0

I created a simple project to see if I could reproduce this. In my project, I have removed the non-wear apis from the client and I was able to successfully see the "wear" node from the phone; I didn't send any messages, or anything fancy but did use your ConnectionUtils to make it as close to your as possible. You might want to try to simplify this to find out where your issue is: 1. remove all non-wear apis from the GoogleApiClient to make things simpler for test 2. make sure the path that you expect to be taken is indeed happening by adding log statements 3. try to add log messages to see if (a) Google Api Client is connected on the mobile, (b) whether the node listener returns the node or not.

I noticed a few things in your code that you might want to look into; for example you have MessageListener.NOTIFICATION_ACTIONS_PATH; the MessageListener from Play Services doesn't have such constant, or when you are calling the getcpability(..).await(), always add a timeout (I can't see a reason to use the blocking version to begin with).

Ali Naddaf
  • 16,951
  • 2
  • 21
  • 28
  • I'll take a closer look at my project. I can't figure out why my watch node wouldn't be showing up, so I'll simplify things. `MessageListener`, in this case, is a my listener class to receive messages in the background. – Nick Felker Jul 23 '15 at 06:05