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