Show navigation Hide navigation

Showing Information in Watch Faces

This lesson teaches you to

  1. Create a Compelling Experience
  2. Add Data to Your Watch Face

Related Samples

You should also read

In addition to telling time, Android Wear devices provide users with contextually relevant information in the form of cards, notifications, and other wearable apps. Creating a custom watch face not only gives you the opportunity to tell time in visually compelling ways, but also to show users relevant information whenever they glance at their device.

Like any other wearable app, your watch face can communicate with apps running on the handheld device using the Wearable Data Layer API. In some cases, you need to create an activity in the handheld app module of your project that retrieves data from the Internet or from the user's profile and then shares it with your watch face.

Figure 1. Examples of watch faces with integrated data.

Create a Compelling Experience

Before you design and implement a contextually-aware watch face, answer the following questions:

  • What kind of data do you want to incorporate?
  • Where can you obtain this data?
  • How often does the data change significantly?
  • How can you present the data such that users understand it at a glance?

Android Wear devices are usually paired with a companion device that has a GPS sensor and cellular connectivity, so you have endless possibilities to integrate different kinds of data in your watch face, such as location, calendar events, social media trends, picture feeds, stock market quotes, news events, sports scores, and so on. However, not all kinds of data are appropriate for a watch face, so you should consider what kinds of data are most relevant to your users throughout the day. Your watch face should also gracefully handle the case where the wearable is not paired with a companion device or when an Internet connection is not available.

The active watch face on an Android Wear device is an app that runs continuously, so you must retrieve data in a battery-efficient manner. For example, you can obtain the current weather every ten minutes and store the results locally, instead of requesting an update every minute. You can also refresh contextual data when the device switches from ambient to interactive mode, since the user is more likely to glance at the watch when this transition occurs.

You should summarize contextual information on your watch face, since there is limited space available on the screen and users just glance at their watch for a second or two at a time. Sometimes the best way to convey contextual information is to react to it using graphics and colors. For example, a watch face could change its background image depending on the current weather.

Add Data to Your Watch Face

Figure 2. The calendar watch face.

The WatchFace sample demonstrates how to obtain calendar data from the user’s profile in the CalendarWatchFaceService class and shows how many meetings there are in the following twenty-four hours.

To implement a watch face that incorporates contextual data, follow these steps:

  1. Provide a task that retrieves the data.
  2. Create a custom timer to invoke your task periodically, or notify your watch face service when external data changes.
  3. Redraw your watch face with the updated data.

The following sections describe these steps in detail.

Provide a task to retrieve data

Create a class inside your CanvasWatchFaceService.Engine implementation that extends AsyncTask and add the code to retrieve the data you’re interested in.

The CalendarWatchFaceService class obtains the number of meetings in the next day as follows:

/* Asynchronous task to load the meetings from the content provider and
 * report the number of meetings back using onMeetingsLoaded() */
private class LoadMeetingsTask extends AsyncTask<Void, Void, Integer> {
    @Override
    protected Integer doInBackground(Void... voids) {
        long begin = System.currentTimeMillis();
        Uri.Builder builder =
                WearableCalendarContract.Instances.CONTENT_URI.buildUpon();
        ContentUris.appendId(builder, begin);
        ContentUris.appendId(builder, begin + DateUtils.DAY_IN_MILLIS);
        final Cursor cursor = getContentResolver() .query(builder.build(),
                null, null, null, null);
        int numMeetings = cursor.getCount();
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "Num meetings: " + numMeetings);
        }
        return numMeetings;
    }

    @Override
    protected void onPostExecute(Integer result) {
        /* get the number of meetings and set the next timer tick */
        onMeetingsLoaded(result);
    }
}

The WearableCalendarContract class from the Wearable Support Library provides direct access to the user's calendar events from the companion device.

When the task finishes retrieving data, your code invokes a callback method. The following sections describe how to implement the callback method in detail.

For more information about obtaining data from the calendar, see the Calendar Provider API guide.

Create a custom timer

You can implement a custom timer that ticks periodically to update your data. The CalendarWatchFaceService class uses a Handler instance that sends and processes delayed messages using the thread's message queue:

private class Engine extends CanvasWatchFaceService.Engine {
    ...
    int mNumMeetings;
    private AsyncTask<Void, Void, Integer> mLoadMeetingsTask;

    /* Handler to load the meetings once a minute in interactive mode. */
    final Handler mLoadMeetingsHandler = new Handler() {
        @Override
        public void handleMessage(Message message) {
            switch (message.what) {
                case MSG_LOAD_MEETINGS:
                    cancelLoadMeetingTask();
                    mLoadMeetingsTask = new LoadMeetingsTask();
                    mLoadMeetingsTask.execute();
                    break;
            }
        }
    };
    ...
}

This method initializes the timer when the watch face becomes visible:

@Override
public void onVisibilityChanged(boolean visible) {
    super.onVisibilityChanged(visible);
    if (visible) {
        mLoadMeetingsHandler.sendEmptyMessage(MSG_LOAD_MEETINGS);
    } else {
        mLoadMeetingsHandler.removeMessages(MSG_LOAD_MEETINGS);
        cancelLoadMeetingTask();
    }
}

The next timer tick is set in the onMeetingsLoaded() method, as shown in the next section.

Redraw your watch face with the updated data

When the task that retrieves your data finishes, call the invalidate() method so the system redraws your watch face. Store your data inside member variables of the Engine class so you can access it inside the onDraw() method.

The CalendarWatchFaceService class provides a callback method for the task to invoke when it finishes retrieving calendar data:

private void onMeetingsLoaded(Integer result) {
    if (result != null) {
        mNumMeetings = result;
        invalidate();
    }
    if (isVisible()) {
        mLoadMeetingsHandler.sendEmptyMessageDelayed(
                MSG_LOAD_MEETINGS, LOAD_MEETINGS_DELAY_MS);
    }
}

The callback method stores the result in a member variable, invalidates the view, and schedules the next timer tick to run the task again.