Tag Archives

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

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!

Heads up for new Fall 2018 Wear OS

Heads up for new Fall 2018 Wear OS

In mid August, Google announced that they would be releasing an updated user interface. It has already begun to roll out, but there is still a lot of information that is unknown and not told to developers and not available in a preview. Luckily, the Reddit user ntauthy was able to find a way to manually enable these existing features on some versions of Wear OS. I’m going to go over the changes that were announced for developers as well as the ones that have not been.

The following were announced by Hoi Lam on the G+ Wear OS Developers commnunity

More Concise Text

Notifications now show a smaller amount of text initially, but users can still expand the notification by tapping on it.

Set custom colors for notifications

Brand Colors

Notifications can show any color you set using the .setColor function in your notification’s builder.

No More Custom Layouts

Tapping on a notification will no longer bring up a custom layout for expanded content, but rather show the full text of a notification with additional actions.

The following changes where seen by me on the new version by manually enabling feature flags. Allow me to make the disclaimer that these are not confirmed for the official release, but are withing the Wear OS app.

Now Playing status in the quick settings panel

Quick Settings Controls

The current media session from your watch or paired device now displays in the top status bar showing the current title. A Play/Pause transport control is available. Tapping on title triggers the Content Intent of the Media Style notification if it was posted from the watch, or brings up the Media Controls activity if not.

The Media Controls Activity

Media Controls in the app drawer and open

One thing that surprised me was a new Activity available in the app drawer that brings up extended controls for the current Media Session. It displays transport controls with the volume slider and the current title. The album art displays full screen behind it with the time at the top. After enabling it, I later got a notification telling me that this activity would automatically be launched when I started playing something, but this could be turned off by long pressing on the screen while it was open. This seems to be to replace the full screen controls previously available, but allows users to disable it if they don’t like it taking up their whole screen.

The Content intent on new notifications

While in the old version tapping on a notification triggered the content intent if one was set, this is not the case for the new one. Instead, the content intent shows up as another action labeled “Open”. 

No more progress bar

New notification with no progress bar
Old notification with progress bar

With the old version, a circular progress bar could be shown around the notifications icon. This does not appear to be the case in the new version, so developers should not depend on it for showing progress.

Let me know if If am missing anything or if you have any questions.