# Python ADB Tap, Swipe, Type and Key Press in MAS

> Python ADB tap, swipe, type and key press on an Android emulator with the MAS SDK, using the real defaults, humanized timing and two worked examples.

Source: https://automationmacro.com/docs/sdk/interaction (Python SDK, updated 2026-09-04)

The interaction functions are how a Macro Automation Studio (MAS) macro touches the screen from Python. Each tap, swipe and key press reaches the device as a real input event, over adb on an emulator, so an app sees it as a finger would. This page explains each function, its defaults, and the timing habits that keep a macro readable and believable. All of them live at the top of the `mas` package. Full signatures are on the [API reference](/docs/api-reference). If you describe the task to [MAS Agent](/agent) instead, it writes this code for you.

## Python ADB tap coordinates and screen size

Every coordinate is a pixel offset from the top-left corner of the screen. `x` grows to the right and `y` grows downward. [`get_screen_size()`](/docs/api-reference#get_screen_size) returns the width and height of the connected device, so you can compute positions instead of hard-coding them.

```python
import mas

size = mas.get_screen_size()
mas.click(size.width // 2, size.height // 2)            # center of the screen
mas.swipe((size.width // 2, int(size.height * 0.8)),
          (size.width // 2, int(size.height * 0.2)))    # scroll down one screen
```

Hard-coded coordinates are tied to the resolution you wrote them on, just like template images. Keep every device that runs a macro on the same resolution, or derive positions from `get_screen_size()` and from `find_object` matches. Points you pick in Asset Lab are already in device pixels.

## click

[`click(x, y, delay_ms=1000)`](/docs/api-reference#click) taps once at a point, then waits `delay_ms` milliseconds before returning. The default pause is a full second, which gives most apps time to react before your next line runs. Shorten it for quick menus and lengthen it for screens that animate.

```python
import mas

mas.click(150, 300)                  # tap, then wait 1000 ms
mas.click(150, 300, delay_ms=250)    # tap, then wait 250 ms
```

The usual pattern is to tap the center of a template match rather than a fixed point. See [Vision](/docs/sdk/vision) for the matching side.

```python
import mas

images = mas.images({"claim_btn": 784})
match = mas.find_object_retry(images.claim_btn, total_tries=3, time_sleep=2.0)
if match:
    mas.click(match.x, match.y)
```

There is no separate long-press or double-tap function. To hold a hardware key, use `key_press` with a `duration_ms` of 500 or more.

## swipe

[`swipe(from_coords, to_coords, duration_ms=1000)`](/docs/api-reference#swipe) drags one finger from a start point to an end point. Both points are `(x, y)` tuples. `duration_ms` is how long the finger stays down, so it sets the speed: a short swipe flicks and scrolls further, a long swipe drags.

```python
import mas

mas.swipe((500, 1500), (500, 500), duration_ms=400)    # swipe up: the list scrolls down
mas.swipe((100, 500), (900, 500), duration_ms=300)     # swipe right: previous page
mas.swipe((200, 300), (800, 300), duration_ms=1000)    # slow drag, for drag-and-drop
```

Mind the direction. To see content further down a list, the finger moves up.

## input_text

[`input_text(text, delay_ms=0, clear=False)`](/docs/api-reference#input_text) types into the field that currently has focus. Tap the field first; typing into nothing does nothing. With `clear=True` the cursor moves to the end of the field and the existing characters are deleted before typing, so the field does not need to be empty. `delay_ms` waits after typing and defaults to 0.

```python
import mas

mas.click(540, 420)                            # focus the field
mas.input_text("user@example.com")
mas.input_text("new value", clear=True)        # replace whatever was there
mas.input_text("SecurePassword123!", delay_ms=500)
```

> [!NOTE]
> `clear=True` clears by sending a run of backspaces (up to 250 characters). Holding the DELETE key with `key_press` does not clear a field, so use `clear=True` whenever you need a clean field.

## key_press

[`key_press(key_code, modifiers=None, duration_ms=100, repeat=1)`](/docs/api-reference#key_press) presses a hardware key. `key_code` is a member of [`KeyCode`](/docs/api-reference#keycode), and `modifiers` is an optional list of [`Modifier`](/docs/api-reference#modifier) members: `SHIFT`, `CTRL`, `ALT` and `META`.

Two parameters need care:

- `duration_ms`: a value of 500 or more sends the key as a long press; anything smaller is a normal press. The Android input bridge has no arbitrary-hold primitive, so the exact number of milliseconds is not honored beyond that distinction. `duration_ms=3000` and `duration_ms=500` do the same thing.
- `repeat`: how many times to press the key, from 1 to 100. All presses go to the device in one command, so `repeat=10` is much faster than ten calls in a loop.

```python
import mas
from mas import KeyCode, Modifier

mas.key_press(KeyCode.BACK)                        # back button
mas.key_press(KeyCode.HOME)                        # home button
mas.key_press(KeyCode.ENTER)                       # submit a form
mas.key_press(KeyCode.POWER, duration_ms=3000)     # long press
mas.key_press(KeyCode.DELETE, repeat=10)           # delete 10 characters
mas.key_press(KeyCode.TAB, modifiers=[Modifier.SHIFT])
```

| Group | Members |
|---|---|
| Navigation and text | `BACK`, `HOME`, `MENU`, `APP_SWITCH`, `ENTER`, `TAB`, `SPACE`, `DELETE` |
| Hardware | `POWER`, `CAMERA`, `VOLUME_UP`, `VOLUME_DOWN`, `BRIGHTNESS_UP`, `BRIGHTNESS_DOWN` |
| D-pad | `DPAD_UP`, `DPAD_DOWN`, `DPAD_LEFT`, `DPAD_RIGHT`, `DPAD_CENTER` |
| Media | `MEDIA_PLAY`, `MEDIA_PAUSE`, `MEDIA_NEXT`, `MEDIA_PREVIOUS` |
| Numbers | `NUM_0` to `NUM_9` |

There is no `ESCAPE` member; use `BACK`. `DELETE` sends `KEYCODE_DEL`, which is backspace.

## zoom_in and zoom_out

[`zoom_in(center=None, percent=50, duration_ms=0, steps=10)`](/docs/api-reference#zoom_in) and [`zoom_out(center=None, percent=50, duration_ms=0, steps=10)`](/docs/api-reference#zoom_out) perform two-finger pinch gestures. The gesture is synthesized through the emulator's touch input device, which MAS detects automatically.

- `center`: the `(x, y)` point the fingers work around. Defaults to the center of the screen.
- `percent`: intensity from 1 to 100, as a share of the largest pinch that fits around the center. For `zoom_in` it is how far the fingers spread; for `zoom_out` it is how far apart they start.
- `duration_ms`: total gesture time. The default `0` runs the gesture as fast as the device executes it.
- `steps`: finger movement steps, from 2 to 100. More steps make a smoother gesture.

```python
import mas

mas.zoom_in()                                    # zoom in at the screen center
mas.zoom_in(center=(540, 960), percent=80)       # strong zoom at a point
mas.zoom_out(center=(540, 960), percent=30)      # gentle zoom out
```

## Humanized timing in ADB Python scripting

MAS sends taps and swipes exactly when you ask for them, so the rhythm of a macro is yours to design. Three habits keep it believable and robust.

1. Let `delay_ms` do the waiting after a tap instead of stacking `time.sleep` calls. One second is a fair default; shorten it only where you know the app responds at once.
2. Vary the pauses. A fixed 1000 ms between every action is a mechanical signature. `random.uniform` gives each pause a little spread.
3. Wait for the screen, not the clock. After an action that changes the screen, look for the next template with `find_object_retry` rather than sleeping a fixed amount.

```python
import random
import time
import mas

def pause(low=0.6, high=1.4):
    time.sleep(random.uniform(low, high))

mas.click(540, 1200, delay_ms=random.randint(700, 1300))
pause()
mas.swipe((540, 1500), (540, 600), duration_ms=random.randint(350, 600))
```

## Example: type into a search field

Focus the field with a template match, replace any old query, submit with the keyboard, then wait for the results.

```python
import mas
from mas import KeyCode

images = mas.images({"search_box": 201, "first_result": 202})

box = mas.find_object_retry(images.search_box, total_tries=3, time_sleep=2.0)
if box is None:
    mas.log("search box not on screen", level="warning")
    raise SystemExit(1)

mas.click(box.x, box.y, delay_ms=500)           # focus the field
mas.input_text("blue widgets", clear=True)      # replace any old query
mas.key_press(KeyCode.ENTER)

result = mas.find_object_retry(images.first_result, total_tries=5, time_sleep=1.5)
if result:
    mas.click(result.x, result.y)
```

## Example: scroll until found

Search, swipe, search again, and give up after a fixed number of swipes so the macro never loops forever.

```python
import mas

images = mas.images({"settings_row": 310})
size = mas.get_screen_size()
x = size.width // 2

match = None
for _ in range(8):                               # at most 8 swipes
    match = mas.find_object(images.settings_row, threshold=0.85)
    if match:
        break
    mas.swipe((x, int(size.height * 0.75)), (x, int(size.height * 0.25)), duration_ms=600)

if match:
    mas.click(match.x, match.y)
else:
    mas.log("row not found after 8 swipes", level="error")
```

In apps that expose an accessibility tree, [`scroll_to_element`](/docs/sdk/elements) does the same loop for you by element text or resource id.

## Errors

Every interaction call raises `DeviceNotConnectedError` when no device is bound to the run and `CommandFailedError` when the device rejects the gesture. Both are subclasses of `RPCError`, so one `except mas.RPCError` catches them. The full hierarchy is on [Errors and exceptions](/docs/sdk/errors).
