Register now for Android Dev Summit 2019!

Input

Input user drivers provide an interface for apps to inject events into Android's input pipeline. With this API, apps can emulate a Human Interface Device (HID) or connect external hardware to the input system using Peripheral I/O.

Adding the required permission

Add the required permission for the user driver to your app's manifest file:

<uses-permission android:name="com.google.android.things.permission.MANAGE_INPUT_DRIVERS" />

Key events

Key events indicate a momentary press and release of an input switch. They are generally used for generic button input (e.g. volume keys, media playback keys) and the keys of a keyboard. Android represents each event as a KeyEvent instance.

  1. Create a new driver instance using the InputDriver.Builder.
  2. Register the driver with the UserDriverManager.

    Kotlin

    import com.google.android.things.userdriver.UserDriverManager
    import com.google.android.things.userdriver.input.InputDriver
    ...
    
    // Driver parameters
    private const val DRIVER_NAME = "EscapeButton"
    private const val DRIVER_VERSION = 1
    // Key code for driver to emulate
    private const val KEY_CODE = KeyEvent.KEYCODE_ESCAPE
    
    class ButtonDriverService : Service() {
    
        private lateinit var driver: InputDriver
    
        override fun onCreate() {
            super.onCreate()
    
            // Create a new driver instance
            driver = InputDriver.Builder()
                    .setName(DRIVER_NAME)
                    .setSupportedKeys(intArrayOf(KEY_CODE))
                    .build()
    
            // Register with the framework
            val manager = UserDriverManager.getInstance()
            manager.registerInputDriver(driver)
        }
    
        override fun onBind(intent: Intent?): IBinder? {
            return null
        }
    }
    

    Java

    import com.google.android.things.userdriver.input.InputDriver;
    import com.google.android.things.userdriver.UserDriverManager;
    ...
    
    public class ButtonDriverService extends Service {
    
        // Driver parameters
        private static final String DRIVER_NAME = "EscapeButton";
        private static final int DRIVER_VERSION = 1;
        // Key code for driver to emulate
        private static final int KEY_CODE = KeyEvent.KEYCODE_ESCAPE;
    
        private InputDriver driver;
    
        @Override
        public void onCreate() {
            super.onCreate();
    
            // Create a new driver instance
            driver = new InputDriver.Builder()
                    .setName(DRIVER_NAME)
                    .setSupportedKeys(new int[] {KEY_CODE})
                    .build();
    
            // Register with the framework
            UserDriverManager manager = UserDriverManager.getInstance();
            manager.registerInputDriver(driver);
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    }
    
  3. When a hardware event occurs, construct a new InputDriverEvent for each state change with the current key code and input action.

  4. Inject the events into the driver with the emit() method.

    Kotlin

    class ButtonDriverService : Service() {
        ...
    
        // A state change has occurred
        private fun triggerEvent(pressed: Boolean) {
            val event = InputDriverEvent()
            event.setKeyPressed(KEY_CODE, pressed)
    
            driver.emit(event)
        }
    }
    

    Java

    public class ButtonDriverService extends Service {
        ...
    
        // A state change has occurred
        private void triggerEvent(boolean pressed) {
            InputDriverEvent event = new InputDriverEvent();
            event.setKeyPressed(KEY_CODE, pressed);
    
            driver.emit(event);
        }
    }
    
  5. Unregister the driver when key events are not longer required.

    Kotlin

    class ButtonDriverService : Service() {
        ...
    
        override fun onDestroy() {
            super.onDestroy()
    
            val manager = UserDriverManager.getInstance()
            manager.unregisterInputDriver(driver)
        }
    }
    

    Java

    public class ButtonDriverService extends Service {
        ...
    
        @Override
        public void onDestroy() {
            super.onDestroy();
    
            UserDriverManager manager = UserDriverManager.getInstance();
            manager.unregisterInputDriver(driver);
        }
    }
    

Motion events

Input drivers can also emit motion events to connect a pointing device to the framework, such as a touchpad or mouse. These devices report an absolute position value as an x/y coordinate. Each event includes an optional pressed state to indicate if the event represents a "tap" or "click" event at that location.

  1. Create a new driver instance using the InputDriver.Builder.
  2. Register the driver with the UserDriverManager.

    Kotlin

    import com.google.android.things.userdriver.input.InputDriver
    ...
    
    // Driver parameters
    private const val DRIVER_NAME = "Touchpad"
    private const val DRIVER_VERSION = 1
    
    class TouchpadDriverService : Service() {
    
        private lateinit var driver: InputDriver
    
        override fun onCreate() {
            super.onCreate()
    
            driver = InputDriver.Builder()
                    .setName(DRIVER_NAME)
                    .setAxisConfiguration(MotionEvent.AXIS_X, 0, 255, 0, 0)
                    .setAxisConfiguration(MotionEvent.AXIS_Y, 0, 255, 0, 0)
                    .build()
    
            val manager = UserDriverManager.getInstance()
            manager.registerInputDriver(driver)
        }
    
        override fun onBind(intent: Intent?): IBinder? {
            return null
        }
    }
    

    Java

    import com.google.android.things.userdriver.input.InputDriver;
    ...
    
    public class TouchpadDriverService extends Service {
    
        // Driver parameters
        private static final String DRIVER_NAME = "Touchpad";
        private static final int DRIVER_VERSION = 1;
    
        private InputDriver driver;
    
        @Override
        public void onCreate() {
            super.onCreate();
    
            driver = InputDriver.Builder()
                    .setName(DRIVER_NAME)
                    .setAxisConfiguration(MotionEvent.AXIS_X, 0, 255, 0, 0)
                    .setAxisConfiguration(MotionEvent.AXIS_Y, 0, 255, 0, 0)
                    .build();
    
            UserDriverManager manager = UserDriverManager.getInstance();
            manager.registerInputDriver(driver);
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    }
    
  3. When a hardware event occurs, construct a new InputDriverEvent and inject it into the driver with the emit() method.

    Kotlin

    class TouchpadDriverService : Service() {
        ...
    
        // A state change has occurred
        private fun triggerEvent(x: Int, y: Int, pressed: Boolean) {
            val event = InputDriverEvent().apply {
                setPosition(MotionEvent.AXIS_X, x)
                setPosition(MotionEvent.AXIS_Y, y)
                setContact(pressed)
            }
    
            driver.emit(event)
        }
    }
    

    Java

    public class TouchpadDriverService extends Service {
        ...
    
        // A state change has occurred
        private void triggerEvent(int x, int y, boolean pressed) {
            InputDriverEvent event = new InputDriverEvent();
            event.setPosition(MotionEvent.AXIS_X, x);
            event.setPosition(MotionEvent.AXIS_Y, y);
            event.setContact(pressed);
    
            driver.emit(event);
        }
    }
    
  4. Unregister the driver when pointer events are not longer required.

    Kotlin

    class TouchpadDriverService : Service() {
        ...
    
        override fun onDestroy() {
            super.onDestroy()
    
            val manager = UserDriverManager.getInstance()
            manager.unregisterInputDriver(driver)
        }
    }
    

    Java

    public class TouchpadDriverService extends Service {
        ...
    
        @Override
        public void onDestroy() {
            super.onDestroy();
    
            UserDriverManager manager = UserDriverManager.getInstance();
            manager.unregisterInputDriver(driver);
        }
    }
    

If your input device emits multiple input events rapidly, consider re-using the same InputDriverEvent object to avoid multiple object allocations.

Kotlin

val inputEvent = InputDriverEvent()
for (keyCode in eventSet) {
    // Clear the event before values are set
    inputEvent.clear()
    // Set properties for the current event
    event.apply {
        setPosition(MotionEvent.AXIS_X, ...)
        setPosition(MotionEvent.AXIS_Y, ...)
        setContact(true)
        driver.emit(inputEvent)
    }
}

Java

InputDriverEvent inputEvent = new InputDriverEvent();
for (int keyCode : eventSet) {
    // Clear the event before values are set
    inputEvent.clear();
    // Set properties for the current event
    event.setPosition(MotionEvent.AXIS_X, ...);
    event.setPosition(MotionEvent.AXIS_Y, ...);
    event.setContact(true);
    driver.emit(inputEvent);
}

See also

See Handling Controller Actions for more details on how Android handles input events from external source devices.