1/* 2 * Copyright (C) 2016 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 17package com.prefabulated.touchlatency; 18 19import android.content.Context; 20import android.graphics.Canvas; 21import android.graphics.Color; 22import android.graphics.Paint; 23import android.os.CountDownTimer; 24import android.support.v7.app.AppCompatActivity; 25import android.os.Bundle; 26import android.text.method.Touch; 27import android.util.AttributeSet; 28import android.util.Log; 29import android.view.Menu; 30import android.view.MenuItem; 31import android.view.MotionEvent; 32import android.view.View; 33 34import java.util.ArrayList; 35import java.util.Collections; 36 37class TouchLatencyView extends View implements View.OnTouchListener { 38 private static final String LOG_TAG = "TouchLatency"; 39 private static final int BACKGROUND_COLOR = 0xFF400080; 40 private static final int INNER_RADIUS = 70; 41 private static final int BALL_RADIUS = 100; 42 43 public TouchLatencyView(Context context, AttributeSet attrs) { 44 super(context, attrs); 45 setOnTouchListener(this); 46 setWillNotDraw(false); 47 mBluePaint = new Paint(Paint.ANTI_ALIAS_FLAG); 48 mBluePaint.setColor(0xFF0000FF); 49 mBluePaint.setStyle(Paint.Style.FILL); 50 mGreenPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 51 mGreenPaint.setColor(0xFF00FF00); 52 mGreenPaint.setStyle(Paint.Style.FILL); 53 mYellowPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 54 mYellowPaint.setColor(0xFFFFFF00); 55 mYellowPaint.setStyle(Paint.Style.FILL); 56 mRedPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 57 mRedPaint.setColor(0xFFFF0000); 58 mRedPaint.setStyle(Paint.Style.FILL); 59 60 mTouching = false; 61 62 mBallX = 100.0f; 63 mBallY = 100.0f; 64 mVelocityX = 7.0f; 65 mVelocityY = 7.0f; 66 } 67 68 @Override 69 public boolean onTouch(View view, MotionEvent event) { 70 int action = event.getActionMasked(); 71 if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE) { 72 mTouching = true; 73 invalidate(); 74 } else if (action == MotionEvent.ACTION_UP) { 75 mTouching = false; 76 invalidate(); 77 return true; 78 } else { 79 return true; 80 } 81 mTouchX = event.getX(); 82 mTouchY = event.getY(); 83 return true; 84 } 85 86 private void drawTouch(Canvas canvas) { 87 if (!mTouching) { 88 Log.d(LOG_TAG, "Filling background"); 89 canvas.drawColor(BACKGROUND_COLOR); 90 return; 91 } 92 93 float deltaX = (mTouchX - mLastDrawnX); 94 float deltaY = (mTouchY - mLastDrawnY); 95 float scaleFactor = (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY) * 1.5f; 96 97 mLastDrawnX = mTouchX; 98 mLastDrawnY = mTouchY; 99 100 canvas.drawColor(BACKGROUND_COLOR); 101 canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + 3 * scaleFactor, mRedPaint); 102 canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + 2 * scaleFactor, mYellowPaint); 103 canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + scaleFactor, mGreenPaint); 104 canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS, mBluePaint); 105 } 106 107 private void drawBall(Canvas canvas) { 108 int width = canvas.getWidth(); 109 int height = canvas.getHeight(); 110 111 // Update position 112 mBallX += mVelocityX; 113 mBallY += mVelocityY; 114 115 // Clamp and change velocity if necessary 116 float left = mBallX - BALL_RADIUS; 117 if (left < 0) { 118 left = 0; 119 mVelocityX *= -1; 120 } 121 122 float top = mBallY - BALL_RADIUS; 123 if (top < 0) { 124 top = 0; 125 mVelocityY *= -1; 126 } 127 128 float right = mBallX + BALL_RADIUS; 129 if (right > width) { 130 right = width; 131 mVelocityX *= -1; 132 } 133 134 float bottom = mBallY + BALL_RADIUS; 135 if (bottom > height) { 136 bottom = height; 137 mVelocityY *= -1; 138 } 139 140 // Draw the ball 141 canvas.drawColor(BACKGROUND_COLOR); 142 canvas.drawOval(left, top, right, bottom, mYellowPaint); 143 invalidate(); 144 } 145 146 @Override 147 protected void onDraw(Canvas canvas) { 148 super.onDraw(canvas); 149 150 if (mMode == 0) { 151 drawTouch(canvas); 152 } else { 153 drawBall(canvas); 154 } 155 } 156 157 public void changeMode(MenuItem item) { 158 final int NUM_MODES = 2; 159 final String modes[] = {"Touch", "Ball"}; 160 mMode = (mMode + 1) % NUM_MODES; 161 invalidate(); 162 item.setTitle(modes[mMode]); 163 } 164 165 private Paint mBluePaint, mGreenPaint, mYellowPaint, mRedPaint; 166 private int mMode; 167 168 private boolean mTouching; 169 private float mTouchX, mTouchY; 170 private float mLastDrawnX, mLastDrawnY; 171 172 private float mBallX, mBallY; 173 private float mVelocityX, mVelocityY; 174} 175 176public class TouchLatencyActivity extends AppCompatActivity { 177 178 @Override 179 protected void onCreate(Bundle savedInstanceState) { 180 super.onCreate(savedInstanceState); 181 setContentView(R.layout.activity_touch_latency); 182 183 mTouchView = findViewById(R.id.canvasView); 184 } 185 186 187 @Override 188 public boolean onCreateOptionsMenu(Menu menu) { 189 // Inflate the menu; this adds items to the action bar if it is present. 190 getMenuInflater().inflate(R.menu.menu_touch_latency, menu); 191 return true; 192 } 193 194 @Override 195 public boolean onOptionsItemSelected(MenuItem item) { 196 // Handle action bar item clicks here. The action bar will 197 // automatically handle clicks on the Home/Up button, so long 198 // as you specify a parent activity in AndroidManifest.xml. 199 int id = item.getItemId(); 200 201 //noinspection SimplifiableIfStatement 202 if (id == R.id.action_settings) { 203 mTouchView.changeMode(item); 204 } 205 206 return super.onOptionsItemSelected(item); 207 } 208 209 private TouchLatencyView mTouchView; 210} 211