GUI-Swift-Mobile-2B / bundle /source /native134_executor.py
dongjintao's picture
Release GUI-Swift-Mobile-2B: Mobile105 SFT step4658
b7fc81a verified
Raw History Blame Contribute Delete
3.49 kB
"""Explicit execution plans for Native134. No device is touched on import.
Desktop backend is PyAutoGUI plus injected Unicode paste. Android backend is
an injected argv-only ADB runner plus Unicode type callback. Lifecycle callbacks
are explicit, so interact/answer/failure cannot silently become task success.
"""
from __future__ import annotations
import re
def plan(codec, action, platform, size, *, installed_apps=None):
a=codec.adapt_target(action,platform).canonical_action
if len(size)!=2 or any(type(d) is not int or d<=0 for d in size): raise ValueError('invalid_screen_size')
def pixel(name):
return [min(d-1,int(v*d/1000+.5)) for v,d in zip(a[name],size)]
name=a['action']; args=[]
if name in ('terminate','answer','interact','wait'):
field={'terminate':'status','answer':'text','interact':'text','wait':'time'}[name]
return [(name,[a[field]])]
if name=='type': return [('type_unicode',[a['text']])]
if platform=='desktop':
if name=='key': return [('hotkey',a['keys'])]
if name=='mouse_move': return [('moveTo',pixel('coordinate'))]
if name=='left_click_drag': return [('dragTo',pixel('coordinate')+[.5])]
if name in ('scroll','hscroll'):
before=[('moveTo',pixel('coordinate'))] if 'coordinate' in a else []
return before+[(name,[a['pixels']])]
buttons={'left_click':('left',1),'right_click':('right',1),'middle_click':('middle',1),
'double_click':('left',2),'triple_click':('left',3)}
button,n=buttons[name]
return [('click',pixel('coordinate')+[button,n])]
if name=='click': args=['input','tap',*pixel('coordinate')]
elif name=='swipe': args=['input','swipe',*pixel('coordinate'),*pixel('coordinate2'),800]
elif name=='long_press':
args=['input','swipe',*pixel('coordinate'),*pixel('coordinate'),int(a['time']*1000+.5)]
elif name=='system_button':
args=['input','keyevent',{'Back':4,'Home':3,'Menu':82,'Enter':66}[a['button']]]
elif name=='key':
key=a['text'].upper()
if not key.startswith('KEYCODE_'): key='KEYCODE_'+key
if not re.fullmatch(r'KEYCODE_[A-Z0-9_]+',key): raise ValueError('invalid_android_key')
args=['input','keyevent',key]
elif name=='open':
apps=installed_apps or {}
package=apps.get(a['text'])
if package is None and a['text'] in apps.values(): package=a['text']
if package is None: raise ValueError('app_not_in_verified_installed_mapping')
if not re.fullmatch(r'[a-zA-Z0-9_]+(?:\.[a-zA-Z0-9_]+)+',package): raise ValueError('invalid_app_package')
args=['monkey','-p',package,'-c','android.intent.category.LAUNCHER','1']
else: raise ValueError('unimplemented_action')
return [('adb_shell',[str(x) for x in args])]
def execute(operations, *, desktop=None, adb_shell=None, type_unicode=None, callbacks=None):
"""Callers inject existing VM/ADB connections; never build shell strings."""
callbacks=callbacks or {}
for name,args in operations:
if name in ('terminate','answer','interact','wait'): callbacks[name](*args)
elif name=='adb_shell': adb_shell(args)
elif name=='type_unicode': type_unicode(*args)
elif name=='click': desktop.click(args[0],args[1],button=args[2],clicks=args[3],interval=.1)
elif name=='dragTo': desktop.dragTo(args[0],args[1],duration=args[2],button='left')
else: getattr(desktop,name)(*args)