I am developing a kiosk and now in admin side. In order to go to the Admin, the user needs to tap the screen 5 times just in 3 seconds or else, nothing will happen.
5 Answers
Please read the comments in the code, it is quite straightforward
import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;
public class MainActivity extends Activity {
private int tapCount = 0;
private long tapCounterStartMillis = 0;
//detect any touch event in the screen (instead of an specific view)
@Override
public boolean onTouchEvent(MotionEvent event) {
int eventaction = event.getAction();
if (eventaction == MotionEvent.ACTION_UP) {
//get system current milliseconds
long time= System.currentTimeMillis();
//if it is the first time, or if it has been more than 3 seconds since the first tap ( so it is like a new try), we reset everything
if (tapCounterStartMillis == 0 || (time-tapCounterStartMillis > 3000) ) {
tapCounterStartMillis = time;
tapCount = 1;
}
//it is not the first, and it has been less than 3 seconds since the first
else{ // time-tapCounterStartMillis < 3000
tapCount ++;
}
if (tapCount == 5) {
//do whatever you need
}
return true;
}
return false;
}

- 2,585
- 3
- 24
- 31

- 10,828
- 3
- 41
- 60
-
1Awesome Carlos, this method is very simple and nice, no need to use handlers. – Jose Ricardo Citerio Alcala Feb 03 '17 at 02:13
-
3In case anyone wants to use this same code on a View, use `setOnClickListener()` instead and will work. You may save `count` and `startMillis` in an object with `setTag()` to avoid a global scope. – saiyancoder Dec 06 '18 at 03:30
-
Could you expand on using this on a View? I want to make it when a logo is tapped 5 times in 3 seconds, not anywhere on the screen. – DJFriar Jan 13 '19 at 05:36
My solution is similar to Andres's. The countdown starts when you lift the finger for the first time, that is, when I consider the tap to be finished. This is similar to clicks, a click occurs when you release the mouse button. After 3 seconds from the first lift, the counter is reset. Andres's approch, on the other side, uses logic based on placing the finger down onto the screen. It also uses an additional thread.
My logic is one of the many possible ones. Another reasonable approach would be to detect 5 consecutive taps within 3 seconds in a stream of taps. Consider:
tap1, 2000ms, tap2, 500ms, tap3, 550ms, tap4, 10ms, tap5, 10ms, tap6.
The second through the sixth tap encompasses a set of five taps in less than 3 seconds; in my approach this would not be detected. To detect this you can use a FIFO queue of fixed size 5 and remember the last 5 timestamps: this sequence is increasing. When you receive a new tap, you check whether 1) there are at least 5 taps occurred, and 2) the oldest timestamp is no older then 3 seconds old.
Anyway, back to the first logic, place this code in an Activity
:
private int mCounter = 0;
private Handler mHandler = new Handler();
private Runnable mResetCounter = new Runnable() {
@Override
public void run() {
mCounter = 0;
}
};
@Override
public boolean onTouchEvent(MotionEvent event) {
switch(MotionEventCompat.getActionMasked(event)) {
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (mCounter == 0)
mHandler.postDelayed(mResetCounter, 3000);
mCounter++;
if (mCounter == 5){
mHandler.removeCallbacks(mResetCounter);
mCounter = 0;
Toast.makeText(this, "Five taps in three seconds", Toast.LENGTH_SHORT).show();
}
return false;
default :
return super.onTouchEvent(event);
}
}
Note: you probably want some state retention on configuration changes too. As a mathematician would say, I let that as an exercise to the reader

- 1
- 1

- 3,542
- 2
- 25
- 37
Override your Activity onTouchEvent()
method to receive the touch events from the screen. Every time the user taps the screen increment a variable and postpone a Runnable in 3 seconds if it's the first time you touch, if 3 seconds passes the touch events will be cleared and nothing happens. A Thread checks for the touch events number to be 5 or more, if they happened before the 3 seconds the variable is not cleared and the if(touchEvent >= 5)
condition is true. I haven't tested it! But it's completely asynchronous :)
// Touch events on screen
@Override
public boolean onTouchEvent(MotionEvent event) {
// User pressed the screen
if(event.getAction() == MotionEvent.ACTION_DOWN){
if(touchEvent == 0) myView.post(mRunnable, 3000); // Execute a Runnable in 3 seconds
++touchEvent;
}
return false;
}
Runnable mRunnable = new Runnable(){
@Override
public void run() {
touchEvent = 0; // 3 seconds passed, clear touch events
}
}
Thread mThread = new Thread(new Runnable(){
@Override
public void run(){
if(touchEvent >= 5){
// Touched 5 times in 3 seconds or less, CARE this is not UI Thread!
}
}
});
mThread.start();

- 6,080
- 13
- 60
- 110
If you want to detect a touch on a view in KOTLIN
private var count = 0
private var startMillis: Long = 0
textView.setOnTouchListener { v, event ->
if (event.action == MotionEvent.ACTION_UP) {
val currentTime = System.currentTimeMillis()
if (startMillis == 0L || currentTime - startMillis > 3000) {
startMillis = currentTime
count = 1
} else {
count++
}
if (count == 4) {
}
logd("count -- $count")
}
false
}

- 285
- 5
- 4
private int touchSequenceCount = 0;
private Handler handlerTouchSequenceDetection;
private Runnable runnableTouchSequenceDetection;
public void setupTouchSequenceDetection(final View view){
try {
view.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
Log.d("setupTouchSequenceDetection", "touchCount: " + (touchSequenceCount+1));
if(touchSequenceCount == 0){
handlerTouchSequenceDetection.postDelayed(runnableTouchSequenceDetection, 2000);
}else{
if(touchSequenceCount == 2){
new AlertDialog.Builder(Activity_CheckIn_SelectLanguage.this)
.setMessage("warning message here")
.setCancelable(true)
.setPositiveButton("yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
resetTouchSequenceDetection(true);
}
})
.setNegativeButton("no", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
resetTouchSequenceDetection(true);
}
}).setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
resetTouchSequenceDetection(true);
}
})
.show();
}
}
touchSequenceCount = touchSequenceCount + 1;
break;
}
return false;
}
});
handlerTouchSequenceDetection = new Handler();
runnableTouchSequenceDetection = new Runnable() {
public void run() {
Log.d("setupTouchSequenceDetection", "reset touchCount: " + (touchSequenceCount+1));
resetTouchSequenceDetection(false);
}
};
}
catch(Exception ex){
if(ex != null){
}
}
}
private void resetTouchSequenceDetection(boolean removeRunnable){
try{
if(removeRunnable){
handlerTouchSequenceDetection.removeCallbacks(runnableTouchSequenceDetection);
}
touchSequenceCount = 0;
}
catch(Exception ex){
if(ex != null){
}
}
}

- 109
- 6
-
Please add some explanation with answer how this answer help to OP and others to fixing posted issue.Thanks – ρяσѕρєя K Jan 07 '16 at 17:48