33

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.

Mr Lister
  • 45,515
  • 15
  • 108
  • 150
calcrisk33
  • 437
  • 1
  • 6
  • 15
  • The test instrumentation, modifying the app to respond to something other than actual touches, and "rooting" the device to inject events at the linux level are your 3 choices. – Chris Stratton Apr 30 '12 at 07:05

4 Answers4

45

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

marcinj
  • 48,511
  • 9
  • 79
  • 100
  • 5
    this is exactly what I am looking for but... isn't instrumentation strictly for testing? I am looking for a solution that I may use in a live app, not testing. thanks in advance. – calcrisk33 Jan 03 '12 at 19:12
  • @calcrisk33 I haven't been able to find any information about whether or not this is a viable solution for a live app. Did you end up using it in that manner? – Kirk Sep 06 '13 at 15:44
  • 1
    @Kirk I did and it worked out very well for simulating a key stroke/press. – calcrisk33 Oct 30 '13 at 02:33
  • 1
    Worked perfectly for my needs (custom keyboard for JNI), although I didnt need the try / catch block, but found it has to be in a Thread. – Adamski Jan 11 '15 at 11:20
  • @george The keyevent menu does not seem to work for me, any ideas? – Ishaan Sep 26 '16 at 12:47
  • After adding INJECT_EVENTS permission to manifest I could manage to call the back button event from a service its system overlay. Thank you! –  Nov 20 '16 at 21:56
  • 4
    Requires INJECT_EVENTS permission, which is available for system applications only. – anemomylos Jun 30 '18 at 08:38
  • I added `INJECT_EVENTS` permission to my manifest, but my instrumentation tests still got the permission error when trying this approach on API 18 – Sam Feb 28 '21 at 02:38
  • The error I'm getting is `java.lang.SecurityException: Injecting to another application requires INJECT_EVENTS permission` – Sam Feb 28 '21 at 02:44
  • I never thought to use it as a cross process way to simulate clicks, but rather as a current / self - process testing tool. It would be a poor security approach by google if such API was easily available. I may suggest you to use UI Automator for testing purposes if this is what you are after. – marcinj Mar 01 '21 at 11:15
15

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).

tpaczesny
  • 666
  • 8
  • 8
  • 2
    It makes me wonder, if you also need to dispatch `KeyEvent.ACTION_UP`. I guess it would be logical to do so. – Peter Sep 24 '20 at 09:38
  • 1
    Would this method work for other views also ? For eg: If I want to simulate DPAD_UP on a RecyclerView ? – theapache64 Nov 02 '20 at 09:46
2

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.

Dharman
  • 30,962
  • 25
  • 85
  • 135
0

If you're running a UI Automator test, there are two techniques you can use depending on the device's Android version:

API 21+

If you only target API 18 or higher, than you can just use the shell:

UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
device.executeShellCommand("input text 1234"); // Type '1234'
device.executeShellCommand("input keyevent 66"); // Press the Enter key

API 18+

If you also support API 18-19, then you cannot use the shell because it's not available, and you cannot use instrumentation key injection if you're interacting with an app that's not your own, such as the system UI. Instead, use UiAutomation.injectInputEvent().

Grab an instance of UiAutomation and store is somewhere:

UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();

Then define some helper methods:

private void sendKey(int keyCode) {
    sendKeyEvent(keyCode, KeyEvent.ACTION_DOWN);
    sendKeyEvent(keyCode, KeyEvent.ACTION_UP);
}

private void sendKeyEvent(int keyCode, int action) {
    long downTime = SystemClock.uptimeMillis();
    KeyEvent event = new KeyEvent(
            downTime,
            downTime,
            action,
            keyCode,
            0,
            0,
            KeyCharacterMap.VIRTUAL_KEYBOARD,
            0,
            KeyEvent.FLAG_FROM_SYSTEM,
            InputDevice.SOURCE_KEYBOARD
    );

    uiAutomation.injectInputEvent(event, true);
}

Then use it like this:

sendKey(KeyEvent.KEYCODE_1);
sendKey(KeyEvent.KEYCODE_2);
sendKey(KeyEvent.KEYCODE_3);
sendKey(KeyEvent.KEYCODE_4);
sendKey(KeyEvent.KEYCODE_ENTER);
Sam
  • 40,644
  • 36
  • 176
  • 219