07 July 2014

Android Wearables First Go

I thought I’d try the new Android wear SDK and see if I could do anything useful with it. It took a while and a few head scratching moments but I got there in the end. What I wanted to do was send a message from the Android wear watch to my Android phone. Seems like such an easy ask!


First up you need Android Studio. I hope they make it possible with Eclipse / ADT, but I couldn't make it happen and quickly gave up! You also need to be super up to date with your SDK, as of today my versions:


  • Android SDK Tools 23.0.2
  • Android 4.4W (API 20)
  • Android 4.4.2 (API 19)
  • Google Play Services revision 18 (5.0)


Once all that is ready and working without error you need a to create a new project and follow the steps as per the Android developer page: http://developer.android.com/training/wearables/apps/creating.html
Basically create an app for mobile as per usual and a partner application for wear. You also need to setup an emulator or use a real watch. I can’t afford a real wear watch so I'm on the emulator. Follow the steps to connect your phone via usb cable and the emulator, bit fiddly but works eventually. The basic idea here is to use Google Play Services to transfer messages between the watch and the phone with the Message API. I believe this is new which is why it is so important to ensure everything is up to date.


Now the code, first the mobile side of things.


This is the gradle dependancies. My min sdk is 9 and my target sdk is 20.
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    wearApp project(':wear')
    compile 'com.android.support:appcompat-v7:19.+'
    compile 'com.google.android.gms:play-services-wearable:+'
}


Now we’re not making a fancy front end here as this is just really a proof of concept. Here’s the MainActivity.java. As you should be able to see this is a fairly simple listener.


public class MainActivity extends Activity implements GoogleApiClient.ConnectionCallbacks,
    GoogleApiClient.OnConnectionFailedListener,
    MessageApi.MessageListener{


Now some member variables:


    GoogleApiClient mGoogleApiClient;
    public static final String START_ACTIVITY_PATH = "/start/MainActivity";


Here is the on create:


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

         // Create a GoogleApiClient instance
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addApi(Wearable.API)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .build();

         mGoogleApiClient.connect();
}


What we’re doing here is initialising the Google API Client with the Wearable.API. These are the methods overwritten from the implements section.


    @Override
    public void onConnected(Bundle bundle) {
        Log.i("mobile", "Connected");
        //We are connected, we can add our listener to this Activity.
        Wearable.MessageApi.addListener(mGoogleApiClient, this);
    }

     @Override
    public void onConnectionSuspended(int i) {
        Log.i("mobile", "Connection Suspended");
    }

     @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        Log.i("mobile", "Connection Failed");
    }

     @Override
    public void onMessageReceived(MessageEvent messageEvent) {
        Log.i("mobile", "msg recieved and understood");

         if (messageEvent.getPath().equals(START_ACTIVITY_PATH)) {
            Log.i("mobile", "******WHOOOO*******");
            //Send a message to a Handler for UI Update.
            myHandler.sendEmptyMessage(DO_UPDATE_TEXT);
        }
    }


That’s the mobile side of things more or less done :) Again, nothing fancy, just proof of concept.


Now the wearable part of the project. Here we’re going to send the message, but again we have to connect to Google API Client.


I’m just going to post the whole file here as it’s probably easier. I’ll skip the layout, as its just a button.


  1. First (in onCreate) we define and connect to our mGoogleApiClient.
  2. OnConnected we start of the getConnectedNodes Async Task. This needs to run seperate from the UI and basically grabs all connected nodes. In our case there is only one, but you should really check here and maybe flash up a dialog or something if there are more or less than one clients connected.


  1. Once that’s done we send a message as such sendMsg(results.get(0));. This sends the node ID we got from the Wearable API and calls the sendMsg function


  1. In SendMsg we call Wearable.MessageApi.sendMessage. As expected this sends our message. Right now the message is meaningless, but you could easily modify this example to send a real message and have the listener display it.


That’s it. Hope it helps, here is the wear project code:



package com.example.com.wearable;

 import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.wearable.view.WatchViewStub;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

 import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
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.Wearable;

 import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;

 public class WearActivity extends Activity implements GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener {

     private TextView mTextView;
    GoogleApiClient mGoogleApiClient;
    public static final String START_ACTIVITY_PATH = "/start/MainActivity";

     @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_wear);

         // Create a GoogleApiClient instance
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addApi(Wearable.API)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .build();

         mGoogleApiClient.connect();

         final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
        stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
            @Override
            public void onLayoutInflated(WatchViewStub stub) {
                mTextView = (TextView) stub.findViewById(R.id.text);

                 findViewById(R.id.activity_wear_send_msg).setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        GetConnectedNodes task = new GetConnectedNodes();
                        task.execute(new String[]{"com"});
                    }
                });

             }
        });
    }

     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());
            Log.i("wear", node.getId());
        }
        return results;
    }

     @Override
    public void onConnected(Bundle bundle) {
        Log.i("wear", "Connection success");
        GetConnectedNodes task = new GetConnectedNodes();
        task.execute(new String[] { "" });
    }

     @Override
    public void onConnectionSuspended(int i) {
        Log.i("wear", "Connection suspended");
    }

     @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        Log.i("wear", "Connection failed");
    }

     private void sendMsg(String node){
        String msg = "All your base";

         MessageApi.SendMessageResult result = Wearable.MessageApi.sendMessage(mGoogleApiClient, node, START_ACTIVITY_PATH, msg.getBytes()).await();
        if (!result.getStatus().isSuccess()) {
            Log.e("wear", "ERROR: failed to send Message: " + result.getStatus());
        }else{
            Log.i("wear", "Message sent: " + result.getStatus());
        }

     }

     private class GetConnectedNodes extends AsyncTask<String, Void, Void> {
        protected Void doInBackground(String... params) {
            ArrayList<String> results = new ArrayList<String>();
            NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).await();

             Log.i("wear", "Hello from GetConnectedNodes");
            Log.i("wear", "node count:" + String.valueOf(nodes.getNodes().size()));

             for (Node node : nodes.getNodes()) {
                results.add(node.getId());
                Log.i("wear", node.getId());
            }

             if(results.size() > 0){
                sendMsg(results.get(0));
            }
            return null;
        }
    }
}

And here's the gradle:

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.google.android.support:wearable:+'
    compile 'com.google.android.gms:play-services:+'
}




No comments: