← 返回 Skills 市场
openlittlebear

Hospital Android Adb

作者 openlittlebear · GitHub ↗ · v1.0.0 · MIT-0
cross-platform ✓ 安全检测通过
48
总下载
0
收藏
0
当前安装
1
版本数
在 OpenClaw 中安装
/install hospital-android-adb
功能描述
Automate hospital appointment booking on the 医联 (Yilian) Android app using uiautomator2. Covers hospital search (with Chinese text input), department selecti...
使用说明 (SKILL.md)

Hospital Appointment Booking (医联 / Yilian)

Automate doctor appointment booking on the 医联 Android app (com.wonders.mobile.app.yilian). The app uses native Android views (no UC WebView issues). Use uiautomator2 for all interactions — Appium is NOT needed.

Before Any Action

import uiautomator2 as u2
d = u2.connect()

Kill 12306 first if running — both apps can't share the accessibility service:

import subprocess
subprocess.run(["adb", "shell", "am", "force-stop", "com.MobileTicket"])

Launch 医联:

subprocess.run(["adb", "shell", "monkey", "-p", "com.wonders.mobile.app.yilian",
                "-c", "android.intent.category.LAUNCHER", "1"])
import time; time.sleep(5)

App Info

Field Value
Package com.wonders.mobile.app.yilian
Activity chain MainActivitySearchActivityHospitalHomePageActivityDepartmentActivitySpecialDiseaseActivitySchedulingActivityReserveInfoActivity
UI type Native Android (RecyclerView, LinearLayout, TextView) — no WebView issues
Input method el.set_text("华山医院") via AccessibilityService — no IME needed

⚠️ Key Technical Constraints

Chinese Text Input: Use set_text()

d.send_keys("华山医院") fails (clipboard permission denied). adb shell input text fails (NullPointerException on Chinese chars). Use set_text() instead:

# Focus the field, then set text via AccessibilityService ACTION_SET_TEXT
el = d(className='android.widget.EditText')
el.click()         # focus first
time.sleep(0.5)
el.set_text("华山医院")  # bypasses IME/clipboard entirely

This works because set_text() uses the Android AccessibilityService's ACTION_SET_TEXT action, which directly injects text into the focused EditText without needing an IME or clipboard permissions.

Element Clickability

Many department/button TextViews have clickable=false. Their parent container (LinearLayout/FrameLayout) has clickable=true. Always check parent containers by bounds containment:

for p in root.iter('node'):
    if p.get('clickable') == 'true' and parent_bounds_contains(p, child_bounds):
        # This is the actual clickable target

Schedule Data Loading Delay

The SchedulingActivity`` RecyclerView takes **2-10 seconds** to populate with available dates. After tapping a slot, poll the dump for date texts like "2026-06-01"` until they appear.

Screen Reading

Primary: uiautomator2 dump_hierarchy + XML parsing

xml = d.dump_hierarchy()
root = __import__('xml.etree.ElementTree', fromlist=['ElementTree']).fromstring(xml.encode())

Helper: find clickable elements with text

import re
for node in root.iter('node'):
    text = node.get('text', '').strip()
    bounds = node.get('bounds', '')
    clickable = node.get('clickable')
    if text and clickable == 'true':
        nums = re.findall(r'\d+', bounds)
        if len(nums) >= 4:
            h = int(nums[3]) - int(nums[1])
            if h > 20:  # meaningful height
                print(f"'{text}' at {bounds}")

Helper: find clickable parent by bounds

def find_clickable_parent(root, child_bounds):
    child_nums = __import__('re').findall(r'\d+', child_bounds)
    if len(child_nums) \x3C 4: return None
    cx1, cy1, cx2, cy2 = int(child_nums[0]), int(child_nums[1]), int(child_nums[2]), int(child_nums[3])
    for p in root.iter('node'):
        if p.get('clickable') != 'true': continue
        pn = __import__('re').findall(r'\d+', p.get('bounds', ''))
        if len(pn) >= 4:
            px1, py1, px2, py2 = int(pn[0]), int(pn[1]), int(pn[2]), int(pn[3])
            if px1 \x3C= cx1 and py1 \x3C= cy1 and px2 >= cx2 and py2 >= cy2:
                return p
    return None

Fallback: Screenshot + OCR

d.screenshot('/tmp/screen.png')
import pytesseract
from PIL import Image
text = pytesseract.image_to_string(Image.open('/tmp/screen.png'),
    lang='chi_sim', config='--psm 6')

Booking Flow

Step 1: Home Page — Search for Hospital

Home page UI:

  • "搜医院、科室、医生" — search box (clickable) at top
  • "当日挂号" — same-day appointments
  • "预约挂号" — appointment booking label (NOT clickable)
d(text='搜医院、科室、医生').click()
time.sleep(2)

On the search page, type the hospital name:

el = d(className='android.widget.EditText')
el.click()
time.sleep(0.5)
el.set_text("华山医院")  # set text via AccessibilityService
time.sleep(2)            # wait for suggestions

After typing, the search results / suggestions appear. Tap the hospital card:

xml = d.dump_hierarchy()
if '华山医院' in xml:
    # Tap at hospital card position
    d.click(540, 1033)  # center of hospital card

Step 2: Hospital Card → Tap to Enter

On the search result page, tap the hospital card to go to its detail page:

# Look for the hospital card in the results
for node in root.iter('node'):
    if '华山医院' in node.get('text', ''):
        parent = find_clickable_parent(root, node.get('bounds', ''))
        # Tap parent's center
        ...

This navigates to HospitalHomePageActivity.

Step 3: Find "预约挂号" Icon

On the hospital detail page (HospitalHomePageActivity), there are icon buttons:

  • 预约挂号 at column 2 (x: 270-540) in the first row
  • 当日挂号 at column 3 (x: 540-810) in the first row
# The "预约挂号" icon is in the clickable container:
# Row 1: [0,1279][270,1522], [270,1279][540,1522], [540,1279][810,1522], [810,1279][1080,1522]
# Column 2 (270-540) = 预约挂号
d.click(405, 1400)  # center of second icon

This navigates to DepartmentActivity.

Step 4: Select Appointment Type Tab + Department

On DepartmentActivity:

  • Tabs at top: "专家门诊", "专病门诊", "普通门诊" (text, NOT clickable — tap their clickable parent)
  • Department list shows available departments

First select the appointment type tab, then tap the target department:

# Tap "普通门诊" tab
d.click(900, 516)  # center of third tab

# The department list refreshes. Now find "皮肤科":
xml = d.dump_hierarchy()
root = __import__('xml.etree.ElementTree', fromlist=['ElementTree']).fromstring(xml.encode())
for node in root.iter('node'):
    if node.get('text', '').strip() == '皮肤科':
        parent = find_clickable_parent(root, node.get('bounds', ''))
        if parent:
            nums = __import__('re').findall(r'\d+', parent.get('bounds', ''))
            cx = (int(nums[0]) + int(nums[2])) // 2
            cy = (int(nums[1]) + int(nums[3])) // 2
            d.click(cx, cy)

This may navigate to SpecialDiseaseActivity (department detail).

Step 5: Select Appointment Entry

On SpecialDiseaseActivity, find the appointment type entry for the department:

# Look for entries like "普通门诊-[医联平台] ¥25" with "有号" (available)
for node in root.iter('node'):
    t = node.get('text', '').strip()
    if '有号' in t or '普通门诊' in t:
        parent = find_clickable_parent(root, node.get('bounds', ''))
        if parent:
            # Tap to navigate to SchedulingActivity

Step 6: Select Date and Time Slot

On SchedulingActivity (activity == ".patient.ui.hospital.SchedulingActivity"):

The RecyclerView takes 2-10 seconds to populate. Poll for data:

# Wait for schedule data to load
for i in range(15):
    time.sleep(1)
    xml = d.dump_hierarchy()
    if '2026-06-01' in xml:  # target date appeared
        break

# Target date will show as: "2026-06-01  星期一 上午"
# Find its clickable parent and tap
root = __import__('xml.etree.ElementTree', fromlist=['ElementTree']).fromstring(xml.encode())
for node in root.iter('node'):
    if node.get('text', '').strip() == '2026-06-01  星期一 上午':
        parent = find_clickable_parent(root, node.get('bounds', ''))
        ...

This navigates to ReserveInfoActivity.

Step 7: Confirm Appointment

On ReserveInfoActivity, verify all details:

  • 医院: 华山医院
  • 科室: 皮肤科
  • 类型: 普通门诊
  • 就诊日期: 2026-06-01 星期一 上午
  • 就诊时段: 07:45-08:00 (changeable by tapping)

Patient selection: tap the patient name to select/deselect. Captcha section:

  • "获取验证码" — tap to request SMS code
  • "请输入验证码" — tap to enter code
  • "确定预约" at bottom — tap to finalize

SMS verification code is required — inform the user to check their phone.

# Tap 确定预约 after captcha is entered
d.click(540, 2292)

Activity Flow Reference

# Activity What You See
1 .patient.ui.MainActivity Home page: search box, 当日挂号
2 .patient.ui.hospital.search.SearchActivity Hospital search results / suggestions
3 .patient.ui.hospital.HospitalHomePageActivity Hospital detail: 预约挂号 icon
4 .patient.ui.hospital.depart.DepartmentActivity Department list with tabs
5 .patient.ui.hospital.SpecialDiseaseActivity Department detail: appointment type entry
6 .patient.ui.hospital.SchedulingActivity Date/time slot picker (RecyclerView)
7 .patient.ui.hospital.ReserveInfoActivity Confirmation: details, captcha, 确定预约

Key Elements Reference

Element Where Notes
搜医院、科室、医生 Home page Clickable search box
预约挂号 (icon) Hospital page 2nd icon in row 1
当日挂号 Hospital page 3rd icon in row 1
普通门诊 Department page Tab, 3rd position
皮肤科 / other dept Department page In scroll list
有号 Various Indicates availability
确定预约 Reserve page Submit button
获取验证码 Reserve page Request SMS
请输入验证码 Reserve page Captcha input

Common Pitfalls

  1. 12306 conflict: Can't run both apps' automation simultaneously. Force-stop 12306 first.
  2. Chinese text input: NOT supported via ADB. Use hot search suggestions or pre-existing text.
  3. Schedule loading delay: RecyclerView populates asynchronously. Poll for date text (up to 15s).
  4. Elements with clickable=false: TextViews may not be clickable — tap their parent container instead.
  5. ADB disconnects: USB can drop. Run adb kill-server && adb start-server to recover.
  6. SMS captcha: The user must enter the verification code manually.
  7. Patient selection: Verify correct patient is checked before confirming.

Decision Tree

d.dump_hierarchy() + check activity:
  ├─ activity == "MainActivity" → HOME
  │   → tap search box → tap hot suggestion
  ├─ activity == "SearchActivity" → SEARCH RESULTS
  │   → tap hospital card
  ├─ activity == "HospitalHomePageActivity" → HOSPITAL PAGE
  │   → tap 预约挂号 icon (2nd icon, 1st row)
  ├─ activity == "DepartmentActivity" → DEPARTMENTS
  │   → select tab (普通门诊) → tap department name
  ├─ activity == "SpecialDiseaseActivity" → DEPT DETAIL
  │   → tap available appointment entry (有号)
  ├─ activity == "SchedulingActivity" → DATE PICKER
  │   → wait for data → tap target date/time slot
  ├─ activity == "ReserveInfoActivity" → CONFIRMATION
  │   → verify details → get SMS captcha → tap 确定预约
  └─ otherwise → unknown state, take screenshot

Files

  • Uses uiautomator2 Python library only (no Appium)
  • No app-specific scripts needed
安全使用建议
Install only if you intend to let the agent control an Android device via ADB/uiautomator2 for 医联 appointment booking. Before use, confirm the target hospital, department, patient, date/time, and SMS code manually, and require explicit approval before the final '确定预约' tap. Be aware it may force-stop the 12306 app during setup.
能力评估
Purpose & Capability
The skill’s stated purpose matches its content: it guides uiautomator2/ADB automation through hospital search, department selection, slot selection, SMS verification, and final appointment confirmation.
Instruction Scope
The instructions are app-specific and include fixed package names, activities, UI text, and coordinates; the final booking step is disclosed, but the text should be treated as requiring explicit user confirmation before submission.
Install Mechanism
The artifact contains only SKILL.md plus small metadata JSON files; no executable helper scripts, package installs, background service, or hidden installer behavior were found.
Credentials
ADB/uiautomator2 phone-control access is proportionate for Android UI automation, but it can interrupt phone state and interact with sensitive medical scheduling screens.
Persistence & Privilege
No persistence, privilege escalation, credential storage, broad local indexing, or data exfiltration behavior is present in the artifact.
如何使用
  1. 确保已安装 OpenClaw(本地或 Docker 部署)
  2. 在对话框中输入安装命令:/install hospital-android-adb
  3. 安装完成后,直接呼叫该 Skill 的名称或使用 /hospital-android-adb 触发
  4. 根据 Skill 的参数说明提供必要输入,即可获得结构化输出
版本历史
v1.0.0
hospital-android-adb 1.0.0 – Initial Release - Automates full doctor appointment booking flow on the 医联 (Yilian) Android app using uiautomator2 only. - Supports hospital search (Chinese input via set_text), department selection, schedule selection, and confirmation without Appium. - Handles Chinese text input limitations and clickable parent detection in native Android layouts. - Provides helpers for XML parsing of UI hierarchy and fallback OCR-based screen reading. - Documents precise UI element access, technical constraints, and key implementation tips for robust automation.
元数据
Slug hospital-android-adb
版本 1.0.0
许可证 MIT-0
累计安装 0
当前安装数 0
历史版本数 1
常见问题

Hospital Android Adb 是什么?

Automate hospital appointment booking on the 医联 (Yilian) Android app using uiautomator2. Covers hospital search (with Chinese text input), department selecti... 它是一个面向 Claude Code / OpenClaw 的 AI Agent Skill 插件,目前累计下载 48 次。

如何安装 Hospital Android Adb?

在 OpenClaw 或 Claude Code 对话框中运行命令「/install hospital-android-adb」即可一键安装,无需额外配置。

Hospital Android Adb 是免费的吗?

是的,Hospital Android Adb 完全免费,采用 MIT-0 许可证,可自由下载、安装和使用。

Hospital Android Adb 支持哪些平台?

Hospital Android Adb 跨平台运行,可在任意部署了 OpenClaw / Claude Code 的环境中使用(cross-platform)。

谁开发了 Hospital Android Adb?

由 openlittlebear(@openlittlebear)开发并维护,当前版本 v1.0.0。

💬 留言讨论