Google is committed to advancing racial equity for Black communities. See how.

Create a watchface for Wear with Kotlin

This codelab shows how easy it is to create your own Wear OS analog watch face using Kotlin DSL (domain-specific language). By the end of the codelab, you'll have a customized watch face that you can call your own.

If you run into any issues (code bugs, grammatical errors, unclear wording, etc.) as you work through this codelab, please report the issue via the Report a mistake link in the lower left corner of the codelab.

Concepts and setup

Concepts

A domain-specific language (DSL) is a "language specialized to a particular application domain". A DSL is a way to deal with a specific set of concerns. In our case, it's used to help us create watch faces.

In this codelab, we show how to use a DSL to greatly simplify the creation of watch faces.

Let's start by learning a little about Wear OS and its most prominent UI element - the watch face.

622ee65abb0d4e64.png

Wear OS by Google is designed for small, powerful devices, worn on the body. It is designed to deliver useful information when you need it most, intelligent answers to spoken questions, and tools to help reach fitness goals.

Style is a big part of such a personal device. Aside from offering a choice of manufacturers, customisable watch faces give users even more ways to express their personal style, and we will help you create your own today.

A watch face is essentially a native service that runs in the background on a Wear OS device. Within this service, there is an engine that renders each screen. You can think of this as an animator flipping through a book of moving cartoon drawings.

You normally write code that manually draws each of these pages (making the watch face move). However, we will use Kotlin DSL to greatly simplify the process.

Let's get started!

Clone the starter project repo

To help you get you started quickly, we have prepared a project for you to build on. It contains some basic code and application settings necessary for building watch faces.

If you have Git installed, run the command below. (You can check by typing git --version in the terminal / command line and verify that it executes correctly.)

git clone https://github.com/googlecodelabs/watchface-kotlin

If you do not have git you can get the project as a zip file:

Download Zip

Import the project

Start Android Studio and select "Open an existing Android Studio project" from the Welcome screen. Open the project directory and double click the build.gradle file in the watchface-kotlin directory:

a3b2a00b0b58393.png

After the project has loaded, you may see an alert saying, "Unregistered VCS root detected". Click "Ignore" or the "X" in the upper-right corner. (You won't be pushing any changes back to the Git repo.)

In the upper-left corner of the project window, if you are in the Android view, you should see a set of folder icons similar to those in the screenshot below. (If you are in the Project view, expand the watchface-kotlin project to see the set of folder icons.)

c232ba6d8db7550.png

There are two folder icons (base and complete). Each folder icon represents a "module". Please note that Android Studio might take several seconds to compile the project in the background for the first time. During this time you will see a spinner in the status bar at the bottom of Android Studio:

27f04b5598a2f95e.png

Wait until the processes finishes before making code changes, to allow Android Studio to pull in all the necessary components.

In addition, if you get a prompt saying "Reload for language changes to take effect?" or something similar, select "Yes".

Understand the starter project

You're ready to create a watch face. You'll start in the base module and build a watch face on top of that code. You'll add code from each step to base.

The complete module can be used for checking your work, or for you to reference if you encounter any issues.

Overview of key components:

  • AnalogDslWatchFace - Code used to create and style the watch face. This file is located in the directory base/java/com.example.android.watchface.watchfacekotlin.
  • res/drawable/ - Directory containing watch face background image.

Emulator setup

If you do not have a Wear OS device and need help setting up a Wear OS emulator, please refer to Launch a Wear emulator and run your Wear app.

Run the starter project

Let's run the watch face on a watch.

  • Connect your Wear OS device or start an emulator.
  • In the toolbar, select the base configuration from the drop-down selector and click the green triangle (Run) button next to it:

9c57d9fb03af68ce.png

  • If you get an error like the one below (Error running ‘base': Default Activity not found), change the default launch Activity (instructions below).

b11b670889b14664.png Conversely, if prompted for the Activity to use when launching the app, choose "Do not launch Activity".

  • To change the default launch Activity (if necessary, from the previous step), click the drop-down selector to the left of the green arrow and click "edit configurations". 8ba47bfc27626061.png

Select base and you will see a window similar the one below. Under the "Launch Options" section, there will be a set of dropdown options. Select "Nothing" under the "Launch Options" section and click OK. Once the prompt dismisses, you will need to click the green triangle again to run the program. Later, if you want to try launching any of the other modules, you will need to do this as well.

ddecac974e660fc4.png

  • Select your Android device or emulator and click OK. This will install the watch face on the Wear OS device or emulator.
  • After a couple of seconds, your watch face is built and ready to deploy. You will see a spinner in the status bar at the bottom of Android Studio while this is happening.
  • If it is not already at the "4: Run" tab at the bottom left side of Android Studio, select that tab to see the installation progress. At the end of the installation process, you should see something similar to this:
$ adb install-multiple -r -t
... lots of stuff ...
Split APKs installed
  • After the "Success" message is provided, long press on your device or emulator to bring up the watch faces picker. Scroll across and select Add more favorites.

5ea92276833c0446.png

Please note, since this is the first time you are running this watch face, you will need to select it from "See more watch faces". After it has been selected once, it will show up as one of the options alongside this option.

  • Select Kotlin DSL Analog.

72c35ccc818a99c6.png

  • Oh no, it failed! Don't worry, we are going to fix that in the next step. You should see an error similar to the one below in your Logcat tab:
04-27 15:40:00.879 14459-14459/com.example.android.watchface.watchfacekotlin E/AndroidRuntime: FATAL EXCEPTION: main
    ...
    Caused by: java.lang.InstantiationException: Must define watch face styles in DSL.
        at com.example.android.watchface.watchfacekotlin.service.AnalogWatchFaceStyleBuilder.build(WatchFaceStyleDsl.kt:146)
        at 
    ....
  • You should now see the default watch face. (This appears when your watch face has an error in it.)

ad7f138b54e76ee9.png

Don't worry if your emulator has a cloud with a strikethrough in place of the airplane icon. We will not need a connection to a phone / internet for this code lab.

Summary

In this step you've learned about:

  • Concepts of Kotlin DSL
  • Wear OS and the concepts behind watch faces
  • The basics of our starting point
  • How to deploy a watch face

Next up

Let's start making this watch face our own.

In this section, you will create watch hands and color for your watch face.

Prepare the paint objects

Just kidding, using Kotlin + DSL will help us avoid manual painting canvases in this codelab!

Define your main color

In the base module, open the AnalogDslWatchFace file and look at analogWatchFaceStyle {}. It should look like this:

return analogWatchFaceStyle { }

Let's define the colors:

  • We're actually using DSL right now, but we don't have all the required values set.
  • Add some space between the brackets and start typing color
  • One of the autocomplete items in Android Studio should be watchFaceColors. Select that and Android Studio will populate your DSL.
  • Define the color used for the main elements of the watch face by typing main inside watchFaceColors. Again, Android Studio should auto-populate this for you.
  • Define a color from the Color class, e.g., color = Color.Green. You should now have something that looks like this:
return analogWatchFaceStyle {
    watchFaceColors {
        main = Color.GREEN
    }
}

Define your arm dimensions

Now that we have the main color set, let's define the length of the hour, minute, and second watch arms:

  • After and outside watchFaceColors, start typing dimensions
  • One of the autocomplete items in Android Studio should be watchFaceDimensions. Select that and Android Studio will populate your DSL with the proper code for dimensions.
  • Within watchFaceDimensions, start typing "hour" and choose hourHandRadiusRatio.
  • Set the value to 0.2f (requires a float).
  • Try setting the radius ratios for the minute and second hands as well. You should end up with something like this:
return analogWatchFaceStyle {
    watchFaceColors {
        main = Color.GREEN
    }
    watchFaceDimensions {
        hourHandRadiusRatio = 0.2f
        minuteHandRadiusRatio = 0.5f
        secondHandRadiusRatio = 0.9f
    }
}

Check your progress

Choose the Kotlin DSL Analog watch face in the Favorites menu:

fa407d4991a549c4.png

You should see something like this:

7056cf3a60ff496a.png

Define more colors

Let's customize more colors in our watch face:

  • Inside watchFaceColors, type highlight and click the suggestion. Set that field to Color.parseColor("#BB0000").
  • Below that, type background and click the suggestion. Set that field to Color.WHITE or whatever color you want.
  • Your code should look like this:
return analogWatchFaceStyle {
   watchFaceColors {
       main = Color.GREEN
       highlight = Color.parseColor("#BB0000")
       background = Color.WHITE
   }
   watchFaceDimensions {
       hourHandRadiusRatio = 0.2f
       minuteHandRadiusRatio = 0.5f
       secondHandRadiusRatio = 0.9f
   }
}

Check your progress

If you run the watch face now and reselect Kotlin DSL Analog, you should see a watch face similar to the image below.

e3aff78a7020bb60.png

Summary

In this step, you've learned:

  • Setting colors and dimensions for your watch face via DSL.

Next up

We will further customize our watch face by setting a background image.

In this step, we add a background image to our watch face. If at any point you are confused by the concepts discussed here, please refer to the complete module and see how these steps may be implemented.

Prepare your image

A background image is supplied in this code lab. However, you can prepare your own image.

To use your own image, select any photograph, but note that some images with tiny details may not scale well on a small watch display. Crop the image to a square shape and resize it to about 600 x 600 pixels, depending on the target density of the screen. It can be in JPEG or PNG format. Then rename it to background_image.jpg or background_image.png (Android needs an underscore in place of a space).

After you completed this, "right click" the res/drawable directory in Android Studio and select "Reveal in Finder" (for Mac) or "Show in Explorer" (for Windows). Copy your image file into the directory res/drawable. It will replace the default image already in the folder.

Set a background image

Open the AnalogDslWatchFace file and look again at analogWatchFaceStyle {}. It should look like this:

return analogWatchFaceStyle {
   watchFaceColors {
       main = Color.GREEN
       highlight = Color.parseColor("#BB0000")
       background = Color.WHITE
   }
   watchFaceDimensions {
       hourHandRadiusRatio = 0.2f
       minuteHandRadiusRatio = 0.5f
       secondHandRadiusRatio = 0.9f
   }
}

Let's add a background image:

  • After and outside watchFaceDimensions, type background and click the suggestion watchFaceBackgroundImage.
  • Inside watchFaceBackgroundImage, type background again and click the suggestion backgroundImageResource and set it equal to R.drawable.background_image.
  • Update two of the watchFaceColors to better match the new background image.
  • Change main to Color.CYAN and the highlight to #ffa500.
  • Your code should look like this:
return analogWatchFaceStyle {
   watchFaceColors {
       main = Color.CYAN
       highlight = Color.parseColor("#ffa500")
       background = Color.WHITE
   }
   watchFaceDimensions {
       hourHandRadiusRatio = 0.2f
       minuteHandRadiusRatio = 0.5f
       secondHandRadiusRatio = 0.9f
   }
   watchFaceBackgroundImage {
       backgroundImageResource = R.drawable.background_image
   }
}

Check your progress

If you run the watch face now and reselect Kotlin DSL Analog, you should see something like the image below.

90934434efe29abf.png

Summary

In this step you've learned to:

  • Set the background of your watch face.

Understanding DSL

Try customizing your watch face even further using DSL by reviewing all the options in the model/AnalogWatchFaceStyleModels.kt class.

As stated earlier, DSLs (domain-specific languages) are "languages specialized to a particular application domain". To learn more about Kotlin DSL, read the Type-Safe Builders page on the Kotlin site.

You can see how the watch face DSL was implemented for this code lab by reviewing the service/WatchFaceStyleDsl.kt class.

If you are interested in how the object that was created with DSL is rendered into a watch face, review the service/AbstractKotlinWatchFace.kt class.

For more details about developing Wear OS watch faces and data providers (and how Java development compares to this code lab), please review

Optimizing for ambient mode

It's worthwhile to note that the implementation of this watch face has differing behavior in ambient mode, depending on the device's screen properties. On screens that require burn-in protection, the background is always black, whereas for other screens the watch face converts our bitmap into grayscale (or uses the background color if no bitmap was provided).

77006a2189d001ae.png

To learn more, read about respecting the device's screen properties. You can find how these properties are used in your watch face in the service/AbstractKotlinWatchFace.tk class.

Learn More

Watch these great videos: