Introduction

The addition of wearables to the mobile world provides developers with yet another channel in which to connect with users. While some are still determining the use cases and practicality of wearables, it is important as developers to keep up with the advancements around us. The purpose of this blog is to get you started in the Android Wear environment and provide you with enough code and knowledge on the topic to build your own wearable application. To accomplish this, this blog will walk you through setting up your environment including adding a wearable module to your existing android project and debugging your wearable application. Using the attached application as an example, code to get you started with Andrioid Wear will be highlighted and explained in depth.

Setting Up Your Wear Environment

For brevity purposes in this blog I will assume that if you are developing for Android Wear that you have a basic knowledge of the Android environment. Whether you are developing in Eclipse or Android Studio, including the Wear SDK is simple and easy to set up. For the purpose of this blog, I will assume you are using an Android Studio environment; however, the concepts mentioned will be applicable to an Eclipse environment as well.

Adding Wear Module

  1. First, open your SDK Manager and download the latest Android Wear system images and SDK platform. While here, be sure you have the latest Google Play Services, build tools, and platform tools installed.
  2. If you are adding the wearable application to an existing application, open the project, right click on the project name, select module settings, and select the plus icon in the upper left portion of the screen. If you are creating a new project, adding the wearable module is as easy as selecting the wearable option during the project creation wizard.
  3. You are now ready to start coding!

Debugging

If you are lucky enough to have your own Android Wear device, and wish to debug your application on the device, please follow the below steps.

  1. On your wearable, open your settings and navigate to the "about" section. On this page, select "build number" 6 times to enable the developer options on your device.
  2. Go back to settings on your wearable and at the bottom you will notice you now have developer options. Here, you need to enable "ADB debugging" and "Debug over Bluetooth."
  3. Next, on your handheld open your Wear App, go to settings, scroll to the bottom and enable "Debugging over Bluetooth."
  4. Now, via your desktop/laptop open a terminal, navigate to your platform tools. For me it is the following command "cd ~/Android-Studio-SDK/platform-tools/"
  5. Next, enter the following commands:
    1. adb connect localhost:4444
    2. adb forward tcp:4444 localabstract:/adb-hub
  6. You are now ready to start debugging a wear application on your wearable.

If you would like to do your development work via the emulator, the below steps will get you started.

  1. Open your AVD Manager.
  2. Select Create a Virtual Device.
  3. On the left select "Wear" and then choose either round or square.
  4. Next, select the system image you wish to use.
  5. Next, name your device, enable "Use Host GPU," and then press finish.
  6. You are now ready to start debugging via your new emulator.

Getting Started with Android Wear

The below sections highlight areas that I believe make up a great start to creating a nice wearable application. All the example code can be found in the attached application. The attached application is meant for demonstration purposes only and is in no way intended to be free of defects or a final Android Wear design.

The example application allows a user to share their location with friends during a run, view their friends' locations, or just share their updated location at any given time. The application will request location updates during a run from the user via demonstrating a wearable only notification that contains wearable actions. The user will then be notified if they have a friend nearby on both the wearable and handheld demonstrating a shared notification with Wear specific actions. The application will also demonstrate launching the wearable application with via a voice command or the Cue Card, requesting and syncing data by showing a list of friends, and demonstrating a Remote Procedure Command (RPC) initiated by the wearable.

Notifications

The easiest way to get started in the wearable environment is to leverage notifications. The handheld will take care of the rest and deliver the notification to the connected wearable. The code below shows how to deliver a simple notification that will appear on both your handheld and wearable device. This code would be placed within an activity in your handheld module.

//create the notification
NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_action_group))
.setContentTitle("Title")
.setContentText("Message")
.setCategory(NotificationCompat.CATEGORY_EVENT)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setDefaults(NotificationCompat.DEFAULT_ALL)
.setPriority(NotificationCompat.PRIORITY_HIGH);
//create the manager and notify
NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(Constants.Friend_Update_ID, builder.build());

Notifications With Wear Specific Actions

It is a nice and expected user experience that a wearable notification will have more specific actions than the notification would on the mobile device. Doing so can be accomplished by creating a notification action and extending the WearableExtender. The below action launches an activity on the user's handheld when the action is selected via the wearable.

Intent viewIntent = new Intent(context, LaunchActivity.class);
viewIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent viewPendingIntent = PendingIntent.getActivity(context, 0, viewIntent, 0);
//create the wear action
NotificationCompat.Action action =new NotificationCompat
.Action.Builder(R.drawable.ic_action_person, "View", viewPendingIntent)
.build();

The action now needs to be added to the notification by adding the following line of code.

NotificationCompat.Builder builder = new NotificationCompat.Builder(context).extend(
new WearableExtender().addAction(action)
.setSmallIcon(R.mipmap.ic_launcher)
ect….

The notification, when fired, will now have an action on the wearable when the user swipes left on it, but the action on the mobile device will not exist.

Syncing Data Between Wearable and Mobile Device

Next, we will cover how we share data between the mobile device and the wearable device and vice versa. This can be done easily if you are sending short pieces of data such as integer values and strings, but requires an extra step when sending larger pieces of data such as whole objects. It is fair to note that any data shared between the devices must be smaller than 100kb. To share the data, we will leverage the Wearable Data Layer API, which is available via Google Play Services. The Data Layer API provides data storage that automatically syncs between the handheld and the wearable. Data can be shared to the Data Layer either from the wearable or the handheld.

Any time we would like to communicate with the wearable device from the handheld or with the handheld from the wearable the following line of code is necessary. It is best to include connection callbacks, but for brevity they are left out here.

GoogleApiClient apiClient = new GoogleApiClient.Builder(this)
.addApi(Wearable.API)
.build();
apiClient.connect();

Next we will need to send the data we wish to sync once the client is connected.

if(apiClient.isConnected()){
//create the dataMapRequest with a path from constants.Path must start with a /
PutDataMapRequest putDataMapRequest = PutDataMapRequest.create(Constants.RUN_UPDATE_NOTIFICATION);
putDataMapRequest.getDataMap().putString(Constants.KEY_TITLE, "title");
putDataMapRequest.getDataMap().putString(Constants.KEY_CONTENT, "some other string data");
PutDataRequest request = putDataMapRequest.asPutDataRequest();
Wearable.DataApi.putDataItem(apiClient, request).setResultCallback(new ResultCallback() {
@Override
public void onResult(DataApi.DataItemResult dataItemResult) {
if (!dataItemResult.getStatus().isSuccess()) {
//data sync failed
}else{
//data sync was success
}
}
});
}

The data item has now been synced with the Data Layer.

WearableListenerService

Now that data has been shared with the Data Layer, it is accessible via the handheld or wearable device. One could ask for this data on demand, but in most cases it is best to have a callback fire when a change in the Data Layer has been detected. A Data Layer event can be detected by creating and registering a WearableListenerService.

First we need to create a WearableListenerService by adding the following class to the module where we wish to receive the Data Layer event callback, i.e., if you wish to sync data from the wearable and receive a callback of the event on the handheld the listener should be created in your handheld module.

public class WatchUpdateService extends WearableListenerService {

@Override
public void onDataChanged(DataEventBuffer dataEvents) {
for (DataEvent dataEvent : dataEvents) {
if (dataEvent.getType() == DataEvent.TYPE_CHANGED) {
DataMap dataMap = DataMapItem.fromDataItem(dataEvent.getDataItem()).getDataMap();
if(Constants.RUN_UPDATE_NOTIFICATION.equalsIgnoreCase(dataEvent.getDataItem().getUri().getPath())){
buildRunUpdateNotification(dataMap);
}
}
}
}
}

Next we need to register this listener via the AndroidManifest of whichever module you chose to add it to.

<>".WatchUpdateService" >

<>"com.google.android.gms.wearable.BIND_LISTENER" />

The listener's onDataChanged event will now be notified whenever a data item is deleted, added, or changed in the Data Layer. Note that if you choose to sync the same piece of data with no changes to the same data path, the onDataChanged callback will not be fired. In a further step I will discuss using Remote Procedure Commands utilizing the MessageApi, which will solve this issue if you run into it.

If you wish to receive event callbacks within an activity in the foreground (this can be seen in the WatchLaunchActivity of the attached code) create a listener class, like shown above, within your desired activity and register it to receive the data via a connected GoogleApiClient.

GoogleApiClient apiClient = new GoogleApiClient.Builder(this)
.addApi(Wearable.API)
.build();
apiClient.connect();
Wearable.DataApi.addListener(googleApiClient, new FriendDataListener());

Syncing Large Data

If you wish to sync a large piece of data such as an object or bitmap, like I do in the attached application, you must first serialize the piece of data before adding it to the Data Layer. When you choose to retrieve the data from the Data Layer, it will need to be deserialized before it is useable again.

Sending Messages Between Wearable and Mobile Device

Sending a message comes in handy when you want to trigger a specific action on the connected device but you do not wish to share any data with the other device. As I have mentioned above, if you try to accomplish this using the Data Layer API you will quickly realize that a new piece of data must be synced each time you wish to receive an event callback. This is less than ideal if we are just simply wishing to start an RPC. To accomplish this, we will use the MessageApi to send short messages where a body is not even required for the message to be received.

Sending a message via the MessageApi is a little more complicated than syncing data. Unlike the Data Layer, which makes all of its data available to any connected device, the MessageApi is for sending specific commands/messages to a specific device. Due to this, we have to know which device or connected node we wish to send our message to.

Just as we did when syncing data, to send a message we must first create and connect with the Google API client.

GoogleApiClient apiClient = new GoogleApiClient.Builder(this)
.addApi(Wearable.API)
.build();
apiClient.connect();

Once we have made our connection we need to find the node we wish to send our message to. We only need to do this once, and then we can hold onto this value for the remainder of the application's life. In the example code I am going to send a message to all the connected nodes rather than to a specific one.


new Thread(new Runnable() {
@Override
public void run() {
NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes(googleApiClient).await();
for(Node node : nodes.getNodes()) {
MessageApi.SendMessageResult result = Wearable.MessageApi.sendMessage(googleApiClient, node.getId(), Constants.FRIEND_RESPONSE_LIST_PATH, new byte[0]).await();
if(!result.getStatus().isSuccess()){
//message failed..
} else {
//message sent!
}
}
}
}).start();

Since an Await() cannot be called on the UI Thread, we must create a new thread to step through the nodes and send our message. In most cases, there will only be one connected node that the message will be sent to.

@Override
public void onMessageReceived(MessageEvent messageEvent) {
if(messageEvent.getPath().equalsIgnoreCase(Constants.FRIEND_RESPONSE_LIST_PATH)){
Intent friendIntent = new Intent(this, RequestService.class);
friendIntent.putExtra(Constants.EXTRA_REQUEST_TYPE, (Parcelable) RequestType.FindFriends);
friendIntent.putExtra(Constants.REQUESTED_FROM_WATCH_EXTRA, true);
this.startService(friendIntent);
}
}

Receiving the message on the device you wish to is just as simple as receiving the data event from the previous example. We will leverage the WearableListenerService and place our code in the onMessageReceived() event. Again, if we are sending the message from the wearable to the handheld we will need to create and register the listener in the handheld module.

Notifications On Wear Only

There may be times when you wish to send a wearable specific notification. This can be seen as the cards that show up in the context stream of the wearable, but no corresponding notification exists on the phone. Unfortunately, this cannot be accomplished as simply as standard notification. You may be wondering why this was not included with the other notifications, well that is because we will leverage either the Data Layer API or the MessageApi to accomplish this. In the attached example I leverage the Data Layer to do this as I am sending pieces of data from the phone to the connected wearable.

In the wearable module create a WearableListenerService and register it within your wearable Manifest like was shown above. Next, within the onMessageReceived or in my case, the onDataChanged event fire your notification like shown below. You can also see this in action via the attached code in the WatchUpdateService. In my case I am choosing to make the notification vibrate, but you may or may not choose to do this. In my notification below I am leveraging bits of data and a bitmap, which I sync with the Data Layer from the handheld.

//create the first page of our message
NotificationCompat.Builder firstPageBuilder = new NotificationCompat.Builder(this);
firstPageBuilder.setContentTitle(title)
.setContentText(content)
.setSmallIcon(R.drawable.ic_watchmerun);
//set the background of the first page to the image of the second page.
firstPageBuilder.setLargeIcon(bitmap);
//vibrate your wrist
firstPageBuilder.setVibrate(new long[] {0, 100, 50, 100});
//create the wearable options we will add to our message
NotificationCompat.WearableExtender firstPageWearableOptions =
new NotificationCompat.WearableExtender();
//the second page
NotificationCompat.Builder secondPageBuilder = new NotificationCompat.Builder(this)
.extend(new NotificationCompat.WearableExtender()
.setBackground(bitmap);
//add the message to the second page..
secondPageBuilder.setContentText(message);
//add the second page to the wearable options
firstPageWearableOptions.addPage(secondPageBuilder.build());
//link the wearable options to the notifcation
firstPageBuilder.extend(firstPageWearableOptions);
//notify
((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
.notify(Constants.RUN_UPDATE_ID, firstPageBuilder.build());

Starting Application or a Specific Activity with Voice Actions

One of the great features of Android Wear is the extensive amount of voice commands available to the user. You may decide to leverage voice capabilities found in Android Wear by allowing the user to launch a specific activity via a Google defined command or allow the user to start your application via the "Start" command.

To enable the user to start a specific activity via a voice command, you must use a Google provided phrase. To start your activity with the selected command, you filter for the action by adding an intent filter to the activity in the wearable module's manifest.

For example, to launch a specific activity when the user says, "Okay Google, track my run" you would include the following in your wearable manifest.

<>"ShareLocationActivity">

<>"vnd.google.fitness.TRACK" />
<>"vnd.google.fitness.activity/running" />

If none of the provided voice commands meet your needs, you can register your app for a "start" command much you like you do when you register for an icon on your mobile device. Doing so will also make your application available via the Android Wear Cue Card. For example, to launch the example application included with this blog just say, "Okay Google, Start watch me run."

<>
android:name=".WatchLaunchActivity"
android:label="Watch Me Run" >

<>"android.intent.action.MAIN" />
<>"android.intent.category.LAUNCHER" />

Packaging Your Wear App

You must have a companion handheld app in order to install your wearable application outside of debug mode. Google Play does not support wearable only APKs. To package your wearable application to ship with your handheld application you must include the following line of code in your handheld build.gradle file as a dependency.

wearApp project(':wear')

Note, that the "wear" in the above line should be replaced with whatever you have named your wearable module in your project.

In order for the wearable application to ship with your handheld application you must include all permissions found within your wearable manifest in your handheld manifest. You must also ensure that the version code and version number in both manifest/build files match.

One issue I had when packaging my application was signing my application with a specific keystore. I had forgotten to sign my both my wearable module and mobile module with the same keystore. Keep this in mind if you use a debug keystore as well as for your release keystore.

Conclusion

Android Wear is intended to be an extension of their handheld, giving users the data they want at just the right time without interrupting their lives. Keep this in mind as you begin moving forward with the design and production of your own wearable application. Keep interactions short, design for big gestures, and remember to keep Google's Wear design principles in mind. I hope this blog has provided you with what you need to design and build your own wearable application. Be sure to download the attached application to see more in depth code and examples of everything that I have covered in this blog.