WatchFaceService

public abstract class WatchFaceService

Known direct subclasses

WatchFaceService and WatchFace are a pair of classes intended to handle much of the boilerplate needed to implement a watch face without being too opinionated. The suggested structure of a WatchFaceService based watch face is:

import android.graphics.RectF
import androidx.wear.complications.ComplicationSlotBounds
import androidx.wear.complications.DefaultComplicationDataSourcePolicy
import androidx.wear.watchface.CanvasComplicationFactory
import androidx.wear.watchface.ComplicationSlotsManager
import androidx.wear.watchface.WatchFace
import androidx.wear.watchface.complications.rendering.CanvasComplicationDrawable
import androidx.wear.watchface.complications.rendering.ComplicationDrawable
import androidx.wear.watchface.style.UserStyleSchema
import androidx.wear.watchface.style.UserStyleSetting.ListUserStyleSetting

class ExampleCanvasWatchFaceService : WatchFaceService() {
    override fun createUserStyleSchema() =
        UserStyleSchema(
            listOf(
                ListUserStyleSetting(
                    UserStyleSetting.Id("color_style_setting"),
                    "Colors",
                    "Watchface colorization",
                    icon = null,
                    options = listOf(
                        ListUserStyleSetting.ListOption(
                            Option.Id("red_style"),
                            "Red",
                            icon = null
                        ),
                        ListUserStyleSetting.ListOption(
                            Option.Id("green_style"),
                            "Green",
                            icon = null
                        ),
                        ListUserStyleSetting.ListOption(
                            Option.Id("blue_style"),
                            "Blue",
                            icon = null
                        )
                    ),
                    listOf(
                        WatchFaceLayer.BASE,
                        WatchFaceLayer.COMPLICATIONS,
                        WatchFaceLayer.COMPLICATIONS_OVERLAY
                    )
                ),
                ListUserStyleSetting(
                    UserStyleSetting.Id("hand_style_setting"),
                    "Hand Style",
                    "Hand visual look",
                    icon = null,
                    options = listOf(
                        ListUserStyleSetting.ListOption(
                            Option.Id("classic_style"), "Classic", icon = null
                        ),
                        ListUserStyleSetting.ListOption(
                            Option.Id("modern_style"), "Modern", icon = null
                        ),
                        ListUserStyleSetting.ListOption(
                            Option.Id("gothic_style"),
                            "Gothic",
                            icon = null
                        )
                    ),
                    listOf(WatchFaceLayer.COMPLICATIONS_OVERLAY)
                )
            )
        )

    override fun createComplicationSlotsManager(
        currentUserStyleRepository: CurrentUserStyleRepository
    ): ComplicationSlotsManager {
        val canvasComplicationFactory =
            CanvasComplicationFactory { watchState, listener ->
                CanvasComplicationDrawable(ComplicationDrawable(this), watchState, listener)
            }
        return ComplicationSlotsManager(
            listOf(
                ComplicationSlot.createRoundRectComplicationSlotBuilder(
                    /*id */ 0,
                    canvasComplicationFactory,
                    listOf(
                        ComplicationType.RANGED_VALUE,
                        ComplicationType.LONG_TEXT,
                        ComplicationType.SHORT_TEXT,
                        ComplicationType.MONOCHROMATIC_IMAGE,
                        ComplicationType.SMALL_IMAGE
                    ),
                    DefaultComplicationDataSourcePolicy(
                        SystemDataSources.DATA_SOURCE_DAY_OF_WEEK
                    ),
                    ComplicationSlotBounds(RectF(0.15625f, 0.1875f, 0.84375f, 0.3125f))
                ).setDefaultDataSourceType(ComplicationType.SHORT_TEXT)
                    .build(),
                ComplicationSlot.createRoundRectComplicationSlotBuilder(
                    /*id */ 1,
                    canvasComplicationFactory,
                    listOf(
                        ComplicationType.RANGED_VALUE,
                        ComplicationType.LONG_TEXT,
                        ComplicationType.SHORT_TEXT,
                        ComplicationType.MONOCHROMATIC_IMAGE,
                        ComplicationType.SMALL_IMAGE
                    ),
                    DefaultComplicationDataSourcePolicy(
                        SystemDataSources.DATA_SOURCE_STEP_COUNT
                    ),
                    ComplicationSlotBounds(RectF(0.1f, 0.5625f, 0.35f, 0.8125f))
                ).setDefaultDataSourceType(ComplicationType.SHORT_TEXT)
                    .build()
            ),
            currentUserStyleRepository
        )
    }

    override suspend fun createWatchFace(
        surfaceHolder: SurfaceHolder,
        watchState: WatchState,
        complicationSlotsManager: ComplicationSlotsManager,
        currentUserStyleRepository: CurrentUserStyleRepository
    ) = WatchFace(
        WatchFaceType.ANALOG,
        object : Renderer.CanvasRenderer(
            surfaceHolder,
            currentUserStyleRepository,
            watchState,
            CanvasType.HARDWARE,
            /* interactiveUpdateRateMillis */ 16,
        ) {
            init {
                currentUserStyleRepository.addUserStyleChangeListener(
                    object : CurrentUserStyleRepository.UserStyleChangeListener {
                        override fun onUserStyleChanged(userStyle: UserStyle) {
                            // `userStyle` will contain two userStyle categories with options
                            // from the lists above. ...
                        }
                    }
                )
            }

            override fun render(
                canvas: Canvas,
                bounds: Rect,
                zonedDateTime: ZonedDateTime
            ) {
                // ...
            }

            override fun renderHighlightLayer(
                canvas: Canvas,
                bounds: Rect,
                zonedDateTime: ZonedDateTime
            ) {
                canvas.drawColor(renderParameters.highlightLayer!!.backgroundTint)

                // ...
            }
        }
    )
}

return ExampleCanvasWatchFaceService()

Sub classes of WatchFaceService are expected to implement createWatchFace which is the factory for making WatchFaces. If the watch faces uses complications then createComplicationSlotsManager should be overridden. All ComplicationSlots are assumed to be enumerated up upfront and passed as a collection into ComplicationSlotsManager's constructor which is returned by createComplicationSlotsManager.

Watch face styling (color and visual look of watch face elements such as numeric fonts, watch hands and ticks, etc...) and companion editing is directly supported via UserStyleSchema and UserStyleSetting. To enable support for styling override createUserStyleSchema.

WatchFaces are initially constructed on a background thread before being used exclusively on the ui thread afterwards. There is a memory barrier between construction and rendering so no special threading primitives are required.

To aid debugging watch face animations, WatchFaceService allows you to speed up or slow down time, and to loop between two instants. This is controlled by MOCK_TIME_INTENT intents with a float extra called "androidx.wear.watchface.extra.MOCK_TIME_SPEED_MULTIPLIE" and to long extras called "androidx.wear.watchface.extra.MOCK_TIME_WRAPPING_MIN_TIME" and "androidx.wear.watchface.extra.MOCK_TIME_WRAPPING_MAX_TIME" (which are UTC time in milliseconds). If minTime is omitted or set to -1 then the current time is sampled as minTime.

E.g, to make time go twice as fast: adb shell am broadcast -a androidx.wear.watchface.MockTime
--ef androidx.wear.watchface.extra.MOCK_TIME_SPEED_MULTIPLIER 2.0

To use the sample on watch face editor UI, import the wear:wear-watchface-editor-samples library and add the following into your watch face's AndroidManifest.xml:

<activity
android:name="androidx.wear.watchface.editor.sample.WatchFaceConfigActivity"
android:exported="true"
android:label="Config"
android:theme="@android:style/Theme.Translucent.NoTitleBar">
<intent-filter>
<action android:name="androidx.wear.watchface.editor.action.WATCH_FACE_EDITOR" />
<category android:name=
"com.google.android.wearable.watchface.category.WEARABLE_CONFIGURATION" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>

To register a WatchFaceService with the system add a tag to the in your watch face's AndroidManifest.xml:

<service
android:name=".MyWatchFaceServiceClass"
android:exported="true"
android:label="@string/watch_face_name"
android:permission="android.permission.BIND_WALLPAPER">
<intent-filter>
<action android:name="android.service.wallpaper.WallpaperService" />
<category android:name="com.google.android.wearable.watchface.category.WATCH_FACE" />
</intent-filter>
<meta-data
android:name="com.google.android.wearable.watchface.preview"
android:resource="@drawable/my_watch_preview" />
<meta-data
android:name="com.google.android.wearable.watchface.preview_circular"
android:resource="@drawable/my_watch_circular_preview" />
<meta-data
android:name="com.google.android.wearable.watchface.wearableConfigurationAction"
android:value="androidx.wear.watchface.editor.action.WATCH_FACE_EDITOR"/>
<meta-data
android:name="android.service.wallpaper"
android:resource="@xml/watch_face" />
<meta-data
android:name=
"com.google.android.wearable.watchface.companionBuiltinConfigurationEnabled"
android:value="true" />
</service>

Multiple watch faces can be defined in the same package, requiring multiple tags.

Summary

Public constructors

Public methods

final @NonNull Handler

Returns the lazily constructed background thread Handler.

final @NonNull Handler

Returns the ui thread Handler.

final @NonNull WallpaperService.Engine

Creates an interactive engine for WallpaperService.

Inherited methods

From class Context
final int
getColor(int id)
final @NonNull ColorStateList
final @Nullable Drawable
getDrawable(int id)
final @NonNull String
getString(int resId)
final @NonNull String
getString(int resId, @NonNull Object formatArgs)
final @NonNull T
<T extends Object> getSystemService(@NonNull Class<@NonNull T> serviceClass)
final @NonNull CharSequence
getText(int resId)
final @NonNull TypedArray
final @NonNull TypedArray
obtainStyledAttributes(int resid, @NonNull int[] attrs)
final @NonNull TypedArray
final @NonNull TypedArray
obtainStyledAttributes(
    @Nullable AttributeSet set,
    @NonNull int[] attrs,
    int defStyleAttr,
    int defStyleRes
)
void
void
sendBroadcastWithMultiplePermissions(
    @NonNull Intent intent,
    @NonNull Array<@NonNull String> receiverPermissions
)
void
From class ContextWrapper
boolean
bindIsolatedService(
    @NonNull Intent service,
    int flags,
    @NonNull String instanceName,
    @NonNull Executor executor,
    @NonNull ServiceConnection conn
)
boolean
bindService(
    @NonNull Intent service,
    @NonNull ServiceConnection conn,
    int flags
)
boolean
bindService(
    @NonNull Intent service,
    int flags,
    @NonNull Executor executor,
    @NonNull ServiceConnection conn
)
boolean
bindServiceAsUser(
    @NonNull Intent service,
    @NonNull ServiceConnection conn,
    int flags,
    @NonNull UserHandle user
)
int
int
@NonNull int[]
checkCallingOrSelfUriPermissions(
    @NonNull List<@NonNull Uri> uris,
    int modeFlags
)
int
int
checkCallingUriPermission(@NonNull Uri uri, int modeFlags)
@NonNull int[]
int
checkPermission(@NonNull String permission, int pid, int uid)
int
int
checkUriPermission(@NonNull Uri uri, int pid, int uid, int modeFlags)
int
checkUriPermission(
    @Nullable Uri uri,
    @Nullable String readPermission,
    @Nullable String writePermission,
    int pid,
    int uid,
    int modeFlags
)
@NonNull int[]
checkUriPermissions(
    @NonNull List<@NonNull Uri> uris,
    int pid,
    int uid,
    int modeFlags
)
void

This method is deprecated.

@NonNull Context
@NonNull Context
@NonNull Context
@NonNull Context
@NonNull Context
@NonNull Context
@NonNull Context
createPackageContext(@NonNull String packageName, int flags)
@NonNull Context
createWindowContext(int type, @Nullable Bundle options)
@NonNull Context
createWindowContext(
    @NonNull Display display,
    int type,
    @Nullable Bundle options
)
@NonNull Array<@NonNull String>
boolean
boolean
boolean
void
enforceCallingOrSelfPermission(
    @NonNull String permission,
    @Nullable String message
)
void
enforceCallingOrSelfUriPermission(
    @NonNull Uri uri,
    int modeFlags,
    @NonNull String message
)
void
enforceCallingPermission(
    @NonNull String permission,
    @Nullable String message
)
void
enforceCallingUriPermission(
    @NonNull Uri uri,
    int modeFlags,
    @NonNull String message
)
void
enforcePermission(
    @NonNull String permission,
    int pid,
    int uid,
    @Nullable String message
)
void