1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package test.motionprediction
18
19 import android.content.Context
20 import android.graphics.Canvas
21 import android.graphics.Color
22 import android.graphics.Paint
23 import android.util.AttributeSet
24 import android.view.MotionEvent
25 import android.view.MotionEvent.ACTION_DOWN
26 import android.view.MotionPredictor
27 import android.view.View
28
29 import java.util.Vector
30
drawLinenull31 private fun drawLine(canvas: Canvas, from: MotionEvent, to: MotionEvent, paint: Paint) {
32 canvas.apply {
33 val x0 = from.getX()
34 val y0 = from.getY()
35 val x1 = to.getX()
36 val y1 = to.getY()
37 // TODO: handle historical data
38 drawLine(x0, y0, x1, y1, paint)
39 }
40 }
41
42 /**
43 * Draw the current stroke and predicted values
44 */
45 class DrawingView(context: Context, attrs: AttributeSet) : View(context, attrs) {
46 private val TAG = "DrawingView"
47
48 val events: MutableMap<Int, Vector<MotionEvent>> = mutableMapOf<Int, Vector<MotionEvent>>()
49
50 var isPredictionAvailable = false
51 private val predictor = MotionPredictor(getContext())
52
53 private var predictionPaint = Paint()
54 private var realPaint = Paint()
55
56 init {
57 setBackgroundColor(Color.WHITE)
58 predictionPaint.color = Color.BLACK
59 predictionPaint.setStrokeWidth(5f)
60 realPaint.color = Color.RED
61 realPaint.setStrokeWidth(5f)
62 }
63
addEventnull64 private fun addEvent(event: MotionEvent) {
65 if (event.getActionMasked() == ACTION_DOWN) {
66 events.remove(event.deviceId)
67 }
68 var vec = events.getOrPut(event.deviceId) { Vector<MotionEvent>() }
69 vec.add(MotionEvent.obtain(event))
70 predictor.record(event)
71 invalidate()
72 }
73
onTouchEventnull74 public override fun onTouchEvent(event: MotionEvent): Boolean {
75 isPredictionAvailable = predictor.isPredictionAvailable(event.getDeviceId(),
76 event.getSource())
77 addEvent(event)
78 return true
79 }
80
onDrawnull81 public override fun onDraw(canvas: Canvas) {
82 super.onDraw(canvas)
83 if (!isPredictionAvailable) {
84 canvas.apply {
85 drawRect(0f, 0f, 200f, 200f, realPaint)
86 }
87 }
88
89 var eventTime = 0L
90
91 // Draw real events
92 for ((_, vec) in events ) {
93 for (i in 1 until vec.size) {
94 drawLine(canvas, vec[i - 1], vec[i], realPaint)
95 }
96 eventTime = vec.lastElement().eventTime
97 }
98
99 // Draw predictions. Convert to nanos and hardcode to +20ms into the future
100 val prediction = predictor.predict(eventTime * 1000000 + 20000000)
101 if (prediction != null) {
102 val realEvents = events.get(prediction.deviceId)!!
103 drawLine(canvas, realEvents[realEvents.size - 1], prediction, predictionPaint)
104 }
105 }
106 }
107