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 31 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 64 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 74 public override fun onTouchEvent(event: MotionEvent): Boolean { 75 isPredictionAvailable = predictor.isPredictionAvailable(event.getDeviceId(), 76 event.getSource()) 77 addEvent(event) 78 return true 79 } 80 81 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