1488 lines
51 KiB
Python
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",
|
|
] |