Building applications for Android Wear

In the last 10 months with my Moto 360, a lot of people asked me tons of questions about smartwatches and Android wear. These questions are divided into two categories. How useful smartwatches are, and what apps can you build for them.

I have already written a blog post in which I explain why I think Moto 360 is useful, so this blog post will be about Android Wear Development. I’ll go through three types of apps you can build for Android Wear, and you’ll see that these apps are not that different from the ones on the mobile phone.

Fun with screen sizes, resolution and shapes

Before going into development, let’s discuss how many Android phones there are in this universe. If you are an Android developer your eye twitches when you think about thousands of phones you have to support. Android has a great support for different screen sizes and resolutions, but there is always that one phone, one Android version or one manufacturer (Samsung, I’m looking at you) which drives you crazy. Well, this doesn’t get any easier with Android Wear. Smartwatches come in two shapes, round and square, and there are different ways to support these shapes. I’ll go over them later in this blog post when I get to full-screen applications, but when developing for smartwatches you need to keep this in mind.

Android smartwatch shapes

1. Android Wear Notifications

Notifications are the simplest thing you can do on Android Wear. If you have a mobile app which supports notifications they are already shown on users smartwatch. Here is a code snippet which creates a notification with one action and shows it on both mobile and wear.

Notification notification = new NotificationCompat.Builder(context)
    .setSmallIcon(R.mipmap.ic_launcher)
    .setContentTitle(text)
    .addAction(new NotificationCompat.Action(R.mipmap.ic_launcher, context.getString(R.string.open_the_app), phonePendingIntent))
.build();
((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE)).notify(0, notification);

Nothing fancy here, this will show a notification with one action.

Android wear notification example

On smartwatch, the user can access the action by swiping from right to left. Clicking on the action will execute pending intent. It’s important to note that actions created on Android phone will always run on the phone, not on Android wear.

NotificationCompat.Builder has a method which allows you to add an extender. In this case, you can use WearableExtender, available in support-v4, to add actions which will be available on Android wear, but not on the phone.

Notification notification = new NotificationCompat.Builder(context)
    .setSmallIcon(R.mipmap.ic_launcher)
    .setContentTitle(text)
    .addAction(new NotificationCompat.Action(R.mipmap.ic_launcher, context.getString(R.string.open_the_app), phonePendingIntent))
    .extend(new NotificationCompat.WearableExtender().addAction(new NotificationCompat.Action(R.mipmap.ic_launcher, context.getString(R.string.action_only_on_wear),wearPendingIntent)))
    .build();
((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE)).notify(0, notification);

Although you call the extend method, you don’t actually extend the actions. By adding WearableExtender actions shown on phone and wear are separated. So the action added through wearable extender will show only on wear, and action added by calling addAction on NotificationComapt.Builder will appear only on mobile. This way, instead of extending the actions you decouple them.

Android wear separate notification

When adding separate actions keep in mind that pending intents need to have a different request code. If you don’t do that different actions will appear on mobile and wear but the phone will receive the intent of the first action which was added to Notification builder.

There are few more things you can do with Android wear notifications, like big style notification, setting notification background or receiving voice input from a notification. Check wearables documentation for more info.

2. Full-screen applications

Android Wear apps are similar to the ones on your phone. The only difference is lack of the back button, so the user navigates back by swiping from left to right.

Let’s see how to create a simple layout which looks good on both round and square watches. To do this, you will have to add wearable support library as a dependency in your build.gradle file.

    compile 'com.google.android.support:wearable:${VERSION}'

When creating a screen on Android wear, you create an Activity just as you would on mobile phone and you have two options to support both screen shapes:

1. Specify Different Layouts for Square and Round Screens
2. Use a Shape-Aware Layout like

Different Layouts for Square and Round Screens

With WatchViewStub, a class available in the wearable library, you can support both shapes by defining two different layouts, and add them through rectLayout and roundLayout XML attributes.

<?xml version="1.0" encoding="utf-8"?>
<android.support.wearable.view.WatchViewStub
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/watch_view_stub"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:rectLayout="@layout/rect_activity_main"
        app:roundLayout="@layout/round_activity_main">
</android.support.wearable.view.WatchViewStub>

When onApplyWindowInsets method is called it will check if the watch is round or square, and onMeasure method will inflate appropriate layout. I encourage you to check the source code of this class to get a better understanding what is happening.

WatchViewStub example

Shape aware layout

Wearable support library contains BoxInsetLayout that can box its children in the center square of the round screen. When using this layout you don’t have to define 2 different layouts because BoxInsetLayout applies the required window insets depending on the screen shape.

<android.support.wearable.view.BoxInsetLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_height="match_parent"
        android:layout_width="match_parent">

    <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_box="all">

        <TextView
                android:gravity="center"
                android:layout_height="wrap_content"
                android:layout_width="match_parent"
                android:text="@string/hello_boxinsetlayout"
                android:textColor="@color/white"/>

    </FrameLayout>
</android.support.wearable.view.BoxInsetLayout>

Important attribute here is app:layout_box=”all”, which will ensure that the FrameLayout element and its children are boxed inside the area defined by the window insets on round screens.

Here is how this layout looks like on two different shapes:

box_insent_layout_example

There is no rule you could apply here which layout you should use. That depends on your use case. However, use BoxInsetLayout whenever you can because maintaining two different layouts for one screen is hard, error-prone and stupid.

If you extend the Activity class and user stops interacting with your application, the watch will go into the so-called Ambient mode. In that case, Android Wear will kill your activity and show default WatchFace. If you’re building an app which has to be always on, and it should not be killed by the OS, you have to extend WearableActivity and add WAKE_LOCK permission to AndroidManifest. You can read more about this in the official documentation.

Communication between a phone and Android wear

Most of the time your Android Wear application will have to communicate with connected mobile phone in order to share data. There are two main mechanisms to do this:

1. Data items
2. Messages

Data items

Data items provide a storage of key-value pairs and automatic synchronization of data between mobile phone and wear device. It’s guaranteed that data items will be synchronized between the devices. In order to send or receive data items your application will have to connect to GoogleApiClient.

GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
    .addApi(Wearable.API)
    .addConnectionCallbacks(this)
    .addOnConnectionFailedListener(this)
    .build();

googleApiClient.connect();

When connected you can send data items via PutDataMapRequest.

PutDataMapRequest putDataMapRequest = PutDataMapRequest.create("/user_info");
putDataMapRequest.getDataMap().putString("name", "Batman");
putDataMapRequest.getDataMap().putInt("age", 40);

PutDataRequest request = putDataMapRequest.asPutDataRequest();

Wearable.DataApi.putDataItem(googleApiClient, request)
    .setResultCallback(new ResultCallback<DataApi.DataItemResult>() {
        @Override
        public void onResult(@NonNull DataApi.DataItemResult dataItemResult) {
            if (dataItemResult.getStatus().isSuccess()) {
                    //Object was stored locally
                } else {
                    //Error storing object
                }
            }
        });

When sending data items, keep in mind two things.

1. When you create PutDataMapRequest it’s path has to be unique because another device will use that path to retrieve data items.

2. The result callback doesn’t trigger when data items are synchronized with another device. It’s triggered when the object is stored locally. DataItems are guaranteed to be synchronized, even if devices are not connected when you send PutDataRequest. If you want a feedback when message is received you’ll have to implement your own logic.

On the receiving side, you have to create a service which will receive those data items and register it in Android Manifest. Your service has to extend WearableListenerService and in AndroidManifest you have to add BIND_LISTENER action.

public class MyService extends WearableListenerService {

    @Override
    public void onDataChanged(DataEventBuffer dataEvents) {
        super.onDataChanged(dataEvents);
        //Get data map from data events
    }

}
<service
    android:name=".MyService"
    android:enabled="true"
    android:exported="true">
        <intent-filter>
            <action
                android:name= "com.google.android.gms.wearable.BIND_LISTENER"/>
        </intent-filter>
            
</service>

You don’t have to start the service yourself because Google play services will do all the work for you.

Messages

Messages are simpler then data items, and they contain byte arrays. Android Wear doesn’t guarantee that the messages will be delivered to the recipient and byte array is limited to 100KB. You should use the messages only when nothing will go wrong if the message is not received. When sending a message you will have to specify connected node id to which the message will be sent. You can get connected nodes through NodeApi.

//Don't do this on UI thread because it will block the thread until all connected nodes are returned
NodeApi.GetConnectedNodesResult nodesResult = Wearable.NodeApi.getConnectedNodes(googleApiClient).await();

for (Node node : nodesResult.getNodes()) {

    Wearable.MessageApi.sendMessage(googleApiClient, node.getId(), "/path/message", new byte[0])
        .setResultCallback(new ResultCallback<MessageApi.SendMessageResult>() {
            @Override
            public void onResult(@NonNull MessageApi.SendMessageResult result) {

            }
    });
}

On the receiver side, you use the same service as you would for DataItems and you’ll receive the message in the onMessageReceived(MessageEvent messageEvent) method.

public class MyService extends WearableListenerService {

    @Override
    public void onMessageReceived(MessageEvent messageEvent) {
        super.onMessageReceived(messageEvent);
    }

}

Whether you use data items or messages to communicate between devices keep in mind that Wear device is not as powerful as a mobile phone. Make sure that most of the data processing is done on the phone, and send only important informations to the wearable device. Check the official documentation for more info about Sending and Syncing Data on Android Wear.

3. WatchFaces

WatchFace is the first thing users see on their watch, and their main intention is to show time. But WatchFaces can do much more, like showing calendar events, weather information, the number of steps etc. The most important principle in WatchFace development is glanceability. The user has to be able to glance at the watch and instantly see time and important informations.

Developing a WatchFace has too many steps to cover in this blog post. But it comes down to three things:

1. Drawing the WatchFace on canvas. There is no difference between this canvas and the one on the mobile phone. You can draw text, colors, lines etc.

2. You have to create your own mechanism for time calculation. Android Wear does not offer any mechanism which would notify you when one second, minute or hour passes. You have to implement the logic your self.

3. Android Wear has two modes: Interactive and Ambient (Low power mode) and your WatchFace has to support both of them. In Interactive mode you can use all colors and show anything you want on the WatchFace. In the Ambient mode, WatchFace should show only current time and use black and white colors.

Android Wear WatchFace

Android Wear is so much more

I have covered all types of applications you can build for Android Wear and you can see that development is not that different from Android apps for mobile. You can use almost all patterns and APIs you used to build Android mobile apps. Android Wear API adds few new classes and methods which help you build Wear applications and communicate with connected devices.

You should always look at Wear applications as an extension of your mobile app. Smartwatches are still not as powerful as mobile devices so standalone applications won’t work as well as you would expect. By extending your app to Android Wear you will give your users a whole new experience and ability to interact with your app in a glance.

This post covered only basic stuff in Android Wear development, and if you are interested to find out more stuff you can read the official documentation. Also, Udacity has a free Android Wear Development course which I really recommend if you are interested in building Wear apps.

Thanks to oFca for proofreading this blog post.