I'm trying to build a SMS application that sends a SMS with one press on a button, this part is working but now I'm trying to implement a spam protection.
The spam protection means that you only can send 1 SMS per 10 seconds (or higher).
I've tried this:
sentSMS.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Timer timer = new Timer();
int seconds = 10000;
timer.schedule(new TimerTask() {
public void run() {
processClick();
}
}, seconds);
}});
But this is not working when I press the button twice the SMS is also sending twice.
Maybe it's also a idea to make a toast which contains how many seconds the user have to wait, something like this:
Toast.makeText(getBaseContext(), "Spam protection, wait "+secondstowait,
Toast.LENGTH_SHORT).show();
Is this all possible to make?
why dont you just take a timestamp when the button was first clicked then compare the time when the button was clicked again and see if the difference is greater than the allotted amount of time?
Potentially, the easier thing to do in my opinion is to disable the button, and use the built-in handler (on the view object) to re-enable the button.
sentSMS.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
final View view = v;
v.setEnabled(false);
v.postDelayed(new Runnable(){
public void run(){
view.setEnabled(true);
}
}, 1000*10);
}});
You need to store when the button was last clicked and then see if 10 seconds have passed.
long lastTimeSent = 0; //start at 0
sentSMS.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if(System.currentTimeMillis() > lastTimeSent + 10000){ //if at least 10 secs has passed from last click
processClick();
lastTimeSent = System.currentTimeMillis(); //last time sent is now current time
}else{
Toast.makeText(getBaseContext(), "Spam protection, please wait.",
Toast.LENGTH_SHORT).show();
}
}});
if you want to show a toast message to user, notifying him about how many seconds are left,
you must write the toast.maketext code inside the timer schedule event too
using timestamp will not be easier way, rather it will make task more difficult.
You can also try to disable the button onclick for ten seconds and re-enable it after the time period is expired.
Related
So my goal is to click a button, disable it and start a timer, once the timer is up enable the button. Simple right? You would do something like this.
button1.onClick {
button1.setEnabled(false);
new CountDownTimer(60000, 1000) { //Set Timer for 5 seconds
public void onTick(long millisUntilFinished) {
}
#Override
public void onFinish() {
button1.setEnabled(true);
}
}.start()
}
However.. If the user closes the app while the timer is running the button will be enabled again, restarting the timer. so instead of having to wait for 60 seconds the user can just close the app and open it within 10 seconds.
So my question is, how do I disable the button for 60 seconds and keep it disabled even if the user closes and opens the app until 60 seconds has passed?
You have to persist that information within a data store that keeps it even when the app is switched off.
One way to do that would be to use https://developer.android.com/training/data-storage/shared-preferences
You have to get a timestamp when starting the timer, or compute the "end time" for the timer. You then save that information, and whenever the app starts up, you first check if your preferences contain such a time stamp. And if so, you check whether you are still in that "timed" window.
Thr key things to remember: you have to remove the persisted information when the timer is up, and: if somebody changes the system clock in the mean time, you got a problem, too. Dealing with that is possible, but requires more effort.
Try this with Handler.
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
btn.setEnabled(false);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// This method will be executed once the timer is over
btn.setEnabled(true);
Log.d(TAG,"resend1");
}
},10000);// set time as per your requirement
}
});
Sorry for the terrible title, I am bad at describing these things.
I am building a metronome and have a (-) UI button that decreases the tempo by 1, and a (+) UI button that increases the tempo by 1.
My problem currently is that whenever I press either buttons, the metronome restarts itself since there's a new tempo, and plays immediately. So if you press the (-) button 10 times in a row, each time you press it you hear the initial metronome "beep".
I would like my app to do the following:
When the user clicks either (-) or (+) buttons, wait for 200 milliseconds
IF the user didn't click the buttons again in that timeframe, play the metronome
If the user DID click the button again, don't play the metronome, repeat the process: wait 200 milliseconds, if no click was made play the metronome, etc
The end result would be that if I'm at 100 bpm and I repeatedly press the (+) button 20 times until I am at 120 bpm, the metronome wouldn't start playing until I am done tapping.
How do I go about implementing this? Thank you!
Declare and instantiate the below in your activity:
private Handler timeoutHandler = new Handler();
private Runnable delayStartThread = new Runnable() {
public void run() {
startMetronome();
}
};
Then insert the below code block in your onClickListener for both + and - buttons:
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
timeoutHandler.removeCallbacks(delayStartThread);
tempoOfMetronome++; //tempoOfMetronome--; for decrease button
stopMetronome();
timeoutHandler.postDelayed(delayStartThread, 200);
}
});
For more details on how the code works, refer the below links for examples (I used these examples to formulate the answer):
Android: clicking TWICE the back button to exit activity - How to use handler.postDelayed()
How to cancel handler.postDelayed? - How to cancel handler.postDelayed()
You should also look at the Android documentation for those methods.
If you want a delay between the action and the effect, there are several ways you can achieve it. This is one.
private boolean pressedAction = false;
#override
public void onClick(View v) {
if (pressedAction) return;
pressedAction = true;
new Thread(new Runnable(
#override
public void run() {
try {
Thread.sleep(200); // 200 miliseconds
} catch (Exception e) {}
// Update views or do work (program logic)
pressedAction = false;
}
}
}
Then, the metronome logic is your bussiness.
In my app, I've got a procedure that can last between 2 and 15 seconds more or less. What I want is to set a kind of toast that when the procedure starts shows:
Loading values. Wait...
Just now, I've setted the toast duration to SHORT, because if the procedure lasts about 5 or less seconds, a LONG will be just that, very long. But having setted the duration to SHORT, when it lasts more than 10 seconds the toast dissapears and there is no message showing that the app is still processing so the user can start touching things.
What I want is to set something like a toast but that I can programmatically cancel when the procedure is finished. Any ideas?
I would recommend that you simply set the Toast duration to the maximum possible time and then use the Toast object returned from Toast.makeText(...) to cancel it when your process is finished.
Toast t = Toast.makeText(....., YERY_LONG_TOAST_TIME);
t.show();
public void onYourTaskFinished() {
t.cancel();
}
Something like that.
I personally would recommend using a ProgressDialog btw: http://developer.android.com/reference/android/app/ProgressDialog.html
Here's an example:
final Toast toast = Toast.makeText(ctx, "This message will disappear in 1 second", Toast.LENGTH_SHORT);
toast.show();
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
toast.cancel();
}
}, 1000); //specify delay here that is shorter than Toast.LENGTH_SHORT
final Toast toast = Toast.makeText(ctx, "hello android", Toast.LENGTH_SHORT);
Handler h= new Handler();
h.postDelayed(new Runnable() {
#Override
public void run() {
toast.show();
}
}, 1000);
h.postDelayed(new Runnable() {
#Override
public void run() {
toast.cancel();
}
}, 3000);
You can have a handler of the Toast when you create it, pass it to you job and call the show() method when the job starts and call the cancel() method when the job finish.
However, from your description, toast message might not be your best choice. Toast is more like a hint which has little impact if the user misses it. With little background infomation about your app, I think you might need a ProgressDialog if you do not want the user to touch anything while you are loading data, or a ProgressBar if you just want the user to know your job's progress.
Do not use Toast for this purpose. You should use progress dialog or you can add progress indicator to notification bar. So user will be able to see progress even not being inside your app.
Quick question here, I have the following code working for the most part, but I would like for the button to go back to it's original state if it is clicked during the countdown. Any suggestions?
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
new CountDownTimer(4000, 1000) {
#Override
public void onFinish() {
button.setText("SENT");
}
#Override
public void onTick(long sec) {
button.setText("CANCEL (" + sec / 1000 + ")");
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
cancel();
}
});
}
}.start();
}
});
Before the button is ever pressed, it will say "Push Me." Once pressed, the countdown will begin and the button's text will change each second (CANCEL (3), CANCEL (2), CANCEL (1)) and after the countdown the button will update its text to "SENT." If the button is pressed during the countdown (onTick), it will cancel the countdown. I would like to know how to make it revert to the "Push Me" state, and basically allow it to be pressed again and begin a new countdown. Any help would be appreciated!
So you should not have a new onClickListener inside of the timer because that uses a lot more memory then is needed, and may cause unexpected results. Instead I would suggest using a boolean value for if the timer is on, and use your existing button's onClickListener. Make sure that your timer is declared globally if you are using this. Here is an example of how that might work:
button.setOnClickListener(new OnClickListener {
if(timerIsOn){
if(timer != null){
timer.cancel();
timerIsOn = false;
}
}
else{
timerIsOn = true;
//start the timer and do whatever else
}
}
The problem i'm facing at the moment is that the android emulator seems to queue the clicks i do on the button below. If i click in a regular speed, it's no problem what so ever. But whenever i so to say, spam-click the button, it seems that it recieves all clicks and does that number of operations.
Example:
The array is 5 values long, if i stand on index 5 and click on the backbutton 4 times very fast, i end up at index 1, but if i stand at index 2 and spam-clicks three times, it throws
java.lang.ArrayIndexOutOfBoundsException
code as follows:
ButtonGoBack.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
// here i set it disabled to prevent mass clicks/taps/touches.
ButtonGoBack.setEnabled(false);
enumCounter--;
temporaryValue = values[enumCounter];
doSomething(temporaryValue);
// hides button
if(enumCounter== 0)
{
ButtonGoBack.setVisibility(4);
}
}
});
100~ or so code-lines after.
ButtonGoBack.setEnabled(true);
does not seem to do the trick.
any suggestions how to do this in a different manner?
Solved by surrounding with try{}catch{} and incrementing the counter by one.
ButtonGoBack.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
try
{
enumCounter--;
temporaryValue = values[enumCounter];
doSomething(temporaryValue);
// hides button
if(enumCounter== 0)
{
ButtonGoBack.setVisibility(4);
}
}
catch(Exception e)
{
ButtonGoBack.setVisibility(4);
enumCounter++;
}
}
});
If you want to start from the last after reaching 0 you can
enumCounter--;
if(enumCounter<0) enumCounter=4;
temporaryValue = values[enumCounter];
doSomething(temporaryValue);