how to spoof arrow keys to my activity on android

2019-02-20 08:03发布

问题:

I am trying to override the volume buttons to act as Up/Down arrow keys (i.e. they should move focus around on all of my activities focus-able Views.)

To do so I am overriding my activities dispatchKeyEvent() method note that I also tried onKeyDown() but some portion of my volume key events was still going thru to the system, my device has audible feedback when you change the volume. I could still hear the beep but the volume was not actually being changed. Switching to dispatchKeyEvent() took away the beep coming from the system.

Here is my current dispatchKeyEvent() method:

@Override
public boolean dispatchKeyEvent(KeyEvent ke){
    int keyCode = ke.getKeyCode();
    if(ke.getAction() == KeyEvent.ACTION_DOWN){
        print("press " + keyCode);
        if(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) 
        { 
            mTxt.postDelayed(pressDown, 600);
            return true;
        }else if(keyCode == KeyEvent.KEYCODE_VOLUME_UP) 
        { 
            KeyEvent key = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_UP); 
            dispatchKeyEvent(key); 
            return true;
        } 
    }else if(ke.getAction() == KeyEvent.ACTION_UP){
        print("release " + keyCode);
        if(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) 
        { 
            /*KeyEvent keyUp = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_DOWN); 
            dispatchKeyEvent(keyUp);*/
            return true;
        }else if(keyCode == KeyEvent.KEYCODE_VOLUME_UP) 
        { 
            KeyEvent keyUp = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_UP); 
            dispatchKeyEvent(keyUp);
            return true;
        } 
    }
    return super.dispatchKeyEvent(ke);
}

the way it is set up now it is using different techniques for up and down, but neither are working correctly to shift focus. For the Up key I simply make a manual call to dispatchKeyEvent() passing it the d-pad up key and the same action that took place on the volume button (so when I press it should press, and when I release it should release) My output for the up button looks like this:

press 24
press 19
release 24
release 19

for the down button I tweaked it slightly because I thought maybe the fact that the dpad press was happening before the volume release was breaking it, so I made a runnable that will both press and release the dpad button

    pressDown = new Runnable(){
        @Override
        public void run(){
            KeyEvent key = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_DOWN); 
            dispatchKeyEvent(key); 
            KeyEvent keyUp = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_DOWN); 
            dispatchKeyEvent(keyUp);
        }
    };

and I delay that by part of a second so that I have a chance to release the volume. My output for the down button looks like this:

press 25
release 25
press 20
release 20

if I plug in a usb keyboard to my device and press the arrow keys the focus moves correctly and I see the output for up:

press 19
release 19

and for down:

press 20
release 20

The only difference (so far as I can tell) is that the focus actually moves correctly when I press the arrows on my keyboard, and doesn't move at all when I press the volume buttons (which spoof the arrow buttons)

Am I overlooking something here? Can anyone help me to figure out how to spoof arrow keys to my activity so that they will actually move focus correctly?

回答1:

I figured out eventually that you need to use the Instrumentation class to send the key events, and you have to do it off of the main thread for some reason. Here are the snippets that will send down and up:

new Thread(new Runnable() {         
   @Override
   public void run() {                 
       new Instrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_DOWN);
   }   
}).start();

new Thread(new Runnable() {         
   @Override
   public void run() {                 
       new Instrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_UP);
   }   
}).start();

If you use these insdie the overridden dispatchKeyEvent from the question it will correctly move focus around.