How can I programmatically simulate a key press on a Droid? I would like to mimic a manual key press (appearing on the droid that someone is pressing a key but it is being done programmatically).

There are solutions out there involving IWindowManager, but that isn't an option anymore in the new SDK.


Solution 1:

You can use instrumentation, ie following code called from onCreate of your activity will cause menu to be opened and closed multiple times:

    new Thread(new Runnable() {         
        @Override
        public void run() {
            try {
            Instrumentation inst = new Instrumentation();
            for ( int i = 0; i < 10; ++i ) {
                inst.sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
                Thread.sleep(2000);
                inst.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
                Thread.sleep(2000);
            }
            }
            catch(InterruptedException e){
            }
        }   
    }).start();

...but I am not sure if this is what you are after

Solution 2:

If you have a view that want to consume the event you can use BaseInputConnection class and its sendKeyEvent method.

To use it you will need to specify a target view (e.g an EditText) that will receive the KeyEvent. For example:

EditText editText;
BaseInputConnection inputConnection = new BaseInputConnection(editText, true);
inputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_POUND));

The result of this is like user would actually pressed # key (while having the edit text focused).

Solution 3:

Using instrumentation in my opinion doesn't work as intended, when editText is focused it sometimes causes soft keyboard to pop.

In my project i have a numeric keyboard fragment which should act like a normal keyboard, that's my way of achieving desired solution:

I tested this solution on 3 devices with android 7+:

Keyboard fragment onClick():

@Override
public void onClick(View v) {

    switch(v.getId()) {

        case R.id.button0:
            simulateKeyPress(KeyEvent.KEYCODE_0);
            break;
        case R.id.button1:
            simulateKeyPress(KeyEvent.KEYCODE_1);
            break;
        case R.id.button2:
            simulateKeyPress(KeyEvent.KEYCODE_2);
            break;
        case R.id.button3:
            simulateKeyPress(KeyEvent.KEYCODE_3);
            break;
        case R.id.button4:
            simulateKeyPress(KeyEvent.KEYCODE_4);
            break;
        case R.id.button5:
            simulateKeyPress(KeyEvent.KEYCODE_5);
            break;
        case R.id.button6:
            simulateKeyPress(KeyEvent.KEYCODE_6);
            break;
        case R.id.button7:
            simulateKeyPress(KeyEvent.KEYCODE_7);
            break;
        case R.id.button8:
            simulateKeyPress(KeyEvent.KEYCODE_8);
            break;
        case R.id.button9:
            simulateKeyPress(KeyEvent.KEYCODE_9);
            break;
    }

}

public void simulateKeyPress(int key){
    Activity a = (Activity) getContext();
    a.getWindow().getDecorView().getRootView();
    BaseInputConnection inputConnection = new BaseInputConnection(a.getWindow().getDecorView().getRootView(),
            true);
    KeyEvent downEvent = new KeyEvent(KeyEvent.ACTION_DOWN, key);
    KeyEvent upEvent = new KeyEvent(KeyEvent.ACTION_UP, key);
    inputConnection.sendKeyEvent(downEvent);
    inputConnection.sendKeyEvent(upEvent);
}

This way i send the event to the root view of an activity and there it goes to the desired focused editText.

It's a bit rough solution but works fine.