Android Wear / Data API / MobileCompanion / WatchFace

Something O’Clock – Part 5

On 1st April 2016 I published Something O’Clock, a watch face app for Android Wear, to Play Store. The app is lighthearted in nature (because of the date of publication), it allows the user to set the time to “beer o’clock”, or “sleep o’clock”, or even “burger o’clock”. Although the app itself is quite lighthearted the code behind it is worthy of study and, in this series we’ll take a look at various aspects of developing custom watch faces for Android Wear. In this article we’ll consider storing the current configuration within the wear app.

round_wear_screenshotPreviously we looked at how the wear app receives the configuration changes made in the mobile companion app, and this component was effectively acting as the local proxy for the mobile companion app. The next thing that we need to consider is how to store the current configuration within the wear app. The mechanism that we’ll use for doing this is the same DataApi that we used to marshall the configuration changes – we’ll just use a different path within the URI that we use so that it is distinct from the remote data. For the remote transfer we use the URI wear://*/mobile, but for the local storage we’ll use wear://<actual host id>/wear instead. Let’s take a look at LocalDataMap which is responsible for this:

The main method that we call from our DataListenerService is overwriteConfig() which is responsible for applying changes. All of the calls are asynchronous so we just chain then up. First we need to fetch the existing data, then we write the new settings to this DataMap. We then make sure that we populate any missing values before finally writing the updated data.

Some ancillary classes are required by all of this. The LocalUriFetcher class is used to build the a URI for the local device with the local device id used in the host of the Uri to ensure that we only use local data:

Next we have MissingDataPopulator which is responsible for filling in any missing data from a DataMap. There are two main use-cases where we need to do this: first before we save the data we want to fill in any missing fields, and this can be done using a synchronous call:

If any data is missing then we populate it from the resources we created in the common module.

The second, slightly more complex use-case is when we load data we want to ensure that we add any missing data before passing it back to the caller. We do this in LocalDataMap by adding an additional step in the chain:

This retrieves the raw data, then passes it to populator (which populates any missing items), and when populator completes it will call the original callback. The populator implementation is also part of MissingDataPopulator:

So if the actual data has been loaded then it will be used, otherwise we create an empty DataMap. Then we use the same populateMissingKeys() method we looked at previously to populate any missing data from the resources in the common module. If we have actually overridden anything then it will be saved before we make the callback with the loaded & populated DataMap. The nice thing that this gives us is that whenever we call fetchConfig() on LocalDataMap we know that we’ll receive valid data.

So now we are storing our data locally all that remains is to go back to where we started and hook it up the the watch-face UI, and we’ll look at this in the concluding article to this series.

Although we don’t yet have it hooked up to the UI, the source will actually compile and is available here.

Many thanks to Daniele Bonaldo, Sebastiano Poggi, Erik Hellman, Hasan Hosgel, Said Tahsin Dane & Murat Yener – my beta testers.

Get it on Google Play

© 2016, Mark Allison. All rights reserved.

CC BY-NC-SA 4.0 Something O’Clock – Part 5 by Styling Android is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. Permissions beyond the scope of this license may be available at http://blog.stylingandroid.com/license-information.

Leave a Reply

Your email address will not be published. Required fields are marked *