Implementing the new ambient second hand for Wear OS with Watch Face Decompositions

The Wear OS team seems to have become more and more negligent about releasing resources for developers. An update to the emulator with the new UI and H update was released a week after some watches got the update.

Now with the new Qualcom 3100 processor with the ability to have constant animations in ambient mode and up to 16 colors has been out for two months, there has still been no documentation on how to add this functionality to watch faces.

Finding the source

I originally tried to add this functionality by continuing to draw the second hand in ambient mode and increasing frame rate, but this used up tons of battery and I could tell this was not the way to go.

I decided to look at the WatchFaceService decompiled class and found a function that was not documented called updateDecomposition that receives a WatchFaceDecomposition object.

 What is Watch Face Decomposition?

There is another class called WatchFaceDecomposition that has a builder with several component types. Think of it kind of like how you would build a watch face in Facer if you have used that before. Different components are passed to the builder such as fonts, text, and images with different ids and z-indexes. There are several Base Types that can also be set to animate. These are as follows.

  • SMOOTH_SECOND_HAND
  • TICKING_SECOND_HAND
  • MINUTE_HAND
  • HOUR_HAND
  • BPH_21600_SECOND_HAND
  • BPH_28800_SECOND_HAND

The different components that can be added are:

  • Image Component: This can be used as a background or icon and can be set to rotate with one of the above hand presets and must have an Icon Drawable set
  • Font Component: Font components are set from a source image Icon Drawable with all the digits that will be used on an image from top to bottom and the number of digits in the image must also be supplied
  • Number Component: The font component can be set to a number component which can update using the same presets in the list above
  • Complication Component: All complications that should be visible can be added as a complication component. I haven’t had a chance to try this yet, but it seems pretty straight forward.
An example of the ambient second hand. I created a custom goals watch face based on the watch face that came with the Fossil Sport. I will release this if I ever get approved by the Play Store 🙁

How to Add to your watchface

Here is an example of the function I created to make a WatchFaceDecomposition:

private WatchFaceDecomposition createDecompositionWatchFace() {
ImageComponent ticksComponent = new ImageComponent.Builder()
.setComponentId(2)
.setZOrder(2)
.setImage(Icon.createWithBitmap(BitmapUtil.loadBitmap(getBaseContext(), “ticks_embossed_decomposable.png”)))
.setBounds(new RectF(0, 0, mCenterX * 2, mCenterY * 2))
.build();

ImageComponent hourHandComponent = new ImageComponent.Builder(HOUR_HAND)
.setComponentId(3)
.setZOrder(3)
.setImage(Icon.createWithBitmap(BitmapUtil.loadBitmap(getBaseContext(), “hour_hand_decomposable.png”)))
.setBounds(new RectF(0.46f, .23f, 0.5f, .75f))
.build();

ImageComponent minuteHandComponent = new ImageComponent.Builder(MINUTE_HAND)
.setComponentId(4)
.setZOrder(4)
.setImage(Icon.createWithBitmap(BitmapUtil.loadBitmap(getBaseContext(), “minute_hand_decomposable.png”)))
.setBounds(new RectF(0.455f, 0f, 0.5f, .75f))
.build();

ImageComponent secondHandComponent = new ImageComponent.Builder(TICKING_SECOND_HAND)
.setComponentId(5)
.setZOrder(5)
.setImage(Icon.createWithBitmap(BitmapUtil.loadBitmap(getBaseContext(), “second_hand_decomposable.png”)))
.setBounds(new RectF(0.46f, .03f, 0.5f, .75f))
.build();

return new WatchFaceDecomposition.Builder()
.addImageComponents(ticksComponent)
.addImageComponents(hourHandComponent)
.addImageComponents(minuteHandComponent)
.addImageComponents(secondHandComponent)
.build();
}

Now actually telling the watch face to display this is another matter, but pretty simple once you know how.

First, at the end of the onCreate method of the watch face engine, run the updateDecomposition function we mentioned earlier:

updateDecomposition(createDecompositionWatchFace());

Next we need to make a receiver class within the engine to update the decomposition occasionally. Running this function seems to black out the screen for a second so this should only be done when one of the components needs to be updated. The new processor seems to handle things like animation.

private class UpdateDecompositionReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Hubcaps.Engine.this.updateDecomposition(createDecompositionWatchFace());
}
}

Now in the watch face class outside the engine, we need to create an alarm that will wake up the watch face and re-build the decomposition watch face. We will start this when the watch face is created and stop it when destroyed

private AlarmManager alarmManager;
private PendingIntent decompositionPendingIntent;

private void startDecompositionAlarm() {
this.alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
decompositionPendingIntent = PendingIntent.getBroadcast(getBaseContext(), 0,
new Intent(getBaseContext(), Hubcaps.Engine.UpdateDecompositionReceiver.class), 0);
this.alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + 3600000L,
3600000L, decompositionPendingIntent);
}

@Override
public void onCreate() {
super.onCreate();
startDecompositionAlarm();
}

@Override
public void onDestroy() {
super.onDestroy();
alarmManager.cancel(decompositionPendingIntent);
}

All done! This is a simple analog watch face I created for my Hubcaps watch face that you can see here if you have either the Montblanc Summit 2 or Fossil Sport.

Hubcaps Preview

I have already bugged lots of people for some official documentation, but in the meantime, I hope this helps. Look forward to seeing a lot of developers using this in their watch faces!

7 thoughts on “Implementing the new ambient second hand for Wear OS with Watch Face Decompositions”

  1. Nice article, but there are a few mistakes you’ve made along the way.
    First of all, you don’t need AlarmManager and BroadcastReceiver, and you don’t need to call updateDecomposition yourself.
    What you do need to do is: create a subclass from
    android.support.wearable.watchface.decompositionface.DecompositionWatchFaceService,
    then add a single method:
    @Override public WatchFaceDecomposition buildDecomposition() { … }
    containing your code to build the decomposition. That’s all you need, really.

  2. You don’t need AlarmManager and BroadcastReceiver at all. And you don’t need to call updateDecomposition yourself. All you need is:
    public class MyWatchFace extends android.support.wearable.watchface.decompositionface.DecompositionWatchFaceService {
    @Override public WatchFaceDecomposition buildDecomposition() { /* paste your code from createDecompositionWatchFace() here */ }
    }
    That’s it, really, you don’t need to do anything else.

    1. Where should the public class be placed and how should it be declared in the manifest please. I was waiting to find any official documentation but of course none has been provided.
      Thanks for any information you could assist with.

  3. Where should the public class be placed and how should it be declared in the manifest please. I was waiting to find any official documentation but of course none has been provided.
    Thanks for any information you could assist with.

  4. Hello, I’m trying to implement this (or better, completely redo my watchface to be even more battery friendly).
    I don’t use images, I need to draw on a canvas, would this work if I gave just an ImageComponent as the background? Doesn’t look like that so far, hope I haven’t done enough testing yet.
    Do you think that the code inside the createDecompositionWatchFace() will be executed on the low power co-processor or is just the internal drawing method that is using it?
    Also, could you give a complete example class?
    Thank you in advance. Will update if there are news

    1. Yes, you can give an image component as a background. I have made some watch faces by completely drawing on a canvas. Just make sure you are not using more than 16 colors. It also doesn’t seem to work if you draw an image on a canvas and use that as an ImageComponent. I would rather not share a whole class.

Leave a Reply

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