├── res ├── drawable-hdpi │ └── ic_launcher.png ├── drawable-ldpi │ └── ic_launcher.png ├── drawable-mdpi │ └── ic_launcher.png ├── values │ └── strings.xml └── layout │ └── main.xml ├── README.md ├── .classpath ├── project.properties ├── AndroidManifest.xml ├── .project ├── proguard.cfg └── src └── pl └── polidea └── gesturedetector ├── BetterGestureDetectorActivity.java └── BetterGestureDetector.java /res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polidea/better-gesture-detector/HEAD/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-ldpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polidea/better-gesture-detector/HEAD/res/drawable-ldpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polidea/better-gesture-detector/HEAD/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | __This repository is no longer maintained. Issue reports and pull requests will not be attended.__ 4 | 5 | --- 6 | 7 | Gesture detector for Android. It'sbetter than the default one. It's configurable and reports more events. 8 | -------------------------------------------------------------------------------- /res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Hello World, BetterGestureDetectorActivity! 5 | BetterGestureDetector 6 | 7 | -------------------------------------------------------------------------------- /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /project.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system use, 7 | # "ant.properties", and override values to adapt the script to your 8 | # project structure. 9 | 10 | # Project target. 11 | target=android-14 12 | android.library=false 13 | -------------------------------------------------------------------------------- /res/layout/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 18 | 19 | -------------------------------------------------------------------------------- /AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | BetterGestureDetector 4 | 5 | 6 | 7 | 8 | 9 | com.android.ide.eclipse.adt.ResourceManagerBuilder 10 | 11 | 12 | 13 | 14 | com.android.ide.eclipse.adt.PreCompilerBuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.jdt.core.javabuilder 20 | 21 | 22 | 23 | 24 | com.android.ide.eclipse.adt.ApkBuilder 25 | 26 | 27 | 28 | 29 | 30 | com.android.ide.eclipse.adt.AndroidNature 31 | org.eclipse.jdt.core.javanature 32 | 33 | 34 | -------------------------------------------------------------------------------- /proguard.cfg: -------------------------------------------------------------------------------- 1 | -optimizationpasses 5 2 | -dontusemixedcaseclassnames 3 | -dontskipnonpubliclibraryclasses 4 | -dontpreverify 5 | -verbose 6 | -optimizations !code/simplification/arithmetic,!field/*,!class/merging/* 7 | 8 | -keep public class * extends android.app.Activity 9 | -keep public class * extends android.app.Application 10 | -keep public class * extends android.app.Service 11 | -keep public class * extends android.content.BroadcastReceiver 12 | -keep public class * extends android.content.ContentProvider 13 | -keep public class * extends android.app.backup.BackupAgentHelper 14 | -keep public class * extends android.preference.Preference 15 | -keep public class com.android.vending.licensing.ILicensingService 16 | 17 | -keepclasseswithmembernames class * { 18 | native ; 19 | } 20 | 21 | -keepclasseswithmembers class * { 22 | public (android.content.Context, android.util.AttributeSet); 23 | } 24 | 25 | -keepclasseswithmembers class * { 26 | public (android.content.Context, android.util.AttributeSet, int); 27 | } 28 | 29 | -keepclassmembers class * extends android.app.Activity { 30 | public void *(android.view.View); 31 | } 32 | 33 | -keepclassmembers enum * { 34 | public static **[] values(); 35 | public static ** valueOf(java.lang.String); 36 | } 37 | 38 | -keep class * implements android.os.Parcelable { 39 | public static final android.os.Parcelable$Creator *; 40 | } 41 | -------------------------------------------------------------------------------- /src/pl/polidea/gesturedetector/BetterGestureDetectorActivity.java: -------------------------------------------------------------------------------- 1 | package pl.polidea.gesturedetector; 2 | 3 | import pl.polidea.gesturedetector.BetterGestureDetector.BetterGestureListener; 4 | import pl.polidea.test.R; 5 | import android.app.Activity; 6 | import android.os.Bundle; 7 | import android.view.MotionEvent; 8 | import android.widget.TextView; 9 | 10 | public class BetterGestureDetectorActivity extends Activity implements BetterGestureListener { 11 | private BetterGestureDetector gestureDetector; 12 | private TextView v; 13 | 14 | /** Called when the activity is first created. */ 15 | @Override 16 | public void onCreate(Bundle savedInstanceState) { 17 | super.onCreate(savedInstanceState); 18 | setContentView(R.layout.main); 19 | v = (TextView) findViewById(R.id.textView1); 20 | gestureDetector = new BetterGestureDetector(this); 21 | } 22 | 23 | @Override 24 | public boolean dispatchTouchEvent(MotionEvent ev) { 25 | gestureDetector.onTouchEvent(ev); 26 | return super.dispatchTouchEvent(ev); 27 | } 28 | 29 | @Override 30 | public void onPress(MotionEvent motionEvent) { 31 | v.setText("press"); 32 | v.setBackgroundColor(0xffff0000); 33 | v.invalidate(); 34 | } 35 | 36 | @Override 37 | public void onTap(MotionEvent motionEvent) { 38 | v.setText("tap"); 39 | v.setBackgroundColor(0xff00ff00); 40 | v.invalidate(); 41 | } 42 | 43 | @Override 44 | public void onDrag(MotionEvent motionEvent) { 45 | v.setText("drag"); 46 | v.setBackgroundColor(0xff0000ff); 47 | v.invalidate(); 48 | } 49 | 50 | @Override 51 | public void onMove(MotionEvent motionEvent) { 52 | v.setText("move"); 53 | v.setBackgroundColor(0xffff00ff); 54 | v.invalidate(); 55 | } 56 | 57 | @Override 58 | public void onRelease(MotionEvent motionEvent) { 59 | v.setText("release"); 60 | v.setBackgroundColor(0xffffff00); 61 | v.invalidate(); 62 | } 63 | 64 | @Override 65 | public void onLongPress(MotionEvent motionEvent) { 66 | v.setText("longpress"); 67 | v.setBackgroundColor(0xff00ffff); 68 | v.invalidate(); 69 | } 70 | 71 | @Override 72 | public void onMultiTap(MotionEvent motionEvent, int clicks) { 73 | v.setText("multitap [" + clicks + "]"); 74 | v.setBackgroundColor(0xff7f7f7f); 75 | v.invalidate(); 76 | } 77 | } -------------------------------------------------------------------------------- /src/pl/polidea/gesturedetector/BetterGestureDetector.java: -------------------------------------------------------------------------------- 1 | package pl.polidea.gesturedetector; 2 | 3 | import android.os.Handler; 4 | import android.util.Log; 5 | import android.view.MotionEvent; 6 | 7 | // TODO: Auto-generated Javadoc 8 | /** 9 | * The Class BetterGestureDetector. 10 | */ 11 | public class BetterGestureDetector { 12 | 13 | /** 14 | * The listener interface for receiving betterGesture events. The class that 15 | * is interested in processing a betterGesture event implements this 16 | * interface, and the object created with that class is registered with a 17 | * component using the component's 18 | * addBetterGestureListener method. When 19 | * the betterGesture event occurs, that object's appropriate 20 | * method is invoked. 21 | * 22 | * @see BetterGestureEvent 23 | */ 24 | public interface BetterGestureListener { 25 | 26 | /** 27 | * On show press. 28 | * 29 | * @param motionEvent 30 | * the motion event 31 | */ 32 | void onPress(MotionEvent motionEvent); 33 | 34 | /** 35 | * On single tap up. 36 | * 37 | * @param motionEvent 38 | * the motion event 39 | */ 40 | void onTap(MotionEvent motionEvent); 41 | 42 | /** 43 | * On drag. 44 | * 45 | * @param motionEvent 46 | * the motion event 47 | */ 48 | void onDrag(MotionEvent motionEvent); 49 | 50 | /** 51 | * On move. 52 | * 53 | * @param motionEvent 54 | * the motion event 55 | */ 56 | void onMove(MotionEvent motionEvent); 57 | 58 | /** 59 | * On release. 60 | * 61 | * @param motionEvent 62 | * the motion event 63 | */ 64 | void onRelease(MotionEvent motionEvent); 65 | 66 | /** 67 | * On long press. 68 | * 69 | * @param motionEvent 70 | * the motion event 71 | */ 72 | void onLongPress(MotionEvent motionEvent); 73 | 74 | /** 75 | * On double tap. 76 | * 77 | * @param motionEvent 78 | * the motion event 79 | * @param clicks 80 | */ 81 | void onMultiTap(MotionEvent motionEvent, int clicks); 82 | } 83 | 84 | /** 85 | * The Class BetterGestureAdapter. 86 | */ 87 | class BetterGestureAdapter implements BetterGestureListener { 88 | 89 | /* 90 | * (non-Javadoc) 91 | * 92 | * @see 93 | * pl.polidea.test.BetterGestureDetector.BetterGestureListener#onShowPress 94 | * () 95 | */ 96 | @Override 97 | public void onPress(MotionEvent motionEvent) { 98 | // TODO Auto-generated method stub 99 | 100 | } 101 | 102 | /* 103 | * (non-Javadoc) 104 | * 105 | * @see 106 | * pl.polidea.test.BetterGestureDetector.BetterGestureListener#onSingleTapUp 107 | * () 108 | */ 109 | @Override 110 | public void onTap(MotionEvent motionEvent) { 111 | // TODO Auto-generated method stub 112 | 113 | } 114 | 115 | /* 116 | * (non-Javadoc) 117 | * 118 | * @see 119 | * pl.polidea.test.BetterGestureDetector.BetterGestureListener#onDrag() 120 | */ 121 | @Override 122 | public void onDrag(MotionEvent motionEvent) { 123 | // TODO Auto-generated method stub 124 | 125 | } 126 | 127 | /* 128 | * (non-Javadoc) 129 | * 130 | * @see 131 | * pl.polidea.test.BetterGestureDetector.BetterGestureListener#onMove() 132 | */ 133 | @Override 134 | public void onMove(MotionEvent motionEvent) { 135 | // TODO Auto-generated method stub 136 | 137 | } 138 | 139 | /* 140 | * (non-Javadoc) 141 | * 142 | * @see 143 | * pl.polidea.test.BetterGestureDetector.BetterGestureListener#onRelease 144 | * () 145 | */ 146 | @Override 147 | public void onRelease(MotionEvent motionEvent) { 148 | // TODO Auto-generated method stub 149 | 150 | } 151 | 152 | /* 153 | * (non-Javadoc) 154 | * 155 | * @see 156 | * pl.polidea.gesturedetector.BetterGestureDetector.BetterGestureListener 157 | * #onLongPress(android.view.MotionEvent) 158 | */ 159 | @Override 160 | public void onLongPress(MotionEvent motionEvent) { 161 | // TODO Auto-generated method stub 162 | 163 | } 164 | 165 | @Override 166 | public void onMultiTap(MotionEvent motionEvent, int clicks) { 167 | // TODO Auto-generated method stub 168 | 169 | } 170 | 171 | } 172 | 173 | /** The Constant DEFAULT_PRESS_TIMEOUT. */ 174 | private static final int DEFAULT_PRESS_TIMEOUT = 100; 175 | 176 | /** The Constant DEFAULT_TAP_TIMEOUT. */ 177 | private static final int DEFAULT_TAP_TIMEOUT = 300; 178 | 179 | /** The Constant DEFAULT_MOVE_EPSILON. */ 180 | private static final int DEFAULT_MOVE_EPSILON = 4; 181 | 182 | /** The press timeout. */ 183 | private int pressTimeout = DEFAULT_PRESS_TIMEOUT; 184 | 185 | /** The tap timeout. */ 186 | private int tapTimeout = DEFAULT_TAP_TIMEOUT; 187 | 188 | /** The move epsilon. */ 189 | private int moveEpsilon = DEFAULT_MOVE_EPSILON; 190 | 191 | /** The handler. */ 192 | private Handler handler; 193 | 194 | /** The tap handler. */ 195 | Runnable tapHandler; 196 | 197 | /** The press handler. */ 198 | Runnable pressHandler; 199 | 200 | /** The listener. */ 201 | private final BetterGestureListener listener; 202 | 203 | /** The prev touch time. */ 204 | private long prevTouchTime; 205 | 206 | /** The prev touch y. */ 207 | private float prevTouchY; 208 | 209 | /** The prev touch x. */ 210 | private float prevTouchX; 211 | 212 | /** The vertical move cancel. */ 213 | boolean verticalMoveCancel = true; 214 | 215 | /** The horizontal move cancel. */ 216 | boolean horizontalMoveCancel = true; 217 | /** The dragging. */ 218 | private boolean dragging; 219 | 220 | /** The moving. */ 221 | private boolean moving; 222 | 223 | /** The long press handler. */ 224 | private Runnable longPressHandler; 225 | 226 | /** The long press timeout. */ 227 | private long longPressTimeout = 500; 228 | 229 | private int clicks = 0; 230 | 231 | /** 232 | * Instantiates a new better gesture detector. 233 | * 234 | * @param listener 235 | * the listener 236 | */ 237 | public BetterGestureDetector(BetterGestureListener listener) { 238 | if (listener == null) { 239 | throw new NullPointerException("Listener cannot be null"); 240 | } 241 | this.listener = listener; 242 | handler = new Handler(); 243 | } 244 | 245 | /** 246 | * On touch event. 247 | * 248 | * @param motionEvent 249 | * the motion event 250 | */ 251 | public void onTouchEvent(final MotionEvent motionEvent) { 252 | float dx = motionEvent.getX() - prevTouchX; 253 | float dy = motionEvent.getY() - prevTouchY; 254 | long dt = System.currentTimeMillis() - prevTouchTime; 255 | handler.removeCallbacks(longPressHandler); 256 | longPressHandler = null; 257 | 258 | switch (motionEvent.getAction()) { 259 | case MotionEvent.ACTION_DOWN: 260 | dragging = false; 261 | moving = false; 262 | if (tapHandler != null) { 263 | clicks++; 264 | Log.e("clicks", "" + clicks); 265 | handler.removeCallbacks(tapHandler); 266 | tapHandler = null; 267 | } 268 | 269 | handler.removeCallbacks(pressHandler); 270 | pressHandler = new Runnable() { 271 | 272 | @Override 273 | public void run() { 274 | // handler.removeCallbacks(tapHandler); 275 | // tapHandler = null; 276 | onPress(motionEvent); 277 | } 278 | 279 | }; 280 | handler.postDelayed(pressHandler, pressTimeout); 281 | 282 | longPressHandler = new Runnable() { 283 | 284 | @Override 285 | public void run() { 286 | onLongPress(motionEvent); 287 | } 288 | }; 289 | handler.postDelayed(longPressHandler, getLongPressTimeout()); 290 | prevTouchTime += dt; 291 | break; 292 | case MotionEvent.ACTION_UP: 293 | if (dt > tapTimeout || moving || dragging) { 294 | onRelease(motionEvent); 295 | break; 296 | } 297 | 298 | tapHandler = new Runnable() { 299 | 300 | @Override 301 | public void run() { 302 | handler.removeCallbacks(longPressHandler); 303 | longPressHandler = null; 304 | onTap(motionEvent, clicks); 305 | } 306 | }; 307 | handler.postDelayed(tapHandler, tapTimeout - dt); 308 | 309 | prevTouchTime += dt; 310 | break; 311 | 312 | case MotionEvent.ACTION_MOVE: 313 | if (Math.abs(dx) > moveEpsilon || Math.abs(dy) > moveEpsilon || dragging || moving) { 314 | if (pressHandler == null && !moving) { 315 | dragging = true; 316 | } 317 | handler.removeCallbacks(tapHandler); 318 | tapHandler = null; 319 | handler.removeCallbacks(pressHandler); 320 | pressHandler = null; 321 | handler.removeCallbacks(longPressHandler); 322 | longPressHandler = null; 323 | moving = true; 324 | if (dragging) { 325 | onDrag(motionEvent); 326 | } else { 327 | onMove(motionEvent); 328 | } 329 | } 330 | break; 331 | case MotionEvent.ACTION_CANCEL: 332 | handler.removeCallbacks(pressHandler); 333 | pressHandler = null; 334 | handler.removeCallbacks(tapHandler); 335 | tapHandler = null; 336 | handler.removeCallbacks(longPressHandler); 337 | longPressHandler = null; 338 | onRelease(motionEvent); 339 | } 340 | prevTouchX = motionEvent.getX(); 341 | prevTouchY = motionEvent.getY(); 342 | } 343 | 344 | /** 345 | * On release. 346 | * 347 | * @param motionEvent 348 | * the motion event 349 | */ 350 | private void onRelease(MotionEvent motionEvent) { 351 | Log.e("gesture", "release"); 352 | clicks = 0; 353 | listener.onRelease(motionEvent); 354 | } 355 | 356 | /** 357 | * On drag. 358 | * 359 | * @param motionEvent 360 | * the motion event 361 | */ 362 | private void onDrag(MotionEvent motionEvent) { 363 | Log.e("gesture", "drag"); 364 | clicks = 0; 365 | listener.onDrag(motionEvent); 366 | } 367 | 368 | /** 369 | * On move. 370 | * 371 | * @param motionEvent 372 | * the motion event 373 | */ 374 | private void onMove(MotionEvent motionEvent) { 375 | Log.e("gesture", "move"); 376 | clicks = 0; 377 | listener.onMove(motionEvent); 378 | } 379 | 380 | /** 381 | * On single tap up. 382 | * 383 | * @param motionEvent 384 | * the motion event 385 | */ 386 | protected void onTap(MotionEvent motionEvent, int clicks) { 387 | tapHandler = null; 388 | if (clicks == 0) { 389 | Log.e("gesture", "tap"); 390 | listener.onTap(motionEvent); 391 | } else { 392 | Log.e("gesture", "multitap"); 393 | listener.onMultiTap(motionEvent, clicks + 1); 394 | } 395 | this.clicks = 0; 396 | } 397 | 398 | /** 399 | * On long press. 400 | * 401 | * @param motionEvent 402 | * the motion event 403 | */ 404 | protected void onLongPress(MotionEvent motionEvent) { 405 | Log.e("gesture", "longpress"); 406 | clicks = 0; 407 | longPressHandler = null; 408 | listener.onLongPress(motionEvent); 409 | } 410 | 411 | /** 412 | * On show press. 413 | * 414 | * @param motionEvent 415 | * the motion event 416 | */ 417 | protected void onPress(MotionEvent motionEvent) { 418 | Log.e("gesture", "press"); 419 | pressHandler = null; 420 | listener.onPress(motionEvent); 421 | } 422 | 423 | /** 424 | * Gets the press timeout. 425 | * 426 | * @return the press timeout 427 | */ 428 | public int getPressTimeout() { 429 | return pressTimeout; 430 | } 431 | 432 | /** 433 | * Sets the press timeout. 434 | * 435 | * @param pressTimeout 436 | * the new press timeout 437 | */ 438 | public void setPressTimeout(int pressTimeout) { 439 | this.pressTimeout = pressTimeout; 440 | } 441 | 442 | /** 443 | * Gets the tap timeout. 444 | * 445 | * @return the tap timeout 446 | */ 447 | public int getTapTimeout() { 448 | return tapTimeout; 449 | } 450 | 451 | /** 452 | * Sets the tap timeout. 453 | * 454 | * @param tapTimeout 455 | * the new tap timeout 456 | */ 457 | public void setTapTimeout(int tapTimeout) { 458 | this.tapTimeout = tapTimeout; 459 | } 460 | 461 | /** 462 | * Gets the move epsilon. 463 | * 464 | * @return the move epsilon 465 | */ 466 | public int getMoveEpsilon() { 467 | return moveEpsilon; 468 | } 469 | 470 | /** 471 | * Sets the move epsilon. 472 | * 473 | * @param moveEpsilon 474 | * the new move epsilon 475 | */ 476 | public void setMoveEpsilon(int moveEpsilon) { 477 | this.moveEpsilon = moveEpsilon; 478 | } 479 | 480 | /** 481 | * Gets the long press timeout. 482 | * 483 | * @return the long press timeout 484 | */ 485 | public long getLongPressTimeout() { 486 | return longPressTimeout; 487 | } 488 | 489 | /** 490 | * Sets the long press timeout. 491 | * 492 | * @param longPressTimeout 493 | * the new long press timeout 494 | */ 495 | public void setLongPressTimeout(long longPressTimeout) { 496 | this.longPressTimeout = longPressTimeout; 497 | } 498 | } 499 | --------------------------------------------------------------------------------