I followed WatchOS documentation for creation of the watchface. I can draw anything on the watchface without any problem, however, when I try to render any complication it doesn't show up.
package com.example.mywatchface
import android.graphics.*
import android.util.Log
import android.view.SurfaceHolder
import androidx.core.content.ContextCompat
import androidx.wear.watchface.*
import androidx.wear.watchface.complications.ComplicationSlotBounds
import androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy
import androidx.wear.watchface.complications.SystemDataSources
import androidx.wear.watchface.complications.data.ComplicationType
import androidx.wear.watchface.complications.rendering.CanvasComplicationDrawable
import androidx.wear.watchface.complications.rendering.ComplicationDrawable
import androidx.wear.watchface.style.CurrentUserStyleRepository
import androidx.wear.watchface.style.UserStyleSchema
import java.time.ZonedDateTime
/**
* Updates rate in milliseconds for interactive mode. We update once a second to advance the
* second hand.
*/
private const val INTERACTIVE_UPDATE_RATE_MS = 1000
/**
* Handler message id for updating the time periodically in interactive mode.
*/
private const val MSG_UPDATE_TIME = 0
private const val HOUR_STROKE_WIDTH = 5f
private const val MINUTE_STROKE_WIDTH = 3f
private const val SECOND_TICK_STROKE_WIDTH = 2f
private const val CENTER_GAP_AND_CIRCLE_RADIUS = 4f
private const val SHADOW_RADIUS = 6f
class MyWatchFace : WatchFaceService() {
inner class MySharedAssets : Renderer.SharedAssets {
override fun onDestroy() {
}
}
/**
* The watch face itself, which includes the renderer for drawing.
*/
override suspend fun createWatchFace(
surfaceHolder: SurfaceHolder,
watchState: WatchState,
complicationSlotsManager: ComplicationSlotsManager,
currentUserStyleRepository: CurrentUserStyleRepository
): WatchFace {
return WatchFace(
WatchFaceType.DIGITAL,
object : Renderer.CanvasRenderer2<MySharedAssets>(
surfaceHolder,
currentUserStyleRepository,
watchState,
CanvasType.HARDWARE,
interactiveDrawModeUpdateDelayMillis = 16,
clearWithBackgroundTintBeforeRenderingHighlightLayer = true
) {
override suspend fun createSharedAssets(): MySharedAssets {
return MySharedAssets()
}
override fun renderHighlightLayer(
canvas: Canvas,
bounds: Rect,
zonedDateTime: ZonedDateTime,
sharedAssets: MySharedAssets
) {
}
override fun render(
canvas: Canvas,
bounds: Rect,
zonedDateTime: ZonedDateTime,
sharedAssets: MySharedAssets
) {
for ((_, complication) in complicationSlotsManager.complicationSlots) {
if (complication.enabled) {
Log.d("Hey", "hey")
complication.render(canvas, zonedDateTime, renderParameters)
}
}
var paint = Paint()
paint.color = ContextCompat.getColor(baseContext, R.color.watch_face_hands)
paint.textSize = 100f
paint.isAntiAlias = true
paint.style = Paint.Style.STROKE
paint.strokeWidth = 2f
val hours = zonedDateTime.hour
val minutes = zonedDateTime.minute
val clockHeight = (paint.fontMetrics.descent + paint.fontMetrics.ascent)
val time = String.format("%02d:%02d", hours, minutes)
drawText(canvas, time, paint, 0f, 0f)
paint = Paint()
paint.color = ContextCompat.getColor(baseContext, R.color.watch_date)
paint.textSize = 20f
paint.isAntiAlias = true
paint.typeface = Typeface.SANS_SERIF
val dayOfWeek = zonedDateTime.dayOfWeek.name.substring(0, 3)
val month = zonedDateTime.month.name
val dayOfMonth = zonedDateTime.dayOfMonth
val dateHeight = paint.fontMetrics.descent + paint.fontMetrics.ascent;
val date = String.format("%s, %s %d", dayOfWeek, month, dayOfMonth)
drawText(canvas, date, paint, 0f, clockHeight / 2 + dateHeight - 5f)
}
}
)
}
private fun drawText(
canvas: Canvas,
text: String,
paint: Paint,
offsetX: Float,
offsetY: Float
) {
val timeWidth = paint.measureText(text)
val timeHeight = (paint.fontMetrics.descent + paint.fontMetrics.ascent) / 2
val startX = (canvas.width.toFloat() - timeWidth) / 2 + offsetX
val startY = canvas.height.toFloat() / 2 - timeHeight + offsetY
canvas.drawText(text, startX, startY, paint)
}
/**
* The specification of settings the watch face supports.
* This is similar to a database schema.
*/
override fun createUserStyleSchema(): UserStyleSchema {
return super.createUserStyleSchema()
}
/**
* The complication slot configuration for the watchface.
*/
override fun createComplicationSlotsManager(currentUserStyleRepository: CurrentUserStyleRepository): ComplicationSlotsManager {
val canvasComplicationFactory = CanvasComplicationFactory { watchState, listener ->
val complicationDrawable = ComplicationDrawable(this)
complicationDrawable.activeStyle.backgroundColor = getColor(R.color.watch_date)
complicationDrawable.activeStyle.textColor = getColor(R.color.watch_date)
complicationDrawable.activeStyle.iconColor = getColor(R.color.watch_date)
complicationDrawable.activeStyle.textSize = 20
CanvasComplicationDrawable(complicationDrawable, watchState, listener)
}
return ComplicationSlotsManager(
listOf(
ComplicationSlot.createRoundRectComplicationSlotBuilder(
0,
canvasComplicationFactory,
listOf(
ComplicationType.RANGED_VALUE,
ComplicationType.LONG_TEXT,
ComplicationType.SHORT_TEXT,
ComplicationType.MONOCHROMATIC_IMAGE,
ComplicationType.SMALL_IMAGE
),
DefaultComplicationDataSourcePolicy(
SystemDataSources.DATA_SOURCE_WORLD_CLOCK,
ComplicationType.SHORT_TEXT
),
ComplicationSlotBounds(RectF(0.1f, 0.5625f, 0.4f, 0.8125f))
).build()
),
currentUserStyleRepository
)
}
}
This is essentially the whole class. createComplicationSlotManager is supposed to create ComplicationSlots. For testing, I used safe complication Data Sources (the ones that don't require watch permission according to the docs) which means there should not be any problems with permissions. I tried to change the active styling... thinking that maybe, for some reason, complication is rendered as transparent or black... so that's why I can't see it on the black screen. It didn't help. When I check whether the complication is "enabled" in the render function, it returns true. So complication is definitely not "null" when I call render method on it in render function.
I'm honestly out of ideas and docs examples are a bit outdated (some of the things they use there is depricated). ChatGPT doesn't help either because it was trained on 2021 data and at that time the docs were completely different.
I would appreciate any pointers/help.
Also, as a side note, I would also appreciate if someone can explain to me how do I choose the ComplicationSlotBounds. Right now I use values from the docs examples but I want to better understand how to properly choose them. I assume that's essentially the position of the complication on the screen and is essentially in the range of 0 - 1 which would mean a % ration related to the screen size?!