IndirectPointerInjectionScope


The receiver scope of the indirect pointer input injection lambda from performIndirectPointerInput.

Indirect pointer input events are dispatched through the focused tree, and components will only receive these events if they are focused, or an ancestor of a focused item. Therefore, this API requires an active focus state.

An indirect pointer gesture (just like a regular touch gesture) starts with a down event, followed by a sequence of move events and finally an up event, optionally combined with more sets of down and up events for multitouch gestures.

Most methods accept a pointerId to specify which pointer (finger) the event applies to. Movement can be expressed absolutely with moveTo and updatePointerTo, or relative to the current pointer position with moveBy and updatePointerBy. The moveTo/By methods enqueue an event immediately, while the updatePointerTo/By methods don't. This allows you to update the position of multiple pointers in a single move event for multitouch gestures. Indirect pointer input gestures can be canceled with cancel. All events, regardless the method used, will always contain the current position of all pointers.

The entire event injection state is shared between all perform.*Input methods, meaning you can continue an unfinished Indirect pointer input gesture in a subsequent invocation of performIndirectPointerInput or performMultiModalInput.

All events sent by these methods are batched together and sent as a whole after performIndirectPointerInput has executed its code block. Because gestures don't have to be defined all in the same performIndirectPointerInput block, keep in mind that while the gesture is not complete, all code you execute in between these blocks will be executed while imaginary fingers are actively touching the indirect pointer input device. Remember, indirect pointer events do NOT correlate to the screen, so those finger locations won't map to a screen x and y. The x and y coordinates are instead mapped to the specific input device being used during the interaction.

The events sent as part of the same batch will not be interrupted by recomposition. However, if a gesture spans multiple performIndirectPointerInput blocks it is important to remember that recomposition, layout and drawing could take place during the gesture, which may lead to events being injected into a moving target.

This scope also provides general capabilities such as advancing event time and accessing the system ViewConfiguration. It also implements Density to facilitate conversion between pixels and density-independent pixels.

For injection methods that require specific node information (e.g., performTouchInput, performMouseInput), use InjectionScope.

Example of performing an indirect pointer click:

import androidx.compose.ui.input.indirect.IndirectPointerEventPrimaryDirectionalMotionAxis
import androidx.compose.ui.test.click
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performIndirectPointerInput
import androidx.compose.ui.test.requestFocus
import androidx.compose.ui.unit.IntSize

// Ensure the node is within the focus path (otherwise, you won't get the event).
composeTestRule.onNodeWithTag("myComponent").requestFocus()
composeTestRule.performIndirectPointerInput(
    indirectPointerEventPrimaryDirectionalMotionAxis =
        IndirectPointerEventPrimaryDirectionalMotionAxis.X,
    // Horizontal trackpad
    inputDeviceSize = IntSize(width = 5000, height = 1000),
) {
    click()
}
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.indirect.IndirectPointerEventPrimaryDirectionalMotionAxis
import androidx.compose.ui.test.inputDeviceCenterX
import androidx.compose.ui.test.inputDeviceCenterY
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performIndirectPointerInput
import androidx.compose.ui.test.requestFocus
import androidx.compose.ui.unit.IntSize

// Ensure the node is within the focus path (otherwise, you won't get the event).
composeTestRule.onNodeWithTag("myComponent").requestFocus()

composeTestRule.performIndirectPointerInput(
    indirectPointerEventPrimaryDirectionalMotionAxis =
        IndirectPointerEventPrimaryDirectionalMotionAxis.X,
    // Horizontal trackpad
    inputDeviceSize = IntSize(width = 5000, height = 1000),
) {
    down(position = Offset(x = inputDeviceCenterX, y = inputDeviceCenterY))
}

// Assert some pressed state is visible

composeTestRule.performIndirectPointerInput(
    indirectPointerEventPrimaryDirectionalMotionAxis =
        IndirectPointerEventPrimaryDirectionalMotionAxis.X,
    // Horizontal trackpad
    inputDeviceSize = IntSize(width = 5000, height = 1000),
) {
    up()
}

Example of performing an indirect pointer swipe:

import androidx.compose.ui.input.indirect.IndirectPointerEventPrimaryDirectionalMotionAxis
import androidx.compose.ui.test.inputDeviceLeft
import androidx.compose.ui.test.inputDeviceRight
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performIndirectPointerInput
import androidx.compose.ui.test.requestFocus
import androidx.compose.ui.test.swipeRight
import androidx.compose.ui.unit.IntSize

// Ensure your node is within the focus path (otherwise, you won't get the event).
composeTestRule.onNodeWithTag("myComponent").requestFocus()
composeTestRule.performIndirectPointerInput(
    indirectPointerEventPrimaryDirectionalMotionAxis =
        IndirectPointerEventPrimaryDirectionalMotionAxis.X,
    // Horizontal trackpad
    inputDeviceSize = IntSize(width = 5000, height = 1000),
) {
    swipeRight(startX = inputDeviceLeft, endX = inputDeviceRight)
}

Summary

Public functions

Unit
advanceEventTime(durationMillis: Long)

Adds the given durationMillis to the current event time, delaying the next event by that time.

Cmn
Unit
cancel(delayMillis: Long)

Sends a cancel event delayMillis after the last sent event to cancel the current gesture.

Cmn
Offset?
currentPosition(pointerId: Int)

Returns the current position of the given pointerId.

Cmn
open Unit
down(position: Offset)

Sends a down event for the default pointer at position on the indirect pointer input device sending the input.

Cmn
Unit
down(pointerId: Int, position: Offset)

Sends a down event for the pointer with the given pointerId at position on the external indirect pointer input device.

Cmn
Unit
move(delayMillis: Long)

Sends a move event delayMillis after the last sent event without updating any of the pointer positions.

Cmn
open Unit
moveBy(delta: Offset, delayMillis: Long)

Sends a move event delayMillis after the last sent event on nodes in the focus path, with the position of the default pointer moved by the given delta.

Cmn
open Unit
moveBy(pointerId: Int, delta: Offset, delayMillis: Long)

Sends a move event delayMillis after the last sent event on nodes in the focus path, with the position of the pointer with the given pointerId moved by the given delta.

Cmn
open Unit
moveTo(position: Offset, delayMillis: Long)

Sends a move event delayMillis after the last sent event on nodes in the focus path, with the position of the default pointer updated to position.

Cmn
open Unit
moveTo(pointerId: Int, position: Offset, delayMillis: Long)

Sends a move event delayMillis after the last sent event on nodes in the focus path, with the position of the pointer with the given pointerId updated to position.

Cmn
open Unit
moveWithHistory(
    relativeHistoricalTimes: List<Long>,
    historicalCoordinates: List<Offset>,
    delayMillis: Long
)

Sends a move event delayMillis after the last sent event without updating any of the pointer positions, while adding the historicalCoordinates at the relativeHistoricalTimes to the move event.

Cmn
Unit
moveWithHistoryMultiPointer(
    relativeHistoricalTimes: List<Long>,
    historicalCoordinates: List<List<Offset>>,
    delayMillis: Long
)

Sends a move event delayMillis after the last sent event without updating any of the pointer positions, while adding the historicalCoordinates at the relativeHistoricalTimes to the move event.

Cmn
Unit
up(pointerId: Int)

Sends an up event for the pointer with the given pointerId, or the default pointer if pointerId is omitted, on nodes in the focus path.

Cmn
open Unit
updatePointerBy(pointerId: Int, delta: Offset)

Updates the position of the pointer with the given pointerId by the given delta, but does not send a move event.

Cmn
open Unit

Updates the position of the default pointer (pointerId = 0) to the given position within the indirect pointer input device's bounds, but does not send a move event.

Cmn
Unit
updatePointerTo(pointerId: Int, position: Offset)

Updates the position of the pointer with the given pointerId to the given position within the indirect pointer input device's bounds, but does not send a move event.

Cmn

Public properties

open Long

The default time between two successive events.

Cmn
IndirectPointerEventPrimaryDirectionalMotionAxis

The primary axis for motion from an IndirectPointerEvent.

Cmn
IntSize

The dimensions of the external indirect pointer input device that provide the boundaries for indirect input.

Cmn
ViewConfiguration

The ViewConfiguration in use by the SemanticsNode from the SemanticsNodeInteraction on which the input injection method is called.

Cmn

Extension functions

Unit

Performs a click gesture (aka a tap) on nodes in the focus path.

Cmn
Unit
IndirectPointerInjectionScope.doubleClick(
    position: Offset,
    delayMillis: Long
)

Performs a double click gesture (aka a double tap) on nodes in the focus path.

Cmn
Unit
IndirectPointerInjectionScope.longClick(
    position: Offset,
    durationMillis: Long
)

Performs a long click gesture (aka a long press) on nodes in the focus path.

Cmn
Unit
IndirectPointerInjectionScope.multiTouchSwipe(
    curves: List<(timeMillis: Long) -> Offset>,
    durationMillis: Long,
    keyTimes: List<Long>
)

Performs a multitouch swipe gesture on nodes in the focus path.

Cmn
Unit
IndirectPointerInjectionScope.pinch(
    start0: Offset,
    end0: Offset,
    start1: Offset,
    end1: Offset,
    durationMillis: Long
)

Performs a pinch gesture on nodes in the focus path.

Cmn
Unit
IndirectPointerInjectionScope.swipe(
    curve: (timeMillis: Long) -> Offset,
    durationMillis: Long,
    keyTimes: List<Long>
)

Performs a swipe gesture on nodes in the focus path.

Cmn
Unit
IndirectPointerInjectionScope.swipe(
    start: Offset,
    end: Offset,
    durationMillis: Long
)

Performs a swipe gesture on nodes in the focus path.

Cmn
Unit
IndirectPointerInjectionScope.swipeDown(
    startY: Float,
    endY: Float,
    durationMillis: Long
)

Performs a swipe down gesture on nodes in the focus path along x = [IndirectPointerInjectionScope.inputDeviceCenter].x, from startY till endY, taking durationMillis milliseconds.

Cmn
Unit
IndirectPointerInjectionScope.swipeLeft(
    startX: Float,
    endX: Float,
    durationMillis: Long
)

Performs a swipe left gesture on nodes in the focus path along y = [IndirectPointerInjectionScope.inputDeviceCenter].y, from startX till endX, taking durationMillis milliseconds.

Cmn
Unit
IndirectPointerInjectionScope.swipeRight(
    startX: Float,
    endX: Float,
    durationMillis: Long
)

Performs a swipe right gesture on nodes in the focus path along y = [IndirectPointerInjectionScope.inputDeviceCenter].y, from startX till endX, taking durationMillis milliseconds.

Cmn
Unit
IndirectPointerInjectionScope.swipeUp(
    startY: Float,
    endY: Float,
    durationMillis: Long
)

Performs a swipe up gesture on nodes in the focus path along x = [IndirectPointerInjectionScope.inputDeviceCenter].x, from startY till endY, taking durationMillis milliseconds.

Cmn
Unit
IndirectPointerInjectionScope.swipeWithVelocity(
    start: Offset,
    end: Offset,
    endVelocity: @FloatRange(from = 0.0) Float,
    durationMillis: Long
)

Performs a swipe gesture on nodes in the focus path such that it ends with the given endVelocity.

Cmn

Extension properties

Float

The y-coordinate for the bottom of the indirect pointer input device we're interacting with in px, where (0, 0) is the top left corner of the indirect pointer input device.

Cmn
Offset

The center of the bottom edge of the indirect pointer input device we're interacting with, where (0, 0) is the top left corner of the indirect pointer input device.

Cmn
Offset

The bottom left corner of the indirect pointer input device we're interacting with, where (0, 0) is the top left corner of the indirect pointer input device.

Cmn
Offset

The bottom right corner of the indirect pointer input device we're interacting with, where (0, 0) is the top left corner of the indirect pointer input device.

Cmn
Offset

The center of the indirect pointer input device we're interacting with, where (0, 0) is the top left corner of the indirect pointer input device.

Cmn
Offset

The center of the left edge of the indirect pointer input device we're interacting with, where (0, 0) is the top left corner of the indirect pointer input device.

Cmn
Offset

The center of the right edge of the indirect pointer input device we're interacting with, where (0, 0) is the top left corner of the indirect pointer input device.

Cmn
Float

The x-coordinate for the center of the indirect pointer input device we're interacting with in px, where (0, 0) is the top left corner of the indirect pointer input device.

Cmn
Float

The y-coordinate for the center of the indirect pointer input device we're interacting with in px, where (0, 0) is the top left corner of the indirect pointer input device.

Cmn
Int

The height of the external indirect pointer input device that provide the height boundary for indirect input.

Cmn
Float

The x-coordinate for the left edge of the indirect pointer input device we're interacting with in px, where (0, 0) is the top left corner of the indirect pointer input device.

Cmn
Float

The x-coordinate for the right edge of the indirect pointer input device we're interacting with in px, where (0, 0) is the top left corner of the indirect pointer input device.

Cmn
Float

The y-coordinate for the top of the indirect pointer input device we're interacting with in px, where (0, 0) is the top left corner of the indirect pointer input device.

Cmn
Offset

The center of the top edge of the indirect pointer input device we're interacting with, where (0, 0) is the top left corner of the indirect pointer input device.

Cmn
Offset

The top left corner of the indirect pointer input device we're interacting with, where (0, 0) is the top left corner of the indirect pointer input device.

Cmn
Offset

The top right corner of the indirect pointer input device we're interacting with, where (0, 0) is the top left corner of the indirect pointer input device.

Cmn
Int

The width of the external indirect pointer input device that provide the width boundary for indirect input.

Cmn

Inherited functions

From androidx.compose.ui.unit.Density
open Int

Convert Dp to Int by rounding

Cmn
open Int

Convert Sp to Int by rounding

Cmn
open Dp

Convert an Int pixel value to Dp.

Cmn
open Dp

Convert a Float pixel value to a Dp

Cmn
open DpSize

Convert a Size to a DpSize.

Cmn
open Float

Convert Dp to pixels.

Cmn
open Float

Convert Sp to pixels.

Cmn
open Rect

Convert a DpRect to a Rect.

Cmn
open Size

Convert a DpSize to a Size.

Cmn
open TextUnit

Convert an Int pixel value to Sp.

Cmn
open TextUnit

Convert a Float pixel value to a Sp

Cmn
From androidx.compose.ui.unit.FontScaling
Dp

Convert Sp to Dp.

Cmn
TextUnit

Convert Dp to Sp.

Cmn

Inherited properties

From androidx.compose.ui.unit.Density
Float

The logical density of the display.

Cmn
From androidx.compose.ui.unit.FontScaling
Float

Current user preference for the scaling factor for fonts.

Cmn

Public functions

advanceEventTime

fun advanceEventTime(durationMillis: Long = eventPeriodMillis): Unit

Adds the given durationMillis to the current event time, delaying the next event by that time.

cancel

fun cancel(delayMillis: Long = eventPeriodMillis): Unit

Sends a cancel event delayMillis after the last sent event to cancel the current gesture. The cancel event contains the current position of all active pointers.

Parameters
delayMillis: Long = eventPeriodMillis

The time between the last sent event and this event. eventPeriodMillis by default.

currentPosition

fun currentPosition(pointerId: Int = 0): Offset?

Returns the current position of the given pointerId. The default pointerId is 0. The position is returned in the coordinate system of the device sending the input (see inputDeviceSize). It is NOT related to the screen location.

down

open fun down(position: Offset): Unit

Sends a down event for the default pointer at position on the indirect pointer input device sending the input. The position is NOT in the node's local coordinate system (see inputDeviceSize).

If no pointers are down yet, this will start a new Indirect pointer input gesture. If a gesture is already in progress, this event is sent at the same timestamp as the last event. If the given pointer is already down, @throws IllegalArgumentException.

Parameters
position: Offset

The position of the down event, in the input device's coordinate system.

down

fun down(pointerId: Int, position: Offset): Unit

Sends a down event for the pointer with the given pointerId at position on the external indirect pointer input device. The position is NOT in the node's local coordinate system (see inputDeviceSize).

If no pointers are down yet, this will start a new Indirect pointer input gesture. If a gesture is already in progress (that is, there are other pointer ids that are down), this event is sent at the same timestamp as the last event. You cannot call down with a pointer id that is already down.

Parameters
pointerId: Int

The id of the pointer, can be any number not yet in use by another pointer

position: Offset

The position of the down event, in the input device's coordinate system.

Throws
IllegalArgumentException

if the given pointer id is already down.

move

fun move(delayMillis: Long = eventPeriodMillis): Unit

Sends a move event delayMillis after the last sent event without updating any of the pointer positions. This can be useful when batching movement of multiple pointers together, which can be done with updatePointerTo and updatePointerBy.

Parameters
delayMillis: Long = eventPeriodMillis

The time between the last sent event and this event. eventPeriodMillis by default.

moveBy

open fun moveBy(delta: Offset, delayMillis: Long = eventPeriodMillis): Unit

Sends a move event delayMillis after the last sent event on nodes in the focus path, with the position of the default pointer moved by the given delta. The default pointer has pointerId = 0.

If the pointer is not yet down, @throws IllegalArgumentException.

Parameters
delta: Offset

The position for this move event, relative to the current position of the pointer. For example, `delta = Offset(10.px, -10.px) will add 10.px to the pointer's x-position, and subtract 10.px from the pointer's y-position.

delayMillis: Long = eventPeriodMillis

The time between the last sent event and this event. eventPeriodMillis by default.

moveBy

open fun moveBy(pointerId: Int, delta: Offset, delayMillis: Long = eventPeriodMillis): Unit

Sends a move event delayMillis after the last sent event on nodes in the focus path, with the position of the pointer with the given pointerId moved by the given delta.

If the pointer is not yet down, @throws IllegalArgumentException.

Parameters
pointerId: Int

The id of the pointer to move, as supplied in down

delta: Offset

The position for this move event, relative to the current position of the pointer. For example, `delta = Offset(10.px, -10.px) will add 10.px to the pointer's x-position, and subtract 10.px from the pointer's y-position.

delayMillis: Long = eventPeriodMillis

The time between the last sent event and this event. eventPeriodMillis by default.

moveTo

open fun moveTo(position: Offset, delayMillis: Long = eventPeriodMillis): Unit

Sends a move event delayMillis after the last sent event on nodes in the focus path, with the position of the default pointer updated to position. The position is NOT in the node's local coordinate system (see inputDeviceSize).

If the default pointer is not yet down, @throws IllegalArgumentException.

Parameters
position: Offset

The new position of the pointer, in the indirect pointer input device's coordinate system

delayMillis: Long = eventPeriodMillis

The time between the last sent event and this event. eventPeriodMillis by default.

moveTo

open fun moveTo(
    pointerId: Int,
    position: Offset,
    delayMillis: Long = eventPeriodMillis
): Unit

Sends a move event delayMillis after the last sent event on nodes in the focus path, with the position of the pointer with the given pointerId updated to position. The position is NOT in the node's local coordinate system (see inputDeviceSize).

If the pointer is not yet down, @throws IllegalArgumentException.

Parameters
pointerId: Int

The id of the pointer to move, as supplied in down

position: Offset

The new position of the pointer, in the indirect pointer input device's coordinate system

delayMillis: Long = eventPeriodMillis

The time between the last sent event and this event. eventPeriodMillis by default.

moveWithHistory

open fun moveWithHistory(
    relativeHistoricalTimes: List<Long>,
    historicalCoordinates: List<Offset>,
    delayMillis: Long = eventPeriodMillis
): Unit

Sends a move event delayMillis after the last sent event without updating any of the pointer positions, while adding the historicalCoordinates at the relativeHistoricalTimes to the move event. This corresponds to the scenario where the external device generates Indirect pointer input events quicker than can be dispatched and batches them together.

This overload is a convenience method for the common case where the gesture only has one pointer.

Parameters
relativeHistoricalTimes: List<Long>

Time of each historical event, as a millisecond relative to the time the actual event is sent. For example, -10L means 10ms earlier.

historicalCoordinates: List<Offset>

Coordinates of each historical event, in the same coordinate space as moveTo. The list must have the same size as relativeHistoricalTimes.

delayMillis: Long = eventPeriodMillis

The time between the last sent event and this event. eventPeriodMillis by default.

moveWithHistoryMultiPointer

fun moveWithHistoryMultiPointer(
    relativeHistoricalTimes: List<Long>,
    historicalCoordinates: List<List<Offset>>,
    delayMillis: Long = eventPeriodMillis
): Unit

Sends a move event delayMillis after the last sent event without updating any of the pointer positions, while adding the historicalCoordinates at the relativeHistoricalTimes to the move event. This corresponds to the scenario where an external touchpad generates Indirect pointer input events quicker than can be dispatched and batches them together.

Parameters
relativeHistoricalTimes: List<Long>

Time of each historical event, as a millisecond relative to the time the actual event is sent. For example, -10L means 10ms earlier.

historicalCoordinates: List<List<Offset>>

Coordinates of each historical event, in the same coordinate space as moveTo. The outer list must have the same size as the number of pointers in the event, and each inner list must have the same size as relativeHistoricalTimes. The ith pointer is assigned the ith history, with the pointers sorted on ascending pointerId.

delayMillis: Long = eventPeriodMillis

The time between the last sent event and this event. eventPeriodMillis by default.

up

fun up(pointerId: Int = 0): Unit

Sends an up event for the pointer with the given pointerId, or the default pointer if pointerId is omitted, on nodes in the focus path.

Parameters
pointerId: Int = 0

The id of the pointer to liftup, as supplied in down

updatePointerBy

open fun updatePointerBy(pointerId: Int, delta: Offset): Unit

Updates the position of the pointer with the given pointerId by the given delta, but does not send a move event. The move event can be sent with move.

If the pointer is not yet down, @throws IllegalArgumentException.

Parameters
pointerId: Int

The id of the pointer to move, as supplied in down

delta: Offset

The position for this move event, relative to the last sent position of the pointer. For example, `delta = Offset(10.px, -10.px) will add 10.px to the pointer's x-position, and subtract 10.px from the pointer's y-position.

updatePointerTo

open fun updatePointerTo(position: Offset): Unit

Updates the position of the default pointer (pointerId = 0) to the given position within the indirect pointer input device's bounds, but does not send a move event. The move event can be sent with move. The position is NOT in the node's local coordinate system (see inputDeviceSize).

If the pointer is not yet down, @throws IllegalArgumentException.

Parameters
position: Offset

The new position of the pointer, in the indirect pointer input device's coordinate system

updatePointerTo

fun updatePointerTo(pointerId: Int, position: Offset): Unit

Updates the position of the pointer with the given pointerId to the given position within the indirect pointer input device's bounds, but does not send a move event. The move event can be sent with move. The position is NOT in the node's local coordinate system (see inputDeviceSize).

If the pointer is not yet down, @throws IllegalArgumentException.

Parameters
pointerId: Int

The id of the pointer to move, as supplied in down

position: Offset

The new position of the pointer, in the indirect pointer input device's coordinate system

Public properties

eventPeriodMillis

open val eventPeriodMillisLong

The default time between two successive events.

indirectPointerEventPrimaryDirectionalMotionAxis

val indirectPointerEventPrimaryDirectionalMotionAxisIndirectPointerEventPrimaryDirectionalMotionAxis

The primary axis for motion from an IndirectPointerEvent. Indirect input devices (such as touchpads) that do not move a cursor on screen may define a primary axis for motion (such as scrolling). This facilitates the translation of a 2D input gesture into a 1D scroll on the screen. For example, an input device might be wide horizontally but narrow vertically. In such a case, it would designate X as its primary axis of motion. This means horizontal scrolling on the input device would cause a horizontal list to scroll horizontally, and a vertical list to scroll vertically - even though the direction of motion on the input device is horizontal in both cases.

inputDeviceSize

val inputDeviceSizeIntSize

The dimensions of the external indirect pointer input device that provide the boundaries for indirect input. If you go outside these dimensions, the tests will throw an exception. Note: This is not related to the screen coordinates.

viewConfiguration

val viewConfigurationViewConfiguration

The ViewConfiguration in use by the SemanticsNode from the SemanticsNodeInteraction on which the input injection method is called.

Extension functions

IndirectPointerInjectionScope.click

fun IndirectPointerInjectionScope.click(position: Offset = inputDeviceCenter): Unit

Performs a click gesture (aka a tap) on nodes in the focus path.

The click is done at the given position within the indirect pointer input device's bounds, or the inputDeviceCenter if the position is omitted. The position is NOT in the node's local coordinate system (see IndirectPointerInjectionScope.inputDeviceSize).

Parameters
position: Offset = inputDeviceCenter

The position where to click, in the indirect pointer input device's coordinate system (IndirectPointerInjectionScope.inputDeviceSize). If omitted, the defaultStartLocation will be used.

IndirectPointerInjectionScope.doubleClick

fun IndirectPointerInjectionScope.doubleClick(
    position: Offset = inputDeviceCenter,
    delayMillis: Long = viewConfiguration.defaultDoubleTapDelayMillis
): Unit

Performs a double click gesture (aka a double tap) on nodes in the focus path.

The double click is done at the given position within the indirect pointer input device's bounds or inputDeviceCenter if the position is omitted. By default, the delayMillis between the first and the second click is halfway in between the minimum and maximum required delay for a double click. The position is NOT in the node's local coordinate system (see IndirectPointerInjectionScope.inputDeviceSize).

Parameters
position: Offset = inputDeviceCenter

The position where to click, in the indirect pointer input device's coordinate system (IndirectPointerInjectionScope.inputDeviceSize). If omitted, the defaultStartLocation will be used.

delayMillis: Long = viewConfiguration.defaultDoubleTapDelayMillis

The time between the up event of the first click and the down event of the second click

IndirectPointerInjectionScope.longClick

fun IndirectPointerInjectionScope.longClick(
    position: Offset = inputDeviceCenter,
    durationMillis: Long = viewConfiguration.longPressTimeoutMillis + 100
): Unit

Performs a long click gesture (aka a long press) on nodes in the focus path.

The long click is done at the given position within the indirect pointer input device's bounds, or inputDeviceCenter if the position is omitted. By default, the durationMillis of the press is 100ms longer than the minimum required duration for a long press. The position is NOT in the node's local coordinate system (see IndirectPointerInjectionScope.inputDeviceSize).

Parameters
position: Offset = inputDeviceCenter

The position where to click, in the indirect pointer input device's coordinate system (see IndirectPointerInjectionScope.inputDeviceSize). If omitted, the defaultStartLocation will be used.

durationMillis: Long = viewConfiguration.longPressTimeoutMillis + 100

The time between the down and the up event.

IndirectPointerInjectionScope.multiTouchSwipe

fun IndirectPointerInjectionScope.multiTouchSwipe(
    curves: List<(timeMillis: Long) -> Offset>,
    durationMillis: Long = 200,
    keyTimes: List<Long> = emptyList()
): Unit

Performs a multitouch swipe gesture on nodes in the focus path.

Each pointer follows curves from 0 till durationMillis. Sampling of an event is forced at all times defined in keyTimes. The time between events is kept as close to eventPeriodMillis as possible, given the constraints.

The coordinates are NOT in the node's local coordinate system (see IndirectPointerInjectionScope.inputDeviceSize) and is usually used for focused movement (that is, a focused node would move to the next/previous focusable node in the hierarchy with a swipe).

Parameters
curves: List<(timeMillis: Long) -> Offset>

The functions that describe the gesture. Function i defines the position over time for pointer id i. The argument passed to each function is the time in milliseconds since the start of the swipe, and the return value is the location of that pointer at that point in time.

durationMillis: Long = 200

The duration of the gesture (default duration is 200 milliseconds)

keyTimes: List<Long> = emptyList()

An optional list of timestamps in milliseconds at which a move event must be sampled

IndirectPointerInjectionScope.pinch

fun IndirectPointerInjectionScope.pinch(
    start0: Offset,
    end0: Offset,
    start1: Offset,
    end1: Offset,
    durationMillis: Long = 400
): Unit

Performs a pinch gesture on nodes in the focus path.

For each pair of start and end Offsets, the motion events are linearly interpolated.

The coordinates are NOT in the node's local coordinate system (see IndirectPointerInjectionScope.inputDeviceSize).

The default duration is 400 milliseconds.

Parameters
start0: Offset

The start position of the first pointer in the indirect pointer input device's coordinate system (IndirectPointerInjectionScope.inputDeviceSize).

end0: Offset

The end position of the first pointer in the indirect pointer input device's coordinate system (IndirectPointerInjectionScope.inputDeviceSize).

start1: Offset

The start position of the second pointer in the indirect pointer input device's coordinate system.

end1: Offset

The end position of the second pointer in the indirect pointer input device's coordinate system (IndirectPointerInjectionScope.inputDeviceSize).

durationMillis: Long = 400

the duration of the pinch gesture

IndirectPointerInjectionScope.swipe

fun IndirectPointerInjectionScope.swipe(
    curve: (timeMillis: Long) -> Offset,
    durationMillis: Long = 200,
    keyTimes: List<Long> = emptyList()
): Unit

Performs a swipe gesture on nodes in the focus path.

The swipe follows the curve from 0 till durationMillis. Will force sampling of an event at all times defined in keyTimes. The time between events is kept as close to eventPeriodMillis as possible, given the constraints.

The coordinates are NOT in the node's local coordinate system (see IndirectPointerInjectionScope.inputDeviceSize) and is usually used for focused movement (that is, a focused node would move to the next/previous focusable node in the hierarchy with a swipe).

Parameters
curve: (timeMillis: Long) -> Offset

The function that describes the gesture. The argument passed to the function is the time in milliseconds since the start of the swipe, and the return value is the location of the pointer at that point in time.

durationMillis: Long = 200

The duration of the gesture (default duration is 200 milliseconds)

keyTimes: List<Long> = emptyList()

An optional list of timestamps in milliseconds at which a move event must be sampled

IndirectPointerInjectionScope.swipe

fun IndirectPointerInjectionScope.swipe(
    start: Offset,
    end: Offset,
    durationMillis: Long = 200
): Unit

Performs a swipe gesture on nodes in the focus path.

The motion events are linearly interpolated between start and end.

The coordinates are NOT in the node's local coordinate system (see IndirectPointerInjectionScope.inputDeviceSize) and is usually used for focused movement (that is, a focused node would move to the next/previous focusable node in the hierarchy with a swipe).

Parameters
start: Offset

The position of the pointer starting the swipe gesture, in the indirect pointer input device's coordinate system (IndirectPointerInjectionScope.inputDeviceSize).

end: Offset

The position of the pointer ending the swipe gesture, in the indirect pointer input device's coordinate system (IndirectPointerInjectionScope.inputDeviceSize).

durationMillis: Long = 200

The duration of the swipe gesture (default duration is 200 milliseconds)

IndirectPointerInjectionScope.swipeDown

fun IndirectPointerInjectionScope.swipeDown(
    startY: Float,
    endY: Float,
    durationMillis: Long = 200
): Unit

Performs a swipe down gesture on nodes in the focus path along x = [IndirectPointerInjectionScope.inputDeviceCenter].x, from startY till endY, taking durationMillis milliseconds.

Parameters
startY: Float

The y-coordinate of the start of the swipe. Must be less than or equal to the endY.

endY: Float

The y-coordinate of the end of the swipe. Must be greater than or equal to the startY.

durationMillis: Long = 200

The duration of the swipe. By default, 200 milliseconds.

IndirectPointerInjectionScope.swipeLeft

fun IndirectPointerInjectionScope.swipeLeft(
    startX: Float,
    endX: Float,
    durationMillis: Long = 200
): Unit

Performs a swipe left gesture on nodes in the focus path along y = [IndirectPointerInjectionScope.inputDeviceCenter].y, from startX till endX, taking durationMillis milliseconds.

Parameters
startX: Float

The x-coordinate of the start of the swipe. Must be greater than or equal to the endX.

endX: Float

The x-coordinate of the end of the swipe. Must be less than or equal to the startX.

durationMillis: Long = 200

The duration of the swipe. By default, 200 milliseconds.

IndirectPointerInjectionScope.swipeRight

fun IndirectPointerInjectionScope.swipeRight(
    startX: Float,
    endX: Float,
    durationMillis: Long = 200
): Unit

Performs a swipe right gesture on nodes in the focus path along y = [IndirectPointerInjectionScope.inputDeviceCenter].y, from startX till endX, taking durationMillis milliseconds.

Parameters
startX: Float

The x-coordinate of the start of the swipe. Must be less than or equal to the endX.

endX: Float

The x-coordinate of the end of the swipe. Must be greater than or equal to the startX.

durationMillis: Long = 200

The duration of the swipe. By default, 200 milliseconds.

IndirectPointerInjectionScope.swipeUp

fun IndirectPointerInjectionScope.swipeUp(
    startY: Float,
    endY: Float,
    durationMillis: Long = 200
): Unit

Performs a swipe up gesture on nodes in the focus path along x = [IndirectPointerInjectionScope.inputDeviceCenter].x, from startY till endY, taking durationMillis milliseconds.

Parameters
startY: Float

The y-coordinate of the start of the swipe. Must be greater than or equal to the endY.

endY: Float

The y-coordinate of the end of the swipe. Must be less than or equal to the startY.

durationMillis: Long = 200

The duration of the swipe. By default, 200 milliseconds.

IndirectPointerInjectionScope.swipeWithVelocity

fun IndirectPointerInjectionScope.swipeWithVelocity(
    start: Offset,
    end: Offset,
    endVelocity: @FloatRange(from = 0.0) Float,
    durationMillis: Long = VelocityPathFinder.calculateDefaultDuration(start, end, endVelocity)
): Unit

Performs a swipe gesture on nodes in the focus path such that it ends with the given endVelocity.

The swipe will go through start at t=0 and through end at t=durationMillis. In between, the swipe will go monotonically from start and end, but not strictly. Due to imprecision, no guarantees can be made for the actual velocity at the end of the gesture, but generally it is within 0.1 of the desired velocity.

When a swipe cannot be created that results in the desired velocity (because the input is too restrictive), an exception will be thrown with suggestions to fix the input.

The coordinates are NOT in the node's local coordinate system and is usually used for focused movement (that is, a focused node would move to the next focusable node in the hierarchy with a swipe).

The default duration is calculated such that a feasible swipe can be created that ends in the given velocity.

Parameters
start: Offset

The position of the pointer starting the swipe gesture, in the indirect pointer input device's coordinate system.

end: Offset

The position of the pointer ending the swipe gesture, in the indirect pointer input device's coordinate system.

endVelocity: @FloatRange(from = 0.0) Float

The velocity of the swipe gesture at the moment it ends in px/second. Must be positive.

durationMillis: Long = VelocityPathFinder.calculateDefaultDuration(start, end, endVelocity)

The duration of the swipe gesture in milliseconds. Must be long enough that at least 3 input events are generated, which happens with a duration of 40ms or more. If omitted, a duration is calculated such that a valid swipe with velocity can be created.

Throws
IllegalArgumentException

When no swipe can be generated that will result in the desired velocity. The error message will suggest changes to the input parameters such that a swipe will become feasible.

Extension properties

IndirectPointerInjectionScope.inputDeviceBottom

val IndirectPointerInjectionScope.inputDeviceBottomFloat

The y-coordinate for the bottom of the indirect pointer input device we're interacting with in px, where (0, 0) is the top left corner of the indirect pointer input device. Note: This is not related to the screen coordinates.

Note that, unless inputDeviceHeight == 0, inputDeviceBottom != inputDeviceHeight. In particular, inputDeviceBottom == inputDeviceHeight - 1f, because pixels are 0-based. If inputDeviceHeight == 0, inputDeviceBottom == 0 too.

IndirectPointerInjectionScope.inputDeviceBottomCenter

val IndirectPointerInjectionScope.inputDeviceBottomCenterOffset

The center of the bottom edge of the indirect pointer input device we're interacting with, where (0, 0) is the top left corner of the indirect pointer input device. Note: This is not related to the screen coordinates.

Note that inputDeviceBottomCenter.y != inputDeviceHeight, see inputDeviceBottom.

IndirectPointerInjectionScope.inputDeviceBottomLeft

val IndirectPointerInjectionScope.inputDeviceBottomLeftOffset

The bottom left corner of the indirect pointer input device we're interacting with, where (0, 0) is the top left corner of the indirect pointer input device. Note: This is not related to the screen coordinates.

Note that inputDeviceBottomLeft.y != inputDeviceHeight, see inputDeviceBottom.

IndirectPointerInjectionScope.inputDeviceBottomRight

val IndirectPointerInjectionScope.inputDeviceBottomRightOffset

The bottom right corner of the indirect pointer input device we're interacting with, where (0, 0) is the top left corner of the indirect pointer input device. Note: This is not related to the screen coordinates.

Note that inputDeviceBottomRight.x != inputDeviceWidth and inputDeviceBottomRight.y != inputDeviceHeight, see inputDeviceRight and inputDeviceBottom.

IndirectPointerInjectionScope.inputDeviceCenter

val IndirectPointerInjectionScope.inputDeviceCenterOffset

The center of the indirect pointer input device we're interacting with, where (0, 0) is the top left corner of the indirect pointer input device. Note: This is not related to the screen coordinates.

IndirectPointerInjectionScope.inputDeviceCenterLeft

val IndirectPointerInjectionScope.inputDeviceCenterLeftOffset

The center of the left edge of the indirect pointer input device we're interacting with, where (0, 0) is the top left corner of the indirect pointer input device. Note: This is not related to the screen coordinates.

IndirectPointerInjectionScope.inputDeviceCenterRight

val IndirectPointerInjectionScope.inputDeviceCenterRightOffset

The center of the right edge of the indirect pointer input device we're interacting with, where (0, 0) is the top left corner of the indirect pointer input device. Note: This is not related to the screen coordinates.

Note that inputDeviceCenterRight.x != inputDeviceWidth, see inputDeviceRight.

IndirectPointerInjectionScope.inputDeviceCenterX

val IndirectPointerInjectionScope.inputDeviceCenterXFloat

The x-coordinate for the center of the indirect pointer input device we're interacting with in px, where (0, 0) is the top left corner of the indirect pointer input device. Note: This is not related to the screen coordinates.

IndirectPointerInjectionScope.inputDeviceCenterY

val IndirectPointerInjectionScope.inputDeviceCenterYFloat

The y-coordinate for the center of the indirect pointer input device we're interacting with in px, where (0, 0) is the top left corner of the indirect pointer input device. Note: This is not related to the screen coordinates.

IndirectPointerInjectionScope.inputDeviceHeight

val IndirectPointerInjectionScope.inputDeviceHeightInt

The height of the external indirect pointer input device that provide the height boundary for indirect input. If you go outside this height, the tests will throw an exception. Note: This is not related to the screen coordinates.

IndirectPointerInjectionScope.inputDeviceLeft

val IndirectPointerInjectionScope.inputDeviceLeftFloat

The x-coordinate for the left edge of the indirect pointer input device we're interacting with in px, where (0, 0) is the top left corner of the indirect pointer input device. Note: This is not related to the screen coordinates.

IndirectPointerInjectionScope.inputDeviceRight

val IndirectPointerInjectionScope.inputDeviceRightFloat

The x-coordinate for the right edge of the indirect pointer input device we're interacting with in px, where (0, 0) is the top left corner of the indirect pointer input device. Note: This is not related to the screen coordinates.

Note that, unless inputDeviceWidth == 0, inputDeviceRight != inputDeviceWidth. In particular, inputDeviceRight == inputDeviceWidth - 1f, because pixels are 0-based. If inputDeviceWidth == 0, inputDeviceRight == 0 too.

IndirectPointerInjectionScope.inputDeviceTop

val IndirectPointerInjectionScope.inputDeviceTopFloat

The y-coordinate for the top of the indirect pointer input device we're interacting with in px, where (0, 0) is the top left corner of the indirect pointer input device. Note: This is not related to the screen coordinates.

IndirectPointerInjectionScope.inputDeviceTopCenter

val IndirectPointerInjectionScope.inputDeviceTopCenterOffset

The center of the top edge of the indirect pointer input device we're interacting with, where (0, 0) is the top left corner of the indirect pointer input device. Note: This is not related to the screen coordinates.

IndirectPointerInjectionScope.inputDeviceTopLeft

val IndirectPointerInjectionScope.inputDeviceTopLeftOffset

The top left corner of the indirect pointer input device we're interacting with, where (0, 0) is the top left corner of the indirect pointer input device. Note: This is not related to the screen coordinates.

IndirectPointerInjectionScope.inputDeviceTopRight

val IndirectPointerInjectionScope.inputDeviceTopRightOffset

The top right corner of the indirect pointer input device we're interacting with, where (0, 0) is the top left corner of the indirect pointer input device. Note: This is not related to the screen coordinates.

Note that inputDeviceTopRight.x != inputDeviceWidth, see inputDeviceRight.

IndirectPointerInjectionScope.inputDeviceWidth

val IndirectPointerInjectionScope.inputDeviceWidthInt

The width of the external indirect pointer input device that provide the width boundary for indirect input. If you go outside this width, the tests will throw an exception. Note: This is not related to the screen coordinates.