Creating a Watch

A few months ago, I created a watch—actually, two—in just a few hours. This feat is amazing and has only recently become possible. This article is both a tutorial and an account of that process, along with my take on the current smartwatch/wearable ecosystems. Much of the existing documentation is unhelpful, often relating to previous versions of Android Wear. I’ll direct you to useful resources and include source code (which non-developers can skip). Creating a watch face is a fascinating challenge, involving aesthetic considerations as well as the limitations and peculiarities of hardware like OLED screens. The goal is to persistently display time in ‘ambient mode’ using as few pixels as possible and varying their positions to prevent burn-in. You aim for elegance and informativeness while managing memory in Java (or Kotlin, as it was 2017!) and scheduling rendering. However, the constraints of physical timepieces no longer apply; you get to build the future.

You can get the watch faces for free from Chronolight on Google Play. Given the low number of downloads, they are among the most exclusive watches in the world. Enjoy.

Background

While doing a major refresh of my productivity app, Habit Streak, I returned to Android after nearly three years on iOS. Having used an Apple Watch for two years (more on that later), I wanted to continue with an equivalent device. In the UK at the time (and I believe still today), the obvious option—considering the latest software and hardware features, battery life, and availability—was the Huawei Watch 2. Amazon had them on sale. Compared to a 38mm Apple Watch, it is ridiculously large, but it has an actual round, watch-like screen, and its battery life was significantly better than the original Apple Watch. The biggest difference as a watch is that its screen stays on all the time, displaying a low-power watch face (updated every minute, not at 60fps). As a watch, there's no comparison to the Apple Watch: Android Wear is a usable watch, whereas the Apple Watch is primarily a fitness device (and secondarily a remote control for your phone).

Of course, as a developer, there was another major attraction: I could build my own watch. Ridiculously, this is still not possible on the Apple Watch in any form. On Samsung’s Tizen-based Gear watch platform, there is a piece of software that non-developers can download to create fully customized watch faces (albeit by composing standard elements like hands and digital displays). On Android, apps like Facer allow users to create standard watch faces without programming. However, to do something custom, you need Android Studio.

Getting Started: Guides and Learning Resources

I spent some time browsing and reading various pieces of documentation and tutorials. Unfortunately, due to substantial changes in the platform, many resources are outdated or confusing. Eventually, I found the Google Code Lab on Android Wear Complications, which contained a decent guide and, more importantly, actual working source code. (There is another, more basic codelab that might also be of interest.) The examples were in Java, unfortunately, but it was pretty easy to clean up the more unpleasant, verbose aspects with some judicious use of Kotlin.

Kotlin at 100%

The core idea

I started by quickly sketching various ways time could be visualized. I definitely wanted something that gave an intuitive sense of time passing while retaining digital precision. With software, you don’t have to choose. Bearing in mind the ambient mode challenge, I conceived an idea where the clock display itself moves throughout the day, its position forming a 24-hour clock orbiting the center. I also wanted to include complications (user-selected additional pieces of information) that could orbit opposite the clock. The final key element was a gradient background simulating sunrise and sunset, hidden in ambient mode but fading in when the watch is activated.

Watch face ideas

Implementation

With this fairly clear idea, I began building it. The code from the Google codelab was helpful, but creating a watch face often necessitates very specific optimizations and trade-offs. You want to minimize the amount of work done as much as possible, perhaps choosing a few areas to innovate while remaining conservative elsewhere. For example, I cared a lot about the transition between ambient and normal modes. I found watch faces that jumped abruptly from a black, unlit OLED to bright colors jarring. On the positive side, you get to write this in your build.gradle:

    minSdkVersion 25
    targetSdkVersion 25

It will be many years before that min sdk will be at all reasonable for a regular Android App.

I made extensive use of Kotlin, though in this context, you must try to avoid unnecessary object creation (particularly in your main drawing code). If you are familiar with Android Canvas drawing, you will find the basics quite straightforward.

class Chronolight : CanvasWatchFaceService()

You will need to do some trigonometry, assuming you want to utilize the circular screen. However, it's fairly simple, and similar calculations will arise in most watch face development. For example, here is some of the basic code (called when drawing each frame). Note how certain objects have been created elsewhere (i.e., not in each frame) to optimize performance.

val now = System.currentTimeMillis()

calendar.timeInMillis = now
val milliSeconds = (now % 60000).toInt()

hours = calendar.get(Calendar.HOUR_OF_DAY)

val minutes = calendar.get(Calendar.MINUTE)
minutesOfDay = 60 * hours + minutes

val r = maxOf(bounds.width()/2, bounds.height()/2).toFloat()
angle = (hours - 6) * Math.PI / 12f
secondsAngle = (milliSeconds - 15000) * Math.PI / 30000f

With these basic quantities, you can draw second hands (or, in my case, circles) and other such elements.

Kotlin allows the creation of extension functions on existing classes. This enables you to add methods missing from third-party libraries and keep your main business logic clean and clear. For example:

fun Canvas.drawCentredText(text: String, cx: Float, cy: Float, paint: Paint) {
    paint.getTextBounds(text, 0, text.length, workingTextBounds)
    drawText(text, cx - workingTextBounds.exactCenterX(), cy - workingTextBounds.exactCenterY(), paint)
}

It also has some nice tricks to make creating, building, or configuring new objects really simple.

    fun complicationDrawableWithContext(context: Context) : ComplicationDrawable {
        return ComplicationDrawable().apply {
            setContext(context)
            setBackgroundColorActive(Color.TRANSPARENT)
            setBorderColorActive(Color.TRANSPARENT)
            setIconColorActive(Color.LTGRAY)
        }
    }

apply can take any object, evaluate a block of code in its context, and then return the object. It's great for creating customized constructor logic.

The second version

I quite liked my first attempt at a watch, but it became clear that a few things could be improved. Battery life wasn't amazing when it was active (not terrible, but I suspect the use of gradient drawables filling the background didn't do the battery any favors). I also found the complications less interesting than I expected and wanted to preserve more of the pure dark background from ambient mode. Furthermore, I wanted to take the idea a bit further: in addition to showing progress through the day, I aimed to display progress through the month and year.

Cutting out complications removed a lot of code. Indeed, the final watch face was only around 200 lines of actual Kotlin. Ignoring boilerplate, it's just 100 lines or so (with some utility functions created for the other watch face). Creating an entire, original watch with such little effort is incredible.

Apple Watch, Today, The Future

I switched back to an iPhone a few months later. Unfortunately, Apple doesn’t play nicely with other platforms, so the experience of using Android Wear with an iPhone was frustrating. I ended up returning to the Apple Watch, which, even with its then-recent update to watchOS 4, was still a terrible watch.

It will be interesting to see how things evolve over the next few years. Technical and hardware constraints are clearly holding things back (particularly battery life). However, making it possible for anyone to create their own watch is pretty amazing. Hopefully, Apple will catch up in some form soon.