blob: c34cb9e12524e0548a80ba8467eb10fae15a42dd [file] [log] [blame]
Kenny Rootf8d0f092010-03-12 08:15:28 -08001/*
2 * Copyright (C) 2010 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
Dianne Hackborn82977882010-03-01 21:42:15 -080017package com.android.internal.widget;
Dianne Hackborn90d2db32010-02-11 22:19:06 -080018
19import android.content.Context;
20import android.graphics.Canvas;
21import android.graphics.Paint;
Jeff Brown8d608662010-08-30 03:02:23 -070022import android.graphics.RectF;
Dianne Hackborn90d2db32010-02-11 22:19:06 -080023import android.graphics.Paint.FontMetricsInt;
24import android.util.Log;
Jeff Brown8d608662010-08-30 03:02:23 -070025import android.view.InputDevice;
Jeff Brownc3fe7662011-03-07 14:35:59 -080026import android.view.KeyEvent;
Dianne Hackborn90d2db32010-02-11 22:19:06 -080027import android.view.MotionEvent;
28import android.view.VelocityTracker;
29import android.view.View;
30import android.view.ViewConfiguration;
Jeff Browncc0c1592011-02-19 05:07:28 -080031import android.view.MotionEvent.PointerCoords;
Dianne Hackborn90d2db32010-02-11 22:19:06 -080032
33import java.util.ArrayList;
34
35public class PointerLocationView extends View {
Jeff Brown8d608662010-08-30 03:02:23 -070036 private static final String TAG = "Pointer";
37
Dianne Hackborn90d2db32010-02-11 22:19:06 -080038 public static class PointerState {
Jeff Brown8d608662010-08-30 03:02:23 -070039 // Trace of previous points.
40 private float[] mTraceX = new float[32];
41 private float[] mTraceY = new float[32];
42 private int mTraceCount;
43
44 // True if the pointer is down.
Dianne Hackborn90d2db32010-02-11 22:19:06 -080045 private boolean mCurDown;
Jeff Brown8d608662010-08-30 03:02:23 -070046
47 // Most recent coordinates.
Jeff Browncc0c1592011-02-19 05:07:28 -080048 private PointerCoords mCoords = new PointerCoords();
Jeff Brown8d608662010-08-30 03:02:23 -070049
50 // Most recent velocity.
Jeff Brown9e2ad362010-07-30 19:20:11 -070051 private float mXVelocity;
52 private float mYVelocity;
Jeff Brown8d608662010-08-30 03:02:23 -070053
54 public void clearTrace() {
55 mTraceCount = 0;
56 }
57
58 public void addTrace(float x, float y) {
59 int traceCapacity = mTraceX.length;
60 if (mTraceCount == traceCapacity) {
61 traceCapacity *= 2;
62 float[] newTraceX = new float[traceCapacity];
63 System.arraycopy(mTraceX, 0, newTraceX, 0, mTraceCount);
64 mTraceX = newTraceX;
65
66 float[] newTraceY = new float[traceCapacity];
67 System.arraycopy(mTraceY, 0, newTraceY, 0, mTraceCount);
68 mTraceY = newTraceY;
69 }
70
71 mTraceX[mTraceCount] = x;
72 mTraceY[mTraceCount] = y;
73 mTraceCount += 1;
74 }
Dianne Hackborn90d2db32010-02-11 22:19:06 -080075 }
76
77 private final ViewConfiguration mVC;
78 private final Paint mTextPaint;
79 private final Paint mTextBackgroundPaint;
80 private final Paint mTextLevelPaint;
81 private final Paint mPaint;
82 private final Paint mTargetPaint;
83 private final Paint mPathPaint;
84 private final FontMetricsInt mTextMetrics = new FontMetricsInt();
85 private int mHeaderBottom;
86 private boolean mCurDown;
87 private int mCurNumPointers;
88 private int mMaxNumPointers;
Jeff Brownd1e0c372010-09-12 19:14:26 -070089 private int mActivePointerId;
Jeff Brown8d608662010-08-30 03:02:23 -070090 private final ArrayList<PointerState> mPointers = new ArrayList<PointerState>();
Jeff Browncc0c1592011-02-19 05:07:28 -080091 private final PointerCoords mHoverCoords = new PointerCoords();
Dianne Hackborn90d2db32010-02-11 22:19:06 -080092
Jeff Brown9e2ad362010-07-30 19:20:11 -070093 private final VelocityTracker mVelocity;
94
Jeff Brown8d608662010-08-30 03:02:23 -070095 private final FasterStringBuilder mText = new FasterStringBuilder();
96
Dianne Hackborn90d2db32010-02-11 22:19:06 -080097 private boolean mPrintCoords = true;
98
99 public PointerLocationView(Context c) {
100 super(c);
Jeff Brownc3fe7662011-03-07 14:35:59 -0800101 setFocusableInTouchMode(true);
102
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800103 mVC = ViewConfiguration.get(c);
104 mTextPaint = new Paint();
105 mTextPaint.setAntiAlias(true);
106 mTextPaint.setTextSize(10
107 * getResources().getDisplayMetrics().density);
108 mTextPaint.setARGB(255, 0, 0, 0);
109 mTextBackgroundPaint = new Paint();
110 mTextBackgroundPaint.setAntiAlias(false);
111 mTextBackgroundPaint.setARGB(128, 255, 255, 255);
112 mTextLevelPaint = new Paint();
113 mTextLevelPaint.setAntiAlias(false);
114 mTextLevelPaint.setARGB(192, 255, 0, 0);
115 mPaint = new Paint();
116 mPaint.setAntiAlias(true);
117 mPaint.setARGB(255, 255, 255, 255);
118 mPaint.setStyle(Paint.Style.STROKE);
119 mPaint.setStrokeWidth(2);
120 mTargetPaint = new Paint();
121 mTargetPaint.setAntiAlias(false);
122 mTargetPaint.setARGB(255, 0, 0, 192);
123 mPathPaint = new Paint();
124 mPathPaint.setAntiAlias(false);
125 mPathPaint.setARGB(255, 0, 96, 255);
126 mPaint.setStyle(Paint.Style.STROKE);
127 mPaint.setStrokeWidth(1);
128
129 PointerState ps = new PointerState();
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800130 mPointers.add(ps);
Jeff Brownd1e0c372010-09-12 19:14:26 -0700131 mActivePointerId = 0;
Jeff Brown9e2ad362010-07-30 19:20:11 -0700132
133 mVelocity = VelocityTracker.obtain();
Jeff Brown8d608662010-08-30 03:02:23 -0700134
135 logInputDeviceCapabilities();
136 }
137
138 private void logInputDeviceCapabilities() {
139 int[] deviceIds = InputDevice.getDeviceIds();
140 for (int i = 0; i < deviceIds.length; i++) {
141 InputDevice device = InputDevice.getDevice(deviceIds[i]);
142 if (device != null) {
143 Log.i(TAG, device.toString());
144 }
145 }
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800146 }
147
148 public void setPrintCoords(boolean state) {
149 mPrintCoords = state;
150 }
151
152 @Override
153 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
154 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
155 mTextPaint.getFontMetricsInt(mTextMetrics);
156 mHeaderBottom = -mTextMetrics.ascent+mTextMetrics.descent+2;
157 if (false) {
158 Log.i("foo", "Metrics: ascent=" + mTextMetrics.ascent
159 + " descent=" + mTextMetrics.descent
160 + " leading=" + mTextMetrics.leading
161 + " top=" + mTextMetrics.top
162 + " bottom=" + mTextMetrics.bottom);
163 }
164 }
Jeff Brown8d608662010-08-30 03:02:23 -0700165
166 // Draw an oval. When angle is 0 radians, orients the major axis vertically,
167 // angles less than or greater than 0 radians rotate the major axis left or right.
168 private RectF mReusableOvalRect = new RectF();
169 private void drawOval(Canvas canvas, float x, float y, float major, float minor,
170 float angle, Paint paint) {
171 canvas.save(Canvas.MATRIX_SAVE_FLAG);
Jeff Brown5068ad82010-09-29 20:14:56 -0700172 canvas.rotate((float) (angle * 180 / Math.PI), x, y);
Jeff Brown8d608662010-08-30 03:02:23 -0700173 mReusableOvalRect.left = x - minor / 2;
174 mReusableOvalRect.right = x + minor / 2;
175 mReusableOvalRect.top = y - major / 2;
176 mReusableOvalRect.bottom = y + major / 2;
177 canvas.drawOval(mReusableOvalRect, paint);
178 canvas.restore();
179 }
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800180
181 @Override
182 protected void onDraw(Canvas canvas) {
183 synchronized (mPointers) {
184 final int w = getWidth();
185 final int itemW = w/7;
186 final int base = -mTextMetrics.ascent+1;
187 final int bottom = mHeaderBottom;
188
189 final int NP = mPointers.size();
190
Jeff Brown8d608662010-08-30 03:02:23 -0700191 // Labels
Jeff Brownd1e0c372010-09-12 19:14:26 -0700192 if (mActivePointerId >= 0) {
193 final PointerState ps = mPointers.get(mActivePointerId);
194
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800195 canvas.drawRect(0, 0, itemW-1, bottom,mTextBackgroundPaint);
Jeff Brown8d608662010-08-30 03:02:23 -0700196 canvas.drawText(mText.clear()
197 .append("P: ").append(mCurNumPointers)
198 .append(" / ").append(mMaxNumPointers)
199 .toString(), 1, base, mTextPaint);
Jeff Brownd1e0c372010-09-12 19:14:26 -0700200
Jeff Brown8d608662010-08-30 03:02:23 -0700201 final int N = ps.mTraceCount;
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800202 if ((mCurDown && ps.mCurDown) || N == 0) {
203 canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom, mTextBackgroundPaint);
Jeff Brown8d608662010-08-30 03:02:23 -0700204 canvas.drawText(mText.clear()
205 .append("X: ").append(ps.mCoords.x, 1)
206 .toString(), 1 + itemW, base, mTextPaint);
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800207 canvas.drawRect(itemW * 2, 0, (itemW * 3) - 1, bottom, mTextBackgroundPaint);
Jeff Brown8d608662010-08-30 03:02:23 -0700208 canvas.drawText(mText.clear()
209 .append("Y: ").append(ps.mCoords.y, 1)
210 .toString(), 1 + itemW * 2, base, mTextPaint);
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800211 } else {
Jeff Brown8d608662010-08-30 03:02:23 -0700212 float dx = ps.mTraceX[N - 1] - ps.mTraceX[0];
213 float dy = ps.mTraceY[N - 1] - ps.mTraceY[0];
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800214 canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom,
215 Math.abs(dx) < mVC.getScaledTouchSlop()
216 ? mTextBackgroundPaint : mTextLevelPaint);
Jeff Brown8d608662010-08-30 03:02:23 -0700217 canvas.drawText(mText.clear()
218 .append("dX: ").append(dx, 1)
219 .toString(), 1 + itemW, base, mTextPaint);
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800220 canvas.drawRect(itemW * 2, 0, (itemW * 3) - 1, bottom,
221 Math.abs(dy) < mVC.getScaledTouchSlop()
222 ? mTextBackgroundPaint : mTextLevelPaint);
Jeff Brown8d608662010-08-30 03:02:23 -0700223 canvas.drawText(mText.clear()
224 .append("dY: ").append(dy, 1)
225 .toString(), 1 + itemW * 2, base, mTextPaint);
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800226 }
227
228 canvas.drawRect(itemW * 3, 0, (itemW * 4) - 1, bottom, mTextBackgroundPaint);
Jeff Brown8d608662010-08-30 03:02:23 -0700229 canvas.drawText(mText.clear()
230 .append("Xv: ").append(ps.mXVelocity, 3)
231 .toString(), 1 + itemW * 3, base, mTextPaint);
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800232
233 canvas.drawRect(itemW * 4, 0, (itemW * 5) - 1, bottom, mTextBackgroundPaint);
Jeff Brown8d608662010-08-30 03:02:23 -0700234 canvas.drawText(mText.clear()
235 .append("Yv: ").append(ps.mYVelocity, 3)
236 .toString(), 1 + itemW * 4, base, mTextPaint);
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800237
238 canvas.drawRect(itemW * 5, 0, (itemW * 6) - 1, bottom, mTextBackgroundPaint);
Jeff Brown8d608662010-08-30 03:02:23 -0700239 canvas.drawRect(itemW * 5, 0, (itemW * 5) + (ps.mCoords.pressure * itemW) - 1,
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800240 bottom, mTextLevelPaint);
Jeff Brown8d608662010-08-30 03:02:23 -0700241 canvas.drawText(mText.clear()
242 .append("Prs: ").append(ps.mCoords.pressure, 2)
243 .toString(), 1 + itemW * 5, base, mTextPaint);
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800244
245 canvas.drawRect(itemW * 6, 0, w, bottom, mTextBackgroundPaint);
Jeff Brown8d608662010-08-30 03:02:23 -0700246 canvas.drawRect(itemW * 6, 0, (itemW * 6) + (ps.mCoords.size * itemW) - 1,
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800247 bottom, mTextLevelPaint);
Jeff Brown8d608662010-08-30 03:02:23 -0700248 canvas.drawText(mText.clear()
249 .append("Size: ").append(ps.mCoords.size, 2)
250 .toString(), 1 + itemW * 6, base, mTextPaint);
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800251 }
252
Jeff Brown8d608662010-08-30 03:02:23 -0700253 // Pointer trace.
254 for (int p = 0; p < NP; p++) {
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800255 final PointerState ps = mPointers.get(p);
256
Jeff Brown8d608662010-08-30 03:02:23 -0700257 // Draw path.
258 final int N = ps.mTraceCount;
259 float lastX = 0, lastY = 0;
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800260 boolean haveLast = false;
261 boolean drawn = false;
262 mPaint.setARGB(255, 128, 255, 255);
Jeff Brown8d608662010-08-30 03:02:23 -0700263 for (int i=0; i < N; i++) {
264 float x = ps.mTraceX[i];
265 float y = ps.mTraceY[i];
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800266 if (Float.isNaN(x)) {
267 haveLast = false;
268 continue;
269 }
270 if (haveLast) {
271 canvas.drawLine(lastX, lastY, x, y, mPathPaint);
272 canvas.drawPoint(lastX, lastY, mPaint);
273 drawn = true;
274 }
275 lastX = x;
276 lastY = y;
277 haveLast = true;
278 }
279
Jeff Brown8d608662010-08-30 03:02:23 -0700280 // Draw velocity vector.
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800281 if (drawn) {
Jeff Brown9e2ad362010-07-30 19:20:11 -0700282 mPaint.setARGB(255, 255, 64, 128);
Jeff Brown8d608662010-08-30 03:02:23 -0700283 float xVel = ps.mXVelocity * (1000 / 60);
284 float yVel = ps.mYVelocity * (1000 / 60);
285 canvas.drawLine(lastX, lastY, lastX + xVel, lastY + yVel, mPaint);
286 }
287
288 if (mCurDown && ps.mCurDown) {
289 // Draw crosshairs.
290 canvas.drawLine(0, ps.mCoords.y, getWidth(), ps.mCoords.y, mTargetPaint);
291 canvas.drawLine(ps.mCoords.x, 0, ps.mCoords.x, getHeight(), mTargetPaint);
292
293 // Draw current point.
294 int pressureLevel = (int)(ps.mCoords.pressure * 255);
295 mPaint.setARGB(255, pressureLevel, 255, 255 - pressureLevel);
296 canvas.drawPoint(ps.mCoords.x, ps.mCoords.y, mPaint);
297
298 // Draw current touch ellipse.
299 mPaint.setARGB(255, pressureLevel, 255 - pressureLevel, 128);
300 drawOval(canvas, ps.mCoords.x, ps.mCoords.y, ps.mCoords.touchMajor,
301 ps.mCoords.touchMinor, ps.mCoords.orientation, mPaint);
302
303 // Draw current tool ellipse.
304 mPaint.setARGB(255, pressureLevel, 128, 255 - pressureLevel);
305 drawOval(canvas, ps.mCoords.x, ps.mCoords.y, ps.mCoords.toolMajor,
306 ps.mCoords.toolMinor, ps.mCoords.orientation, mPaint);
Jeff Brown517bb4c2011-01-14 19:09:23 -0800307
308 // Draw the orientation arrow.
309 mPaint.setARGB(255, pressureLevel, 255, 0);
310 float orientationVectorX = (float) (Math.sin(-ps.mCoords.orientation)
311 * ps.mCoords.toolMajor * 0.7);
312 float orientationVectorY = (float) (Math.cos(-ps.mCoords.orientation)
313 * ps.mCoords.toolMajor * 0.7);
314 canvas.drawLine(
315 ps.mCoords.x - orientationVectorX, ps.mCoords.y - orientationVectorY,
316 ps.mCoords.x + orientationVectorX, ps.mCoords.y + orientationVectorY,
317 mPaint);
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800318 }
319 }
320 }
321 }
Jeff Brown8d608662010-08-30 03:02:23 -0700322
Jeff Brown33bbfd22011-02-24 20:55:35 -0800323 private void logPointerCoords(int action, int index, MotionEvent.PointerCoords coords, int id) {
324 final String prefix;
325 switch (action & MotionEvent.ACTION_MASK) {
326 case MotionEvent.ACTION_DOWN:
327 prefix = "DOWN";
328 break;
329 case MotionEvent.ACTION_UP:
330 prefix = "UP";
331 break;
332 case MotionEvent.ACTION_MOVE:
333 prefix = "MOVE";
334 break;
335 case MotionEvent.ACTION_CANCEL:
336 prefix = "CANCEL";
337 break;
338 case MotionEvent.ACTION_OUTSIDE:
339 prefix = "OUTSIDE";
340 break;
341 case MotionEvent.ACTION_POINTER_DOWN:
342 if (index == ((action & MotionEvent.ACTION_POINTER_INDEX_MASK)
343 >> MotionEvent.ACTION_POINTER_INDEX_SHIFT)) {
344 prefix = "DOWN";
345 } else {
346 prefix = "MOVE";
347 }
348 break;
349 case MotionEvent.ACTION_POINTER_UP:
350 if (index == ((action & MotionEvent.ACTION_POINTER_INDEX_MASK)
351 >> MotionEvent.ACTION_POINTER_INDEX_SHIFT)) {
352 prefix = "UP";
353 } else {
354 prefix = "MOVE";
355 }
356 break;
357 case MotionEvent.ACTION_HOVER_MOVE:
358 prefix = "HOVER MOVE";
359 break;
Jeff Browna032cc02011-03-07 16:56:21 -0800360 case MotionEvent.ACTION_HOVER_ENTER:
361 prefix = "HOVER ENTER";
362 break;
363 case MotionEvent.ACTION_HOVER_EXIT:
364 prefix = "HOVER EXIT";
365 break;
Jeff Brown33bbfd22011-02-24 20:55:35 -0800366 case MotionEvent.ACTION_SCROLL:
367 prefix = "SCROLL";
368 break;
369 default:
370 prefix = Integer.toString(action);
371 break;
372 }
373
Jeff Brown8d608662010-08-30 03:02:23 -0700374 Log.i(TAG, mText.clear()
375 .append("Pointer ").append(id + 1)
Jeff Brown33bbfd22011-02-24 20:55:35 -0800376 .append(": ")
377 .append(prefix)
378 .append(" (").append(coords.x, 3).append(", ").append(coords.y, 3)
Jeff Brown8d608662010-08-30 03:02:23 -0700379 .append(") Pressure=").append(coords.pressure, 3)
380 .append(" Size=").append(coords.size, 3)
381 .append(" TouchMajor=").append(coords.touchMajor, 3)
382 .append(" TouchMinor=").append(coords.touchMinor, 3)
383 .append(" ToolMajor=").append(coords.toolMajor, 3)
384 .append(" ToolMinor=").append(coords.toolMinor, 3)
385 .append(" Orientation=").append((float)(coords.orientation * 180 / Math.PI), 1)
Jeff Brown6f2fba42011-02-19 01:08:02 -0800386 .append("deg")
387 .append(" VScroll=").append(coords.getAxisValue(MotionEvent.AXIS_VSCROLL), 1)
388 .append(" HScroll=").append(coords.getAxisValue(MotionEvent.AXIS_HSCROLL), 1)
389 .toString());
Jeff Brown8d608662010-08-30 03:02:23 -0700390 }
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800391
Jeff Brown33bbfd22011-02-24 20:55:35 -0800392 public void addPointerEvent(MotionEvent event) {
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800393 synchronized (mPointers) {
394 int action = event.getAction();
395
Jeff Brown8d608662010-08-30 03:02:23 -0700396 //Log.i(TAG, "Motion: action=0x" + Integer.toHexString(action)
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800397 // + " pointers=" + event.getPointerCount());
398
399 int NP = mPointers.size();
400
401 //mRect.set(0, 0, getWidth(), mHeaderBottom+1);
402 //invalidate(mRect);
403 //if (mCurDown) {
404 // mRect.set(mCurX-mCurWidth-3, mCurY-mCurWidth-3,
405 // mCurX+mCurWidth+3, mCurY+mCurWidth+3);
406 //} else {
407 // mRect.setEmpty();
408 //}
Jeff Brown8d608662010-08-30 03:02:23 -0700409 if (action == MotionEvent.ACTION_DOWN
410 || (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_DOWN) {
411 final int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
412 >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; // will be 0 for down
413 if (action == MotionEvent.ACTION_DOWN) {
414 for (int p=0; p<NP; p++) {
415 final PointerState ps = mPointers.get(p);
416 ps.clearTrace();
417 ps.mCurDown = false;
418 }
419 mCurDown = true;
Jeff Brown33bbfd22011-02-24 20:55:35 -0800420 mCurNumPointers = 0;
Jeff Brown8d608662010-08-30 03:02:23 -0700421 mMaxNumPointers = 0;
422 mVelocity.clear();
423 }
Jeff Brown33bbfd22011-02-24 20:55:35 -0800424
425 mCurNumPointers += 1;
426 if (mMaxNumPointers < mCurNumPointers) {
427 mMaxNumPointers = mCurNumPointers;
428 }
429
Dianne Hackbornb125dc52010-02-12 15:52:09 -0800430 final int id = event.getPointerId(index);
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800431 while (NP <= id) {
432 PointerState ps = new PointerState();
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800433 mPointers.add(ps);
434 NP++;
435 }
Jeff Brown8d608662010-08-30 03:02:23 -0700436
Jeff Brownd1e0c372010-09-12 19:14:26 -0700437 if (mActivePointerId < 0 ||
Jeff Brown33bbfd22011-02-24 20:55:35 -0800438 !mPointers.get(mActivePointerId).mCurDown) {
Jeff Brownd1e0c372010-09-12 19:14:26 -0700439 mActivePointerId = id;
440 }
441
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800442 final PointerState ps = mPointers.get(id);
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800443 ps.mCurDown = true;
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800444 }
Jeff Browncc0c1592011-02-19 05:07:28 -0800445
Jeff Brown33bbfd22011-02-24 20:55:35 -0800446 final int NI = event.getPointerCount();
Jeff Brown9e2ad362010-07-30 19:20:11 -0700447
448 mVelocity.addMovement(event);
449 mVelocity.computeCurrentVelocity(1);
Jeff Brown33bbfd22011-02-24 20:55:35 -0800450
451 final int N = event.getHistorySize();
452 for (int historyPos = 0; historyPos < N; historyPos++) {
453 for (int i = 0; i < NI; i++) {
454 final int id = event.getPointerId(i);
455 final PointerState ps = mCurDown ? mPointers.get(id) : null;
456 final PointerCoords coords = ps != null ? ps.mCoords : mHoverCoords;
457 event.getHistoricalPointerCoords(i, historyPos, coords);
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800458 if (mPrintCoords) {
Jeff Brown33bbfd22011-02-24 20:55:35 -0800459 logPointerCoords(action, i, coords, id);
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800460 }
Jeff Browncc0c1592011-02-19 05:07:28 -0800461 if (ps != null) {
Jeff Brown33bbfd22011-02-24 20:55:35 -0800462 ps.addTrace(coords.x, coords.y);
Jeff Browncc0c1592011-02-19 05:07:28 -0800463 }
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800464 }
Jeff Brown33bbfd22011-02-24 20:55:35 -0800465 }
466 for (int i = 0; i < NI; i++) {
467 final int id = event.getPointerId(i);
468 final PointerState ps = mCurDown ? mPointers.get(id) : null;
469 final PointerCoords coords = ps != null ? ps.mCoords : mHoverCoords;
Jeff Browncc0c1592011-02-19 05:07:28 -0800470 event.getPointerCoords(i, coords);
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800471 if (mPrintCoords) {
Jeff Brown33bbfd22011-02-24 20:55:35 -0800472 logPointerCoords(action, i, coords, id);
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800473 }
Jeff Browncc0c1592011-02-19 05:07:28 -0800474 if (ps != null) {
475 ps.addTrace(coords.x, coords.y);
476 ps.mXVelocity = mVelocity.getXVelocity(id);
477 ps.mYVelocity = mVelocity.getYVelocity(id);
478 }
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800479 }
Jeff Brown33bbfd22011-02-24 20:55:35 -0800480
Jeff Brown8d608662010-08-30 03:02:23 -0700481 if (action == MotionEvent.ACTION_UP
Jeff Brownd1e0c372010-09-12 19:14:26 -0700482 || action == MotionEvent.ACTION_CANCEL
Jeff Brown8d608662010-08-30 03:02:23 -0700483 || (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP) {
484 final int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
485 >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; // will be 0 for UP
486
Dianne Hackbornb125dc52010-02-12 15:52:09 -0800487 final int id = event.getPointerId(index);
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800488 final PointerState ps = mPointers.get(id);
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800489 ps.mCurDown = false;
Jeff Brownd1e0c372010-09-12 19:14:26 -0700490
491 if (action == MotionEvent.ACTION_UP
492 || action == MotionEvent.ACTION_CANCEL) {
Jeff Brown8d608662010-08-30 03:02:23 -0700493 mCurDown = false;
Jeff Brown33bbfd22011-02-24 20:55:35 -0800494 mCurNumPointers = 0;
Jeff Brown8d608662010-08-30 03:02:23 -0700495 } else {
Jeff Brown33bbfd22011-02-24 20:55:35 -0800496 mCurNumPointers -= 1;
Jeff Brownd1e0c372010-09-12 19:14:26 -0700497 if (mActivePointerId == id) {
498 mActivePointerId = event.getPointerId(index == 0 ? 1 : 0);
499 }
Jeff Brown8d608662010-08-30 03:02:23 -0700500 ps.addTrace(Float.NaN, Float.NaN);
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800501 }
502 }
503
504 //if (mCurDown) {
505 // mRect.union(mCurX-mCurWidth-3, mCurY-mCurWidth-3,
506 // mCurX+mCurWidth+3, mCurY+mCurWidth+3);
507 //}
508 //invalidate(mRect);
509 postInvalidate();
510 }
511 }
512
513 @Override
514 public boolean onTouchEvent(MotionEvent event) {
Jeff Brown33bbfd22011-02-24 20:55:35 -0800515 addPointerEvent(event);
Jeff Brownc3fe7662011-03-07 14:35:59 -0800516
517 if (event.getAction() == MotionEvent.ACTION_DOWN && !isFocused()) {
518 requestFocus();
519 }
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800520 return true;
521 }
Dianne Hackborn7d9af5a2010-03-18 23:40:21 -0700522
523 @Override
Jeff Brown33bbfd22011-02-24 20:55:35 -0800524 public boolean onGenericMotionEvent(MotionEvent event) {
Jeff Brownc3fe7662011-03-07 14:35:59 -0800525 final int source = event.getSource();
526 if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
Jeff Brown33bbfd22011-02-24 20:55:35 -0800527 addPointerEvent(event);
Jeff Brownc3fe7662011-03-07 14:35:59 -0800528 } else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
529 Log.i(TAG, "Joystick: " + event);
530 } else if ((source & InputDevice.SOURCE_CLASS_POSITION) != 0) {
531 Log.i(TAG, "Position: " + event);
532 } else {
533 Log.i(TAG, "Generic: " + event);
534 }
535 return true;
536 }
537
538 @Override
539 public boolean onKeyDown(int keyCode, KeyEvent event) {
540 if (shouldLogKey(keyCode)) {
541 final int repeatCount = event.getRepeatCount();
542 if (repeatCount == 0) {
543 Log.i(TAG, "Key Down: " + event);
544 } else {
545 Log.i(TAG, "Key Repeat #" + repeatCount + ": " + event);
546 }
Jeff Brown33bbfd22011-02-24 20:55:35 -0800547 return true;
548 }
Jeff Brownc3fe7662011-03-07 14:35:59 -0800549 return super.onKeyDown(keyCode, event);
550 }
551
552 @Override
553 public boolean onKeyUp(int keyCode, KeyEvent event) {
554 if (shouldLogKey(keyCode)) {
555 Log.i(TAG, "Key Up: " + event);
556 return true;
557 }
558 return super.onKeyUp(keyCode, event);
559 }
560
561 private static boolean shouldLogKey(int keyCode) {
562 switch (keyCode) {
563 case KeyEvent.KEYCODE_DPAD_UP:
564 case KeyEvent.KEYCODE_DPAD_DOWN:
565 case KeyEvent.KEYCODE_DPAD_LEFT:
566 case KeyEvent.KEYCODE_DPAD_RIGHT:
567 case KeyEvent.KEYCODE_DPAD_CENTER:
568 return true;
569 default:
570 return KeyEvent.isGamepadButton(keyCode)
571 || KeyEvent.isModifierKey(keyCode);
572 }
Jeff Brown33bbfd22011-02-24 20:55:35 -0800573 }
574
575 @Override
Dianne Hackborn7d9af5a2010-03-18 23:40:21 -0700576 public boolean onTrackballEvent(MotionEvent event) {
Jeff Brown8d608662010-08-30 03:02:23 -0700577 Log.i(TAG, "Trackball: " + event);
Jeff Brownc3fe7662011-03-07 14:35:59 -0800578 return true;
Dianne Hackborn7d9af5a2010-03-18 23:40:21 -0700579 }
580
Jeff Brown8d608662010-08-30 03:02:23 -0700581 // HACK
582 // A quick and dirty string builder implementation optimized for GC.
Jeff Brownd1e0c372010-09-12 19:14:26 -0700583 // Using String.format causes the application grind to a halt when
584 // more than a couple of pointers are down due to the number of
585 // temporary objects allocated while formatting strings for drawing or logging.
Jeff Brown8d608662010-08-30 03:02:23 -0700586 private static final class FasterStringBuilder {
587 private char[] mChars;
588 private int mLength;
589
590 public FasterStringBuilder() {
591 mChars = new char[64];
592 }
593
594 public FasterStringBuilder clear() {
595 mLength = 0;
596 return this;
597 }
598
599 public FasterStringBuilder append(String value) {
600 final int valueLength = value.length();
601 final int index = reserve(valueLength);
602 value.getChars(0, valueLength, mChars, index);
603 mLength += valueLength;
604 return this;
605 }
606
607 public FasterStringBuilder append(int value) {
608 return append(value, 0);
609 }
610
611 public FasterStringBuilder append(int value, int zeroPadWidth) {
612 final boolean negative = value < 0;
613 if (negative) {
614 value = - value;
615 if (value < 0) {
616 append("-2147483648");
617 return this;
618 }
619 }
620
621 int index = reserve(11);
622 final char[] chars = mChars;
623
624 if (value == 0) {
625 chars[index++] = '0';
626 mLength += 1;
627 return this;
628 }
629
630 if (negative) {
631 chars[index++] = '-';
632 }
633
634 int divisor = 1000000000;
635 int numberWidth = 10;
636 while (value < divisor) {
637 divisor /= 10;
638 numberWidth -= 1;
639 if (numberWidth < zeroPadWidth) {
640 chars[index++] = '0';
641 }
642 }
643
644 do {
645 int digit = value / divisor;
646 value -= digit * divisor;
647 divisor /= 10;
648 chars[index++] = (char) (digit + '0');
649 } while (divisor != 0);
650
651 mLength = index;
652 return this;
653 }
654
655 public FasterStringBuilder append(float value, int precision) {
656 int scale = 1;
657 for (int i = 0; i < precision; i++) {
658 scale *= 10;
659 }
660 value = (float) (Math.rint(value * scale) / scale);
661
662 append((int) value);
663
664 if (precision != 0) {
665 append(".");
666 value = Math.abs(value);
667 value -= Math.floor(value);
668 append((int) (value * scale), precision);
669 }
670
671 return this;
672 }
673
674 @Override
675 public String toString() {
676 return new String(mChars, 0, mLength);
677 }
678
679 private int reserve(int length) {
680 final int oldLength = mLength;
681 final int newLength = mLength + length;
682 final char[] oldChars = mChars;
683 final int oldCapacity = oldChars.length;
684 if (newLength > oldCapacity) {
685 final int newCapacity = oldCapacity * 2;
686 final char[] newChars = new char[newCapacity];
687 System.arraycopy(oldChars, 0, newChars, 0, oldLength);
688 mChars = newChars;
689 }
690 return oldLength;
691 }
692 }
Dianne Hackborn90d2db32010-02-11 22:19:06 -0800693}