[go: nahoru, domu]

blob: d8dcea7a91ac0808cac9ae5dd14ea50413a725b4 [file] [log] [blame]
/*
* 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.widget
import android.app.Activity
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.util.AttributeSet
import android.view.InputDevice
import android.view.MotionEvent
import android.view.View
import android.view.View.MeasureSpec
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.TextView
import androidx.core.view.children
import androidx.core.view.forEach
import androidx.test.core.app.ActivityScenario
import androidx.test.core.app.ApplicationProvider
import androidx.test.espresso.Espresso
import androidx.test.espresso.UiController
import androidx.test.espresso.ViewAction
import androidx.test.espresso.action.GeneralClickAction
import androidx.test.espresso.action.Press
import androidx.test.espresso.action.Tap
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.filters.MediumTest
import androidx.test.screenshot.AndroidXScreenshotTestRule
import androidx.test.screenshot.assertAgainstGolden
import androidx.wear.test.R
import androidx.wear.widget.ArcLayout.LayoutParams.VERTICAL_ALIGN_CENTER
import androidx.wear.widget.ArcLayout.LayoutParams.VERTICAL_ALIGN_INNER
import androidx.wear.widget.ArcLayout.LayoutParams.VERTICAL_ALIGN_OUTER
import androidx.wear.widget.util.AsyncViewActions.waitForMatchingView
import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.CoreMatchers.any
import org.hamcrest.Matcher
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
@RunWith(Parameterized::class)
@MediumTest
class ArcLayoutTest(private val testHeight: Int) {
private val testWidth: Int = SCREEN_SIZE_DEFAULT
private val renderDoneLatch = CountDownLatch(1)
@get:Rule
val screenshotRule = AndroidXScreenshotTestRule("wear/wear")
private fun doOneTest(
key: String,
views: List<View>,
backgroundColor: Int = Color.GRAY,
interactiveFunction: (FrameLayout.() -> Unit)? = null
) {
val bitmap = Bitmap.createBitmap(testWidth, testHeight, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
// Set the main frame.
val mainFrame = FrameLayout(ApplicationProvider.getApplicationContext())
mainFrame.setBackgroundColor(backgroundColor)
for (view in views) {
mainFrame.addView(view)
}
val screenWidth = MeasureSpec.makeMeasureSpec(testWidth, MeasureSpec.EXACTLY)
val screenHeight = MeasureSpec.makeMeasureSpec(testHeight, MeasureSpec.EXACTLY)
mainFrame.measure(screenWidth, screenHeight)
mainFrame.layout(0, 0, testWidth, testHeight)
mainFrame.draw(canvas)
// If an interactive function is set, call it now and redraw.
// The function will generate mouse events and then we draw again to see the result
// displayed on the views (the test records and shows mouse events in the view)
interactiveFunction?.let {
it(mainFrame)
mainFrame.draw(canvas)
}
renderDoneLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)
bitmap.assertAgainstGolden(screenshotRule, key + "_" + testHeight)
}
private fun createArc(text1: String = "SWEEP", text2: String = "Default") =
ArcLayout(ApplicationProvider.getApplicationContext())
.apply {
addView(
CurvedTextView(ApplicationProvider.getApplicationContext())
.apply {
text = text1
textColor = Color.BLUE
setBackgroundColor(Color.rgb(100, 100, 0))
setSweepRangeDegrees(45f, 360f)
}
)
addView(
TextView(ApplicationProvider.getApplicationContext()).apply {
text = "TXT"
setTextColor(Color.GREEN)
layoutParams =
ArcLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
}
)
addView(
CurvedTextView(ApplicationProvider.getApplicationContext())
.apply {
text = text2
textColor = Color.RED
setBackgroundColor(Color.rgb(0, 100, 100))
}
)
}
private fun createMixedArc() =
ArcLayout(ApplicationProvider.getApplicationContext())
.apply {
addView(
CurvedTextView(ApplicationProvider.getApplicationContext())
.apply {
text = "One"
setBackgroundColor(Color.rgb(100, 100, 100))
setSweepRangeDegrees(0f, 20f)
isClockwise = true
}
)
addView(
CurvedTextView(ApplicationProvider.getApplicationContext())
.apply {
text = "Two"
setBackgroundColor(Color.rgb(150, 150, 150))
setSweepRangeDegrees(0f, 20f)
isClockwise = false
}
)
addView(
TextView(ApplicationProvider.getApplicationContext()).apply {
text = "TXT"
setTextColor(Color.GREEN)
layoutParams =
ArcLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
).apply { isRotated = false }
}
)
addView(
CurvedTextView(ApplicationProvider.getApplicationContext())
.apply {
text = "Three"
setBackgroundColor(Color.rgb(100, 100, 100))
setSweepRangeDegrees(0f, 20f)
isClockwise = true
}
)
addView(
CurvedTextView(ApplicationProvider.getApplicationContext())
.apply {
text = "Four"
setBackgroundColor(Color.rgb(150, 150, 150))
setSweepRangeDegrees(0f, 20f)
isClockwise = false
}
)
}
@Test
@Throws(Exception::class)
fun testArcs() {
doOneTest(
"basic_arcs_screenshot",
listOf(
createArc(),
createArc("SWEEP", "Start").apply {
anchorAngleDegrees = 90f
anchorType = ArcLayout.ANCHOR_START
},
createArc("SWEEP", "End").apply {
anchorAngleDegrees = 270f
anchorType = ArcLayout.ANCHOR_END
},
createArc("SWEEP", "Center").apply {
anchorAngleDegrees = 315f
anchorType = ArcLayout.ANCHOR_CENTER
}
)
)
}
@Test
@Throws(Exception::class)
fun testArcsCcw() {
doOneTest(
"basic_arcs_ccw_screenshot",
listOf(
createArc(),
createArc("SWEEP", "Start").apply {
anchorAngleDegrees = 270f
anchorType = ArcLayout.ANCHOR_START
},
createArc("SWEEP", "End").apply {
anchorAngleDegrees = 90f
anchorType = ArcLayout.ANCHOR_END
},
createArc("SWEEP", "Center").apply {
anchorAngleDegrees = 45f
anchorType = ArcLayout.ANCHOR_CENTER
}
).apply { forEach { it.isClockwise = false } }
)
}
@Test
@Throws(Exception::class)
fun testArcsMixed() {
doOneTest(
"basic_arcs_mix_screenshot",
listOf(
createMixedArc(),
createMixedArc().apply {
isClockwise = false
}
)
)
}
// We keep track of the color of added widgets, to use on touch tests.
// We should try to avoid using white since it have special meaning.
var colorProcessor: (Int) -> Int = { color ->
when (color) {
Color.WHITE -> 0xFFCCCCCC.toInt()
else -> color or 0xFF000000.toInt()
}
}
var testColors = mutableListOf<Int>()
@Before
fun setup() {
testColors = mutableListOf(0) // Used when no view got the event
}
// Extension functions to make the margin test more readable.
fun ArcLayout.addSeparator(angle: Float = 10f) {
addView(
CurvedTextView(ApplicationProvider.getApplicationContext())
.apply {
text = " "
setSweepRangeDegrees(angle, 360f)
setBackgroundColor(Color.rgb(100, 100, 100))
isClockwise = true
textSize = 40f
}
)
testColors.add(colorProcessor(Color.rgb(150, 150, 150)))
}
fun ArcLayout.addCurvedText(
text: String,
color: Int,
marginLeft: Int? = null,
marginTop: Int? = null,
marginRight: Int? = null,
marginBottom: Int? = null,
margin: Int? = null,
paddingLeft: Int? = null,
paddingTop: Int? = null,
paddingRight: Int? = null,
paddingBottom: Int? = null,
padding: Int? = null,
vAlign: Int = VERTICAL_ALIGN_CENTER,
clockwise: Boolean = true,
textSize: Float = 14f,
textAlignment: Int = View.TEXT_ALIGNMENT_TEXT_START,
minSweep: Float = 0f
) {
addView(
CurvedTextView(ApplicationProvider.getApplicationContext())
.also {
it.text = text
it.setBackgroundColor(color)
it.isClockwise = clockwise
it.textSize = textSize
it.textAlignment = textAlignment
it.setSweepRangeDegrees(minSweep, 360f)
it.setPadding(
paddingLeft ?: padding ?: 0,
paddingTop ?: padding ?: 0,
paddingRight ?: padding ?: 0,
paddingBottom ?: padding ?: 0
)
it.layoutParams = ArcLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
).apply {
setMargins(
marginLeft ?: margin ?: 0,
marginTop ?: margin ?: 0,
marginRight ?: margin ?: 0,
marginBottom ?: margin ?: 0
)
verticalAlignment = vAlign
}
}
)
testColors.add(colorProcessor(color))
}
fun ArcLayout.addTextView(
text: String,
color: Int,
textSize: Float = 14f
) {
addView(
TextView(context).also {
it.text = text
it.background = ColorDrawable(color)
it.textSize = textSize
}
)
testColors.add(colorProcessor(color))
}
fun ArcLayout.addInvisibleTextView() {
addView(
TextView(context).also {
it.text = "Invisible"
it.visibility = View.INVISIBLE
}
)
testColors.add(0xFF13579B.toInt())
}
fun ArcLayout.addGoneTextView() {
addView(
TextView(context).also {
it.text = "Gone"
it.visibility = View.GONE
}
)
testColors.add(0xFF13579B.toInt())
}
private fun createArcWithMargin() =
ArcLayout(ApplicationProvider.getApplicationContext())
.apply {
anchorType = ArcLayout.ANCHOR_CENTER
addSeparator()
addCurvedText("RI", Color.RED, marginTop = 16, vAlign = VERTICAL_ALIGN_INNER)
addCurvedText(
"GI",
Color.GREEN,
marginTop = 8,
marginBottom = 8,
vAlign = VERTICAL_ALIGN_INNER
)
addCurvedText("BI", Color.BLUE, marginBottom = 16, vAlign = VERTICAL_ALIGN_INNER)
addSeparator()
addCurvedText("Red", Color.RED, marginTop = 16)
addCurvedText("Green", Color.GREEN, marginTop = 8, marginBottom = 8)
addCurvedText("Blue", Color.BLUE, marginBottom = 16)
addSeparator()
addCurvedText("RO", Color.RED, marginTop = 16, vAlign = VERTICAL_ALIGN_OUTER)
addCurvedText(
"GO",
Color.GREEN,
marginTop = 8,
marginBottom = 8,
vAlign = VERTICAL_ALIGN_OUTER
)
addCurvedText("BO", Color.BLUE, marginBottom = 16, vAlign = VERTICAL_ALIGN_OUTER)
addSeparator()
addCurvedText("L", Color.WHITE, marginRight = 20)
addSeparator()
addCurvedText("C", Color.WHITE, marginRight = 10, marginLeft = 10)
addSeparator()
addCurvedText("R", Color.WHITE, marginLeft = 20)
addSeparator()
}
private fun createTwoArcsWithMargin() = listOf(
// First arc goes on top
createArcWithMargin(),
// Second arc in the bottom, and we change al children to go counterclockwise.
createArcWithMargin().apply {
anchorAngleDegrees = 180f
children.forEach {
(it as? CurvedTextView)?.isClockwise = false
}
}
)
@Test
fun testMargins() {
doOneTest("margin_test", createTwoArcsWithMargin())
}
@Test
fun testMarginsCcw() {
doOneTest(
"margin_ccw_test",
createTwoArcsWithMargin().map {
it.apply { isClockwise = false }
}
)
}
@Test
fun testInvisibleAndGone() {
doOneTest(
"inivisible_gone_test",
listOf(
ArcLayout(ApplicationProvider.getApplicationContext())
.apply {
anchorType = ArcLayout.ANCHOR_CENTER
addCurvedText("Initial", Color.RED, textSize = 30f)
addInvisibleTextView()
addCurvedText("Second", Color.GREEN, textSize = 30f)
addGoneTextView()
addCurvedText("Third", Color.BLUE, textSize = 30f)
addSeparator()
addCurvedText("Initial", Color.RED, textSize = 30f, clockwise = false)
addInvisibleTextView()
addCurvedText("Second", Color.GREEN, textSize = 30f, clockwise = false)
addGoneTextView()
addCurvedText("Third", Color.BLUE, textSize = 30f, clockwise = false)
}
)
)
}
private fun createArcsWithPaddingAndMargins() = listOf(
ArcLayout(ApplicationProvider.getApplicationContext())
.apply {
anchorType = ArcLayout.ANCHOR_CENTER
listOf(VERTICAL_ALIGN_INNER, VERTICAL_ALIGN_CENTER, VERTICAL_ALIGN_OUTER).forEach {
align ->
addSeparator()
addCurvedText("None", 0xFFFF0000.toInt(), vAlign = align)
addSeparator(angle = 1f)
addCurvedText("Pad", 0xFF80FF00.toInt(), padding = 8, vAlign = align)
addSeparator(angle = 1f)
addCurvedText("Mar", 0xFF00FFFF.toInt(), margin = 8, vAlign = align)
addSeparator(angle = 1f)
addCurvedText(
"Both",
0xFF8000FF.toInt(),
padding = 8,
margin = 8,
vAlign = align
)
}
addSeparator()
},
ArcLayout(ApplicationProvider.getApplicationContext())
.apply {
anchorType = ArcLayout.ANCHOR_CENTER
anchorAngleDegrees = 180f
addSeparator()
addCurvedText("Top", 0xFFFF0000.toInt(), paddingTop = 16)
addSeparator()
addCurvedText("Bottom", 0xFF80FF00.toInt(), paddingBottom = 16)
addSeparator()
addCurvedText("Left", 0xFF00FFFF.toInt(), paddingLeft = 16)
addSeparator()
addCurvedText("Right", 0xFF8000FF.toInt(), paddingRight = 16)
addSeparator()
}
)
@Test
fun testMarginsAndPadding() {
doOneTest(
"margin_padding_test",
createArcsWithPaddingAndMargins()
)
}
@Test
fun testMarginsAndPaddingCcw() {
doOneTest(
"margin_padding_ccw_test",
// For each WearArcLayout, change all WearCurvedTextView children to counter-clockwise
createArcsWithPaddingAndMargins().map {
it.apply {
children.forEach { child ->
(child as? CurvedTextView)?.let { cv -> cv.isClockwise = false }
}
}
}
)
}
@Test
fun testLayoutRtl() {
doOneTest(
"layout_rtl",
listOf(
ArcLayout(ApplicationProvider.getApplicationContext()).apply {
anchorAngleDegrees = 0f
anchorType = ArcLayout.ANCHOR_CENTER
layoutDirection = View.LAYOUT_DIRECTION_RTL
isClockwise = true
listOf("a", "b", "c").forEach { text ->
addSeparator()
addCurvedText(text, 0xFFFF0000.toInt())
}
},
ArcLayout(ApplicationProvider.getApplicationContext()).apply {
anchorAngleDegrees = 180f
anchorType = ArcLayout.ANCHOR_CENTER
layoutDirection = View.LAYOUT_DIRECTION_RTL
isClockwise = false
listOf("d", "e", "f").forEach { text ->
addSeparator()
addCurvedText(text, 0xFFFF0000.toInt())
}
},
)
)
}
// Generates a click in the x,y coordinates in the view's coordinate system.
fun customClick(x: Float, y: Float) = ViewActions.actionWithAssertions(
GeneralClickAction(
Tap.SINGLE,
{ view ->
val xy = IntArray(2)
view.getLocationOnScreen(xy)
floatArrayOf(x + xy[0], y + xy[1])
},
Press.PINPOINT,
InputDevice.SOURCE_UNKNOWN,
MotionEvent.BUTTON_PRIMARY
)
)
// Sending clicks is slow, around a quarter of a second each, on a desktop emulator.
@Test(timeout = 100000)
fun testTouchEvents() {
val bitmap = Bitmap.createBitmap(testWidth, testHeight, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
val scenario = ActivityScenario.launch(TouchTestActivity::class.java)
val STEP = 30
DrawableSurface.radius = 6f
// Find the main FrameLayout that contains all widgets under test.
val theView = Espresso.onView(withId(R.id.curved_frame))
.perform(
waitForMatchingView(
allOf(
withId(R.id.curved_frame),
isDisplayed()
),
2000
)
)
theView.perform(object : ViewAction {
override fun getConstraints(): Matcher<View> = any(View::class.java)
override fun getDescription(): String = "Resize view to fit the test."
override fun perform(uiController: UiController?, view: View?) {
(view as? FrameLayout)?.layoutParams =
FrameLayout.LayoutParams(testWidth, testHeight)
}
})
// Setup on-click handlers for each view so we can get the index of the clicked view.
var clicked: Int
scenario.onActivity {
listOf(
R.id.curved_text1, R.id.curved_text2, R.id.curved_text3,
R.id.curved_text4, R.id.text5, R.id.curved_text6
).mapIndexed { ix, viewId ->
it.findViewById<View>(viewId)?.setOnClickListener {
clicked = ix
}
}
}
// Simulate clicks in a grid all over the screen and draw a circle centered in the
// position of the click and which color indicates the view that got clicked.
// Black means no view got the click event, white means a out of range value.
for (y in STEP / 2 until testHeight step STEP) {
val points = mutableListOf<ColoredPoint>()
for (x in STEP / 2 until testWidth step STEP) {
// Perform a click, and record a point colored according to which view was clicked.
clicked = -1
theView.perform(customClick(x.toFloat(), y.toFloat()))
points.add(
ColoredPoint(
x.toFloat(), y.toFloat(),
// Color the circle.
listOf(
Color.BLACK, // no view got the event.
Color.RED, Color.GREEN, Color.BLUE,
Color.YELLOW, Color.MAGENTA, Color.CYAN
).elementAtOrNull(clicked + 1) ?: Color.WHITE
)
)
}
// Add all circles on the current line to the DrawableSurface.
// Points are batched to improve performance a bit.
Espresso.onView(withId(R.id.drawable_surface)).perform(object : ViewAction {
override fun getConstraints(): Matcher<View> = any(View::class.java)
override fun getDescription(): String = "Add Points"
override fun perform(uiController: UiController?, view: View?) {
(view as? DrawableSurface)?.addPoints(points)
}
})
}
// At the end, get a screenshot to compare against the golden
scenario.onActivity {
it.findViewById<View>(R.id.curved_frame).draw(canvas)
}
bitmap.assertAgainstGolden(screenshotRule, "touch_screenshot" + "_" + testHeight)
}
// This is not testing the full event journey as the previous method does, but it's faster so
// we can make more tests, and test more points in them.
private fun testEventsFast(key: String, testViews: List<View>) {
val context: Context = ApplicationProvider.getApplicationContext()
// We setup the "mouse event display" view (on top, with a semi-transparent background)
// and the views under test.
val drawableSurface = DrawableSurface(context)
drawableSurface.background = ColorDrawable(0x40000000.toInt())
val views = testViews + drawableSurface
// Setup the click handlers
var clicked: Int
var viewNumber = 0
// We need this function because we want each listener to capture it's view number by value,
// (and a reference to the clicked variable).
val onTouchListenerGenerator = { myNumber: Int ->
{ _: View, _: MotionEvent ->
clicked = myNumber
true
}
}
views.forEach { view ->
(view as? ArcLayout)?.let { arcLayout ->
arcLayout.forEach { innerView ->
if (innerView is TextView || innerView is CurvedTextView) {
innerView.setOnTouchListener(onTouchListenerGenerator(viewNumber++))
}
}
}
}
// Do the test, sending the events
var time = 0L
DrawableSurface.radius = 1.5f
doOneTest(
key, views,
backgroundColor = Color.rgb(0xFF, 0xFF, 0xC0)
) {
val STEP = 4
// Simulate clicks in a grid all over the screen and draw a circle centered in the
// position of the click and which color indicates the view that got clicked.
// Black means no view got the click event, white means a out of range value.
for (y in STEP / 2 until testHeight step STEP) {
for (x in STEP / 2 until testWidth step STEP) {
// Perform a click, and record a point colored according to which view was clicked.
clicked = -1
val down_event = MotionEvent.obtain(
time, time, MotionEvent.ACTION_DOWN,
x.toFloat(), y.toFloat(), 0
)
dispatchTouchEvent(down_event)
val up_event = MotionEvent.obtain(
time, time + 5, MotionEvent.ACTION_UP,
x.toFloat(), y.toFloat(), 0
)
dispatchTouchEvent(up_event)
time += 10
drawableSurface.addPoints(
listOf(
ColoredPoint(
x.toFloat(), y.toFloat(),
// Color the circle.
// We use Transparent for not touched and white for out of index
testColors.elementAtOrNull(clicked + 1) ?: Color.WHITE
)
)
)
}
}
}
}
@Test(timeout = 5000)
fun testBasicTouch() {
val context: Context = ApplicationProvider.getApplicationContext()
// This views are the same as the test testTouchEvents()
val views = listOf(
ArcLayout(context).apply {
anchorAngleDegrees = 0f
anchorType = ArcLayout.ANCHOR_CENTER
isClockwise = true
addCurvedText(
"Left", color = 0x66FF0000, textSize = 48f, minSweep = 60f,
textAlignment = View.TEXT_ALIGNMENT_TEXT_START
)
addGoneTextView()
addCurvedText(
"Center", color = 0x6600FF00, textSize = 48f, minSweep = 60f,
textAlignment = View.TEXT_ALIGNMENT_CENTER
)
addCurvedText(
"Right", color = 0x660000FF, textSize = 48f, minSweep = 60f,
textAlignment = View.TEXT_ALIGNMENT_TEXT_END
)
addGoneTextView()
},
ArcLayout(context).apply {
anchorAngleDegrees = 180f
anchorType = ArcLayout.ANCHOR_CENTER
isClockwise = true
addGoneTextView()
addCurvedText(
"ACL", color = 0x66FFFF00, textSize = 48f, minSweep = 40f,
textAlignment = View.TEXT_ALIGNMENT_TEXT_START
)
addTextView(text = "N-TXT", color = 0x66FF00FF, textSize = 20f)
addCurvedText(
"ACR", color = 0x6600FFFF, textSize = 60f, minSweep = 50f,
textAlignment = View.TEXT_ALIGNMENT_TEXT_END, clockwise = false
)
}
)
testEventsFast("touch_fast_screenshot", views)
}
@Test(timeout = 10000)
fun testMarginTouch() {
val views = createTwoArcsWithMargin()
testEventsFast("touch_fast_margin_screenshot", views)
}
companion object {
private const val SCREEN_SIZE_DEFAULT = 390
private const val SCREEN_SIZE_DIFF = 100
private const val TIMEOUT_MS = 1000L
@JvmStatic
@Parameterized.Parameters(name = "testHeight={0}")
fun initParameters() = listOf(
SCREEN_SIZE_DEFAULT,
SCREEN_SIZE_DEFAULT + SCREEN_SIZE_DIFF,
SCREEN_SIZE_DEFAULT - SCREEN_SIZE_DIFF
)
}
}
// Helper activity for testing touch.
class TouchTestActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.wear_arc_layout_touch_test)
}
}
data class ColoredPoint(val x: Float, val y: Float, val c: Int)
// Helper class to draw some point/circles of different colors. Used by the touch test.
open class DrawableSurface @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
private var points = mutableListOf<ColoredPoint>()
override fun onDraw(canvas: Canvas) {
val paint = Paint().apply {
strokeWidth = radius / 2f
style = Paint.Style.STROKE
alpha = 0
}
points.forEach { p ->
paint.color = p.c
canvas.drawCircle(p.x, p.y, radius, paint)
}
}
fun addPoints(newPoints: Collection<ColoredPoint>) {
points.addAll(newPoints)
invalidate()
}
companion object {
var radius = 6f
}
}