The Android Things console is no longer accepting new projects, and will be turned down for all existing projects on January 5, 2022. For more details, visit the FAQ page.

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.