Compose Multiplatform Patterns
monta-app/ocpp-emulator
Compose Multiplatform and Jetpack Compose patterns for KMP projects — state management, navigation, theming, performance, and platform-specific UI.
A skill your agent uses to drive Jetpack Compose UI with synthetic touch events through performTouchInput and the TouchInjectionScope DSL — click, longClick, doubleClick, swipe, swipeUp / swipeDown…
$ npx skills add skydoves/android-testing-skills --skill injecting-touch-gestures -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install skydoves/android-testing-skills injecting-touch-gestures --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/skydoves/android-testing-skills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/compose/actions/injecting-touch-gestures .claude/skills/injecting-touch-gestures && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "injecting-touch-gestures" agent skill from https://github.com/skydoves/android-testing-skills/tree/main/compose/actions/injecting-touch-gestures into .claude/skills/injecting-touch-gestures/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "injecting-touch-gestures", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/skydoves/android-testing-skills/tree/main/compose/actions/injecting-touch-gesturesType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add skydoves/android-testing-skills --skill injecting-touch-gestures -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install skydoves/android-testing-skills injecting-touch-gestures --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/skydoves/android-testing-skills.git skills-src && mkdir -p .agents/skills && cp -r skills-src/compose/actions/injecting-touch-gestures .agents/skills/injecting-touch-gestures && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "injecting-touch-gestures" agent skill from https://github.com/skydoves/android-testing-skills/tree/main/compose/actions/injecting-touch-gestures into .agents/skills/injecting-touch-gestures/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "injecting-touch-gestures", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add skydoves/android-testing-skills --skill injecting-touch-gestures -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install skydoves/android-testing-skills injecting-touch-gestures --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/skydoves/android-testing-skills.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/compose/actions/injecting-touch-gestures .cursor/skills/injecting-touch-gestures && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "injecting-touch-gestures" agent skill from https://github.com/skydoves/android-testing-skills/tree/main/compose/actions/injecting-touch-gestures into .cursor/skills/injecting-touch-gestures/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "injecting-touch-gestures", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/skydoves/android-testing-skills.git --path compose/actions/injecting-touch-gestures--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add skydoves/android-testing-skills --skill injecting-touch-gestures -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install skydoves/android-testing-skills injecting-touch-gestures --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/skydoves/android-testing-skills.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/compose/actions/injecting-touch-gestures .gemini/skills/injecting-touch-gestures && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "injecting-touch-gestures" agent skill from https://github.com/skydoves/android-testing-skills/tree/main/compose/actions/injecting-touch-gestures into .gemini/skills/injecting-touch-gestures/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "injecting-touch-gestures", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install skydoves/android-testing-skills injecting-touch-gesturesInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add skydoves/android-testing-skills --skill injecting-touch-gestures -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/skydoves/android-testing-skills.git skills-src && mkdir -p .github/skills && cp -r skills-src/compose/actions/injecting-touch-gestures .github/skills/injecting-touch-gestures && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "injecting-touch-gestures" agent skill from https://github.com/skydoves/android-testing-skills/tree/main/compose/actions/injecting-touch-gestures into .github/skills/injecting-touch-gestures/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "injecting-touch-gestures", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add skydoves/android-testing-skills --skill injecting-touch-gestures -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install skydoves/android-testing-skills injecting-touch-gestures --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/skydoves/android-testing-skills.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/compose/actions/injecting-touch-gestures .opencode/skills/injecting-touch-gestures && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "injecting-touch-gestures" agent skill from https://github.com/skydoves/android-testing-skills/tree/main/compose/actions/injecting-touch-gestures into .opencode/skills/injecting-touch-gestures/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "injecting-touch-gestures", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
injecting-touch-gesturesA skill your agent uses to drive Jetpack Compose UI with synthetic touch events through performTouchInput and the TouchInjectionScope DSL — click, longClick, doubleClick, swipe, swipeUp / swipeDown…
Injecting Touch Gestures is an agent skill from skydoves/android-testing-skills. Use this skill to drive Jetpack Compose UI with synthetic touch events through performTouchInput and the TouchInjectionScope DSL — click, longClick, doubleClick, swipe, swipeUp / swipeDown / swipeLeft / swipeRight, swipeWithVelocity, pinch, multiTouchSwipe, plus the low-level down / moveTo / moveBy / move / up / cancel primitives. Covers node-local coordinates, the geometry helpers (center, topLeft, bottomRight, percentOffset), event batching, splitting a gesture across multiple performTouchInput blocks, and why…
Its SKILL.md is about 3.7k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Mobile, covering Android development. It works with Jetpack Compose. The repository describes itself as: ⚡️ A set of skills for Android testing: Compose UI, AndroidX Test, JVM unit tests, and ADB. The licence is Apache-2.0.
3 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit 8665ed5. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
gitFrom the folder's file list and the shell code blocks in SKILL.md.
Links to these hosts (documentation or services it may open):
developer.android.comgithub.comFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Injecting Touch Gestures loads about 3.7k tokens when it runs. Until then it costs about 217 tokens; SKILL.md has 1,110 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from skydoves/android-testing-skills at commit 8665ed5, republished under its Apache-2.0 licence (© skydoves). 1,110 words, ~3,728 tokens.
.claude/skills/injecting-touch-gestures/SKILL.md (or your agent's skills folder).performTouchInput { … } is the single entry point for synthetic touch events in Compose tests. It runs a block in a TouchInjectionScope whose coordinate system is node-local (origin at the node's top-left), batches every event the block enqueues, and flushes them as a single non-recomposing burst when the block returns. The legacy performGesture { … } is @Deprecated("Replaced by performTouchInput") (Actions.kt:337-355) — MUST NOT appear in any new code.
center).performTouchInput blocks while a finger is still "down".performGesture { } and must be migrated.performClick() from ../clicking-and-scrolling/SKILL.md. It is platform-appropriate and shorter.../injecting-mouse-and-keyboard/SKILL.md.../../assertions/asserting-node-state-and-text/SKILL.md, not by inspecting touch state.../../synchronization/testing-animations-deterministically/SKILL.md.androidx.compose.ui:ui-test-junit4 (or ui-test for runComposeUiTest) configured per ../../setup/configuring-test-dependencies/SKILL.md.Modifier.testTag("…") from production code so the test selects it deterministically.pinch, multiTouchSwipe), the target composable accepts pointer input via Modifier.pointerInput { … } — typically Modifier.transformable, Modifier.draggable2D, or a custom detectTransformGestures.performTouchInput is an extension on SemanticsNodeInteraction. Find the node first.rule.onNodeWithTag(DragSurfaceTag).performTouchInput { /* DSL */ }Use node-local coordinates. Inside the block, (0, 0) is the node's top-left. Reach for the geometry helpers from InjectionScope (InjectionScope.kt:34-206): center, topLeft, topCenter, topRight, centerLeft, centerRight, bottomLeft, bottomCenter, bottomRight, width, height, plus right == width - 1f and bottom == height - 1f (pixels are 0-based — InjectionScope.kt:96-112). Use percentOffset(.5f, .5f) for relative positioning. MUST NOT hardcode screen coordinates.
Pick the right level of abstraction. High-level helpers cover most cases:
| Helper | Default | What it does |
|---|---|---|
click(position = center) | tap at center | down(); move(); up() (TouchInjectionScope.kt:372) |
longClick(position = center, durationMillis = vc.longPressTimeoutMillis + 100) | 100 ms past the long-press timeout | press, hold, release (TouchInjectionScope.kt:390) |
doubleClick(position = center, delayMillis = …) | midway between min/max double-tap window | click(); advanceEventTime(delay); click() (TouchInjectionScope.kt:417) |
swipe(start, end, durationMillis = 200) | 200 ms linear | linear interpolation between two points (TouchInjectionScope.kt:445) |
swipeUp / swipeDown / swipeLeft / swipeRight | along axis through center | edge-to-edge sweeps (TouchInjectionScope.kt:649+) |
swipeWithVelocity(start, end, endVelocity, durationMillis) | computed for feasibility | shapes the path so the final velocity matches (TouchInjectionScope.kt:617) |
pinch(start0, end0, start1, end1, durationMillis = 400) | 400 ms two-pointer | two simultaneous linear paths (TouchInjectionScope.kt:574) |
multiTouchSwipe(curves, durationMillis, keyTimes) | — | one curve per pointer id (TouchInjectionScope.kt:491) |
Drop to low-level events for partial gestures. All in TouchInjectionScope (TouchInjectionScope.kt):
down(pointerId = 0, position) — start a pointer at position.moveTo(pointerId = 0, position, delayMillis = eventPeriodMillis) — enqueue a move event.moveBy(pointerId = 0, delta, delayMillis = eventPeriodMillis) — relative form.updatePointerTo(pointerId, position) / updatePointerBy(pointerId, delta) — adjust the pointer position without enqueueing a move; useful when batching multiple pointer updates into a single move(delayMillis) call.move(delayMillis = eventPeriodMillis) — flush a move event combining every pending pointer update.up(pointerId = 0) — release.cancel(delayMillis = eventPeriodMillis) — emit a MotionEvent.ACTION_CANCEL-equivalent.currentPosition(pointerId = 0): Offset? — query the last-known position of a pointer.Split gestures across blocks when recomposition matters mid-gesture. Pointer state is shared across performTouchInput invocations on the same node — a pointer left "down" stays down. Use this to assert state at a frame the gesture passes through.
rule.onNodeWithTag(Tag).performTouchInput { down(center) }
// recomposition + assertions happen here
rule.onNodeWithTag(Tag).performTouchInput { moveBy(Offset(0f, 100f)); up() }Subsequent invocations on a different node remap pointer positions into that node's local space (TouchInjectionScope.kt:50-55), so positions are still local, not screen-global.
performTouchInput block is sent as one batch when the block returns; recomposition cannot interleave inside a block (Actions.kt:362-377). That is why all coordinates are resolved up front — and why a moving target does not invalidate the gesture inside one block.// WRONG
@Suppress("DEPRECATION")
rule.onNodeWithTag(Tag).performGesture { swipeUp() }
// WRONG because: performGesture is @Deprecated("Replaced by performTouchInput") (Actions.kt:337).
// The replaceWith metadata in the @Deprecated annotation explicitly suggests performTouchInput.// RIGHT
rule.onNodeWithTag(Tag).performTouchInput { swipeUp() }This is a non-negotiable migration. performGesture exists only for binary compatibility.
// WRONG
rule.onNodeWithTag(Tag).performTouchInput {
down(Offset(500f, 800f))
moveTo(Offset(500f, 700f))
up()
}
// WRONG because: (500, 800) only happens to land on the node on certain device profiles.
// On a tablet or in landscape the node sits elsewhere, and on a small phone it may not even
// be inside the visible bounds — the test becomes device-shaped.// RIGHT
rule.onNodeWithTag(Tag).performTouchInput {
down(center)
moveBy(Offset(0f, -100f))
up()
}center, bottomCenter, topRight, etc. are derived from visibleSize of the node (InjectionScope.kt:49-50, :118-185). The test runs unchanged across screen sizes.
The canonical horizontal-drag pattern from DraggableTest.kt:114-146:
@Test
fun draggable_horizontalDrag() {
var total = 0f
setDraggableContent { Modifier.draggable(Orientation.Horizontal) { total += it } }
rule.onNodeWithTag(draggableBoxTag).performTouchInput {
this.swipe(
start = this.center,
end = Offset(this.center.x + 100f, this.center.y),
durationMillis = 100,
)
}
val lastTotal = rule.runOnIdle {
assertThat(total).isGreaterThan(0)
total
}
}Notes worth copying: this.center makes the receiver explicit (handy when nesting blocks); durationMillis = 100 keeps the test fast; the assertion runs inside runOnIdle (skydoves hot take #5).
From TransformableTest.kt:128-141:
rule.onNodeWithTag(TEST_TAG).performTouchInput {
val leftStartX = center.x - 10
val leftEndX = visibleSize.toSize().width * EDGE_FUZZ_FACTOR
val rightStartX = center.x + 10
val rightEndX = visibleSize.toSize().width * (1 - EDGE_FUZZ_FACTOR)
pinch(
Offset(leftStartX, center.y),
Offset(leftEndX, center.y),
Offset(rightStartX, center.y),
Offset(rightEndX, center.y),
)
}pinch(start0, end0, start1, end1) interpolates two pointers in parallel for durationMillis = 400 (TouchInjectionScope.kt:574-589). Use EDGE_FUZZ_FACTOR (e.g. 0.05f) instead of 0f to avoid landing exactly on the right/bottom edge — right == width - 1f, see InjectionScope.kt:96-102.
@Test fun longPress_thenAssert_thenRelease() {
rule.setContent { /* … */ }
// Hold finger down at center, do not release.
rule.onNodeWithTag(Tag).performTouchInput { down(center) }
// Wait for the long-press timeout to elapse via the test clock.
rule.mainClock.advanceTimeBy(viewConfiguration.longPressTimeoutMillis + 100)
rule.onNodeWithTag(Tag).assertHasIndicationOfLongPress() // your assertion
// Release in a second block; the pointer is still "down" between blocks.
rule.onNodeWithTag(Tag).performTouchInput { up() }
}Pointer state survives across blocks; the recomposer runs between them. This is how CombinedClickableTest exercises haptic-feedback-on-long-press paths (e.g. CombinedClickableTest.kt:446-471).
rule.onNodeWithTag(Tag).performTouchInput {
swipeWithVelocity(
start = bottomCenter,
end = topCenter,
endVelocity = 4_000f, // px/second
durationMillis = 200,
)
}swipeWithVelocity shapes the path so the final velocity is within ~0.1 of the target (TouchInjectionScope.kt:617-637). The duration must be long enough for at least 3 input events (~40 ms minimum); the API throws IllegalArgumentException with a fix suggestion if the input is infeasible.
performTouchInput { … }. MUST NOT use performGesture { … } — it is @Deprecated (Actions.kt:337-355). This is non-negotiable.center, topLeft, bottomRight, percentOffset(...), etc. MUST NOT hardcode screen pixel coordinates — the test will be device-shaped.rule.runOnIdle { … } (skydoves hot take #5). Reading state on the test thread races with the recomposer that processed the gesture.rule.mainClock.advanceTimeBy(...) between split performTouchInput blocks when the test depends on a duration (long-press, double-tap window). MUST NOT use Thread.sleep — see ../../synchronization/synchronizing-with-idle/SKILL.md.center and use moveBy with relative deltas. Absolute paths (moveTo(topLeft)) work but read worse and break under RTL layouts.swipe for monotonic linear motion, swipeWithVelocity only when fling behaviour is under test, and multiTouchSwipe only for >2 pointers or non-linear curves.performGesture in the test source set: git grep -n 'performGesture' src/androidTest src/test returns nothing.Thread.sleep inside or between performTouchInput blocks (skydoves hot take #7).Offset(<screen-pixel>, <screen-pixel>) in the test — every position resolves through center, top*, bottom*, centerLeft, etc.rule.runOnIdle { … }../gradlew :app:connectedDebugAndroidTest (or :app:testDebugUnitTest) passes for the test under change.compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Actions.kt — performTouchInput (Actions.kt:399), performGesture deprecation (Actions.kt:337-355).compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/TouchInjectionScope.kt — DSL surface (down, moveTo, moveBy, up, cancel, click, longClick, doubleClick, swipe, swipeUp / Down / Left / Right, swipeWithVelocity, pinch, multiTouchSwipe).compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/InjectionScope.kt — geometry helpers (center, topLeft, topCenter, bottomRight, width, height, right, bottom, percentOffset, visibleSize, advanceEventTime).compose/foundation/foundation/src/androidDeviceTest/.../DraggableTest.kt — canonical swipe(start, end, durationMillis) (DraggableTest.kt:114-146).compose/foundation/foundation/src/androidDeviceTest/.../TransformableTest.kt — pinch(...) four-point gesture (TransformableTest.kt:128-141).compose/foundation/foundation/src/androidDeviceTest/.../CombinedClickableTest.kt — performTouchInput { longClick() }, split down(center) / up() blocks for haptic tests.compose/foundation/foundation/src/androidDeviceTest/.../ScrollableAreaTest.kt — low-level down(); moveBy() flow.© skydoves, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in compose/actions/injecting-touch-gestures of skydoves/android-testing-skills.
Open the folder on GitHubat commit 8665ed5
Injecting Touch Gestures next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Injecting Touch Gestures this skillskydoves/android-testing-skills | 333 | — | ~3.7k | Automated safety check: Pass | Apache-2.0 | |
| Compose Multiplatform Patternsmonta-app/ocpp-emulator | 180 | 5 repos | ~2k | Automated safety check: Pass | Apache-2.0 | |
| Deferring State ReadsrosuH/EasyWatermark | 1.9k | 1 repos | ~3.8k | Automated safety check: Pass | Apache-2.0 | |
| Android Developmentdpconde/claude-android-skill | 336 | — | ~1.7k | Automated safety check: Pass | MIT | |
| Footgun Scanmaxrave-dev/kotlin-footguns | 1k | 1 repos | ~518 | Automated safety check: Pass | GPL-3.0 | |
| Diagnosing Compose StabilityrosuH/EasyWatermark | 1.9k | 1 repos | ~3.3k | Automated safety check: Pass | Apache-2.0 |
monta-app/ocpp-emulator
Compose Multiplatform and Jetpack Compose patterns for KMP projects — state management, navigation, theming, performance, and platform-specific UI.
rosuH/EasyWatermark
A skill your agent uses to push frequently-changing Jetpack Compose state reads (scroll position, animation values, drag offsets) out of the Composition phase and down into Layout or Draw using…
dpconde/claude-android-skill
Create production-quality Android applications following Google's official architecture guidance and NowInAndroid best practices.
maxrave-dev/kotlin-footguns
Scan a Kotlin or Compose Multiplatform diff for known footgun shapes and open the matching trap to confirm each hit.
rosuH/EasyWatermark
A skill your agent uses to diagnose Jetpack Compose stability problems by enabling and reading the Compose Compiler Reports (classes.txt, composables.txt, composables.csv, module.json).
arindamxd/camerax-android
A skill your agent uses to integrate the Jetpack Compose Styles API into an Android project.
skydoves/android-testing-skills
A skill your agent uses to verify Compose layout measurements from a UI test using assertWidthIsEqualTo, assertHeightIsEqualTo, assertWidthIsAtLeast, assertHeightIsAtLeast…
skydoves/android-testing-skills
A skill your agent uses to verify a Compose semantics node's properties from a UI test using assertExists, assertDoesNotExist, assertIsDisplayed, assertIsNotDisplayed, assertIsDeactivated…
skydoves/android-testing-skills
A skill your agent uses to render every Jetpack Compose @Preview as a screenshot on a real Android device or emulator and publish a browsable HTML catalog from CI.
skydoves/android-testing-skills
A skill your agent uses to capture visual artefacts from a device for test failures, golden image generation, QA repro, and demo videos.
skydoves/android-testing-skills
A skill your agent uses to pick the correct Compose UI test entry point.
skydoves/android-testing-skills
A skill your agent uses to pick which behaviors to cover in an Android test suite using Google's five-category state vocabulary plus the explicit "what NOT to test" list from…
Works with
Categories
A skill your agent uses to drive Jetpack Compose UI with synthetic touch events through performTouchInput and the TouchInjectionScope DSL — click, longClick, doubleClick, swipe, swipeUp / swipeDown…. Injecting Touch Gestures is an agent skill from skydoves/android-testing-skills. Use this skill to drive Jetpack Compose UI with synthetic touch events through performTouchInput and the TouchInjectionScope DSL — click, longClick, doubleClick, swipe, swipeUp / swipeDown / swipeLeft / swipeRight, swipeWithVelocity, pinch, multiTouchSwipe, plus the low-level down / moveTo / moveBy / move / up / cancel primitives.
Injecting Touch Gestures fits situations like: drive Jetpack Compose UI with synthetic touch events through performTouchInput and the TouchInjectionScope DSL — click; swipeUp / swipeDown / swipeLeft / swipeRight; swipeWithVelocity; multiTouchSwipe.
Run `npx skills add skydoves/android-testing-skills --skill injecting-touch-gestures -a claude-code`. Or copy the skill folder (compose/actions/injecting-touch-gestures in skydoves/android-testing-skills) into .claude/skills/injecting-touch-gestures in your project. Claude Code loads it when a task matches its description.
Run `npx skills add skydoves/android-testing-skills --skill injecting-touch-gestures -a codex`. Or copy the skill folder (compose/actions/injecting-touch-gestures in skydoves/android-testing-skills) into .agents/skills/injecting-touch-gestures in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add skydoves/android-testing-skills --skill injecting-touch-gestures -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/injecting-touch-gestures, .gemini/skills/injecting-touch-gestures, .github/skills/injecting-touch-gestures and .opencode/skills/injecting-touch-gestures in your project.
Going by SKILL.md and its folder, Injecting Touch Gestures needs the command-line tools its instructions call (git).
SKILL.md names 2 domains. As links in the text: developer.android.com and github.com. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Injecting Touch Gestures is published under the Apache-2.0 licence (declared in SKILL.md). It allows redistribution, so the full SKILL.md is shown on this page.
About 3.7k tokens (SKILL.md is roughly 15k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Injecting Touch Gestures: Compose Multiplatform Patterns (monta-app/ocpp-emulator, 180 stars), Deferring State Reads (rosuH/EasyWatermark, 1.9k stars), Android Development (dpconde/claude-android-skill, 336 stars) and Footgun Scan (maxrave-dev/kotlin-footguns, 1k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
skydoves (a GitHub user) maintains it in skydoves/android-testing-skills, which has 333 GitHub stars. The repository holds 50 skills in this directory. The repository was last updated on May 25, 2026.
Source: skydoves/android-testing-skills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.