How to inject click event with Android UiAutomation.injectInputEvent
Asked Answered
R

1

7

I'm automating the testing of a flow in my app where I install a device administrator. To activate a device administrator on most devices (let's assume here I don't have some enterprise API that lets me do this like what Samsung offers) the system displays a popup to the user who then has to click the "Activate" button.

I'm using Robotium and Android JUnit to drive my tests. In a normal testing case one can only interact with the app and process under test and not any system activities that come up.

The UiAutomation claims to allow you to interact with other applications by leveraging the Accessibility Framework, and then allowing one to inject arbitrary input events.

So - here's what I'm trying to do:

public class AbcTests extends ActivityInstrumentationTestCase2<AbcActivity> {

    private Solo mSolo

    @Override
    public void setUp() {
        mSolo = new Solo(getInstrumentation(), getActivity());

    }

    ...

    public void testAbc(){
    
        final UiAutomation automation = getInstrumentation().getUiAutomation();         
        
        MotionEvent motionDown = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), KeyEvent.ACTION_DOWN,
                100,  100, 0);

        automation.injectInputEvent(motionDown, true)
        MotionEvent motionUp = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), KeyEvent.ACTION_UP,
                100, 100, 0);

        automation.injectInputEvent(motionUp, true)
        motionUp.recycle();
        motionDown.recycle();
     }
    
 }

When this test is run the System popup to "Activate" the device administrator is active, and I want to just click on the screen. I've hardcoded in 100,100 as the position for clicks for the purposes of this question but realistically I'll click in the bottom right corner of the screen so I can hit the button.

I do not get any click events occurring on the screen. Does anyone have experience with this? Are there any alternatives to do what I want to do? From my understanding there are very few tools that do this.

Thanks.

Update Added setSource for right answer

Replenish answered 18/4, 2014 at 17:41 Comment(1)
I think you should take out the setSource() calls that you edited in. The original question should demonstrate the problem, where the answer shows the solution. As it is, comparing the two causes confusion since they both work.Illa
R
9

Finally figured this out. I compared my MotionEvents to the two events that get dispatched when I clicked on a button and the only difference was the source. So, I set the source on the two motionEvents and it worked.

....
motionDown.setSource(InputDevice.SOURCE_TOUCHSCREEN);
....
motionUp.setSource(InputDevice.SOURCE_TOUCHSCREEN);

And here's a full version of the method

//=========================================================================
//==                        Utility Methods                             ===
//=========================================================================
/**
 * Helper method injects a click event at a point on the active screen via the UiAutomation object.
 * @param x the x position on the screen to inject the click event
 * @param y the y position on the screen to inject the click event
 * @param automation a UiAutomation object rtreived through the current Instrumentation
 */
static void injectClickEvent(float x, float y, UiAutomation automation){
    //A MotionEvent is a type of InputEvent.  
    //The event time must be the current uptime.
    final long eventTime = SystemClock.uptimeMillis();

    //A typical click event triggered by a user click on the touchscreen creates two MotionEvents,
    //first one with the action KeyEvent.ACTION_DOWN and the 2nd with the action KeyEvent.ACTION_UP
    MotionEvent motionDown = MotionEvent.obtain(eventTime, eventTime, KeyEvent.ACTION_DOWN,
            x,  y, 0); 
    //We must set the source of the MotionEvent or the click doesn't work.
    motionDown.setSource(InputDevice.SOURCE_TOUCHSCREEN);
    automation.injectInputEvent(motionDown, true);
    MotionEvent motionUp = MotionEvent.obtain(eventTime, eventTime, KeyEvent.ACTION_UP,
            x, y, 0);
    motionUp.setSource(InputDevice.SOURCE_TOUCHSCREEN);
    automation.injectInputEvent(motionUp, true);
    //Recycle our events back to the system pool.
    motionUp.recycle();
    motionDown.recycle();
}
Replenish answered 18/4, 2014 at 21:55 Comment(1)
I love your javadoc and commentsCygnet

© 2022 - 2024 — McMap. All rights reserved.