Wear OS Watch Face, Part 2: Watch Face Elements
The diagram below is the result of several hours of considerate pulling apart and re-thinking several different elements regarding Android Wallpaper Wear OS Watch Face Architecture. It halves a 500 line code file into several smaller classes, and hopefully the following discussion will clarify the reasoning of the component separation and provide a better understanding of the Wear OS watch face ecosystem.
The sample code can be found here.
Part 1 focused on the WatchFaceRenderable.kt
and WatchFaceRenderer.kt
classes, which reduces the complexity of drawing a watch face to simple canvas drawing. This mechanism allows implementations of WatchFaceRenderer.kt
to be injected into watch face and standard Android applications, for the purpose of separating concerns, debugging and testing.
The above classes are all that need to be understood in order to get an initial Wear OS watch face rendering on a canvas, with ExampleWatchRenderer.kt
as a general guide that also demonstrates how to incorporate bitmaps as background images. The injection
of the ExampleWatchRenderer.kt
is defined in the WatchFaceModule.kt
in the watchfacerenderer
module.
Service and Engine
At the core of every Watch Face is the WatchFaceService.kt
, which has an inner class of type WatchFaceEngine
- this engine does most, if not all, the heavy lifting required for the watch face. It responds to the lifecycle of the watch face (handled by the WatchFaceService.kt
), much like an activity. It gets notified if the watch face’s state changes. An interesting discussion to be had is whether inner classes violate SOLID principles, namely that of single responsibility. This discussion is left for the comments and potentially a future article.
The service documentation demonstrates the simple relationship between a service and it’s Engine
- the CanvasWatchFaceService
is a foreground service that will fire the necessary events in the Engine
which in turn can be processed by the watch face.
There are a few methods of which are worth taking note:
onPropertiesChanged
: used to determine the screen states oflowBitAmbientStatus
: fewer bits per colour in ambient modeisBurnInProtectionMode
: view is periodically offset when in ambient mode
onInterruptionFilterChanged
: Used to indicate that the watch face is inMute Mode
, where the “watch face should adjust the amount of information it displays.” It’s assumed from the example code that modifying the alpha is appropriate for the simple watch face, and hiding any extra information for more complicated faces.onVisibilityChanged
: Used to update the time zone to default, register the broadcast receiver to listen to time zone changes and ensure that the interactive mode timer is operational if necessary.
Interactive Mode Handler
While the Engine
inner class has a onTimeTick()
method, it is not sufficient for a watch face in interactive mode. According to the documentation:
In ambient mode, the system calls the
Engine.onTimeTick()
method every minute. It is usually sufficient to update your watch face once per minute in this mode. To update your watch face while in interactive mode, you must provide a custom timer as described in Initialize the custom timer.
The sample code has an added Handler
that is used to “Handle updating the time periodically in interactive mode”. The control of the handler
is managed every time onAmbientModeChanged()
and onVisibilityChanged()
are invoked. The primary function is to ensure that every second, on the second, invalidate
is invoked. These methods are mutually recursive with a check every loop.
WatchFaceEngineHandler
In order to separate the WatchFaceEngine
(event handler) from the WatchFaceRenderer.kt
(rendering handler), the WatchFaceEngineHandler.kt
interface is used to surface and simplify the important events from the WatchFaceEngine
to the WatchFaceService.kt
class, allowing implementations to be agnostic of the WatchFaceEngine
lifecycle.
The WatchFaceService.kt
class is able communicate events to the renderable components, which are both watch faces and complications. This is why WatchFaceRenderer.kt which implements WatchFaceRenderable rather than being one class.
A small note on complications
The discussion has naturally tended towards the next logical step in the journey to complete an Android Wear OS watch face: complications. This section aims to be a brief teaser to the concept, which will be covered in more detail in part 3.
As can be seen from the WatchFaceRenderable.kt
abstract class, all these methods relate to the rendering of the watch face in addition to any other visual components, such as complications. While these elements will be the particular focus of a subsequent article, complications are defined as follows:
A complication is any feature in a watch face that is displayed in addition to time. For example, a battery indicator is a complication. The Complications API is for both watch faces and data provider apps.
A watch face complication is a traditional horological term for an element displayed on the face that goes beyond of the concept of hours, minutes and seconds. These are the smaller screen elements that display something else that may interest the user, such as steps, light bulb status or calendar events.
Comments
No comments found for this article.
Join the discussion for this article on github. Comments appear on this page instantly.