Reputation: 1488
I want my application to react differently to a normal and a long pressed Key Event of the Volume Buttons.
I've already seen this, but if I keep the volume button pressed, I get a lot of KeyDown
Events before I get the KeyLongPressed
Event.
I'd like to have either one event or the other, not both, so that I can adjust the Volume at a short press and skip a track at a long press.
Can you help me out here?
This is my code:
@Override
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)
{
Log.d("Test", "Long press!");
return true;
}
return super.onKeyLongPress(keyCode, event);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
event.startTracking();
Log.d("Test", "Short");
return true;
}
return super.onKeyDown(keyCode, event);
}
Any help appreciated! - Iris
Upvotes: 12
Views: 45920
Reputation: 11190
Correct way according to the SDK to handle long button presses.
import android.app.Activity;
import android.util.Log;
import android.view.KeyEvent;
public class TestVolumeActivity extends Activity
{
private static final String TAG = TestVolumeActivity.class.getSimpleName();
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if( keyCode == KeyEvent.KEYCODE_VOLUME_UP ||
keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)
{
event.startTracking();
return true;
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onKeyLongPress(int keyCode, KeyEvent event)
{
if(keyCode == KeyEvent.KEYCODE_VOLUME_UP){
Log.d(TAG, "Long press KEYCODE_VOLUME_UP");
return true;
}
else if(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN){
Log.d(TAG, "Long press KEYCODE_VOLUME_DOWN");
return true;
}
return super.onKeyLongPress(keyCode, event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event)
{
if((event.getFlags() & KeyEvent.FLAG_CANCELED_LONG_PRESS) == 0){
if(keyCode == KeyEvent.KEYCODE_VOLUME_UP){
Log.e(TAG, "Short press KEYCODE_VOLUME_UP");
return true;
}
else if(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN){
Log.e(TAG, "Short press KEYCODE_VOLUME_DOWN");
return true;
}
}
return super.onKeyUp(keyCode, event);
}
}
Upvotes: 12
Reputation: 8781
When i was about to post my answer i found out some one already got some kind of solution....
But here is mine, simple and works like a charm. Just one flag ;)
This code detects shortpresses and longpresses, when a longpress occurs no shortpress will be fired!
Note: if you want the normal volume up and down behavior change the return true in the onKeyPress method to the super call like this:
event.startTracking();
if(event.getRepeatCount() == 0){
shortPress = true;
}
//return true;
return super.onKeyDown(keyCode, event);
Code without the super call:
private boolean shortPress = false;
@Override
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
shortPress = false;
Toast.makeText(this, "longPress", Toast.LENGTH_LONG).show();
return true;
}
//Just return false because the super call does always the same (returning false)
return false;
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
if(event.getAction() == KeyEvent.ACTION_DOWN){
event.startTracking();
if(event.getRepeatCount() == 0){
shortPress = true;
}
return true;
}
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
if(shortPress){
Toast.makeText(this, "shortPress", Toast.LENGTH_LONG).show();
} else {
//Don't handle longpress here, because the user will have to get his finger back up first
}
shortPress = false;
return true;
}
return super.onKeyUp(keyCode, event);
}
Code down here is with the volume up key added, just pick your side ;)
private boolean shortPress = false;
@Override
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
shortPress = false;
Toast.makeText(this, "longPress Volume Down", Toast.LENGTH_LONG).show();
return true;
} else if(keyCode == KeyEvent.KEYCODE_VOLUME_UP){
shortPress = false;
Toast.makeText(this, "longPress Volume Up", Toast.LENGTH_LONG).show();
return true;
}
//Just return false because the super call does always the same (returning false)
return false;
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
if(event.getAction() == KeyEvent.ACTION_DOWN){
event.startTracking();
if(event.getRepeatCount() == 0){
shortPress = true;
}
return true;
}
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
if(shortPress){
Toast.makeText(this, "shortPress Volume Down", Toast.LENGTH_LONG).show();
} else {
//Don't handle longpress here, because the user will have to get his finger back up first
}
shortPress = false;
return true;
} else if(keyCode == KeyEvent.KEYCODE_VOLUME_UP){
if(shortPress){
Toast.makeText(this, "shortPress Volume up", Toast.LENGTH_LONG).show();
} else {
//Don't handle longpress here, because the user will have to get his finger back up first
}
shortPress = false;
return true;
}
return super.onKeyUp(keyCode, event);
}
Upvotes: 5
Reputation: 3111
Here is the code that I wrote. It works like a charm. May be you can optimize it for better logic. But you will get the point with it. The key is to use flags. Short press is a press where we press volume button for short time and release. So onKeyUp is the one which will help us detect short presses.
package com.example.demo;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
public class TestVolumeActivity extends Activity {
boolean flag = false;
boolean flag2 = false;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash_screen);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_splash_screen, menu);
return true;
}
@Override
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
Log.d("Test", "Long press!");
flag = false;
flag2 = true;
return true;
}
return super.onKeyLongPress(keyCode, event);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
event.startTracking();
if (flag2 == true) {
flag = false;
} else {
flag = true;
flag2 = false;
}
return true;
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
event.startTracking();
if (flag) {
Log.d("Test", "Short");
}
flag = true;
flag2 = false;
return true;
}
return super.onKeyUp(keyCode, event);
}
}
Logcat for all long presses(No short press detected):
10-18 02:06:15.369: D/Test(16834): Long press!
10-18 02:06:18.683: D/Test(16834): Long press!
10-18 02:06:21.566: D/Test(16834): Long press!
10-18 02:06:23.738: D/Test(16834): Long press!
Logcat for all short presses:
10-18 02:07:42.422: D/Test(16834): Short
10-18 02:07:43.203: D/Test(16834): Short
10-18 02:07:43.663: D/Test(16834): Short
10-18 02:07:44.144: D/Test(16834): Short
Upvotes: 16
Reputation: 3669
I don't know if this answer will provide an acceptable solution to your problem, as it has relies on getting frequent KeyDown
events on a constant basis.
You could try remembering the system time when the last KeyDown
event is thrown (I'll name it tLast
), and ignore all KeyDown
events until you get a KeyLongPressed
event.
In order to get the "right" KeyDown
events (the ones you ignored in the previous step), you could have a thread checking if the time difference between the current system time and tLast
(I'll name it tDelta
) is big enough so as not to be considered a continuous press.
Given that a lot of KeyDown
events are thrown in a short period of time, you could theoretically determine the the volume button wasn't pressed continuously when the events are spaced enough (tDelta
is greater than a fixed value).
The short-coming of this solution is that, if the user presses the volume button really fast (tDelta
between presses is lower than the fixed value used to evaluate a continuous press), the multiple key presses will be ignored/considered as a continuous key press.
Another (minor) short-coming is that there would be a delay before interpreting the regular key presses, as tDelta
would have to be greater than the fixed value used when evaluating if you are dealing with a regular or continuous key press.
Regards,
Lucian
EDIT:
Hmm ... second thought: Aren't you using a KeyListener
Android implementation?
If you are using this, check out the onKeyUp
method defined for this.
I think it would be more elegant if you'd just extend this class (or one of it's derived classes) and use the onKeyUp
method to determine if you are dealing with a continuous presss (i.e. the time the button has been held down is greater than a fixed value).
If you can use this method, please do it. It's also more efficient, easier to maintain and more straightforward than the work-around presented above.
Upvotes: 0