Syncing wearable

configuration across mobile & android wear


We are android developers who work together at Ophio to build awesomeness.

Background

After creating a watch face we wanted to enable the user to customize the watch face. We added settings screens to the watch and hand held. After this we came across the problem of keeping both the activities and watch face synced at all times. Changes to one should be reflected on the other.

Initially we used MessageApi to deal with this problem. We realised that the MessageApi was not well suited for syncing as the messages get dropped when the nodes are disconnected. We then used the DataApi for syncing the configurations. The architecture which we finally implemented (inspired from FORM Watch Face) is explained here.

Architecture

The architecture is explained in the illustration 1 below :

The above diagram may be a little perplexing at first but it will be clear when we understand it step by step.

source

Following are the main components :

  1. WearConfigActivity (wear) : Wear Configuration Activity.
  2. CanvasWatchFaceService (wear) : service which shows and updates the watch-face
  3. CompanionConfigActivity (mobile) : Handheld Configuration Activity.
  4. ConfigListenerService (wear and mobile) : WearableListenerService which listen to the DataEvents and update the config in SharedPreferences.
  5. ConfigUpdateService (wear and mobile) : IntentService which updates the config in data-layer and SharedPreferences.

Let us discuss a scenario here in order to understand the architecture better. User installed the watch face on mobile. CanvasWatchFaceService will write the initial config to the Data Layer using the DataApi. ConfigListenerServices will then receive onDataChanged()callbacks on wear and mobile. ConfigListenerServices of wear and mobile will then update the config in the SharedPreferences of wear and mobile respectively. CanvasWatchFaceService will receive a callback onSharedPreferencesChanged() and will draw it’s watch face again (WearConfigActivity and CompanionConfigActivity would also receive onSharedPreferencesChanged() callbacks when they are created).

User opens the Settings from the Android wear app on mobile. CompanionConfigActivity will read the initial config from the SharedPreferences and initialize its view. User changes the configuration on the mobile (CompanionConfigActivity). ConfigUpdateService will read the old config from Shared Preferences and if the config is dirty the new config will be updated on DataLayer and SharedPreferences. Upon updating DataLayer config, ConfigListenerService of wear and mobile will receive onDataChanged() callbacks and the DataEvent on mobile will be discarded as it originated on the mobile itself. DataEvent received on wear will update the config saved in SharedPreferences and onSharedPreferencesChanged() callback will be received by CanvasWatchFaceService or WearConfigActivity whichever is visible. Upon receiving onSharedPreferencesChanged() callback, the respective screens can update their views.

Pointers

Pointers to be kept in mind while working with Data Layer API:

  • DataItem URIs are of the following format in the Data Layer:
wear://watchNodeId/config_path
wear://mobileNodeId/config_path
  • At all times the configurations are maintained separately for all the nodes once the configuration is created, i.e if PutDataMapRequest.create() was called on both wear and emulator two separate configs will be created and both the configs will keep existing in the system with the following URIs :
wear://watchNodeId/config_path
wear://mobileNodeId/config_path
  • If there are multiple configurations created and a node then reads the data items by DataApi.getDataItems(), it will find all the configs:
found DataItem with URI wear://watchNodeId/config_path
found DataItem with URI wear://mobileNodeId/config_path
  • Calling DataApi.putDataItem() after creation will write to only the local (current) node’s config. Example: Wear creates a config and then calls putDataItem(), then wear config wear://watchNodeId/config_path will be created and synced.
  • Even though the recently updated node’s config is synced, the other node’s config is not updated. App needs to take care of updating the other node’s config. Developer documentation mentions some measures to avoid this situation. If you are still not able to stick to the guidelines due to the need of your app (as in our case), then you need to take care of the synchronization on your own. For this WearableListenerService along with SharedPreferences can be used as used by FORM watch face by Roman Nurik.

Do not worry if it all sounds utter gibberish to you, it will all make sense when you start exploring in similar direction.

source
  • WearableListenerService is used to receive callbacks when the app is in background.
  • SharedPreferences is also used for performance as answered by Roman Nurik in this google plus post.
  • onDataChanged() callback always receives the latest DataEvent.
  • “onDataChanged() is only called when the data really did change. If you put the same data into the DataApi multiple times, the method is only called once until you write different data”, from stackoverflow.
  • When the nodes get disconnected, MessageApi messages are dropped while the DataLayer DataItems are synced later when the nodes get connected again.
  • You should also check if the Google Play Services are updated or not and show a dialog to update it, if not up-to-date.
source

Good Luck! :)