Python SDK
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.
- Windows
- Mac
- Emulator
- Cloud device
- Python SDK
On this page
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. If you describe the task to MAS 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() returns the width and height of the connected device, so you can compute positions instead of hard-coding them.
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 screenHard-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) 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.
import mas
mas.click(150, 300) # tap, then wait 1000 ms
mas.click(150, 300, delay_ms=250) # tap, then wait 250 msThe usual pattern is to tap the center of a template match rather than a fixed point. See Vision for the matching side.
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) 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.
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-dropMind the direction. To see content further down a list, the finger moves up.
input_text
input_text(text, delay_ms=0, clear=False) 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.
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)key_press
key_press(key_code, modifiers=None, duration_ms=100, repeat=1) presses a hardware key. key_code is a member of KeyCode, and modifiers is an optional list of 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=3000andduration_ms=500do the same thing.repeat: how many times to press the key, from 1 to 100. All presses go to the device in one command, sorepeat=10is much faster than ten calls in a loop.
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) and zoom_out(center=None, percent=50, duration_ms=0, steps=10) 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. Forzoom_init is how far the fingers spread; forzoom_outit is how far apart they start.duration_ms: total gesture time. The default0runs the gesture as fast as the device executes it.steps: finger movement steps, from 2 to 100. More steps make a smoother gesture.
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 outHumanized 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.
- Let
delay_msdo the waiting after a tap instead of stackingtime.sleepcalls. One second is a fair default; shorten it only where you know the app responds at once. - Vary the pauses. A fixed 1000 ms between every action is a mechanical signature.
random.uniformgives each pause a little spread. - Wait for the screen, not the clock. After an action that changes the screen, look for the next template with
find_object_retryrather than sleeping a fixed amount.
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.
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.
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 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.
Next steps
Related pages
Thanks. If something is wrong, tell us in Discord.
Questions? Ask in Discord