ComplicationOutlineRenderer.kt
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.wear.watchface
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import kotlin.math.cos
import kotlin.math.sin
/**
* Helper for rendering a dashed outline around a complication. Intended for use with
* [LayerMode#DRAW_HIGHLIGHTED].
*/
public class ComplicationOutlineRenderer {
public companion object {
// Dashed lines are used for complication selection.
internal val DASH_WIDTH = 10.0f
internal var DASH_GAP = 2.0f
internal var DASH_LENGTH = 5.0f
internal val dashPaint = Paint().apply {
strokeWidth = DASH_WIDTH
style = Paint.Style.FILL_AND_STROKE
isAntiAlias = true
color = Color.RED
}
/** Draws a thick dotted line around the complication with the given bounds. */
@JvmStatic
public fun drawComplicationSelectOutline(canvas: Canvas, bounds: Rect) {
if (bounds.width() == bounds.height()) {
drawCircleDashBorder(canvas, bounds)
return
}
val radius = bounds.height() / 2.0f
// Draw left arc dash.
var cx = bounds.left + radius
var cy = bounds.centerY().toFloat()
var startAngle = (Math.PI / 2.0f).toFloat()
val dashCount = (Math.PI * radius / (DASH_WIDTH + DASH_GAP)).toInt()
drawArcDashBorder(canvas, cx, cy, radius, startAngle, DASH_LENGTH, dashCount)
// Draw right arc dash.
cx = bounds.right - radius
cy = bounds.centerY().toFloat()
startAngle = (Math.PI / 2.0f).toFloat() * 3.0f
drawArcDashBorder(canvas, cx, cy, radius, startAngle, DASH_LENGTH, dashCount)
// Draw straight line dash.
val rectangleWidth = bounds.width() - 2.0f * radius - 2.0f * DASH_GAP
val cnt = (rectangleWidth / (DASH_WIDTH + DASH_GAP)).toInt()
val baseX: Float = bounds.left + radius + DASH_GAP
val fixGap: Float = (rectangleWidth - cnt * DASH_WIDTH) / (cnt - 1)
for (i in 0 until cnt) {
val startX: Float = baseX + i * (fixGap + DASH_WIDTH) + DASH_WIDTH / 2
var startY = bounds.top.toFloat()
var endY: Float = bounds.top - DASH_LENGTH
canvas.drawLine(startX, startY, startX, endY, dashPaint)
startY = bounds.bottom.toFloat()
endY = startY + DASH_LENGTH
canvas.drawLine(startX, startY, startX, endY, dashPaint)
}
}
internal fun drawArcDashBorder(
canvas: Canvas,
cx: Float,
cy: Float,
r: Float,
startAngle: Float,
dashLength: Float,
dashCount: Int
) {
for (i in 0 until dashCount) {
val rot = (2.0 * Math.PI / (2.0 * (dashCount - 1).toDouble()) * i + startAngle)
val startX = (r * cos(rot)).toFloat() + cx
val startY = (r * sin(rot)).toFloat() + cy
val endX = ((r + dashLength) * cos(rot).toFloat()) + cx
val endY = ((r + dashLength) * sin(rot).toFloat()) + cy
canvas.drawLine(startX, startY, endX, endY, dashPaint)
}
}
internal fun drawCircleDashBorder(canvas: Canvas, bounds: Rect) {
val radius = bounds.width() / 2.0f
val dashCount = (2.0 * Math.PI * radius / (DASH_WIDTH + DASH_GAP)).toInt()
val cx = bounds.exactCenterX()
val cy = bounds.exactCenterY()
for (i in 0 until dashCount) {
val rot = (i * 2.0 * Math.PI / dashCount)
val startX = (radius * cos(rot).toFloat()) + cx
val startY = (radius * sin(rot).toFloat()) + cy
val endX = ((radius + DASH_LENGTH) * cos(rot).toFloat()) + cx
val endY = ((radius + DASH_LENGTH) * sin(rot).toFloat()) + cy
canvas.drawLine(startX, startY, endX, endY, dashPaint)
}
}
}
}