1 /* 2 * Copyright (C) 2007 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 com.android.development; 18 19 import android.app.Activity; 20 import android.content.Context; 21 import android.graphics.Canvas; 22 import android.graphics.Paint; 23 import android.graphics.Paint.FontMetricsInt; 24 import android.os.Bundle; 25 import android.util.Log; 26 import android.view.MotionEvent; 27 import android.view.ViewConfiguration; 28 import android.view.WindowManager; 29 import android.view.VelocityTracker; 30 import android.view.View; 31 32 import java.util.ArrayList; 33 34 /** 35 * Demonstrates wrapping a layout in a ScrollView. 36 * 37 */ 38 public class PointerLocation extends Activity { 39 @Override onCreate(Bundle icicle)40 protected void onCreate(Bundle icicle) { 41 super.onCreate(icicle); 42 setContentView(new MyView(this)); 43 44 // Make the screen full bright for this activity. 45 WindowManager.LayoutParams lp = getWindow().getAttributes(); 46 lp.screenBrightness = 1.0f; 47 getWindow().setAttributes(lp); 48 } 49 50 public static class PointerState { 51 private final ArrayList<Float> mXs = new ArrayList<Float>(); 52 private final ArrayList<Float> mYs = new ArrayList<Float>(); 53 private boolean mCurDown; 54 private int mCurX; 55 private int mCurY; 56 private float mCurPressure; 57 private float mCurSize; 58 private int mCurWidth; 59 private VelocityTracker mVelocity; 60 } 61 62 public class MyView extends View { 63 private final ViewConfiguration mVC; 64 private final Paint mTextPaint; 65 private final Paint mTextBackgroundPaint; 66 private final Paint mTextLevelPaint; 67 private final Paint mPaint; 68 private final Paint mTargetPaint; 69 private final Paint mPathPaint; 70 private final FontMetricsInt mTextMetrics = new FontMetricsInt(); 71 private int mHeaderBottom; 72 private boolean mCurDown; 73 private int mCurNumPointers; 74 private int mMaxNumPointers; 75 private final ArrayList<PointerState> mPointers 76 = new ArrayList<PointerState>(); 77 MyView(Context c)78 public MyView(Context c) { 79 super(c); 80 mVC = ViewConfiguration.get(c); 81 mTextPaint = new Paint(); 82 mTextPaint.setAntiAlias(true); 83 mTextPaint.setTextSize(10 84 * getResources().getDisplayMetrics().density); 85 mTextPaint.setARGB(255, 0, 0, 0); 86 mTextBackgroundPaint = new Paint(); 87 mTextBackgroundPaint.setAntiAlias(false); 88 mTextBackgroundPaint.setARGB(128, 255, 255, 255); 89 mTextLevelPaint = new Paint(); 90 mTextLevelPaint.setAntiAlias(false); 91 mTextLevelPaint.setARGB(192, 255, 0, 0); 92 mPaint = new Paint(); 93 mPaint.setAntiAlias(true); 94 mPaint.setARGB(255, 255, 255, 255); 95 mPaint.setStyle(Paint.Style.STROKE); 96 mPaint.setStrokeWidth(2); 97 mTargetPaint = new Paint(); 98 mTargetPaint.setAntiAlias(false); 99 mTargetPaint.setARGB(255, 0, 0, 192); 100 mPathPaint = new Paint(); 101 mPathPaint.setAntiAlias(false); 102 mPathPaint.setARGB(255, 0, 96, 255); 103 mPaint.setStyle(Paint.Style.STROKE); 104 mPaint.setStrokeWidth(1); 105 106 PointerState ps = new PointerState(); 107 ps.mVelocity = VelocityTracker.obtain(); 108 mPointers.add(ps); 109 } 110 111 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)112 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 113 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 114 mTextPaint.getFontMetricsInt(mTextMetrics); 115 mHeaderBottom = -mTextMetrics.ascent+mTextMetrics.descent+2; 116 Log.i("foo", "Metrics: ascent=" + mTextMetrics.ascent 117 + " descent=" + mTextMetrics.descent 118 + " leading=" + mTextMetrics.leading 119 + " top=" + mTextMetrics.top 120 + " bottom=" + mTextMetrics.bottom); 121 } 122 123 @Override onDraw(Canvas canvas)124 protected void onDraw(Canvas canvas) { 125 final int w = getWidth(); 126 final int itemW = w/7; 127 final int base = -mTextMetrics.ascent+1; 128 final int bottom = mHeaderBottom; 129 130 final int NP = mPointers.size(); 131 132 if (NP > 0) { 133 final PointerState ps = mPointers.get(0); 134 canvas.drawRect(0, 0, itemW-1, bottom,mTextBackgroundPaint); 135 canvas.drawText("P: " + mCurNumPointers + " / " + mMaxNumPointers, 136 1, base, mTextPaint); 137 138 final int N = ps.mXs.size(); 139 if ((mCurDown && ps.mCurDown) || N == 0) { 140 canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom, mTextBackgroundPaint); 141 canvas.drawText("X: " + ps.mCurX, 1 + itemW, base, mTextPaint); 142 canvas.drawRect(itemW * 2, 0, (itemW * 3) - 1, bottom, mTextBackgroundPaint); 143 canvas.drawText("Y: " + ps.mCurY, 1 + itemW * 2, base, mTextPaint); 144 } else { 145 float dx = ps.mXs.get(N-1) - ps.mXs.get(0); 146 float dy = ps.mYs.get(N-1) - ps.mYs.get(0); 147 canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom, 148 Math.abs(dx) < mVC.getScaledTouchSlop() 149 ? mTextBackgroundPaint : mTextLevelPaint); 150 canvas.drawText("dX: " + String.format("%.1f", dx), 1 + itemW, base, mTextPaint); 151 canvas.drawRect(itemW * 2, 0, (itemW * 3) - 1, bottom, 152 Math.abs(dy) < mVC.getScaledTouchSlop() 153 ? mTextBackgroundPaint : mTextLevelPaint); 154 canvas.drawText("dY: " + String.format("%.1f", dy), 1 + itemW * 2, base, mTextPaint); 155 } 156 157 canvas.drawRect(itemW * 3, 0, (itemW * 4) - 1, bottom, mTextBackgroundPaint); 158 int velocity = ps.mVelocity == null ? 0 : (int) (ps.mVelocity.getXVelocity() * 1000); 159 canvas.drawText("Xv: " + velocity, 1 + itemW * 3, base, mTextPaint); 160 161 canvas.drawRect(itemW * 4, 0, (itemW * 5) - 1, bottom, mTextBackgroundPaint); 162 velocity = ps.mVelocity == null ? 0 : (int) (ps.mVelocity.getYVelocity() * 1000); 163 canvas.drawText("Yv: " + velocity, 1 + itemW * 4, base, mTextPaint); 164 165 canvas.drawRect(itemW * 5, 0, (itemW * 6) - 1, bottom, mTextBackgroundPaint); 166 canvas.drawRect(itemW * 5, 0, (itemW * 5) + (ps.mCurPressure * itemW) - 1, 167 bottom, mTextLevelPaint); 168 canvas.drawText("Prs: " + String.format("%.2f", ps.mCurPressure), 1 + itemW * 5, 169 base, mTextPaint); 170 171 canvas.drawRect(itemW * 6, 0, w, bottom, mTextBackgroundPaint); 172 canvas.drawRect(itemW * 6, 0, (itemW * 6) + (ps.mCurSize * itemW) - 1, 173 bottom, mTextLevelPaint); 174 canvas.drawText("Size: " + String.format("%.2f", ps.mCurSize), 1 + itemW * 6, 175 base, mTextPaint); 176 } 177 178 for (int p=0; p<NP; p++) { 179 final PointerState ps = mPointers.get(p); 180 181 if (mCurDown && ps.mCurDown) { 182 canvas.drawLine(0, (int)ps.mCurY, getWidth(), (int)ps.mCurY, mTargetPaint); 183 canvas.drawLine((int)ps.mCurX, 0, (int)ps.mCurX, getHeight(), mTargetPaint); 184 int pressureLevel = (int)(ps.mCurPressure*255); 185 mPaint.setARGB(255, pressureLevel, 128, 255-pressureLevel); 186 canvas.drawPoint(ps.mCurX, ps.mCurY, mPaint); 187 canvas.drawCircle(ps.mCurX, ps.mCurY, ps.mCurWidth, mPaint); 188 } 189 } 190 191 for (int p=0; p<NP; p++) { 192 final PointerState ps = mPointers.get(p); 193 194 final int N = ps.mXs.size(); 195 float lastX=0, lastY=0; 196 boolean haveLast = false; 197 boolean drawn = false; 198 mPaint.setARGB(255, 128, 255, 255); 199 for (int i=0; i<N; i++) { 200 float x = ps.mXs.get(i); 201 float y = ps.mYs.get(i); 202 if (Float.isNaN(x)) { 203 haveLast = false; 204 continue; 205 } 206 if (haveLast) { 207 canvas.drawLine(lastX, lastY, x, y, mPathPaint); 208 canvas.drawPoint(lastX, lastY, mPaint); 209 drawn = true; 210 } 211 lastX = x; 212 lastY = y; 213 haveLast = true; 214 } 215 216 if (drawn) { 217 if (ps.mVelocity != null) { 218 mPaint.setARGB(255, 255, 64, 128); 219 float xVel = ps.mVelocity.getXVelocity() * (1000/60); 220 float yVel = ps.mVelocity.getYVelocity() * (1000/60); 221 canvas.drawLine(lastX, lastY, lastX+xVel, lastY+yVel, mPaint); 222 } else { 223 canvas.drawPoint(lastX, lastY, mPaint); 224 } 225 } 226 } 227 } 228 229 @Override onTouchEvent(MotionEvent event)230 public boolean onTouchEvent(MotionEvent event) { 231 int action = event.getAction(); 232 233 //Log.i("Pointer", "Motion: action=0x" + Integer.toHexString(action) 234 // + " pointers=" + event.getPointerCount()); 235 236 int NP = mPointers.size(); 237 238 //mRect.set(0, 0, getWidth(), mHeaderBottom+1); 239 //invalidate(mRect); 240 //if (mCurDown) { 241 // mRect.set(mCurX-mCurWidth-3, mCurY-mCurWidth-3, 242 // mCurX+mCurWidth+3, mCurY+mCurWidth+3); 243 //} else { 244 // mRect.setEmpty(); 245 //} 246 if (action == MotionEvent.ACTION_DOWN) { 247 for (int p=0; p<NP; p++) { 248 final PointerState ps = mPointers.get(p); 249 ps.mXs.clear(); 250 ps.mYs.clear(); 251 ps.mVelocity = VelocityTracker.obtain(); 252 ps.mCurDown = false; 253 } 254 mPointers.get(0).mCurDown = true; 255 mMaxNumPointers = 0; 256 Log.i("Pointer", "Pointer 1: DOWN"); 257 } 258 259 if ((action&MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_DOWN) { 260 final int id = (action&MotionEvent.ACTION_POINTER_ID_MASK) 261 >> MotionEvent.ACTION_POINTER_ID_SHIFT; 262 while (NP <= id) { 263 PointerState ps = new PointerState(); 264 ps.mVelocity = VelocityTracker.obtain(); 265 mPointers.add(ps); 266 NP++; 267 } 268 final PointerState ps = mPointers.get(id); 269 ps.mVelocity = VelocityTracker.obtain(); 270 ps.mCurDown = true; 271 Log.i("Pointer", "Pointer " + (id+1) + ": DOWN"); 272 } 273 274 final int NI = event.getPointerCount(); 275 276 mCurDown = action != MotionEvent.ACTION_UP 277 && action != MotionEvent.ACTION_CANCEL; 278 mCurNumPointers = mCurDown ? NI : 0; 279 if (mMaxNumPointers < mCurNumPointers) { 280 mMaxNumPointers = mCurNumPointers; 281 } 282 283 for (int i=0; i<NI; i++) { 284 final PointerState ps = mPointers.get(event.getPointerId(i)); 285 ps.mVelocity.addMovement(event); 286 ps.mVelocity.computeCurrentVelocity(1); 287 final int N = event.getHistorySize(); 288 for (int j=0; j<N; j++) { 289 Log.i("Pointer", "Pointer " + (i+1) + ": (" 290 + event.getHistoricalX(i, j) 291 + ", " + event.getHistoricalY(i, j) + ")" 292 + " Prs=" + event.getHistoricalPressure(i, j) 293 + " Size=" + event.getHistoricalSize(i, j)); 294 ps.mXs.add(event.getHistoricalX(i, j)); 295 ps.mYs.add(event.getHistoricalY(i, j)); 296 } 297 Log.i("Pointer", "Pointer " + (i+1) + ": (" 298 + event.getX(i) + ", " + event.getY(i) + ")" 299 + " Prs=" + event.getPressure(i) 300 + " Size=" + event.getSize(i)); 301 ps.mXs.add(event.getX(i)); 302 ps.mYs.add(event.getY(i)); 303 ps.mCurX = (int)event.getX(i); 304 ps.mCurY = (int)event.getY(i); 305 //Log.i("Pointer", "Pointer #" + p + ": (" + ps.mCurX 306 // + "," + ps.mCurY + ")"); 307 ps.mCurPressure = event.getPressure(i); 308 ps.mCurSize = event.getSize(i); 309 ps.mCurWidth = (int)(ps.mCurSize*(getWidth()/3)); 310 } 311 312 if ((action&MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP) { 313 final int id = (action&MotionEvent.ACTION_POINTER_ID_MASK) 314 >> MotionEvent.ACTION_POINTER_ID_SHIFT; 315 final PointerState ps = mPointers.get(id); 316 ps.mXs.add(Float.NaN); 317 ps.mYs.add(Float.NaN); 318 ps.mCurDown = false; 319 Log.i("Pointer", "Pointer " + (id+1) + ": UP"); 320 } 321 322 if (action == MotionEvent.ACTION_UP) { 323 for (int i=0; i<NI; i++) { 324 final PointerState ps = mPointers.get(event.getPointerId(i)); 325 if (ps.mCurDown) { 326 ps.mCurDown = false; 327 Log.i("Pointer", "Pointer " + (i+1) + ": UP"); 328 } 329 } 330 } 331 332 //if (mCurDown) { 333 // mRect.union(mCurX-mCurWidth-3, mCurY-mCurWidth-3, 334 // mCurX+mCurWidth+3, mCurY+mCurWidth+3); 335 //} 336 //invalidate(mRect); 337 invalidate(); 338 return true; 339 } 340 341 } 342 } 343