Files
Android-pyApi/android_pyapi/android/view_accessibility.py
T
shuiyu1123 bfe27fdc08 feat: Android-pyApi v1.0 - 全面翻译 Android SDK API 为 Python
52 个模块覆盖 Android + Java + org.json 标准库
2026-06-24 11:16:16 +00:00

1488 lines
51 KiB
Python

"""
Android View Accessibility 组件 — AccessibilityEvent, AccessibilityNodeInfo,
AccessibilityService, AccessibilityManager 等
方法签名完全对齐 Android SDK
"""
import sys
from typing import Any, Callable, Dict, List, Optional, Tuple
from .._core import Stub, AndroidOnlyError, android_context
# ═══════════════════════════════════════════════════════════════
# AccessibilityEvent
# ═══════════════════════════════════════════════════════════════
class AccessibilityEvent(Stub):
"""AccessibilityEvent — 无障碍事件"""
# 事件类型
TYPE_VIEW_CLICKED = 0x00000001
TYPE_VIEW_LONG_CLICKED = 0x00000002
TYPE_VIEW_SELECTED = 0x00000004
TYPE_VIEW_FOCUSED = 0x00000008
TYPE_VIEW_TEXT_CHANGED = 0x00000010
TYPE_WINDOW_STATE_CHANGED = 0x00000020
TYPE_NOTIFICATION_STATE_CHANGED = 0x00000040
TYPE_VIEW_HOVER_ENTER = 0x00000080
TYPE_VIEW_HOVER_EXIT = 0x00000100
TYPE_TOUCH_EXPLORATION_GESTURE_START = 0x00000200
TYPE_TOUCH_EXPLORATION_GESTURE_END = 0x00000400
TYPE_WINDOW_CONTENT_CHANGED = 0x00000800
TYPE_VIEW_SCROLLED = 0x00001000
TYPE_VIEW_TEXT_SELECTION_CHANGED = 0x00002000
TYPE_ANNOUNCEMENT = 0x00004000
TYPE_VIEW_ACCESSIBILITY_FOCUSED = 0x00008000
TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED = 0x00010000
TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY = 0x00020000
TYPE_GESTURE_DETECTION_START = 0x00040000
TYPE_GESTURE_DETECTION_END = 0x00080000
TYPE_TOUCH_INTERACTION_START = 0x00100000
TYPE_TOUCH_INTERACTION_END = 0x00200000
TYPE_WINDOWS_CHANGED = 0x00400000
TYPE_VIEW_CONTEXT_CLICKED = 0x00800000
TYPE_ASSIST_READING_CONTENT = 0x01000000
# 内容变化类型
CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 0x00000001
CONTENT_CHANGE_TYPE_STATE_DESCRIPTION = 0x00000002
CONTENT_CHANGE_TYPE_SUBTREE = 0x00000004
CONTENT_CHANGE_TYPE_TEXT = 0x00000010
CONTENT_CHANGE_TYPE_UNDEFINED = 0x00000000
CONTENT_CHANGE_TYPE_PANE_TITLE = 0x00000020
CONTENT_CHANGE_TYPE_DRAG_STARTED = 0x00000040
CONTENT_CHANGE_TYPE_DRAG_CANCELLED = 0x00000080
CONTENT_CHANGE_TYPE_DRAG_DROPPED = 0x00000100
CONTENT_CHANGE_TYPE_CLEAR_FOCUS = 0x00000200
CONTENT_CHANGE_TYPE_ADDED = 0x00000400
CONTENT_CHANGE_TYPE_REMOVED = 0x00000800
CONTENT_CHANGE_TYPE_VISIBILITY_CHANGED = 0x00001000
CONTENT_CHANGE_TYPE_PANE_APPEARED = 0x00002000
CONTENT_CHANGE_TYPE_PANE_DISAPPEARED = 0x00004000
CONTENT_CHANGE_TYPE_ERROR = 0x00008000
# 窗口变化类型
WINDOWS_CHANGE_ADDED = 0x00000001
WINDOWS_CHANGE_REMOVED = 0x00000002
WINDOWS_CHANGE_TITLE = 0x00000004
WINDOWS_CHANGE_BOUNDS = 0x00000008
WINDOWS_CHANGE_LAYER = 0x00000010
WINDOWS_CHANGE_ACTIVE = 0x00000020
WINDOWS_CHANGE_FOCUSED = 0x00000040
WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED = 0x00000080
WINDOWS_CHANGE_PARENT = 0x00000100
WINDOWS_CHANGE_CHILDREN = 0x00000200
WINDOWS_CHANGE_PIP = 0x00000400
MAX_TEXT_LENGTH = 500
# 预留回收池
_pool: List["AccessibilityEvent"] = []
_pool_max = 10
def __init__(self):
super().__init__()
self._event_type: int = 0
self._source: Optional[Any] = None
self._source_node_id: int = -1
self._class_name: Optional[str] = None
self._package_name: Optional[str] = None
self._content_description: Optional[str] = None
self._text: List[str] = []
self._action: int = 0
self._from_index: int = -1
self._to_index: int = -1
self._item_count: int = 0
self._current_item_index: int = 0
self._enabled: bool = True
self._password: bool = False
self._checked: bool = False
self._full_package_name: Optional[str] = None
self._before_text: Optional[str] = None
self._movement_granularity: int = 0
self._added_count: int = 0
self._removed_count: int = 0
self._content_change_types: int = 0
self._scroll_delta_x: float = 0.0
self._scroll_delta_y: float = 0.0
self._max_scroll_x: float = 0.0
self._max_scroll_y: float = 0.0
self._event_time: int = 0
self._parcelable_data: Dict[str, Any] = {}
self._record_count: int = 0
@staticmethod
def obtain(event: Optional["AccessibilityEvent"] = None) -> "AccessibilityEvent":
if AccessibilityEvent._pool:
result = AccessibilityEvent._pool.pop()
else:
result = AccessibilityEvent()
if event is not None:
result._event_type = event._event_type
result._source = event._source
result._class_name = event._class_name
result._package_name = event._package_name
result._content_description = event._content_description
result._text = list(event._text)
result._action = event._action
return result
def recycle(self):
self._event_type = 0
self._source = None
self._source_node_id = -1
self._class_name = None
self._package_name = None
self._content_description = None
self._text.clear()
self._action = 0
self._from_index = -1
self._to_index = -1
self._item_count = 0
if len(AccessibilityEvent._pool) < AccessibilityEvent._pool_max:
AccessibilityEvent._pool.append(self)
def getEventType(self) -> int:
return self._event_type
def setEventType(self, event_type: int):
self._event_type = event_type
def getSource(self) -> Optional[Any]:
return self._source
def setSource(self, source: Optional[Any]):
self._source = source
def getClassName(self) -> Optional[str]:
return self._class_name
def setClassName(self, class_name: Optional[str]):
self._class_name = class_name
def getPackageName(self) -> Optional[str]:
return self._package_name
def setPackageName(self, package_name: Optional[str]):
self._package_name = package_name
def getContentDescription(self) -> Optional[str]:
return self._content_description
def setContentDescription(self, content_description: Optional[str]):
self._content_description = content_description
def getText(self) -> List[str]:
return list(self._text)
def getAction(self) -> int:
return self._action
def setAction(self, action: int):
self._action = action
def setFromIndex(self, from_index: int):
self._from_index = from_index
def getFromIndex(self) -> int:
return self._from_index
def setToIndex(self, to_index: int):
self._to_index = to_index
def getToIndex(self) -> int:
return self._to_index
def setItemCount(self, item_count: int):
self._item_count = item_count
def getItemCount(self) -> int:
return self._item_count
def setCurrentItemIndex(self, index: int):
self._current_item_index = index
def getCurrentItemIndex(self) -> int:
return self._current_item_index
def isEnabled(self) -> bool:
return self._enabled
def setEnabled(self, enabled: bool):
self._enabled = enabled
def isPassword(self) -> bool:
return self._password
def setPassword(self, password: bool):
self._password = password
def isChecked(self) -> bool:
return self._checked
def setChecked(self, checked: bool):
self._checked = checked
def getBeforeText(self) -> Optional[str]:
return self._before_text
def setBeforeText(self, before_text: Optional[str]):
self._before_text = before_text
def getMovementGranularity(self) -> int:
return self._movement_granularity
def setMovementGranularity(self, granularity: int):
self._movement_granularity = granularity
def getAddedCount(self) -> int:
return self._added_count
def setAddedCount(self, added_count: int):
self._added_count = added_count
def getRemovedCount(self) -> int:
return self._removed_count
def setRemovedCount(self, removed_count: int):
self._removed_count = removed_count
def getContentChangeTypes(self) -> int:
return self._content_change_types
def setContentChangeTypes(self, change_types: int):
self._content_change_types = change_types
def getScrollDeltaX(self) -> float:
return self._scroll_delta_x
def setScrollDeltaX(self, delta_x: float):
self._scroll_delta_x = delta_x
def getScrollDeltaY(self) -> float:
return self._scroll_delta_y
def setScrollDeltaY(self, delta_y: float):
self._scroll_delta_y = delta_y
def getMaxScrollX(self) -> float:
return self._max_scroll_x
def setMaxScrollX(self, max_x: float):
self._max_scroll_x = max_x
def getMaxScrollY(self) -> float:
return self._max_scroll_y
def setMaxScrollY(self, max_y: float):
self._max_scroll_y = max_y
def getEventTime(self) -> int:
return self._event_time
def setEventTime(self, event_time: int):
self._event_time = event_time
def getParcelableData(self) -> Any:
return self._parcelable_data
def setParcelableData(self, data: Any):
if isinstance(data, dict):
self._parcelable_data = data
else:
self._parcelable_data = {"data": data}
def getRecordCount(self) -> int:
return self._record_count
def appendRecord(self, record: "AccessibilityRecord"):
self._record_count += 1
def getRecord(self, index: int) -> Optional["AccessibilityRecord"]:
return None
def toString(self) -> str:
return (f"AccessibilityEvent(type={hex(self._event_type)}, "
f"className={self._class_name}, "
f"packageName={self._package_name}, "
f"text={self._text})")
# ═══════════════════════════════════════════════════════════════
# AccessibilityRecord
# ═══════════════════════════════════════════════════════════════
class AccessibilityRecord(Stub):
"""AccessibilityRecord — 无障碍记录"""
def __init__(self):
super().__init__()
self._source: Optional[Any] = None
self._source_node_id: int = -1
self._class_name: Optional[str] = None
self._package_name: Optional[str] = None
self._content_description: Optional[str] = None
self._text: List[str] = []
self._from_index: int = -1
self._to_index: int = -1
self._item_count: int = 0
self._current_item_index: int = 0
self._scrollable: bool = False
self._scroll_x: int = 0
self._scroll_y: int = 0
self._max_scroll_x: int = 0
self._max_scroll_y: int = 0
self._enabled: bool = True
self._password: bool = False
self._checked: bool = False
self._full_package_name: Optional[str] = None
self._before_text: Optional[str] = None
self._removed_count: int = 0
self._added_count: int = 0
def setSource(self, source: Optional[Any]):
self._source = source
def getSource(self) -> Optional[Any]:
return self._source
def setClassName(self, class_name: Optional[str]):
self._class_name = class_name
def getClassName(self) -> Optional[str]:
return self._class_name
def setContentDescription(self, content_description: Optional[str]):
self._content_description = content_description
def getContentDescription(self) -> Optional[str]:
return self._content_description
def setPackageName(self, package_name: Optional[str]):
self._package_name = package_name
def getPackageName(self) -> Optional[str]:
return self._package_name
def getText(self) -> List[str]:
return list(self._text)
def setFromIndex(self, from_index: int):
self._from_index = from_index
def getFromIndex(self) -> int:
return self._from_index
def setToIndex(self, to_index: int):
self._to_index = to_index
def getToIndex(self) -> int:
return self._to_index
def setItemCount(self, item_count: int):
self._item_count = item_count
def getItemCount(self) -> int:
return self._item_count
def setScrollable(self, scrollable: bool):
self._scrollable = scrollable
def getScrollable(self) -> bool:
return self._scrollable
def setScrollX(self, scroll_x: int):
self._scroll_x = scroll_x
def getScrollX(self) -> int:
return self._scroll_x
def setScrollY(self, scroll_y: int):
self._scroll_y = scroll_y
def getScrollY(self) -> int:
return self._scroll_y
def setMaxScrollX(self, max_scroll_x: int):
self._max_scroll_x = max_scroll_x
def getMaxScrollX(self) -> int:
return self._max_scroll_x
def setMaxScrollY(self, max_scroll_y: int):
self._max_scroll_y = max_scroll_y
def getMaxScrollY(self) -> int:
return self._max_scroll_y
def isEnabled(self) -> bool:
return self._enabled
def setEnabled(self, enabled: bool):
self._enabled = enabled
def isPassword(self) -> bool:
return self._password
def setPassword(self, password: bool):
self._password = password
def isChecked(self) -> bool:
return self._checked
def setChecked(self, checked: bool):
self._checked = checked
def getBeforeText(self) -> Optional[str]:
return self._before_text
def setBeforeText(self, before_text: Optional[str]):
self._before_text = before_text
def getAddedCount(self) -> int:
return self._added_count
def setAddedCount(self, added_count: int):
self._added_count = added_count
def getRemovedCount(self) -> int:
return self._removed_count
def setRemovedCount(self, removed_count: int):
self._removed_count = removed_count
def toString(self) -> str:
return (f"AccessibilityRecord(className={self._class_name}, "
f"packageName={self._package_name})")
# ═══════════════════════════════════════════════════════════════
# AccessibilityNodeInfo
# ═══════════════════════════════════════════════════════════════
class AccessibilityNodeInfo(Stub):
"""AccessibilityNodeInfo — 无障碍节点信息"""
FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x00000002
FLAG_REPORT_VIEW_IDS = 0x00000010
FLAG_PREFETCH_PREDECESSORS = 0x00000001
FLAG_PREFETCH_SIBLINGS = 0x00000002
FLAG_PREFETCH_DESCENDANTS = 0x00000004
ACTION_CLICK = 0x00000010
ACTION_LONG_CLICK = 0x00000020
ACTION_FOCUS = 0x00000040
ACTION_CLEAR_FOCUS = 0x00000080
ACTION_SELECT = 0x00000100
ACTION_CLEAR_SELECTION = 0x00000200
ACTION_ACCESSIBILITY_FOCUS = 0x00000400
ACTION_CLEAR_ACCESSIBILITY_FOCUS = 0x00000800
ACTION_SCROLL_FORWARD = 0x00001000
ACTION_SCROLL_BACKWARD = 0x00002000
ACTION_SCROLL_TO_POSITION = 0x00004000
ACTION_SET_SELECTION = 0x00008000
ACTION_COPY = 0x00010000
ACTION_PASTE = 0x00020000
ACTION_CUT = 0x00030000
ACTION_SET_TEXT = 0x00040000
ACTION_EXPAND = 0x00080000
ACTION_COLLAPSE = 0x00100000
ACTION_DISMISS = 0x00200000
ACTION_MOVE_WINDOW = 0x00400000
ACTION_SHOW_ON_SCREEN = 0x00800000
ACTION_CONTEXT_CLICK = 0x01000000
ACTION_SCROLL_UP = 0x02000000
ACTION_SCROLL_DOWN = 0x04000000
ACTION_SCROLL_LEFT = 0x08000000
ACTION_SCROLL_RIGHT = 0x10000000
ACTION_PAGE_UP = 0x20000000
ACTION_PAGE_DOWN = 0x40000000
ACTION_PAGE_LEFT = 0x80000000
ACTION_PAGE_RIGHT = 0x100000000
# 移动粒度
MOVEMENT_GRANULARITY_CHARACTER = 1
MOVEMENT_GRANULARITY_WORD = 2
MOVEMENT_GRANULARITY_LINE = 4
MOVEMENT_GRANULARITY_PARAGRAPH = 8
MOVEMENT_GRANULARITY_PAGE = 16
def __init__(self):
super().__init__()
self._children: List["AccessibilityNodeInfo"] = []
self._actions: int = 0
self._clickable: bool = False
self._focusable: bool = False
self._long_clickable: bool = False
self._scrollable: bool = False
self._content_description: Optional[str] = None
self._text: Optional[str] = None
self._class_name: Optional[str] = None
self._package_name: Optional[str] = None
self._parent: Optional["AccessibilityNodeInfo"] = None
self._view_id: Optional[str] = None
self._enabled: bool = True
self._password: bool = False
self._checked: bool = False
self._checkable: bool = False
self._selected: bool = False
self._focused: bool = False
self._visible_to_user: bool = True
self._accessibility_focused: bool = False
self._can_open_popup: bool = False
self._dismissable: bool = False
self._editable: bool = False
self._multi_line: bool = False
self._important_for_accessibility: bool = True
self._pane_title: Optional[str] = None
self._hint_text: Optional[str] = None
self._tooltip_text: Optional[str] = None
self._error: Optional[str] = None
self._min_text_length: int = -1
self._max_text_length: int = -1
self._child_count: int = 0
self._bounds_in_parent: Optional[Tuple[int, int, int, int]] = None
self._bounds_in_screen: Optional[Tuple[int, int, int, int]] = None
def findAccessibilityNodeInfosByText(self, text: str) -> List["AccessibilityNodeInfo"]:
result: List["AccessibilityNodeInfo"] = []
if self._text and text and text.lower() in self._text.lower():
result.append(self)
for child in self._children:
result.extend(child.findAccessibilityNodeInfosByText(text))
return result
def findAccessibilityNodeInfosByViewId(self, view_id: str) -> List["AccessibilityNodeInfo"]:
result: List["AccessibilityNodeInfo"] = []
if self._view_id == view_id:
result.append(self)
for child in self._children:
result.extend(child.findAccessibilityNodeInfosByViewId(view_id))
return result
def performAction(self, action: int) -> bool:
if action & self._actions:
sys.stderr.write(f"[AccessibilityNodeInfo] performAction: {hex(action)}\n")
return True
return False
def setClickable(self, clickable: bool):
self._clickable = clickable
def isClickable(self) -> bool:
return self._clickable
def setFocusable(self, focusable: bool):
self._focusable = focusable
def isFocusable(self) -> bool:
return self._focusable
def setLongClickable(self, long_clickable: bool):
self._long_clickable = long_clickable
def isLongClickable(self) -> bool:
return self._long_clickable
def setScrollable(self, scrollable: bool):
self._scrollable = scrollable
def isScrollable(self) -> bool:
return self._scrollable
def setContentDescription(self, content_description: Optional[str]):
self._content_description = content_description
def getContentDescription(self) -> Optional[str]:
return self._content_description
def setText(self, text: Optional[str]):
self._text = text
def getText(self) -> Optional[str]:
return self._text
def setClassName(self, class_name: Optional[str]):
self._class_name = class_name
def getClassName(self) -> Optional[str]:
return self._class_name
def setPackageName(self, package_name: Optional[str]):
self._package_name = package_name
def getPackageName(self) -> Optional[str]:
return self._package_name
def getChild(self, index: int) -> Optional["AccessibilityNodeInfo"]:
if 0 <= index < len(self._children):
return self._children[index]
return None
def setParent(self, parent: Optional["AccessibilityNodeInfo"]):
self._parent = parent
def getParent(self) -> Optional["AccessibilityNodeInfo"]:
return self._parent
def getChildCount(self) -> int:
return len(self._children)
def addChild(self, child: "AccessibilityNodeInfo"):
self._children.append(child)
child._parent = self
def addAction(self, action: int):
self._actions |= action
def setActions(self, actions: int):
self._actions = actions
def getActions(self) -> int:
return self._actions
def setViewIdResourceName(self, view_id: str):
self._view_id = view_id
def getViewIdResourceName(self) -> Optional[str]:
return self._view_id
def isEnabled(self) -> bool:
return self._enabled
def setEnabled(self, enabled: bool):
self._enabled = enabled
def isPassword(self) -> bool:
return self._password
def setPassword(self, password: bool):
self._password = password
def isChecked(self) -> bool:
return self._checked
def setChecked(self, checked: bool):
self._checked = checked
def isCheckable(self) -> bool:
return self._checkable
def setCheckable(self, checkable: bool):
self._checkable = checkable
def isSelected(self) -> bool:
return self._selected
def setSelected(self, selected: bool):
self._selected = selected
def isFocused(self) -> bool:
return self._focused
def setFocused(self, focused: bool):
self._focused = focused
def isVisibleToUser(self) -> bool:
return self._visible_to_user
def setVisibleToUser(self, visible: bool):
self._visible_to_user = visible
def isAccessibilityFocused(self) -> bool:
return self._accessibility_focused
def setAccessibilityFocused(self, focused: bool):
self._accessibility_focused = focused
def isEditable(self) -> bool:
return self._editable
def setEditable(self, editable: bool):
self._editable = editable
def isMultiLine(self) -> bool:
return self._multi_line
def setMultiLine(self, multi_line: bool):
self._multi_line = multi_line
def isImportantForAccessibility(self) -> bool:
return self._important_for_accessibility
def setImportantForAccessibility(self, important: bool):
self._important_for_accessibility = important
def setPaneTitle(self, pane_title: Optional[str]):
self._pane_title = pane_title
def getPaneTitle(self) -> Optional[str]:
return self._pane_title
def setHintText(self, hint_text: Optional[str]):
self._hint_text = hint_text
def getHintText(self) -> Optional[str]:
return self._hint_text
def setTooltipText(self, tooltip_text: Optional[str]):
self._tooltip_text = tooltip_text
def getTooltipText(self) -> Optional[str]:
return self._tooltip_text
def setError(self, error: Optional[str]):
self._error = error
def getError(self) -> Optional[str]:
return self._error
def setBoundsInParent(self, left: int, top: int, right: int, bottom: int):
self._bounds_in_parent = (left, top, right, bottom)
def getBoundsInParent(self) -> Optional[Tuple[int, int, int, int]]:
return self._bounds_in_parent
def setBoundsInScreen(self, left: int, top: int, right: int, bottom: int):
self._bounds_in_screen = (left, top, right, bottom)
def getBoundsInScreen(self) -> Optional[Tuple[int, int, int, int]]:
return self._bounds_in_screen
def getWindowId(self) -> int:
return -1
def recycle(self):
self._children.clear()
self._parent = None
self._actions = 0
def toString(self) -> str:
return (f"AccessibilityNodeInfo(className={self._class_name}, "
f"text={self._text}, "
f"contentDescription={self._content_description})")
# ═══════════════════════════════════════════════════════════════
# AccessibilityNodeProvider
# ═══════════════════════════════════════════════════════════════
class AccessibilityNodeProvider(Stub):
"""AccessibilityNodeProvider — 无障碍节点提供者"""
HOST_VIEW_ID = -1
def __init__(self):
super().__init__()
def createAccessibilityNodeInfo(self, virtual_view_id: int) -> AccessibilityNodeInfo:
raise AndroidOnlyError("AccessibilityNodeProvider.createAccessibilityNodeInfo")
def addExtraDataToAccessibilityNodeInfo(self, virtual_view_id: int,
info: AccessibilityNodeInfo,
extra_data_key: str,
arguments: Optional[Any]):
pass
def performAction(self, virtual_view_id: int, action: int,
arguments: Optional[Any]) -> bool:
return False
def findFocus(self, focus: int) -> Optional[AccessibilityNodeInfo]:
return None
# ═══════════════════════════════════════════════════════════════
# AccessibilityServiceInfo
# ═══════════════════════════════════════════════════════════════
class AccessibilityServiceInfo(Stub):
"""AccessibilityServiceInfo — 无障碍服务信息"""
FEEDBACK_GENERIC = 16
FEEDBACK_SPOKEN = 1
FEEDBACK_HAPTIC = 2
FEEDBACK_AUDIBLE = 4
FEEDBACK_VISUAL = 8
FEEDBACK_BRAILLE = 32
FEEDBACK_ALL_MASK = -1
FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 0x00000001
FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 0x00000002
FLAG_REPORT_VIEW_IDS = 0x00000010
FLAG_REQUEST_FILTER_KEY_EVENTS = 0x00000020
FLAG_RETRIEVE_INTERACTIVE_WINDOWS = 0x00000040
FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x00000080
FLAG_REQUEST_ACCESSIBILITY_BUTTON = 0x00000100
FLAG_REQUEST_SHORTCUT_WARNING_DIALOG = 0x00000200
CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT = 1
CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 2
CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 4
CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS = 8
CAPABILITY_CAN_CONTROL_MAGNIFICATION = 16
CAPABILITY_CAN_PERFORM_GESTURES = 32
def __init__(self):
super().__init__()
self.eventTypes: int = 0
self.feedbackType: int = 0
self.flags: int = 0
self.notificationTimeout: int = 0
self.filterTouchExploration: bool = False
self.packageNames: Optional[List[str]] = None
self.descriptionResId: int = 0
self.description: Optional[str] = None
self.settingsActivityName: Optional[str] = None
self.capabilities: int = 0
self.interactiveUiTimeout: int = 0
self.nonInteractiveUiTimeout: int = 0
def getCapabilityInfo(self, capability: int) -> Optional[str]:
cap_map = {
self.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT: "CAN_RETRIEVE_WINDOW_CONTENT",
self.CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION: "CAN_REQUEST_TOUCH_EXPLORATION",
self.CAPABILITY_CAN_CONTROL_MAGNIFICATION: "CAN_CONTROL_MAGNIFICATION",
self.CAPABILITY_CAN_PERFORM_GESTURES: "CAN_PERFORM_GESTURES",
}
return cap_map.get(capability)
def toString(self) -> str:
return (f"AccessibilityServiceInfo(eventTypes={hex(self.eventTypes)}, "
f"feedbackType={self.feedbackType}, "
f"flags={hex(self.flags)})")
# ═══════════════════════════════════════════════════════════════
# AccessibilityWindowInfo
# ═══════════════════════════════════════════════════════════════
class AccessibilityWindowInfo(Stub):
"""AccessibilityWindowInfo — 无障碍窗口信息"""
TYPE_APPLICATION = 1
TYPE_INPUT_METHOD = 2
TYPE_SYSTEM = 3
TYPE_ACCESSIBILITY_OVERLAY = 4
TYPE_SPLIT_SCREEN_DIVIDER = 5
UNDEFINED = -1
ACTIVE = 0x00000001
FOCUSED = 0x00000002
ACCESSIBILITY_FOCUSED = 0x00000004
def __init__(self):
super().__init__()
self._type: int = TYPE_SYSTEM
self._bounds_in_screen: Tuple[int, int, int, int] = (0, 0, 0, 0)
self._root: Optional[AccessibilityNodeInfo] = None
self._title: Optional[str] = None
self._anchor: Optional[Any] = None
self._parent: Optional["AccessibilityWindowInfo"] = None
self._children: List["AccessibilityWindowInfo"] = []
self._id: int = -1
self._layer: int = 0
self._active: bool = False
self._focused: bool = False
self._accessibility_focused: bool = False
self._picture_in_picture: bool = False
@staticmethod
def obtain() -> "AccessibilityWindowInfo":
return AccessibilityWindowInfo()
def recycle(self):
self._root = None
self._children.clear()
self._parent = None
def getType(self) -> int:
return self._type
def setType(self, window_type: int):
self._type = window_type
def getBoundsInScreen(self) -> Tuple[int, int, int, int]:
return self._bounds_in_screen
def setBoundsInScreen(self, left: int, top: int, right: int, bottom: int):
self._bounds_in_screen = (left, top, right, bottom)
def getRoot(self) -> Optional[AccessibilityNodeInfo]:
return self._root
def setRoot(self, root: Optional[AccessibilityNodeInfo]):
self._root = root
def getTitle(self) -> Optional[str]:
return self._title
def setTitle(self, title: Optional[str]):
self._title = title
def getAnchor(self) -> Optional[Any]:
return self._anchor
def setAnchor(self, anchor: Optional[Any]):
self._anchor = anchor
def getId(self) -> int:
return self._id
def setId(self, window_id: int):
self._id = window_id
def getLayer(self) -> int:
return self._layer
def setLayer(self, layer: int):
self._layer = layer
def getParent(self) -> Optional["AccessibilityWindowInfo"]:
return self._parent
def setParent(self, parent: Optional["AccessibilityWindowInfo"]):
self._parent = parent
def getChild(self, index: int) -> Optional["AccessibilityWindowInfo"]:
if 0 <= index < len(self._children):
return self._children[index]
return None
def getChildCount(self) -> int:
return len(self._children)
def addChild(self, child: "AccessibilityWindowInfo"):
self._children.append(child)
def isActive(self) -> bool:
return self._active
def setActive(self, active: bool):
self._active = active
def isFocused(self) -> bool:
return self._focused
def setFocused(self, focused: bool):
self._focused = focused
def isAccessibilityFocused(self) -> bool:
return self._accessibility_focused
def setAccessibilityFocused(self, focused: bool):
self._accessibility_focused = focused
def isInPictureInPictureMode(self) -> bool:
return self._picture_in_picture
def setPictureInPictureMode(self, pip: bool):
self._picture_in_picture = pip
def toString(self) -> str:
type_names = {1: "APPLICATION", 2: "INPUT_METHOD", 3: "SYSTEM",
4: "ACCESSIBILITY_OVERLAY", 5: "SPLIT_SCREEN_DIVIDER"}
type_name = type_names.get(self._type, "UNKNOWN")
return f"AccessibilityWindowInfo(id={self._id}, type={type_name}, title={self._title})"
# ═══════════════════════════════════════════════════════════════
# AccessibilityService
# ═══════════════════════════════════════════════════════════════
class AccessibilityService(Stub):
"""AccessibilityService — 无障碍服务基类"""
GLOBAL_ACTION_BACK = 1
GLOBAL_ACTION_HOME = 2
GLOBAL_ACTION_RECENTS = 3
GLOBAL_ACTION_NOTIFICATIONS = 4
GLOBAL_ACTION_QUICK_SETTINGS = 5
GLOBAL_ACTION_POWER_DIALOG = 6
GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7
GLOBAL_ACTION_LOCK_SCREEN = 8
GLOBAL_ACTION_TAKE_SCREENSHOT = 9
GLOBAL_ACTION_KEYCODE_HEADSETHOOK = 10
GLOBAL_ACTION_ACCESSIBILITY_SHORTCUT = 11
GLOBAL_ACTION_ACCESSIBILITY_BUTTON = 12
GLOBAL_ACTION_ACCESSIBILITY_BUTTON_CHOOSER = 13
GLOBAL_ACTION_ACCESSIBILITY_SHORTCUT_CHOOSER = 14
GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS = 15
def __init__(self):
super().__init__()
self._service_info: Optional[AccessibilityServiceInfo] = None
self._root_node: Optional[AccessibilityNodeInfo] = None
self._windows: List[AccessibilityWindowInfo] = []
def onAccessibilityEvent(self, event: AccessibilityEvent):
raise AndroidOnlyError("AccessibilityService.onAccessibilityEvent: not implemented")
def onInterrupt(self):
raise AndroidOnlyError("AccessibilityService.onInterrupt: not implemented")
def onServiceConnected(self):
pass
def performGlobalAction(self, action: int) -> bool:
action_names = {
self.GLOBAL_ACTION_BACK: "BACK",
self.GLOBAL_ACTION_HOME: "HOME",
self.GLOBAL_ACTION_RECENTS: "RECENTS",
self.GLOBAL_ACTION_NOTIFICATIONS: "NOTIFICATIONS",
self.GLOBAL_ACTION_QUICK_SETTINGS: "QUICK_SETTINGS",
}
sys.stderr.write(f"[AccessibilityService] performGlobalAction: "
f"{action_names.get(action, hex(action))}\n")
return True
def getRootInActiveWindow(self) -> Optional[AccessibilityNodeInfo]:
return self._root_node
def findFocus(self, focus_type: int) -> Optional[AccessibilityNodeInfo]:
return self._root_node
def getServiceInfo(self) -> Optional[AccessibilityServiceInfo]:
return self._service_info
def setServiceInfo(self, info: AccessibilityServiceInfo):
self._service_info = info
def getWindows(self) -> List[AccessibilityWindowInfo]:
return list(self._windows)
def getWindowsOnScreen(self) -> List[AccessibilityWindowInfo]:
return [w for w in self._windows if w.isActive()]
def disableSelf(self):
sys.stderr.write("[AccessibilityService] disableSelf\n")
def onKeyEvent(self, event: Any) -> bool:
return False
def onGesture(self, gesture_id: int) -> bool:
return False
def onMotionEvent(self, event: Any) -> bool:
return False
def onTouchExplorationStateChanged(self, enabled: bool):
pass
def onMagnificationChanged(self, region: Any, scale: float):
pass
def onSoftKeyboardShowModeChanged(self, show_mode: int):
pass
def dispatchKeyEventToInputMethod(self, event: Any) -> bool:
return False
# ═══════════════════════════════════════════════════════════════
# AccessibilityManager
# ═══════════════════════════════════════════════════════════════
class AccessibilityManager(Stub):
"""AccessibilityManager — 无障碍管理器"""
def __init__(self):
super().__init__()
self._enabled: bool = False
self._touch_exploration_enabled: bool = False
self._state_listeners: List[Callable] = []
self._touch_exploration_listeners: List[Callable] = []
self._accessibility_service_list: List[AccessibilityServiceInfo] = []
self._request_preparers: List[Any] = []
def isEnabled(self) -> bool:
return self._enabled
def setEnabled(self, enabled: bool):
old = self._enabled
self._enabled = enabled
if old != enabled:
for listener in list(self._state_listeners):
try:
listener.onAccessibilityStateChanged(enabled)
except Exception:
pass
def isTouchExplorationEnabled(self) -> bool:
return self._touch_exploration_enabled
def addAccessibilityStateChangeListener(self, listener: Callable) -> bool:
if listener not in self._state_listeners:
self._state_listeners.append(listener)
return True
return False
def removeAccessibilityStateChangeListener(self, listener: Callable) -> bool:
if listener in self._state_listeners:
self._state_listeners.remove(listener)
return True
return False
def addTouchExplorationStateChangeListener(self, listener: Callable) -> bool:
if listener not in self._touch_exploration_listeners:
self._touch_exploration_listeners.append(listener)
return True
return False
def removeTouchExplorationStateChangeListener(self, listener: Callable) -> bool:
if listener in self._touch_exploration_listeners:
self._touch_exploration_listeners.remove(listener)
return True
return False
def getAccessibilityServiceList(self) -> List[AccessibilityServiceInfo]:
return list(self._accessibility_service_list)
def addAccessibilityRequestPreparer(self, preparer: Any):
if preparer not in self._request_preparers:
self._request_preparers.append(preparer)
def removeAccessibilityRequestPreparer(self, preparer: Any):
if preparer in self._request_preparers:
self._request_preparers.remove(preparer)
def sendAccessibilityEvent(self, event: AccessibilityEvent):
sys.stderr.write(f"[AccessibilityManager] sendEvent: type={hex(event.getEventType())}\n")
def interrupt(self):
sys.stderr.write("[AccessibilityManager] interrupt\n")
def getEnabledAccessibilityServiceList(self, feedback_type_flags: int) -> List[AccessibilityServiceInfo]:
return [s for s in self._accessibility_service_list
if s.feedbackType & feedback_type_flags]
def getInstalledAccessibilityServiceList(self) -> List[AccessibilityServiceInfo]:
return list(self._accessibility_service_list)
def addAccessibilityStateChangeListener(self, listener: Callable) -> bool:
if listener not in self._state_listeners:
self._state_listeners.append(listener)
return True
return False
def removeAccessibilityStateChangeListener(self, listener: Callable) -> bool:
if listener in self._state_listeners:
self._state_listeners.remove(listener)
return True
return False
def toString(self) -> str:
return f"AccessibilityManager(enabled={self._enabled})"
# ═══════════════════════════════════════════════════════════════
# CaptioningManager
# ═══════════════════════════════════════════════════════════════
class CaptioningManager(Stub):
"""CaptioningManager — 字幕管理器"""
class CaptionStyle(Stub):
"""CaptionStyle — 字幕样式"""
EDGE_TYPE_NONE = 0
EDGE_TYPE_OUTLINE = 1
EDGE_TYPE_DROP_SHADOW = 2
EDGE_TYPE_RAISED = 3
EDGE_TYPE_DEPRESSED = 4
def __init__(self):
super().__init__()
self._foreground_color: int = 0xFFFFFFFF
self._background_color: int = 0xFF000000
self._edge_color: int = 0xFF000000
self._edge_type: int = EDGE_TYPE_NONE
self._typeface: Optional[Any] = None
self._font_scale: float = 1.0
self._window_color: int = 0xFF000000
@staticmethod
def getCustomStyle(css: Any) -> "CaptioningManager.CaptionStyle":
return CaptioningManager.CaptionStyle()
def getForegroundColor(self) -> int:
return self._foreground_color
def getBackgroundColor(self) -> int:
return self._background_color
def getEdgeColor(self) -> int:
return self._edge_color
def getEdgeType(self) -> int:
return self._edge_type
def getTypeface(self) -> Optional[Any]:
return self._typeface
def hasForegroundColor(self) -> bool:
return True
def hasBackgroundColor(self) -> bool:
return True
def hasEdgeColor(self) -> bool:
return True
def hasEdgeType(self) -> bool:
return True
def hasWindowColor(self) -> bool:
return True
def getWindowColor(self) -> int:
return self._window_color
def __init__(self):
super().__init__()
self._enabled: bool = False
self._locale: Optional[str] = None
self._font_scale: float = 1.0
self._user_style: Optional["CaptioningManager.CaptionStyle"] = None
def isCaptioningEnabled(self) -> bool:
return self._enabled
def getLocale(self) -> Optional[str]:
return self._locale
def getFontScale(self) -> float:
return self._font_scale
def getUserStyle(self) -> "CaptioningManager.CaptionStyle":
if self._user_style is None:
self._user_style = CaptioningManager.CaptionStyle()
return self._user_style
def addCaptioningChangeListener(self, listener: Any):
pass
def removeCaptioningChangeListener(self, listener: Any):
pass
# ═══════════════════════════════════════════════════════════════
# AccessibilityServiceGestureResult
# ═══════════════════════════════════════════════════════════════
class AccessibilityServiceGestureResult(Stub):
"""AccessibilityServiceGestureResult — 手势结果"""
def __init__(self, completed: bool):
super().__init__()
self._completed: bool = completed
def isCompleted(self) -> bool:
return self._completed
# ═══════════════════════════════════════════════════════════════
# TouchExplorationStateChangeListener
# ═══════════════════════════════════════════════════════════════
class TouchExplorationStateChangeListener(Stub):
"""TouchExplorationStateChangeListener — 触摸探索状态变更监听器"""
def onTouchExplorationStateChanged(self, enabled: bool):
pass
# ═══════════════════════════════════════════════════════════════
# AccessibilityButtonController
# ═══════════════════════════════════════════════════════════════
class AccessibilityButtonController(Stub):
"""AccessibilityButtonController — 无障碍按钮控制器"""
class AccessibilityButtonCallback(Stub):
"""AccessibilityButtonCallback — 无障碍按钮回调"""
def onAvailabilityChanged(self, available: bool):
pass
def onClicked(self, controller: "AccessibilityButtonController"):
pass
def __init__(self):
super().__init__()
self._callback: Optional["AccessibilityButtonController.AccessibilityButtonCallback"] = None
def isAccessibilityButtonAvailable(self) -> bool:
return False
def registerAccessibilityButtonCallback(self, callback: "AccessibilityButtonController.AccessibilityButtonCallback"):
self._callback = callback
def unregisterAccessibilityButtonCallback(self, callback: "AccessibilityButtonController.AccessibilityButtonCallback"):
if self._callback == callback:
self._callback = None
def performAccessibilityButtonAction(self) -> bool:
if self._callback:
self._callback.onClicked(self)
return True
return False
# ═══════════════════════════════════════════════════════════════
# AccessibilityButtonControllerCallback
# ═══════════════════════════════════════════════════════════════
class AccessibilityButtonControllerCallback(Stub):
"""AccessibilityButtonControllerCallback — 别名"""
def onAvailabilityChanged(self, available: bool):
pass
def onClicked(self, controller: AccessibilityButtonController):
pass
# ═══════════════════════════════════════════════════════════════
# MagnificationConfig
# ═══════════════════════════════════════════════════════════════
class MagnificationConfig(Stub):
"""MagnificationConfig — 放大配置"""
SCALE = 0
CENTER_X = 1
CENTER_Y = 2
ALL = -1
def __init__(self):
super().__init__()
self._scale: float = 1.0
self._center_x: float = 0.0
self._center_y: float = 0.0
self._mode: int = 0
self._frame_left: float = 0.0
self._frame_top: float = 0.0
self._frame_right: float = 0.0
self._frame_bottom: float = 0.0
def getScale(self) -> float:
return self._scale
def setScale(self, scale: float):
self._scale = scale
def getCenterX(self) -> float:
return self._center_x
def setCenterX(self, center_x: float):
self._center_x = center_x
def getCenterY(self) -> float:
return self._center_y
def setCenterY(self, center_y: float):
self._center_y = center_y
def getMode(self) -> int:
return self._mode
def setMode(self, mode: int):
self._mode = mode
def toString(self) -> str:
return (f"MagnificationConfig(scale={self._scale}, "
f"center=({self._center_x}, {self._center_y}))")
# ═══════════════════════════════════════════════════════════════
# MagnificationController
# ═══════════════════════════════════════════════════════════════
class MagnificationController(Stub):
"""MagnificationController — 放大控制器"""
class OnMagnificationChangedListener(Stub):
def onMagnificationChanged(self, controller: "MagnificationController",
region: Any, scale: float, center_x: float, center_y: float):
pass
def __init__(self):
super().__init__()
self._listeners: List["MagnificationController.OnMagnificationChangedListener"] = []
self._scale: float = 1.0
self._center_x: float = 0.0
self._center_y: float = 0.0
self._magnification_region: Any = None
def getScale(self) -> float:
return self._scale
def getCenterX(self) -> float:
return self._center_x
def getCenterY(self) -> float:
return self._center_y
def getMagnificationRegion(self) -> Any:
return self._magnification_region
def setScale(self, scale: float, animate: bool = False) -> bool:
self._scale = scale
self._notifyListeners()
return True
def setCenter(self, center_x: float, center_y: float, animate: bool = False) -> bool:
self._center_x = center_x
self._center_y = center_y
self._notifyListeners()
return True
def addListener(self, listener: "MagnificationController.OnMagnificationChangedListener"):
if listener not in self._listeners:
self._listeners.append(listener)
def removeListener(self, listener: "MagnificationController.OnMagnificationChangedListener"):
if listener in self._listeners:
self._listeners.remove(listener)
def _notifyListeners(self):
for listener in list(self._listeners):
try:
listener.onMagnificationChanged(self, self._magnification_region,
self._scale, self._center_x, self._center_y)
except Exception:
pass
def isMagnifying(self) -> bool:
return abs(self._scale - 1.0) > 0.01
def reset(self, animate: bool = False) -> bool:
if self.isMagnifying():
self._scale = 1.0
self._center_x = 0.0
self._center_y = 0.0
self._notifyListeners()
return True
return False
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"AccessibilityEvent", "AccessibilityRecord",
"AccessibilityNodeInfo", "AccessibilityNodeProvider",
"AccessibilityService", "AccessibilityServiceInfo",
"AccessibilityWindowInfo",
"CaptioningManager",
"AccessibilityManager",
"AccessibilityServiceGestureResult",
"TouchExplorationStateChangeListener",
"AccessibilityButtonController",
"AccessibilityButtonControllerCallback",
"MagnificationConfig",
"MagnificationController",
]