Home | History | Annotate | Download | only in watch-faces
      1 page.title=Showing Information in Watch Faces
      2 
      3 @jd:body
      4 
      5 <div id="tb-wrapper">
      6 <div id="tb">
      7 <h2>This lesson teaches you to</h2>
      8 <ol>
      9   <li><a href="#Experience">Create a Compelling Experience</a></li>
     10   <li><a href="#AddData">Add Data to Your Watch Face</a></li>
     11 </ol>
     12 <h2>You should also read</h2>
     13 <ul>
     14   <li><a href="{@docRoot}design/wear/watchfaces.html">Watch Faces for Android Wear</a></li>
     15 </ul>
     16 </div>
     17 </div>
     18 
     19 <p>In addition to telling time, Android Wear devices provide users with contextually relevant
     20 information in the form of cards, notifications, and other wearable apps. Creating a custom
     21 watch face not only gives you the opportunity to tell time in visually compelling ways, but
     22 also to show users relevant information whenever they glance at their device.</p>
     23 
     24 <p>Like any other wearable app, your watch face can communicate with apps running on the handheld
     25 device using the <a href="{@docRoot}training/wearables/data-layer/index.html">Wearable Data Layer
     26 API</a>. In some cases, you need to create an activity in the handheld app module of your project
     27 that retrieves data from the Internet or from the user's profile and then shares it with your
     28 watch face.</p>
     29 
     30 <img src="{@docRoot}training/wearables/watch-faces/images/Render_Saturn.png"
     31      width="200" height="196" alt="" style="margin-top:12px;margin-left:-20px"/>
     32 <img src="{@docRoot}training/wearables/watch-faces/images/Render_Episode.png"
     33      width="200" height="196" alt="" style="margin-top:12px;margin-left:-25px"/>
     34 <p class="img-caption">
     35 <strong>Figure 1.</strong> Examples of watch faces with integrated data.</p>
     36 
     37 
     38 <h2 id="Experience">Create a Compelling Experience</h2>
     39 
     40 <p>Before you design and implement a contextually-aware watch face, answer the following
     41 questions:</p>
     42 
     43 <ul>
     44 <li>What kind of data do you want to incorporate?</li>
     45 <li>Where can you obtain this data?</li>
     46 <li>How often does the data change significantly?</li>
     47 <li>How can you present the data such that users understand it at a glance?</li>
     48 </ul>
     49 
     50 <p>Android Wear devices are usually paired with a companion device that has a GPS sensor and
     51 cellular connectivity, so you have endless possibilities to integrate different kinds of data
     52 in your watch face, such as location, calendar events, social media trends, picture feeds, stock
     53 market quotes, news events, sports scores, and so on. However, not all kinds of data are
     54 appropriate for a watch face, so you should consider what kinds of data are most relevant to
     55 your users throughout the day. Your watch face should also gracefully handle the case where the
     56 wearable is not paired with a companion device or when an Internet connection is not available.</p>
     57 
     58 <p>The active watch face on an Android Wear device is an app that runs continuously, so you
     59 must retrieve data in a battery-efficient manner. For example, you can obtain the current
     60 weather every ten minutes and store the results locally, instead of requesting an update every
     61 minute. You can also refresh contextual data when the device switches from ambient to interactive
     62 mode, since the user is more likely to glance at the watch when this transition occurs.</p>
     63 
     64 <p>You should summarize contextual information on your watch face, since there is limited
     65 space available on the screen and users just glance at their watch for a second or two at a
     66 time. Sometimes the best way to convey contextual information is to react to it using graphics
     67 and colors. For example, a watch face could change its background image depending on the current
     68 weather.</p>
     69 
     70 
     71 
     72 <h2 id="AddData">Add Data to Your Watch Face</h2>
     73 
     74 <div style="float:right;margin-left:20px">
     75 <img src="{@docRoot}training/wearables/watch-faces/images/preview_calendar.png"
     76      width="180" height="180" alt="" style="margin-left:10px;margin-top:10px"/>
     77 <p class="img-caption"><strong>Figure 2.</strong> The calendar watch face.</p>
     78 </div>
     79 
     80 <p>The <em>WatchFace</em> sample in the Android SDK demonstrates how to obtain calendar data
     81 from the users profile in the <code>CalendarWatchFaceService</code> class and shows how many
     82 meetings there are in the following twenty-four hours. This sample is located in the
     83 <code>android-sdk/samples/android-21/wearable/WatchFace</code> directory.</p>
     84 
     85 <p>To implement a watch face that incorporates contextual data, follow these steps:</p>
     86 
     87 <ol>
     88 <li>Provide a task that retrieves the data.</li>
     89 <li>Create a custom timer to invoke your task periodically, or notify your watch face service
     90     when external data changes.</li>
     91 <li>Redraw your watch face with the updated data.</li>
     92 </ol>
     93 
     94 <p>The following sections describe these steps in detail.</p>
     95 
     96 <h3 id="Task">Provide a task to retrieve data</h3>
     97 
     98 <p>Create a class inside your <code>CanvasWatchFaceService.Engine</code> implementation that
     99 extends {@link android.os.AsyncTask} and add the code to retrieve the data youre interested
    100 in.</p>
    101 
    102 <p>The <code>CalendarWatchFaceService</code> class obtains the number of meetings in the next
    103 day as follows:</p>
    104 
    105 <pre>
    106 /* Asynchronous task to load the meetings from the content provider and
    107  * report the number of meetings back using onMeetingsLoaded() */
    108 private class LoadMeetingsTask extends AsyncTask&lt;Void, Void, Integer> {
    109     &#64;Override
    110     protected Integer doInBackground(Void... voids) {
    111         long begin = System.currentTimeMillis();
    112         Uri.Builder builder =
    113                 WearableCalendarContract.Instances.CONTENT_URI.buildUpon();
    114         ContentUris.appendId(builder, begin);
    115         ContentUris.appendId(builder, begin + DateUtils.DAY_IN_MILLIS);
    116         final Cursor cursor = getContentResolver() .query(builder.build(),
    117                 null, null, null, null);
    118         int numMeetings = cursor.getCount();
    119         if (Log.isLoggable(TAG, Log.VERBOSE)) {
    120             Log.v(TAG, "Num meetings: " + numMeetings);
    121         }
    122         return numMeetings;
    123     }
    124 
    125     &#64;Override
    126     protected void onPostExecute(Integer result) {
    127         /* get the number of meetings and set the next timer tick */
    128         onMeetingsLoaded(result);
    129     }
    130 }
    131 </pre>
    132 
    133 <p>The <code>WearableCalendarContract</code> class from the Wearable Support Library provides
    134 direct access to the user's calendar events from the companion device.</p>
    135 
    136 <p>When the task finishes retrieving data, your code invokes a callback method. The following
    137 sections describe how to implement the callback method in detail.</p>
    138 
    139 <p>For more information about obtaining data from the calendar, see the <a
    140 href="{@docRoot}guide/topics/providers/calendar-provider.html">Calendar Provider</a> API
    141 guide.</p>
    142 
    143 <h3 id="Timer">Create a custom timer</h3>
    144 
    145 <p>You can implement a custom timer that ticks periodically to update your data.
    146 The <code>CalendarWatchFaceService</code> class uses a {@link android.os.Handler} instance
    147 that sends and processes delayed messages using the thread's message queue:</p>
    148 
    149 <pre>
    150 private class Engine extends CanvasWatchFaceService.Engine {
    151     ...
    152     int mNumMeetings;
    153     private AsyncTask&lt;Void, Void, Integer> mLoadMeetingsTask;
    154 
    155     /* Handler to load the meetings once a minute in interactive mode. */
    156     final Handler mLoadMeetingsHandler = new Handler() {
    157         &#64;Override
    158         public void handleMessage(Message message) {
    159             switch (message.what) {
    160                 case MSG_LOAD_MEETINGS:
    161                     cancelLoadMeetingTask();
    162                     mLoadMeetingsTask = new LoadMeetingsTask();
    163                     mLoadMeetingsTask.execute();
    164                     break;
    165             }
    166         }
    167     };
    168     ...
    169 }
    170 </pre>
    171 
    172 <p>This method initializes the timer when the watch face becomes visible:</p>
    173 
    174 <pre>
    175 &#64;Override
    176 public void onVisibilityChanged(boolean visible) {
    177     super.onVisibilityChanged(visible);
    178     if (visible) {
    179         mLoadMeetingsHandler.sendEmptyMessage(MSG_LOAD_MEETINGS);
    180     } else {
    181         mLoadMeetingsHandler.removeMessages(MSG_LOAD_MEETINGS);
    182         cancelLoadMeetingTask();
    183     }
    184 }
    185 </pre>
    186 
    187 <p>The next timer tick is set in the <code>onMeetingsLoaded()</code> method, as shown in the next
    188 section.</p>
    189 
    190 <h3 id="Redraw">Redraw your watch face with the updated data</h3>
    191 
    192 <p>When the task that retrieves your data finishes, call the <code>invalidate()</code> method
    193 so the system redraws your watch face. Store your data inside member variables of the
    194 <code>Engine</code> class so you can access it inside the <code>onDraw()</code> method.</p>
    195 
    196 <p>The <code>CalendarWatchFaceService</code> class provides a callback method for the task to
    197 invoke when it finishes retrieving calendar data:</p>
    198 
    199 <pre>
    200 private void onMeetingsLoaded(Integer result) {
    201     if (result != null) {
    202         mNumMeetings = result;
    203         invalidate();
    204     }
    205     if (isVisible()) {
    206         mLoadMeetingsHandler.sendEmptyMessageDelayed(
    207                 MSG_LOAD_MEETINGS, LOAD_MEETINGS_DELAY_MS);
    208     }
    209 }
    210 </pre>
    211 
    212 <p>The callback method stores the result in a member variable, invalidates the view, and
    213 schedules the next timer tick to run the task again.</p>
    214