""" Android View 组件 — View, ViewGroup, MotionEvent, KeyEvent, GestureDetector 等 方法签名完全对齐 Android SDK """ import math import time import weakref from typing import Any, Callable, Dict, List, Optional, Tuple, Union from .._core import Stub, AndroidOnlyError, android_context, HAS_PYAUTOGUI try: from .._linux_impl import IS_LINUX, LinuxDisplay, LinuxUIAutomation, HAS_XDOTOOL _USE_LINUX = IS_LINUX except ImportError: _USE_LINUX = False # ═══════════════════════════════════════════════════════════════ # Gravity # ═══════════════════════════════════════════════════════════════ class Gravity: """Gravity — 布局重力常量""" NO_GRAVITY = 0x0000 TOP = 0x0030 BOTTOM = 0x0050 LEFT = 0x0003 RIGHT = 0x0005 CENTER = 0x0011 CENTER_HORIZONTAL = 0x0001 CENTER_VERTICAL = 0x0010 FILL = 0x0077 FILL_HORIZONTAL = 0x0007 FILL_VERTICAL = 0x0070 CLIP_HORIZONTAL = 0x0008 CLIP_VERTICAL = 0x0080 START = 0x00800001 END = 0x00800005 RELATIVE_LAYOUT_DIRECTION = 0x00800000 @staticmethod def getAbsoluteGravity(gravity: int, layout_direction: int) -> int: from ..os import View if layout_direction == View.LAYOUT_DIRECTION_RTL: if gravity & Gravity.START: gravity = (gravity & ~Gravity.START) | Gravity.RIGHT if gravity & Gravity.END: gravity = (gravity & ~Gravity.END) | Gravity.LEFT else: if gravity & Gravity.START: gravity = (gravity & ~Gravity.START) | Gravity.LEFT if gravity & Gravity.END: gravity = (gravity & ~Gravity.END) | Gravity.RIGHT return gravity @staticmethod def apply(gravity: int, w: int, h: int, container: "Rect") -> int: return 0 Rect = None def _set_rect_class(): global Rect if Rect is None: class _Rect: def __init__(self, l=0, t=0, r=0, b=0): self.left = l self.top = t self.right = r self.bottom = b def width(self): return self.right - self.left def height(self): return self.bottom - self.top def set(self, l, t, r, b): self.left, self.top, self.right, self.bottom = l, t, r, b def isEmpty(self): return self.width() <= 0 or self.height() <= 0 def toString(self): return f"Rect({self.left}, {self.top} - {self.right}, {self.bottom})" Rect = _Rect _set_rect_class() # ═══════════════════════════════════════════════════════════════ # View # ═══════════════════════════════════════════════════════════════ class View(Stub): """View — 所有 UI 组件的基类""" # 可见性常量 VISIBLE = 0 INVISIBLE = 4 GONE = 8 # 测量常量 MEASURED_SIZE_STATE_MASK = 0xFF000000 MEASURED_STATE_MASK = 0xFF000000 MEASURED_HEIGHT_STATE_SHIFT = 16 MEASURED_WIDTH_STATE_SHIFT = 0 # 测量模式 EXACTLY = 0x40000000 AT_MOST = 0x80000000 # -2147483648 UNSPECIFIED = 0 # 滚动轴 SCROLL_AXIS_NONE = 0 SCROLL_AXIS_HORIZONTAL = 1 SCROLL_AXIS_VERTICAL = 2 # 布局方向 LAYOUT_DIRECTION_LTR = 0 LAYOUT_DIRECTION_RTL = 1 # 绘制缓存 DRAWING_CACHE_QUALITY_LOW = 0x00040000 DRAWING_CACHE_QUALITY_HIGH = 0x00080000 DRAWING_CACHE_QUALITY_AUTO = 0 # 焦点方向 FOCUS_BACKWARD = 0x00000001 FOCUS_FORWARD = 0x00000002 FOCUS_LEFT = 0x00000011 FOCUS_UP = 0x00000021 FOCUS_RIGHT = 0x00000042 FOCUS_DOWN = 0x00000082 # Keep screen on KEEP_SCREEN_ON = 0x02000000 # 状态 STATUS_BAR_HIDDEN = 0x00000001 STATUS_BAR_VISIBLE = 0x00000000 NAVIGATION_BAR_HIDDEN = 0x00000002 NAVIGATION_BAR_VISIBLE = 0x00000000 # 系统UI可见性 SYSTEM_UI_FLAG_VISIBLE = 0 SYSTEM_UI_FLAG_LOW_PROFILE = 0x00000001 SYSTEM_UI_FLAG_HIDE_NAVIGATION = 0x00000002 SYSTEM_UI_FLAG_FULLSCREEN = 0x00000004 SYSTEM_UI_FLAG_LAYOUT_STABLE = 0x00000100 SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 0x00000200 SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 0x00000400 SYSTEM_UI_FLAG_IMMERSIVE = 0x00000800 SYSTEM_UI_FLAG_IMMERSIVE_STICKY = 0x00001000 _next_id = 0x10000000 _id_lock = None def __init__(self, context=None): super().__init__() if context is None: context = android_context self._context = context self._id = View.NO_ID self._tag: Any = None self._layout_params: Optional["ViewGroup.LayoutParams"] = None self._visibility = View.VISIBLE self._enabled = True self._clickable = False self._focusable = False self._selected = False self._activated = False self._x: float = 0.0 self._y: float = 0.0 self._translation_x: float = 0.0 self._translation_y: float = 0.0 self._rotation: float = 0.0 self._scale_x: float = 1.0 self._scale_y: float = 1.0 self._alpha: float = 1.0 self._pivot_x: float = 0.0 self._pivot_y: float = 0.0 self._measured_width: int = 0 self._measured_height: int = 0 self._left: int = 0 self._top: int = 0 self._right: int = 0 self._bottom: int = 0 self._padding_left: int = 0 self._padding_top: int = 0 self._padding_right: int = 0 self._padding_bottom: int = 0 self._parent: Optional["ViewGroup"] = None self._background_color: Optional[int] = None self._background_drawable: Any = None self._background_resource: int = 0 self._on_click_listener: Optional[Callable] = None self._on_long_click_listener: Optional[Callable] = None self._on_touch_listener: Optional[Callable] = None self._on_key_listener: Optional[Callable] = None self._callbacks: Dict[Any, Tuple[Callable, float]] = {} self._scroll_x: int = 0 self._scroll_y: int = 0 self._will_not_draw = False self._layer_type = View.LAYER_TYPE_NONE self._layout_direction = View.LAYOUT_DIRECTION_LTR self._min_width: int = 0 self._min_height: int = 0 self._elevation: float = 0.0 self._clickable = False self._long_clickable = False self._context_clickable = False self._children: List["View"] = [] # ─── 常量 ────────────────────────────────────────────────── NO_ID = -1 DRAWING_CACHE_QUALITY_LOW = 0x00040000 DRAWING_CACHE_QUALITY_HIGH = 0x00080000 DRAWING_CACHE_QUALITY_AUTO = 0 LAYER_TYPE_NONE = 0 LAYER_TYPE_SOFTWARE = 1 LAYER_TYPE_HARDWARE = 2 FADING_EDGE_NONE = 0 FADING_EDGE_HORIZONTAL = 0x00001000 FADING_EDGE_VERTICAL = 0x00002000 OVER_SCROLL_ALWAYS = 0 OVER_SCROLL_IF_CONTENT_SCROLLS = 1 OVER_SCROLL_NEVER = 2 # ─── id / tag ────────────────────────────────────────────── def setId(self, id_: int): self._id = id_ def getId(self) -> int: return self._id def setTag(self, tag: Any): self._tag = tag def getTag(self) -> Any: return self._tag # ─── layout params ───────────────────────────────────────── def setLayoutParams(self, params: "ViewGroup.LayoutParams"): self._layout_params = params def getLayoutParams(self) -> Optional["ViewGroup.LayoutParams"]: return self._layout_params # ─── visibility ──────────────────────────────────────────── def setVisibility(self, visibility: int): self._visibility = visibility def getVisibility(self) -> int: return self._visibility def isShown(self) -> bool: return self._visibility == View.VISIBLE # ─── position / size ─────────────────────────────────────── def getX(self) -> float: return self._x def setX(self, x: float): self._x = x def getY(self) -> float: return self._y def setY(self, y: float): self._y = y def getWidth(self) -> int: return self._right - self._left def getHeight(self) -> int: return self._bottom - self._top def getLeft(self) -> int: return self._left def getTop(self) -> int: return self._top def getRight(self) -> int: return self._right def getBottom(self) -> int: return self._bottom def getMeasuredWidth(self) -> int: return self._measured_width def getMeasuredHeight(self) -> int: return self._measured_height def setFrame(self, left: int, top: int, right: int, bottom: int) -> bool: changed = (left != self._left or top != self._top or right != self._right or bottom != self._bottom) self._left, self._top, self._right, self._bottom = left, top, right, bottom return changed def layout(self, l: int, t: int, r: int, b: int): old_l, old_t, old_r, old_b = self._left, self._top, self._right, self._bottom changed = self.setFrame(l, t, r, b) if changed: self.onLayout(changed, l - old_l, t - old_t, r - old_r, b - old_b) def onLayout(self, changed: bool, left: int, top: int, right: int, bottom: int): pass def measure(self, width_measure_spec: int, height_measure_spec: int): self.onMeasure(width_measure_spec, height_measure_spec) def onMeasure(self, width_measure_spec: int, height_measure_spec: int): w = View.getDefaultSize(self.getSuggestedMinimumWidth(), width_measure_spec) h = View.getDefaultSize(self.getSuggestedMinimumHeight(), height_measure_spec) self.setMeasuredDimension(w, h) def setMeasuredDimension(self, measured_width: int, measured_height: int): self._measured_width = measured_width self._measured_height = measured_height def getSuggestedMinimumWidth(self) -> int: return self._min_width def getSuggestedMinimumHeight(self) -> int: return self._min_height def getMinimumWidth(self) -> int: return self._min_width def getMinimumHeight(self) -> int: return self._min_height def setMinimumWidth(self, min_w: int): self._min_width = min_w def setMinimumHeight(self, min_h: int): self._min_height = min_h def draw(self, canvas: Any): self.onDraw(canvas) def onDraw(self, canvas: Any): pass # ─── listeners ───────────────────────────────────────────── def setOnClickListener(self, listener: Optional[Callable]): self._on_click_listener = listener def setOnLongClickListener(self, listener: Optional[Callable]) -> None: self._on_long_click_listener = listener def setOnTouchListener(self, listener: Optional[Callable]) -> None: self._on_touch_listener = listener def setOnKeyListener(self, listener: Optional[Callable]) -> None: self._on_key_listener = listener def callOnClick(self) -> bool: if self._on_click_listener: self._on_click_listener(self) return True return False def performClick(self) -> bool: return self.callOnClick() def screenshot(self, path=None): """截屏""" if _USE_LINUX: return LinuxDisplay.screenshot(path) return None def performLongClick(self) -> bool: if self._on_long_click_listener: self._on_long_click_listener(self) return True return False # ─── post / delayed ──────────────────────────────────────── def post(self, action: Callable) -> bool: action() return True def postDelayed(self, action: Callable, delay_millis: int) -> bool: import threading timer = threading.Timer(delay_millis / 1000.0, action) timer.daemon = True timer.start() self._callbacks[action] = (action, time.time() + delay_millis / 1000.0) return True def removeCallbacks(self, action: Optional[Callable] = None): if action is None: self._callbacks.clear() else: self._callbacks.pop(action, None) # ─── invalidate / requestLayout ──────────────────────────── def invalidate(self): pass def requestLayout(self): pass def requestFocus(self, direction: int = FOCUS_FORWARD) -> bool: if self._visibility != View.VISIBLE or not self._enabled: return False return True # ─── padding ─────────────────────────────────────────────── def setPadding(self, left: int, top: int, right: int, bottom: int): self._padding_left = left self._padding_top = top self._padding_right = right self._padding_bottom = bottom def getPaddingLeft(self) -> int: return self._padding_left def getPaddingTop(self) -> int: return self._padding_top def getPaddingRight(self) -> int: return self._padding_right def getPaddingBottom(self) -> int: return self._padding_bottom def getPaddingStart(self) -> int: return self._padding_left def getPaddingEnd(self) -> int: return self._padding_right # ─── background ──────────────────────────────────────────── def setBackgroundColor(self, color: int): self._background_color = color self._background_drawable = None def setBackground(self, background: Any): self._background_drawable = background def setBackgroundDrawable(self, background: Any): self._background_drawable = background def setBackgroundResource(self, resid: int): self._background_resource = resid def getBackground(self) -> Any: return self._background_drawable # ─── transform properties ────────────────────────────────── def setAlpha(self, alpha: float): self._alpha = alpha def getAlpha(self) -> float: return self._alpha def setRotation(self, rotation: float): self._rotation = rotation def getRotation(self) -> float: return self._rotation def setScaleX(self, scale_x: float): self._scale_x = scale_x def getScaleX(self) -> float: return self._scale_x def setScaleY(self, scale_y: float): self._scale_y = scale_y def getScaleY(self) -> float: return self._scale_y def setTranslationX(self, translation_x: float): self._translation_x = translation_x def getTranslationX(self) -> float: return self._translation_x def setTranslationY(self, translation_y: float): self._translation_y = translation_y def getTranslationY(self) -> float: return self._translation_y def getPivotX(self) -> float: return self._pivot_x def setPivotX(self, pivot_x: float): self._pivot_x = pivot_x def getPivotY(self) -> float: return self._pivot_y def setPivotY(self, pivot_y: float): self._pivot_y = pivot_y def setElevation(self, elevation: float): self._elevation = elevation def getElevation(self) -> float: return self._elevation def animate(self) -> "ViewPropertyAnimator": return ViewPropertyAnimator(self) # ─── parent / root ───────────────────────────────────────── def getParent(self) -> Optional["ViewParent"]: return self._parent def getRootView(self) -> "View": v = self while v._parent is not None: v = v._parent return v # ─── hierarchy ───────────────────────────────────────────── def findViewById(self, id_: int) -> Optional["View"]: if self._id == id_: return self if hasattr(self, "_children"): for child in self._children: result = child.findViewById(id_) if result is not None: return result return None def bringToFront(self): if self._parent is not None: self._parent.bringChildToFront(self) # ─── enable / focusable / clickable ──────────────────────── def setEnabled(self, enabled: bool): self._enabled = enabled def isEnabled(self) -> bool: return self._enabled def setFocusable(self, focusable: bool): self._focusable = focusable def isFocusable(self) -> bool: return self._focusable def setClickable(self, clickable: bool): self._clickable = clickable def isClickable(self) -> bool: return self._clickable def setLongClickable(self, long_clickable: bool): self._long_clickable = long_clickable def isLongClickable(self) -> bool: return self._long_clickable def setContextClickable(self, context_clickable: bool): self._context_clickable = context_clickable def isContextClickable(self) -> bool: return self._context_clickable def setSelected(self, selected: bool): self._selected = selected def isSelected(self) -> bool: return self._selected def setActivated(self, activated: bool): self._activated = activated def isActivated(self) -> bool: return self._activated # ─── context / resources ─────────────────────────────────── def getContext(self): return self._context def getResources(self): if hasattr(self._context, "getResources"): return self._context.getResources() return None # ─── scroll ──────────────────────────────────────────────── def setScrollX(self, value: int): self._scroll_x = value def setScrollY(self, value: int): self._scroll_y = value def getScrollX(self) -> int: return self._scroll_x def getScrollY(self) -> int: return self._scroll_y def scrollTo(self, x: int, y: int): self._scroll_x = x self._scroll_y = y def scrollBy(self, x: int, y: int): self._scroll_x += x self._scroll_y += y def computeScroll(self): pass # ─── drawing / layer ─────────────────────────────────────── def setWillNotDraw(self, will_not_draw: bool): self._will_not_draw = will_not_draw def willNotDraw(self) -> bool: return self._will_not_draw def setLayerType(self, layer_type: int, paint: Any = None): self._layer_type = layer_type def getLayerType(self) -> int: return self._layer_type def buildLayer(self): pass def setDrawingCacheQuality(self, quality: int): pass def getDrawingCacheQuality(self) -> int: return View.DRAWING_CACHE_QUALITY_AUTO def getDrawingCache(self, auto_scale: bool = True) -> Any: return None def buildDrawingCache(self, auto_scale: bool = True): pass def destroyDrawingCache(self): pass # ─── layout direction ────────────────────────────────────── def setLayoutDirection(self, layout_direction: int): self._layout_direction = layout_direction def getLayoutDirection(self) -> int: return self._layout_direction def setTextDirection(self, text_direction: int): pass def getTextDirection(self) -> int: return 0 def setTextAlignment(self, text_alignment: int): pass def getTextAlignment(self) -> int: return 0 # ─── hit / focus ─────────────────────────────────────────── def onTouchEvent(self, event: "MotionEvent") -> bool: return self._clickable or self._long_clickable def onTrackballEvent(self, event: "MotionEvent") -> bool: return False def onKeyDown(self, key_code: int, event: "KeyEvent") -> bool: return False def onKeyUp(self, key_code: int, event: "KeyEvent") -> bool: return False def onKeyLongPress(self, key_code: int, event: "KeyEvent") -> bool: return False def onKeyMultiple(self, key_code: int, repeat_count: int, event: "KeyEvent") -> bool: return False def dispatchTouchEvent(self, event: "MotionEvent") -> bool: if self._on_touch_listener is not None: handled = self._on_touch_listener(self, event) if handled: return True return self.onTouchEvent(event) def dispatchKeyEvent(self, event: "KeyEvent") -> bool: if self._on_key_listener is not None: handled = self._on_key_listener(self, event.getKeyCode(), event) if handled: return True if event.getAction() == KeyEvent.ACTION_DOWN: return self.onKeyDown(event.getKeyCode(), event) elif event.getAction() == KeyEvent.ACTION_UP: return self.onKeyUp(event.getKeyCode(), event) return False def dispatchGenericMotionEvent(self, event: "MotionEvent") -> bool: return False def hasFocus(self) -> bool: return False def isFocused(self) -> bool: return self.hasFocus() def clearFocus(self): pass def onWindowFocusChanged(self, has_window_focus: bool): pass def hasWindowFocus(self) -> bool: return True def getWindowToken(self) -> Any: return None def getWindowVisibility(self) -> int: return View.VISIBLE def getWindowAttachCount(self) -> int: return 1 def onAttachedToWindow(self): pass def onDetachedFromWindow(self): pass def isAttachedToWindow(self) -> bool: return self._parent is not None # ─── Haptic ──────────────────────────────────────────────── def performHapticFeedback(self, feedback_constant: int) -> bool: return False HapticFeedbackConstants = None # ─── static helpers ──────────────────────────────────────── @staticmethod def generateViewId() -> int: View._next_id += 1 return View._next_id @staticmethod def getDefaultSize(size: int, measure_spec: int) -> int: spec_mode = View.MeasureSpec.getMode(measure_spec) spec_size = View.MeasureSpec.getSize(measure_spec) if spec_mode == View.UNSPECIFIED: return size else: return spec_size @staticmethod def combineMeasuredStates(cur_state: int, new_state: int) -> int: return cur_state | new_state @staticmethod def resolveSize(size: int, measure_spec: int) -> int: return View.getDefaultSize(size, measure_spec) @staticmethod def resolveSizeAndState(size: int, measure_spec: int, child_measured_state: int) -> int: result = View.resolveSize(size, measure_spec) return result | (child_measured_state & View.MEASURED_STATE_MASK) @staticmethod def inflate(context, resource, root): return View(context) # ─── 事件处理 (扩展) ─────────────────────────────────────── def onGenericMotionEvent(self, event) -> bool: return False def onHoverEvent(self, event) -> bool: return False def onDragEvent(self, event) -> bool: return False def onSizeChanged(self, w, h, oldw, oldh): pass def onFocusChanged(self, gainFocus, direction, previouslyFocusedRect): pass def onScrollChanged(self, l, t, oldl, oldt): pass def onVisibilityChanged(self, changedView, visibility): pass # ─── 监听器设置 (扩展) ───────────────────────────────────── def setOnGenericMotionListener(self, listener): self._on_generic_motion_listener = listener return self def setOnHoverListener(self, listener): self._on_hover_listener = listener return self def setOnDragListener(self, listener): self._on_drag_listener = listener return self def setOnFocusChangeListener(self, listener): self._on_focus_change_listener = listener return self def setOnContextClickListener(self, listener): self._on_context_click_listener = listener return self def addOnLayoutChangeListener(self, listener): if not hasattr(self, '_layout_change_listeners'): self._layout_change_listeners = [] self._layout_change_listeners.append(listener) def removeOnLayoutChangeListener(self, listener): if hasattr(self, '_layout_change_listeners'): try: self._layout_change_listeners.remove(listener) except ValueError: pass def setOnApplyWindowInsetsListener(self, listener): self._on_apply_window_insets_listener = listener return self # ─── 位置和尺寸 (扩展) ───────────────────────────────────── def getLocationOnScreen(self, location): location[0] = self._left if hasattr(self, '_left') else 0 location[1] = self._top if hasattr(self, '_top') else 0 def getLocationInWindow(self, location): self.getLocationOnScreen(location) def offsetLeftAndRight(self, offset): if hasattr(self, '_left'): self._left += offset def offsetTopAndBottom(self, offset): if hasattr(self, '_top'): self._top += offset def getHitRect(self, out_rect): out_rect.left = self._left if hasattr(self, '_left') else 0 out_rect.top = self._top if hasattr(self, '_top') else 0 out_rect.right = out_rect.left + (self._width if hasattr(self, '_width') else 0) out_rect.bottom = out_rect.top + (self._height if hasattr(self, '_height') else 0) def getDrawingRect(self, out_rect): out_rect.left = 0 out_rect.top = 0 out_rect.right = self._width if hasattr(self, '_width') else 0 out_rect.bottom = self._height if hasattr(self, '_height') else 0 # ─── 其他扩展方法 ────────────────────────────────────────── def setTooltipText(self, text): self._tooltip_text = text def getTooltipText(self): return getattr(self, '_tooltip_text', None) def setTranslationZ(self, translationZ): self._translation_z = translationZ def getTranslationZ(self): return getattr(self, '_translation_z', 0.0) def setZ(self, z): self._z = z def getZ(self): return getattr(self, '_z', 0.0) def setTransitionName(self, name): self._transition_name = name def getTransitionName(self): return getattr(self, '_transition_name', None) def setClipToOutline(self, clipToOutline): self._clip_to_outline = clipToOutline def getClipToOutline(self): return getattr(self, '_clip_to_outline', False) def setFitsSystemWindows(self, fitSystemWindows): self._fits_system_windows = fitSystemWindows def getFitsSystemWindows(self): return getattr(self, '_fits_system_windows', False) def playSoundEffect(self, soundConstant): pass def setSaveEnabled(self, enabled): self._save_enabled = enabled def isSaveEnabled(self): return getattr(self, '_save_enabled', True) def setSystemUiVisibility(self, visibility): self._system_ui_visibility = visibility def getSystemUiVisibility(self): return getattr(self, '_system_ui_visibility', 0) def requestApplyInsets(self): pass def onApplyWindowInsets(self, insets): return insets def setOnCreateContextMenuListener(self, listener): self._on_create_context_menu_listener = listener return self def getBaseline(self): return -1 def setFocusableInTouchMode(self, focusable): self._focusable_in_touch_mode = focusable def isFocusableInTouchMode(self): return getattr(self, '_focusable_in_touch_mode', False) def setRotationX(self, rotationX): self._rotation_x = rotationX def getRotationX(self): return getattr(self, '_rotation_x', 0.0) def setRotationY(self, rotationY): self._rotation_y = rotationY def getRotationY(self): return getattr(self, '_rotation_y', 0.0) def setCameraDistance(self, distance): self._camera_distance = distance def getCameraDistance(self): return getattr(self, '_camera_distance', 0.0) def setForeground(self, foreground): self._foreground = foreground def getForeground(self): return getattr(self, '_foreground', None) def setBackgroundTintList(self, tint): self._background_tint = tint def setBackgroundTintMode(self, tintMode): self._background_tint_mode = tintMode def setScrollbarFadingEnabled(self, fadeScrollbars): self._fade_scrollbars = fadeScrollbars def isScrollbarFadingEnabled(self): return getattr(self, '_fade_scrollbars', True) def setVerticalScrollBarEnabled(self, enabled): self._vertical_scrollbar = enabled def isVerticalScrollBarEnabled(self): return getattr(self, '_vertical_scrollbar', True) def setHorizontalScrollBarEnabled(self, enabled): self._horizontal_scrollbar = enabled def isHorizontalScrollBarEnabled(self): return getattr(self, '_horizontal_scrollbar', True) def setLabelFor(self, id): self._label_for = id def getLabelFor(self): return getattr(self, '_label_for', 0) def cancelLongPress(self): pass def setPaddingRelative(self, start, top, end, bottom): self._padding_start = start self._padding_top = top self._padding_end = end self._padding_bottom = bottom def setSoundEffectsEnabled(self, soundEffectsEnabled): self._sound_effects = soundEffectsEnabled def isSoundEffectsEnabled(self): return getattr(self, '_sound_effects', True) def setHapticFeedbackEnabled(self, hapticFeedbackEnabled): self._haptic_feedback = hapticFeedbackEnabled def isHapticFeedbackEnabled(self): return getattr(self, '_haptic_feedback', True) def refreshDrawableState(self): pass def getDrawableState(self): return [] def requestRectangleOnScreen(self, rectangle): return False def setAllCaps(self, allCaps): self._all_caps = allCaps class MeasureSpec: """MeasureSpec — 测量规格包装""" EXACTLY = View.EXACTLY AT_MOST = 0x80000000 # -2147483648 UNSPECIFIED = 0 MODE_SHIFT = 30 MODE_MASK = 0xC0000000 @staticmethod def makeMeasureSpec(size: int, mode: int) -> int: return (size & ~MeasureSpec.MODE_MASK) | (mode & MeasureSpec.MODE_MASK) @staticmethod def getMode(measure_spec: int) -> int: return measure_spec & MeasureSpec.MODE_MASK @staticmethod def getSize(measure_spec: int) -> int: return measure_spec & ~MeasureSpec.MODE_MASK @staticmethod def toString(measure_spec: int) -> str: mode = MeasureSpec.getMode(measure_spec) size = MeasureSpec.getSize(measure_spec) mode_str = "UNSPECIFIED" if mode == MeasureSpec.EXACTLY: mode_str = "EXACTLY" elif mode == MeasureSpec.AT_MOST: mode_str = "AT_MOST" return f"MeasureSpec: {mode_str} {size}" View.MeasureSpec = MeasureSpec class ViewPropertyAnimator: """ViewPropertyAnimator — View 属性动画""" def __init__(self, view: View): self._view = view self._duration: int = 300 self._interpolator: Any = None def setDuration(self, duration: int) -> "ViewPropertyAnimator": self._duration = duration return self def alpha(self, value: float) -> "ViewPropertyAnimator": self._view.setAlpha(value) return self def translationX(self, value: float) -> "ViewPropertyAnimator": self._view.setTranslationX(value) return self def translationY(self, value: float) -> "ViewPropertyAnimator": self._view.setTranslationY(value) return self def rotation(self, value: float) -> "ViewPropertyAnimator": self._view.setRotation(value) return self def scaleX(self, value: float) -> "ViewPropertyAnimator": self._view.setScaleX(value) return self def scaleY(self, value: float) -> "ViewPropertyAnimator": self._view.setScaleY(value) return self def x(self, value: float) -> "ViewPropertyAnimator": self._view.setX(value) return self def y(self, value: float) -> "ViewPropertyAnimator": self._view.setY(value) return self def start(self): pass def cancel(self): pass def setInterpolator(self, interpolator: Any) -> "ViewPropertyAnimator": self._interpolator = interpolator return self def withStartAction(self, runnable: Callable) -> "ViewPropertyAnimator": return self def withEndAction(self, runnable: Callable) -> "ViewPropertyAnimator": return self # ═══════════════════════════════════════════════════════════════ # ViewParent / ViewManager # ═══════════════════════════════════════════════════════════════ class ViewParent(Stub): """ViewParent — 视图父接口""" def requestLayout(self): pass def invalidateChild(self, child: View, r: Any): pass def requestChildFocus(self, child: View, focused: View): pass def clearChildFocus(self, child: View): pass def getParent(self) -> Optional["ViewParent"]: return None def bringChildToFront(self, child: View): pass def focusSearch(self, child: View, direction: int) -> Optional[View]: return None def requestChildRectangleOnScreen(self, child: View, rectangle: Any, immediate: bool) -> bool: return False def getChildVisibleRect(self, child: View, r: Any, offset: Any) -> bool: return False def requestTransparentRegion(self, child: View): pass def onStartNestedScroll(self, child: View, target: View, nested_scroll_axes: int) -> bool: return False def onStopNestedScroll(self, target: View): pass def onNestedScroll(self, target: View, dx_consumed: int, dy_consumed: int, dx_unconsumed: int, dy_unconsumed: int): pass def onNestedPreScroll(self, target: View, dx: int, dy: int, consumed: List[int]): pass def onNestedFling(self, target: View, velocity_x: float, velocity_y: float, consumed: bool) -> bool: return False def onNestedPreFling(self, target: View, velocity_x: float, velocity_y: float) -> bool: return False def isNestedScrollingEnabled(self) -> bool: return False def setNestedScrollingEnabled(self, enabled: bool): pass def notifySubtreeAccessibilityStateChanged(self, child: View, source: View, change_type: int): pass class ViewManager(Stub): """ViewManager — 视图管理接口""" def addView(self, view: View, params: "ViewGroup.LayoutParams"): pass def updateViewLayout(self, view: View, params: "ViewGroup.LayoutParams"): pass def removeView(self, view: View): pass # ═══════════════════════════════════════════════════════════════ # ViewGroup # ═══════════════════════════════════════════════════════════════ class ViewGroup(View, ViewParent, ViewManager): """ViewGroup — 可包含子视图的容器""" CLIP_TO_PADDING_MASK = 0x00000001 CLIP_CHILDREN_MASK = 0x00000002 FLAG_CLIP_CHILDREN = 0x00000001 FLAG_CLIP_TO_PADDING = 0x00000002 FLAG_USE_CHILD_DRAWING_ORDER = 0x00000400 def __init__(self, context=None): super().__init__(context) self._children: List[View] = [] self._clip_children = True self._clip_to_padding = True self._layout_mode = 0 self._child_drawing_order_reversed = False self._persistent_drawing_cache = False self._always_drawn_with_cache = True self._animate_on_set_layout = True self._add_stateset = True self._layout_animations = False # ─── child management ────────────────────────────────────── def addView(self, child: View, index: Optional[int] = -1, params: Optional["ViewGroup.LayoutParams"] = None): """addView — 支持 addView(child), addView(child, params), addView(child, index), addView(child, index, params)""" # 智能 参数分派:第二个参数可能是 index(int) 或 params(LayoutParams) if isinstance(index, ViewGroup.LayoutParams): params = index index = -1 if params is None: params = ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) if index < 0 or index >= len(self._children): self._children.append(child) else: self._children.insert(index, child) child._parent = self child.setLayoutParams(params) child.onAttachedToWindow() def removeView(self, view: View): if view in self._children: self._children.remove(view) view._parent = None view.onDetachedFromWindow() def removeViewAt(self, index: int): if 0 <= index < len(self._children): self.removeView(self._children[index]) def removeAllViews(self): for child in list(self._children): child._parent = None child.onDetachedFromWindow() self._children.clear() def getChildCount(self) -> int: return len(self._children) def getChildAt(self, index: int) -> Optional[View]: if 0 <= index < len(self._children): return self._children[index] return None def indexOfChild(self, child: View) -> int: try: return self._children.index(child) except ValueError: return -1 def bringChildToFront(self, child: View): if child in self._children: self._children.remove(child) self._children.append(child) def requestChildFocus(self, child: View, focused: View): pass def onInterceptTouchEvent(self, event: "MotionEvent") -> bool: return False def onInterceptHoverEvent(self, event: "MotionEvent") -> bool: return False def addChildrenForAccessibility(self, children: List[View]): for child in self._children: children.append(child) def setClipChildren(self, clip_children: bool): self._clip_children = clip_children def getClipChildren(self) -> bool: return self._clip_children def setClipToPadding(self, clip_to_padding: bool): self._clip_to_padding = clip_to_padding def getClipToPadding(self) -> bool: return self._clip_to_padding def setLayoutAnimation(self, layout_animation: Any): pass def getLayoutAnimation(self) -> Any: return None def setLayoutMode(self, layout_mode: int): self._layout_mode = layout_mode def getLayoutMode(self) -> int: return self._layout_mode def setPersistentDrawingCache(self, drawing_cache_to_keep: int): self._persistent_drawing_cache = bool(drawing_cache_to_keep) def setAddStatesFromChildren(self, add_states: bool): self._add_stateset = add_states def addStatesFromChildren(self) -> bool: return self._add_stateset def setChildDrawingOrderCallback(self, callback: Any): pass def setChildrenDrawingOrderEnabled(self, enabled: bool): self._child_drawing_order_reversed = enabled def isChildrenDrawingOrderEnabled(self) -> bool: return self._child_drawing_order_reversed def setChildrenDrawnWithCacheEnabled(self, enabled: bool): self._always_drawn_with_cache = enabled def isChildrenDrawnWithCacheEnabled(self) -> bool: return self._always_drawn_with_cache def setMotionEventSplittingEnabled(self, split: bool): pass def isMotionEventSplittingEnabled(self) -> bool: return True # ─── layout transition ──────────────────────────────────── def setLayoutTransition(self, transition: Any): self._layout_transition = transition def getLayoutTransition(self) -> Any: return getattr(self, '_layout_transition', None) # ─── focus / touch blocking ─────────────────────────────── def setTouchscreenBlocksFocus(self, blocks: bool): self._touchscreen_blocks_focus = blocks def isTouchscreenBlocksFocus(self) -> bool: return getattr(self, '_touchscreen_blocks_focus', False) # ─── child visible rect ─────────────────────────────────── def getChildVisibleRect(self, child: View, r: Any, offset: Any) -> bool: if r is not None and hasattr(r, 'set'): r.set(child.getLeft(), child.getTop(), child.getRight(), child.getBottom()) if offset is not None and hasattr(offset, 'set'): offset.set(0, 0) return True def setTransitionGroup(self, is_transition_group: bool): pass def isTransitionGroup(self) -> bool: return False def startLayoutAnimation(self): pass def scheduleLayoutAnimation(self): pass def gatherTransparentRegion(self, region: Any) -> bool: return False def requestTransparentRegion(self, child: View): pass def onChildVisibilityChanged(self, child: View, old_visibility: int, new_visibility: int): pass def focusSearch(self, focused: View, direction: int) -> Optional[View]: return None def focusableViewAvailable(self, v: View): pass def showContextMenuForChild(self, original_view: View) -> bool: return False def createContextMenu(self, menu: Any): pass def showContextMenu(self) -> bool: return False def startActionModeForChild(self, original_view: View, action_mode: Any) -> Optional[Any]: return None # ─── LayoutParams ────────────────────────────────────────── class LayoutParams: """ViewGroup.LayoutParams — 布局参数""" MATCH_PARENT = -1 WRAP_CONTENT = -2 def __init__(self, width: int = WRAP_CONTENT, height: int = WRAP_CONTENT): self.width = width self.height = height @staticmethod def getSize(size: int) -> int: return size # ─── MarginLayoutParams ──────────────────────────────────── class MarginLayoutParams(LayoutParams): """ViewGroup.MarginLayoutParams — 带边距的布局参数""" def __init__(self, width: int = -2, height: int = -2): super().__init__(width, height) self.leftMargin = 0 self.topMargin = 0 self.rightMargin = 0 self.bottomMargin = 0 self.startMargin = 0 self.endMargin = 0 def setMargins(self, left: int, top: int, right: int, bottom: int): self.leftMargin = left self.topMargin = top self.rightMargin = right self.bottomMargin = bottom def getMarginLeft(self) -> int: return self.leftMargin def getMarginTop(self) -> int: return self.topMargin def getMarginRight(self) -> int: return self.rightMargin def getMarginBottom(self) -> int: return self.bottomMargin def getMarginStart(self) -> int: return self.startMargin if self.startMargin != 0 else self.leftMargin def getMarginEnd(self) -> int: return self.endMargin if self.endMargin != 0 else self.rightMargin def setMarginStart(self, start: int): self.startMargin = start def setMarginEnd(self, end: int): self.endMargin = end # ═══════════════════════════════════════════════════════════════ # WindowManager # ═══════════════════════════════════════════════════════════════ class WindowManager(ViewManager): """WindowManager — 窗口管理器""" class LayoutParams(ViewGroup.LayoutParams): """WindowManager.LayoutParams — 窗口布局参数""" # 窗口类型 TYPE_FIRST_APPLICATION = 1 TYPE_APPLICATION = 2 TYPE_APPLICATION_STARTING = 3 TYPE_APPLICATION_PANEL = 1000 TYPE_APPLICATION_MEDIA = 1001 TYPE_APPLICATION_SUB_PANEL = 1002 TYPE_APPLICATION_ATTACHED_DIALOG = 1003 TYPE_APPLICATION_OVERLAY = 1004 # 标志位 FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001 FLAG_DIM_BEHIND = 0x00000002 FLAG_BLUR_BEHIND = 0x00000004 FLAG_NOT_FOCUSABLE = 0x00000008 FLAG_NOT_TOUCHABLE = 0x00000010 FLAG_NOT_TOUCH_MODAL = 0x00000020 FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040 FLAG_KEEP_SCREEN_ON = 0x00000080 FLAG_LAYOUT_IN_SCREEN = 0x00000100 FLAG_LAYOUT_NO_LIMITS = 0x00000200 FLAG_FULLSCREEN = 0x00000400 FLAG_FORCE_NOT_FULLSCREEN = 0x00000800 FLAG_DITHER = 0x00001000 FLAG_SECURE = 0x00002000 FLAG_SCALED = 0x00004000 FLAG_IGNORE_CHEEK_PRESSES = 0x00008000 FLAG_LAYOUT_INSET_DECOR = 0x00010000 FLAG_ALT_FOCUSABLE_IM = 0x00020000 FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000 FLAG_SHOW_WHEN_LOCKED = 0x00080000 FLAG_SHOW_WALLPAPER = 0x00100000 FLAG_TURN_SCREEN_ON = 0x00200000 FLAG_DISMISS_KEYGUARD = 0x00400000 FLAG_SPLIT_TOUCH = 0x00800000 FLAG_HARDWARE_ACCELERATED = 0x01000000 FLAG_LOCAL_FOCUS_MODE = 0x02000000 FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS = 0x04000000 FLAG_TRANSLUCENT_STATUS = 0x04000000 FLAG_TRANSLUCENT_NAVIGATION = 0x08000000 # 软输入模式 SOFT_INPUT_STATE_UNSPECIFIED = 0 SOFT_INPUT_STATE_UNCHANGED = 1 SOFT_INPUT_STATE_HIDDEN = 2 SOFT_INPUT_STATE_ALWAYS_HIDDEN = 3 SOFT_INPUT_STATE_VISIBLE = 4 SOFT_INPUT_STATE_ALWAYS_VISIBLE = 5 SOFT_INPUT_ADJUST_UNSPECIFIED = 0x00 SOFT_INPUT_ADJUST_RESIZE = 0x10 SOFT_INPUT_ADJUST_PAN = 0x20 SOFT_INPUT_ADJUST_NOTHING = 0x30 SOFT_INPUT_IS_FORWARD_NAVIGATION = 0x100 # 屏幕方向 SCREEN_ORIENTATION_UNSPECIFIED = -1 SCREEN_ORIENTATION_LANDSCAPE = 0 SCREEN_ORIENTATION_PORTRAIT = 1 SCREEN_ORIENTATION_USER = 2 SCREEN_ORIENTATION_BEHIND = 3 SCREEN_ORIENTATION_SENSOR = 4 SCREEN_ORIENTATION_NOSENSOR = 5 SCREEN_ORIENTATION_SENSOR_LANDSCAPE = 6 SCREEN_ORIENTATION_SENSOR_PORTRAIT = 7 SCREEN_ORIENTATION_REVERSE_LANDSCAPE = 8 SCREEN_ORIENTATION_REVERSE_PORTRAIT = 9 SCREEN_ORIENTATION_FULL_SENSOR = 10 SCREEN_ORIENTATION_USER_LANDSCAPE = 11 SCREEN_ORIENTATION_USER_PORTRAIT = 12 SCREEN_ORIENTATION_FULL_USER = 13 SCREEN_ORIENTATION_LOCKED = 14 # 重力 gravity = Gravity.NO_GRAVITY x = 0 y = 0 horizontalMargin = 0.0 verticalMargin = 0.0 def __init__(self, w: int = ViewGroup.LayoutParams.WRAP_CONTENT, h: int = ViewGroup.LayoutParams.WRAP_CONTENT): super().__init__(w, h) self.type = WindowManager.LayoutParams.TYPE_APPLICATION self.flags = 0 self.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED self.screenOrientation = WindowManager.LayoutParams.SCREEN_ORIENTATION_UNSPECIFIED self.gravity = Gravity.NO_GRAVITY self.x = 0 self.y = 0 self.token: Any = None self.windowAnimations: int = 0 self.packageName: str = "" self.screenBrightness = -1.0 self.buttonBrightness = -1.0 self.rotationAnimation = 0 self.preferredDisplayModeId = 0 self.colorMode = 0 self.format = -1 def __init__(self): super().__init__() self._views: Dict[View, WindowManager.LayoutParams] = {} def addView(self, view: View, params: LayoutParams): self._views[view] = params def updateViewLayout(self, view: View, params: LayoutParams): if view in self._views: self._views[view] = params def removeView(self, view: View): self._views.pop(view, None) def removeViewImmediate(self, view: View): self._views.pop(view, None) def getDefaultDisplay(self) -> "Display": return Display() # ═══════════════════════════════════════════════════════════════ # MotionEvent # ═══════════════════════════════════════════════════════════════ class MotionEvent(Stub): """MotionEvent — 触摸事件""" # 动作常量 ACTION_DOWN = 0 ACTION_UP = 1 ACTION_MOVE = 2 ACTION_CANCEL = 3 ACTION_OUTSIDE = 4 ACTION_POINTER_DOWN = 5 ACTION_POINTER_UP = 6 ACTION_HOVER_MOVE = 7 ACTION_HOVER_ENTER = 8 ACTION_HOVER_EXIT = 9 ACTION_SCROLL = 11 ACTION_BUTTON_PRESS = 12 ACTION_BUTTON_RELEASE = 13 # 动作掩码 ACTION_MASK = 0xFF ACTION_POINTER_INDEX_MASK = 0xFF00 ACTION_POINTER_INDEX_SHIFT = 8 # 按钮 BUTTON_PRIMARY = 1 BUTTON_SECONDARY = 2 BUTTON_TERTIARY = 4 BUTTON_BACK = 8 BUTTON_FORWARD = 16 BUTTON_STYLUS_PRIMARY = 32 BUTTON_STYLUS_SECONDARY = 64 # 边缘 EDGE_LEFT = 1 EDGE_TOP = 2 EDGE_RIGHT = 4 EDGE_BOTTOM = 8 # 坐标分类 AXIS_X = 0 AXIS_Y = 1 AXIS_PRESSURE = 2 AXIS_SIZE = 3 AXIS_TOUCH_MAJOR = 4 AXIS_TOUCH_MINOR = 5 AXIS_TOOL_MAJOR = 6 AXIS_TOOL_MINOR = 7 AXIS_ORIENTATION = 8 AXIS_VSCROLL = 9 AXIS_HSCROLL = 10 AXIS_Z = 11 AXIS_RX = 12 AXIS_RY = 13 AXIS_RZ = 14 AXIS_HAT_X = 15 AXIS_HAT_Y = 16 AXIS_LTRIGGER = 17 AXIS_RTRIGGER = 18 AXIS_THROTTLE = 19 AXIS_RUDDER = 20 AXIS_WHEEL = 21 AXIS_GAS = 22 AXIS_BRAKE = 23 AXIS_DISTANCE = 24 AXIS_TILT = 25 AXIS_SCROLL = 26 AXIS_RELATIVE_X = 27 AXIS_RELATIVE_Y = 28 AXIS_GENERIC_1 = 32 AXIS_GENERIC_2 = 33 AXIS_GENERIC_3 = 34 AXIS_GENERIC_4 = 35 AXIS_GENERIC_5 = 36 AXIS_GENERIC_6 = 37 AXIS_GENERIC_7 = 38 AXIS_GENERIC_8 = 39 AXIS_GENERIC_9 = 40 AXIS_GENERIC_10 = 41 AXIS_GENERIC_11 = 42 AXIS_GENERIC_12 = 43 AXIS_GENERIC_13 = 44 AXIS_GENERIC_14 = 45 AXIS_GENERIC_15 = 46 AXIS_GENERIC_16 = 47 # 输入类型 TOOL_TYPE_UNKNOWN = 0 TOOL_TYPE_FINGER = 1 TOOL_TYPE_STYLUS = 2 TOOL_TYPE_MOUSE = 3 TOOL_TYPE_ERASER = 4 INVALID_POINTER_ID = -1 def __init__(self, action: int = 0, x: float = 0.0, y: float = 0.0, event_time: int = 0, pointer_count: int = 1, meta_state: int = 0, button_state: int = 0): super().__init__() self._action = action self._x = x self._y = y self._raw_x = x self._raw_y = y self._event_time = event_time if event_time else int(time.time() * 1000) self._down_time = self._event_time self._pointer_count = pointer_count self._meta_state = meta_state self._button_state = button_state self._pointers: List[Dict] = [] for i in range(pointer_count): self._pointers.append({ "id": i, "x": x, "y": y, "pressure": 1.0, "size": 0.5, "tool_type": MotionEvent.TOOL_TYPE_FINGER, }) @classmethod def obtain(cls, down_time: int, event_time: int, action: int, x: float, y: float, meta_state: int = 0) -> "MotionEvent": return cls(action=action, x=x, y=y, event_time=event_time) @classmethod def obtainNoHistory(cls, me: "MotionEvent") -> "MotionEvent": return cls(action=me._action, x=me._x, y=me._y, event_time=me._event_time, pointer_count=me._pointer_count) def recycle(self): pass def getAction(self) -> int: return self._action def getActionMasked(self) -> int: return self._action & MotionEvent.ACTION_MASK def getActionIndex(self) -> int: return (self._action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT def getX(self, pointer_index: int = 0) -> float: if 0 <= pointer_index < len(self._pointers): return self._pointers[pointer_index]["x"] return self._x def getY(self, pointer_index: int = 0) -> float: if 0 <= pointer_index < len(self._pointers): return self._pointers[pointer_index]["y"] return self._y def getRawX(self) -> float: return self._raw_x def getRawY(self) -> float: return self._raw_y def getPointerCount(self) -> int: return self._pointer_count def getPointerId(self, pointer_index: int) -> int: if 0 <= pointer_index < len(self._pointers): return self._pointers[pointer_index]["id"] return pointer_index def findPointerIndex(self, pointer_id: int) -> int: for i, p in enumerate(self._pointers): if p["id"] == pointer_id: return i return -1 def getPressure(self, pointer_index: int = 0) -> float: if 0 <= pointer_index < len(self._pointers): return self._pointers[pointer_index]["pressure"] return 1.0 def getSize(self, pointer_index: int = 0) -> float: if 0 <= pointer_index < len(self._pointers): return self._pointers[pointer_index]["size"] return 0.5 def getEventTime(self) -> int: return self._event_time def getDownTime(self) -> int: return self._down_time def getMetaState(self) -> int: return self._meta_state def getButtonState(self) -> int: return self._button_state def getToolType(self, pointer_index: int = 0) -> int: if 0 <= pointer_index < len(self._pointers): return self._pointers[pointer_index]["tool_type"] return MotionEvent.TOOL_TYPE_FINGER def setLocation(self, x: float, y: float): self._x = x self._y = y def offsetLocation(self, dx: float, dy: float): self._x += dx self._y += dy def transform(self, matrix: Any): pass def isLongPress(self) -> bool: return (self._event_time - self._down_time) > 500 def getHistoricalSize(self, pos: int, pointer: int = 0) -> float: return 0.5 # ─── historical coordinates ─────────────────────────────── def getHistorySize(self) -> int: return len(getattr(self, '_history', [])) def getHistoricalX(self, pointer_index: int = 0, pos: int = 0) -> float: history = getattr(self, '_history', []) if 0 <= pos < len(history): return history[pos].get('x', self.getX(pointer_index)) return self.getX(pointer_index) def getHistoricalY(self, pointer_index: int = 0, pos: int = 0) -> float: history = getattr(self, '_history', []) if 0 <= pos < len(history): return history[pos].get('y', self.getY(pointer_index)) return self.getY(pointer_index) def getHistoricalPressure(self, pointer_index: int = 0, pos: int = 0) -> float: history = getattr(self, '_history', []) if 0 <= pos < len(history): return history[pos].get('pressure', 1.0) return 1.0 def getHistoricalEventTime(self, pos: int = 0) -> int: history = getattr(self, '_history', []) if 0 <= pos < len(history): return history[pos].get('event_time', self._event_time) return self._event_time # ─── edge flags ─────────────────────────────────────────── def getEdgeFlags(self) -> int: return getattr(self, '_edge_flags', 0) def setEdgeFlags(self, edge_flags: int): self._edge_flags = edge_flags # ─── action mutation ────────────────────────────────────── def setAction(self, action: int): self._action = action # ─── batch ──────────────────────────────────────────────── def addBatch(self, event_time: int, x: float, y: float, pressure: float = 1.0, size: float = 0.5, meta_state: int = 0): if not hasattr(self, '_history'): self._history = [] self._history.append({ 'event_time': self._event_time, 'x': self._x, 'y': self._y, 'pressure': self.getPressure(0), 'size': self.getSize(0), 'meta_state': self._meta_state, }) self._x = x self._y = y self._raw_x = x self._raw_y = y self._event_time = event_time self._meta_state = meta_state if self._pointers: self._pointers[0]['x'] = x self._pointers[0]['y'] = y self._pointers[0]['pressure'] = pressure self._pointers[0]['size'] = size # ═══════════════════════════════════════════════════════════════ # KeyEvent # ═══════════════════════════════════════════════════════════════ class KeyEvent(Stub): """KeyEvent — 按键事件""" # 动作 ACTION_DOWN = 0 ACTION_UP = 1 ACTION_MULTIPLE = 2 # 键码 KEYCODE_UNKNOWN = 0 KEYCODE_SOFT_LEFT = 1 KEYCODE_SOFT_RIGHT = 2 KEYCODE_HOME = 3 KEYCODE_BACK = 4 KEYCODE_CALL = 5 KEYCODE_ENDCALL = 6 KEYCODE_0 = 7 KEYCODE_1 = 8 KEYCODE_2 = 9 KEYCODE_3 = 10 KEYCODE_4 = 11 KEYCODE_5 = 12 KEYCODE_6 = 13 KEYCODE_7 = 14 KEYCODE_8 = 15 KEYCODE_9 = 16 KEYCODE_STAR = 17 KEYCODE_POUND = 18 KEYCODE_DPAD_UP = 19 KEYCODE_DPAD_DOWN = 20 KEYCODE_DPAD_LEFT = 21 KEYCODE_DPAD_RIGHT = 22 KEYCODE_DPAD_CENTER = 23 KEYCODE_VOLUME_UP = 24 KEYCODE_VOLUME_DOWN = 25 KEYCODE_POWER = 26 KEYCODE_CAMERA = 27 KEYCODE_CLEAR = 28 KEYCODE_A = 29 KEYCODE_B = 30 KEYCODE_C = 31 KEYCODE_D = 32 KEYCODE_E = 33 KEYCODE_F = 34 KEYCODE_G = 35 KEYCODE_H = 36 KEYCODE_I = 37 KEYCODE_J = 38 KEYCODE_K = 39 KEYCODE_L = 40 KEYCODE_M = 41 KEYCODE_N = 42 KEYCODE_O = 43 KEYCODE_P = 44 KEYCODE_Q = 45 KEYCODE_R = 46 KEYCODE_S = 47 KEYCODE_T = 48 KEYCODE_U = 49 KEYCODE_V = 50 KEYCODE_W = 51 KEYCODE_X = 52 KEYCODE_Y = 53 KEYCODE_Z = 54 KEYCODE_COMMA = 55 KEYCODE_PERIOD = 56 KEYCODE_ALT_LEFT = 57 KEYCODE_ALT_RIGHT = 58 KEYCODE_SHIFT_LEFT = 59 KEYCODE_SHIFT_RIGHT = 60 KEYCODE_TAB = 61 KEYCODE_SPACE = 62 KEYCODE_SYM = 63 KEYCODE_EXPLORER = 64 KEYCODE_ENVELOPE = 65 KEYCODE_ENTER = 66 KEYCODE_DEL = 67 KEYCODE_GRAVE = 68 KEYCODE_MINUS = 69 KEYCODE_EQUALS = 70 KEYCODE_LEFT_BRACKET = 71 KEYCODE_RIGHT_BRACKET = 72 KEYCODE_BACKSLASH = 73 KEYCODE_SEMICOLON = 74 KEYCODE_APOSTROPHE = 75 KEYCODE_SLASH = 76 KEYCODE_AT = 77 KEYCODE_NUM = 78 KEYCODE_HEADSETHOOK = 79 KEYCODE_FOCUS = 80 KEYCODE_PLUS = 81 KEYCODE_MENU = 82 KEYCODE_NOTIFICATION = 83 KEYCODE_SEARCH = 84 KEYCODE_MEDIA_PLAY_PAUSE = 85 KEYCODE_MEDIA_STOP = 86 KEYCODE_MEDIA_NEXT = 87 KEYCODE_MEDIA_PREVIOUS = 88 KEYCODE_MEDIA_REWIND = 89 KEYCODE_MEDIA_FAST_FORWARD = 90 KEYCODE_MUTE = 91 KEYCODE_PAGE_UP = 92 KEYCODE_PAGE_DOWN = 93 KEYCODE_PICTSYMBOLS = 94 KEYCODE_SWITCH_CHARSET = 95 KEYCODE_BUTTON_A = 96 KEYCODE_BUTTON_B = 97 KEYCODE_BUTTON_C = 98 KEYCODE_BUTTON_X = 99 KEYCODE_BUTTON_Y = 100 KEYCODE_BUTTON_Z = 101 KEYCODE_BUTTON_L1 = 102 KEYCODE_BUTTON_R1 = 103 KEYCODE_BUTTON_L2 = 104 KEYCODE_BUTTON_R2 = 105 KEYCODE_BUTTON_THUMBL = 106 KEYCODE_BUTTON_THUMBR = 107 KEYCODE_BUTTON_START = 108 KEYCODE_BUTTON_SELECT = 109 KEYCODE_BUTTON_MODE = 110 KEYCODE_ESCAPE = 111 KEYCODE_FORWARD_DEL = 112 KEYCODE_CTRL_LEFT = 113 KEYCODE_CTRL_RIGHT = 114 KEYCODE_CAPS_LOCK = 115 KEYCODE_SCROLL_LOCK = 116 KEYCODE_META_LEFT = 117 KEYCODE_META_RIGHT = 118 KEYCODE_FUNCTION = 119 KEYCODE_SYSRQ = 120 KEYCODE_BREAK = 121 KEYCODE_MOVE_HOME = 122 KEYCODE_MOVE_END = 123 KEYCODE_INSERT = 124 KEYCODE_FORWARD = 125 KEYCODE_MEDIA_PLAY = 126 KEYCODE_MEDIA_PAUSE = 127 KEYCODE_MEDIA_CLOSE = 128 KEYCODE_MEDIA_EJECT = 129 KEYCODE_MEDIA_RECORD = 130 KEYCODE_F1 = 131 KEYCODE_F2 = 132 KEYCODE_F3 = 133 KEYCODE_F4 = 134 KEYCODE_F5 = 135 KEYCODE_F6 = 136 KEYCODE_F7 = 137 KEYCODE_F8 = 138 KEYCODE_F9 = 139 KEYCODE_F10 = 140 KEYCODE_F11 = 141 KEYCODE_F12 = 142 KEYCODE_NUM_LOCK = 143 KEYCODE_NUMPAD_0 = 144 KEYCODE_NUMPAD_1 = 145 KEYCODE_NUMPAD_2 = 146 KEYCODE_NUMPAD_3 = 147 KEYCODE_NUMPAD_4 = 148 KEYCODE_NUMPAD_5 = 149 KEYCODE_NUMPAD_6 = 150 KEYCODE_NUMPAD_7 = 151 KEYCODE_NUMPAD_8 = 152 KEYCODE_NUMPAD_9 = 153 KEYCODE_NUMPAD_DIVIDE = 154 KEYCODE_NUMPAD_MULTIPLY = 155 KEYCODE_NUMPAD_SUBTRACT = 156 KEYCODE_NUMPAD_ADD = 157 KEYCODE_NUMPAD_DOT = 158 KEYCODE_NUMPAD_COMMA = 159 KEYCODE_NUMPAD_ENTER = 160 KEYCODE_NUMPAD_EQUALS = 161 KEYCODE_NUMPAD_LEFT_PAREN = 162 KEYCODE_NUMPAD_RIGHT_PAREN = 163 KEYCODE_META_CTRL_LEFT = 164 KEYCODE_META_CTRL_RIGHT = 165 KEYCODE_META_ALT_LEFT = 166 KEYCODE_META_ALT_RIGHT = 167 KEYCODE_META_SHIFT_LEFT = 168 KEYCODE_META_SHIFT_RIGHT = 169 KEYCODE_META_SYM = 170 # 元键掩码 META_ALT_ON = 0x02 META_ALT_LEFT_ON = 0x10 META_ALT_RIGHT_ON = 0x20 META_SHIFT_ON = 0x01 META_SHIFT_LEFT_ON = 0x40 META_SHIFT_RIGHT_ON = 0x80 META_SYM_ON = 0x04 META_FUNCTION_ON = 0x08 META_CTRL_ON = 0x1000 META_CTRL_LEFT_ON = 0x2000 META_CTRL_RIGHT_ON = 0x4000 META_META_ON = 0x10000 META_META_LEFT_ON = 0x20000 META_META_RIGHT_ON = 0x40000 META_CAPS_LOCK_ON = 0x100000 META_NUM_LOCK_ON = 0x200000 META_SCROLL_LOCK_ON = 0x400000 def __init__(self, action: int = ACTION_DOWN, key_code: int = KEYCODE_UNKNOWN, repeat_count: int = 0, event_time: int = 0, meta_state: int = 0): super().__init__() self._action = action self._key_code = key_code self._repeat_count = repeat_count self._event_time = event_time if event_time else int(time.time() * 1000) self._down_time = self._event_time self._meta_state = meta_state self._device_id = 0 self._source = 0x101 # InputDevice.SOURCE_KEYBOARD self._flags = 0 self._display_id = 0 getAction = lambda self: self._action getKeyCode = lambda self: self._key_code getRepeatCount = lambda self: self._repeat_count getEventTime = lambda self: self._event_time getDownTime = lambda self: self._down_time getMetaState = lambda self: self._meta_state getDeviceId = lambda self: self._device_id getSource = lambda self: self._source getFlags = lambda self: self._flags getDisplayId = lambda self: self._display_id def isLongPress(self) -> bool: return self._repeat_count > 0 def dispatch(self, receiver: View, on_receive: Callable) -> bool: return on_receive(receiver, self) def isAltPressed(self) -> bool: return bool(self._meta_state & KeyEvent.META_ALT_ON) def isShiftPressed(self) -> bool: return bool(self._meta_state & KeyEvent.META_SHIFT_ON) def isCtrlPressed(self) -> bool: return bool(self._meta_state & KeyEvent.META_CTRL_ON) def isMetaPressed(self) -> bool: return bool(self._meta_state & KeyEvent.META_META_ON) def isSymPressed(self) -> bool: return bool(self._meta_state & KeyEvent.META_SYM_ON) def isFunctionPressed(self) -> bool: return bool(self._meta_state & KeyEvent.META_FUNCTION_ON) def isCapsLockOn(self) -> bool: return bool(self._meta_state & KeyEvent.META_CAPS_LOCK_ON) def isNumLockOn(self) -> bool: return bool(self._meta_state & KeyEvent.META_NUM_LOCK_ON) def isScrollLockOn(self) -> bool: return bool(self._meta_state & KeyEvent.META_SCROLL_LOCK_ON) @staticmethod def changeTimeRepeat(event: "KeyEvent", event_time: int, repeat_count: int) -> "KeyEvent": return KeyEvent( action=event._action, key_code=event._key_code, repeat_count=repeat_count, event_time=event_time, meta_state=event._meta_state, ) @staticmethod def changeAction(event: "KeyEvent", action: int) -> "KeyEvent": return KeyEvent( action=action, key_code=event._key_code, repeat_count=event._repeat_count, event_time=event._event_time, meta_state=event._meta_state, ) def getCharacters(self): return getattr(self, '_characters', None) def isSystem(self): return False def isPrintingKey(self): return False def isModifierKey(self, keyCode): return keyCode in (KEYCODE_SHIFT_LEFT, KEYCODE_SHIFT_RIGHT, KEYCODE_ALT_LEFT, KEYCODE_ALT_RIGHT, KEYCODE_CTRL_LEFT, KEYCODE_CTRL_RIGHT, KEYCODE_META_LEFT, KEYCODE_META_RIGHT) def startTracking(self): self._tracking = True def isTracking(self): return getattr(self, '_tracking', False) @staticmethod def keyCodeToString(keyCode): return f"KEYCODE_{keyCode}" # ═══════════════════════════════════════════════════════════════ # GestureDetector # ═══════════════════════════════════════════════════════════════ class GestureDetector(Stub): """GestureDetector — 手势检测器""" class SimpleOnGestureListener: """SimpleOnGestureListener — 简单手势监听器""" def onDown(self, e: MotionEvent) -> bool: return False def onShowPress(self, e: MotionEvent): pass def onSingleTapUp(self, e: MotionEvent) -> bool: return False def onScroll(self, e1: MotionEvent, e2: MotionEvent, distance_x: float, distance_y: float) -> bool: return False def onLongPress(self, e: MotionEvent): pass def onFling(self, e1: MotionEvent, e2: MotionEvent, velocity_x: float, velocity_y: float) -> bool: return False def onSingleTapConfirmed(self, e: MotionEvent) -> bool: return False def onDoubleTap(self, e: MotionEvent) -> bool: return False def onDoubleTapEvent(self, e: MotionEvent) -> bool: return False def onContextClick(self, e: MotionEvent) -> bool: return False class OnGestureListener: def onDown(self, e: MotionEvent) -> bool: return False def onShowPress(self, e: MotionEvent): pass def onSingleTapUp(self, e: MotionEvent) -> bool: return False def onScroll(self, e1: MotionEvent, e2: MotionEvent, distance_x: float, distance_y: float) -> bool: return False def onLongPress(self, e: MotionEvent): pass def onFling(self, e1: MotionEvent, e2: MotionEvent, velocity_x: float, velocity_y: float) -> bool: return False class OnDoubleTapListener: def onSingleTapConfirmed(self, e: MotionEvent) -> bool: return False def onDoubleTap(self, e: MotionEvent) -> bool: return False def onDoubleTapEvent(self, e: MotionEvent) -> bool: return False def __init__(self, context=None, listener: Optional["GestureDetector.OnGestureListener"] = None, handler: Any = None): super().__init__() self._listener = listener self._double_tap_listener: Optional["GestureDetector.OnDoubleTapListener"] = None self._is_longpress_enabled = True self._context = context self._current_down_event: Optional[MotionEvent] = None self._previous_down_event: Optional[MotionEvent] = None self._in_long_press = False self._long_press_timeout = 500 self._touch_slop = 8 def setOnDoubleTapListener(self, listener: Optional["GestureDetector.OnDoubleTapListener"]): self._double_tap_listener = listener def setIsLongpressEnabled(self, enabled: bool): self._is_longpress_enabled = enabled def isLongpressEnabled(self) -> bool: return self._is_longpress_enabled def onTouchEvent(self, ev: MotionEvent) -> bool: action = ev.getAction() if action == MotionEvent.ACTION_DOWN: self._in_long_press = False self._current_down_event = ev if self._listener: return self._listener.onDown(ev) elif action == MotionEvent.ACTION_MOVE: pass elif action == MotionEvent.ACTION_UP: if self._listener: return self._listener.onSingleTapUp(ev) elif action == MotionEvent.ACTION_CANCEL: pass return False def setContextClickListener(self, listener: Callable): pass def setContextClickable(self, context_clickable: bool): pass def isContextClickable(self) -> bool: return False # ═══════════════════════════════════════════════════════════════ # ScaleGestureDetector # ═══════════════════════════════════════════════════════════════ class ScaleGestureDetector(Stub): """ScaleGestureDetector — 缩放手势检测器""" class SimpleOnScaleGestureListener: """SimpleOnScaleGestureListener — 简单缩放手势监听器""" def onScaleBegin(self, detector: "ScaleGestureDetector") -> bool: return True def onScale(self, detector: "ScaleGestureDetector") -> bool: return False def onScaleEnd(self, detector: "ScaleGestureDetector"): pass class OnScaleGestureListener: def onScaleBegin(self, detector: "ScaleGestureDetector") -> bool: return True def onScale(self, detector: "ScaleGestureDetector") -> bool: return False def onScaleEnd(self, detector: "ScaleGestureDetector"): pass def __init__(self, context=None, listener: Optional[OnScaleGestureListener] = None): super().__init__() self._listener = listener self._scale_factor = 1.0 self._current_span = 0.0 self._current_span_x = 0.0 self._current_span_y = 0.0 self._focus_x = 0.0 self._focus_y = 0.0 self._previous_span = 0.0 self._time_delta = 0 self._in_progress = False def getScaleFactor(self) -> float: return self._scale_factor def getCurrentSpan(self) -> float: return self._current_span def getCurrentSpanX(self) -> float: return self._current_span_x def getCurrentSpanY(self) -> float: return self._current_span_y def getPreviousSpan(self) -> float: return self._previous_span def getPreviousSpanX(self) -> float: return self._current_span_x def getPreviousSpanY(self) -> float: return self._current_span_y def getFocusX(self) -> float: return self._focus_x def getFocusY(self) -> float: return self._focus_y def getTimeDelta(self) -> int: return self._time_delta def getEventTime(self) -> int: return int(time.time() * 1000) def isInProgress(self) -> bool: return self._in_progress def onTouchEvent(self, event: MotionEvent) -> bool: action = event.getActionMasked() if action == MotionEvent.ACTION_DOWN: self._in_progress = False elif action == MotionEvent.ACTION_POINTER_DOWN: if event.getPointerCount() >= 2: self._in_progress = True x1, y1 = event.getX(0), event.getY(0) x2, y2 = event.getX(1), event.getY(1) self._current_span = math.hypot(x2 - x1, y2 - y1) self._focus_x = (x1 + x2) / 2.0 self._focus_y = (y1 + y2) / 2.0 if self._listener: self._listener.onScaleBegin(self) return True elif action == MotionEvent.ACTION_MOVE: if self._in_progress and event.getPointerCount() >= 2: x1, y1 = event.getX(0), event.getY(0) x2, y2 = event.getX(1), event.getY(1) new_span = math.hypot(x2 - x1, y2 - y1) self._previous_span = self._current_span self._current_span = new_span if self._previous_span > 0: self._scale_factor = self._current_span / self._previous_span self._focus_x = (x1 + x2) / 2.0 self._focus_y = (y1 + y2) / 2.0 if self._listener: self._listener.onScale(self) return True elif action in (MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL): if self._in_progress and self._listener: self._listener.onScaleEnd(self) self._in_progress = False return False # ═══════════════════════════════════════════════════════════════ # VelocityTracker # ═══════════════════════════════════════════════════════════════ class VelocityTracker(Stub): """VelocityTracker — 速度追踪器""" def __init__(self): super().__init__() self._movements: List[Tuple[float, float, float]] = [] # (x, y, time) self._x_velocity: float = 0.0 self._y_velocity: float = 0.0 @staticmethod def obtain() -> "VelocityTracker": return VelocityTracker() @staticmethod def obtainUnit(max_units: int, generator_id: int) -> "VelocityTracker": return VelocityTracker() def addMovement(self, event: MotionEvent): self._movements.append((event.getRawX(), event.getRawY(), event.getEventTime() / 1000.0)) if len(self._movements) > 100: self._movements = self._movements[-50:] def computeCurrentVelocity(self, units: int, max_velocity: float = float("inf")): if len(self._movements) < 2: self._x_velocity = 0.0 self._y_velocity = 0.0 return recent = self._movements[-4:] if len(self._movements) >= 4 else self._movements first_x, first_y, first_t = recent[0] last_x, last_y, last_t = recent[-1] dt = last_t - first_t if dt > 0: unit_scale = units / 1000.0 self._x_velocity = (last_x - first_x) / dt * unit_scale self._y_velocity = (last_y - first_y) / dt * unit_scale self._x_velocity = max(-max_velocity, min(max_velocity, self._x_velocity)) self._y_velocity = max(-max_velocity, min(max_velocity, self._y_velocity)) else: self._x_velocity = 0.0 self._y_velocity = 0.0 def getXVelocity(self, id_: int = -1) -> float: return self._x_velocity def getYVelocity(self, id_: int = -1) -> float: return self._y_velocity def recycle(self): self._movements.clear() self._x_velocity = 0.0 self._y_velocity = 0.0 def clear(self): self._movements.clear() def getEstimator(self) -> Any: return None # ═══════════════════════════════════════════════════════════════ # DragEvent # ═══════════════════════════════════════════════════════════════ class DragEvent(Stub): """DragEvent — 拖放事件""" ACTION_DRAG_STARTED = 1 ACTION_DRAG_LOCATION = 2 ACTION_DROP = 3 ACTION_DRAG_ENTERED = 4 ACTION_DRAG_EXITED = 5 ACTION_DRAG_ENDED = 6 def __init__(self, action: int = 0, x: float = 0.0, y: float = 0.0, clip_data: Any = None, result: bool = False): super().__init__() self._action = action self._x = x self._y = y self._clip_data = clip_data self._result = result self._local_state: Any = None def getAction(self) -> int: return self._action def getX(self) -> float: return self._x def getY(self) -> float: return self._y def getClipData(self) -> Any: return self._clip_data def getResult(self) -> bool: return self._result def getLocalState(self) -> Any: return self._local_state def setLocalState(self, state: Any): self._local_state = state def toString(self) -> str: action_names = { 1: "ACTION_DRAG_STARTED", 2: "ACTION_DRAG_LOCATION", 3: "ACTION_DROP", 4: "ACTION_DRAG_ENTERED", 5: "ACTION_DRAG_EXITED", 6: "ACTION_DRAG_ENDED", } name = action_names.get(self._action, f"UNKNOWN({self._action})") return f"DragEvent(action={name}, x={self._x}, y={self._y})" # ═══════════════════════════════════════════════════════════════ # AccessibilityDelegate # ═══════════════════════════════════════════════════════════════ class AccessibilityDelegate(Stub): """AccessibilityDelegate — 辅助功能委托""" def sendAccessibilityEvent(self, host: View, event_type: int): pass def sendAccessibilityEventUnchecked(self, host: View, event: Any): pass def dispatchPopulateAccessibilityEvent(self, host: View, event: Any) -> bool: return False def onPopulateAccessibilityEvent(self, host: View, event: Any): pass def onInitializeAccessibilityEvent(self, host: View, event: Any): pass def onInitializeAccessibilityNodeInfo(self, host: View, info: Any): pass def onRequestSendAccessibilityEvent(self, host: ViewGroup, child: View, event: Any) -> bool: return True def getAccessibilityNodeProvider(self, host: View) -> Optional[Any]: return None def performAccessibilityAction(self, host: View, action: int, arguments: Any) -> bool: return False # ═══════════════════════════════════════════════════════════════ # ViewTreeObserver # ═══════════════════════════════════════════════════════════════ class ViewTreeObserver(Stub): """ViewTreeObserver — 视图树观察者""" class OnGlobalLayoutListener: def onGlobalLayout(self): pass class OnGlobalFocusChangeListener: def onGlobalFocusChanged(self, old_focus: View, new_focus: View): pass class OnScrollChangedListener: def onScrollChanged(self): pass class OnTouchModeChangeListener: def onTouchModeChanged(self, is_in_touch_mode: bool): pass class OnWindowAttachListener: def onWindowAttached(self): pass def onWindowDetached(self): pass class OnWindowFocusChangeListener: def onWindowFocusChanged(self, has_focus: bool): pass class OnDrawListener: def onDraw(self): pass class OnPreDrawListener: def onPreDraw(self) -> bool: return True class OnWindowShownListener: def onWindowShown(self): pass def __init__(self): super().__init__() self._global_layout_listeners: List[ViewTreeObserver.OnGlobalLayoutListener] = [] self._scroll_changed_listeners: List[ViewTreeObserver.OnScrollChangedListener] = [] self._pre_draw_listeners: List[ViewTreeObserver.OnPreDrawListener] = [] self._draw_listeners: List[ViewTreeObserver.OnDrawListener] = [] self._touch_mode_listeners: List[ViewTreeObserver.OnTouchModeChangeListener] = [] self._window_focus_listeners: List[ViewTreeObserver.OnWindowFocusChangeListener] = [] self._window_attach_listeners: List[ViewTreeObserver.OnWindowAttachListener] = [] self._alive = True def addOnGlobalLayoutListener(self, listener: OnGlobalLayoutListener): self._global_layout_listeners.append(listener) def removeOnGlobalLayoutListener(self, listener: OnGlobalLayoutListener): if listener in self._global_layout_listeners: self._global_layout_listeners.remove(listener) def addOnScrollChangedListener(self, listener: OnScrollChangedListener): self._scroll_changed_listeners.append(listener) def removeOnScrollChangedListener(self, listener: OnScrollChangedListener): if listener in self._scroll_changed_listeners: self._scroll_changed_listeners.remove(listener) def addOnPreDrawListener(self, listener: OnPreDrawListener): self._pre_draw_listeners.append(listener) def removeOnPreDrawListener(self, listener: OnPreDrawListener): if listener in self._pre_draw_listeners: self._pre_draw_listeners.remove(listener) def addOnDrawListener(self, listener: OnDrawListener): self._draw_listeners.append(listener) def removeOnDrawListener(self, listener: OnDrawListener): if listener in self._draw_listeners: self._draw_listeners.remove(listener) def addOnGlobalFocusChangeListener(self, listener: OnGlobalFocusChangeListener): pass def removeOnGlobalFocusChangeListener(self, listener: OnGlobalFocusChangeListener): pass def addOnTouchModeChangeListener(self, listener: OnTouchModeChangeListener): self._touch_mode_listeners.append(listener) def removeOnTouchModeChangeListener(self, listener: OnTouchModeChangeListener): if listener in self._touch_mode_listeners: self._touch_mode_listeners.remove(listener) def addOnWindowAttachListener(self, listener: OnWindowAttachListener): self._window_attach_listeners.append(listener) def removeOnWindowAttachListener(self, listener: OnWindowAttachListener): if listener in self._window_attach_listeners: self._window_attach_listeners.remove(listener) def addOnWindowFocusChangeListener(self, listener: OnWindowFocusChangeListener): self._window_focus_listeners.append(listener) def removeOnWindowFocusChangeListener(self, listener: OnWindowFocusChangeListener): if listener in self._window_focus_listeners: self._window_focus_listeners.remove(listener) def addOnWindowShownListener(self, listener: OnWindowShownListener): pass def removeOnWindowShownListener(self, listener: OnWindowShownListener): pass def dispatchOnGlobalLayout(self): for listener in list(self._global_layout_listeners): listener.onGlobalLayout() def dispatchOnScrollChanged(self): for listener in list(self._scroll_changed_listeners): listener.onScrollChanged() def isAlive(self) -> bool: return self._alive def kill(self): self._alive = False self._global_layout_listeners.clear() self._scroll_changed_listeners.clear() self._pre_draw_listeners.clear() self._draw_listeners.clear() # ═══════════════════════════════════════════════════════════════ # ViewConfiguration # ═══════════════════════════════════════════════════════════════ class ViewConfiguration(Stub): """ViewConfiguration — 视图配置常量""" def __init__(self, context=None): super().__init__() if context is None: context = android_context self._context = context @staticmethod def get(context=None) -> "ViewConfiguration": return ViewConfiguration(context) @staticmethod def getLongPressTimeout() -> int: return 500 @staticmethod def getTapTimeout() -> int: return 100 @staticmethod def getJumpTapTimeout() -> int: return 500 @staticmethod def getDoubleTapTimeout() -> int: return 300 @staticmethod def getEdgeSlop() -> int: return 12 @staticmethod def getTouchSlop() -> int: return 8 @staticmethod def getScaledTouchSlop(self) -> int: return 8 @staticmethod def getScaledEdgeSlop(self) -> int: return 12 @staticmethod def getScaledFadingEdgeLength(self) -> int: return 12 @staticmethod def getScaledMinimumFlingVelocity(self) -> int: return 50 @staticmethod def getScaledMaximumFlingVelocity(self) -> int: return 8000 @staticmethod def getScaledPagingTouchSlop(self) -> int: return 16 @staticmethod def getScaledOverflingDistance(self) -> int: return 32 @staticmethod def getScaledOverscrollDistance(self) -> int: return 32 @staticmethod def getScaledScrollFactor(self) -> int: return 64 @staticmethod def getScrollFriction() -> float: return 0.015 @staticmethod def getMaximumDrawingCacheSize() -> int: return 480 * 800 * 4 @staticmethod def getWindowTouchSlop() -> int: return 16 @staticmethod def getGlobalActionKeySlop() -> int: return 12 def getScaledWindowTouchSlop(self) -> int: return 16 # ═══════════════════════════════════════════════════════════════ # SurfaceView # ═══════════════════════════════════════════════════════════════ class SurfaceView(View): """SurfaceView — 表面视图,用于直接绘制""" def __init__(self, context=None): super().__init__(context) self._surface = Surface() self._surface_holder = SurfaceHolder() def getHolder(self) -> "SurfaceHolder": return self._surface_holder def getSurfaceControl(self) -> Any: return None def setVisibility(self, visibility: int): super().setVisibility(visibility) def setSecure(self, is_secure: bool): pass def setZOrderMediaOverlay(self, is_media_overlay: bool): pass def setZOrderOnTop(self, on_top: bool): pass def gatherTransparentRegion(self, region: Any) -> bool: return False def draw(self, canvas: Any): pass class SurfaceHolder(Stub): """SurfaceHolder — Surface 持有者""" class Callback: def surfaceCreated(self, holder: "SurfaceHolder"): pass def surfaceChanged(self, holder: "SurfaceHolder", format: int, width: int, height: int): pass def surfaceDestroyed(self, holder: "SurfaceHolder"): pass class Callback2(Callback): def surfaceRedrawNeeded(self, holder: "SurfaceHolder"): pass SURFACE_TYPE_NORMAL = 0 SURFACE_TYPE_HARDWARE = 1 SURFACE_TYPE_GPU = 2 SURFACE_TYPE_PUSH_BUFFERS = 3 def __init__(self): super().__init__() self._surface = Surface() self._callbacks: List[SurfaceHolder.Callback] = [] self._type = SurfaceHolder.SURFACE_TYPE_NORMAL self._fixed_size: Optional[Tuple[int, int]] = None self._format = -1 def getSurface(self) -> "Surface": return self._surface def addCallback(self, callback: Callback): self._callbacks.append(callback) def removeCallback(self, callback: Callback): if callback in self._callbacks: self._callbacks.remove(callback) def isCreating(self) -> bool: return False def setType(self, type_: int): self._type = type_ def setFixedSize(self, width: int, height: int): self._fixed_size = (width, height) def setSizeFromLayout(self): self._fixed_size = None def setFormat(self, format: int): self._format = format def lockCanvas(self, *args): """锁定画布并返回 Canvas 对象""" if hasattr(self, '_surface') and self._surface: from .graphics import Canvas, Bitmap if hasattr(self._surface, '_bitmap') and self._surface._bitmap: return Canvas(self._surface._bitmap) from .graphics import Canvas, Bitmap return Canvas(Bitmap(100, 100)) def unlockCanvasAndPost(self, canvas): """解锁画布并提交""" pass # 桌面环境无需实际提交 # ═══════════════════════════════════════════════════════════════ # TextureView # ═══════════════════════════════════════════════════════════════ class TextureView(View): """TextureView — 纹理视图""" def __init__(self, context=None): super().__init__(context) self._surface_texture = SurfaceTexture() self._layer = None self._update_listener: Optional[Callable] = None self._surface_texture_listener: Optional[Callable] = None class SurfaceTextureListener: def onSurfaceTextureAvailable(self, surface: "SurfaceTexture", width: int, height: int): pass def onSurfaceTextureSizeChanged(self, surface: "SurfaceTexture", width: int, height: int): pass def onSurfaceTextureDestroyed(self, surface: "SurfaceTexture") -> bool: return True def onSurfaceTextureUpdated(self, surface: "SurfaceTexture"): pass def isOpaque(self) -> bool: return False def setOpaque(self, opaque: bool): pass def getSurfaceTexture(self) -> "SurfaceTexture": return self._surface_texture def setSurfaceTexture(self, surface_texture: "SurfaceTexture"): self._surface_texture = surface_texture def setSurfaceTextureListener(self, listener: SurfaceTextureListener): self._surface_texture_listener = listener def getSurfaceTextureListener(self) -> Optional[SurfaceTextureListener]: return self._surface_texture_listener def buildLayer(self): pass def setLayerPaint(self, paint: Any): pass def getLayerPaint(self) -> Any: return None def setTransform(self, transform: Any): pass def getTransform(self, transform: Any) -> Any: return transform def draw(self, canvas: Any): pass def lockCanvas(self) -> Any: return None def unlockCanvasAndPost(self, canvas: Any): pass class SurfaceTexture(Stub): """SurfaceTexture — 表面纹理""" def __init__(self, tex_name: int = 0, allow_legacy: bool = True): super().__init__() self._tex_name = tex_name self._timestamp = 0 self._frame_available_listener: Optional[Callable] = None self._default_buffer_size: Optional[Tuple[int, int]] = None def setOnFrameAvailableListener(self, listener: Callable, handler: Any = None): self._frame_available_listener = listener def getTimestamp(self) -> int: return self._timestamp def updateTexImage(self): pass def release(self): pass def setDefaultBufferSize(self, width: int, height: int): self._default_buffer_size = (width, height) def getTransformMatrix(self, mtx: List[float]): for i in range(16): mtx[i] = 1.0 if i % 5 == 0 else 0.0 # ═══════════════════════════════════════════════════════════════ # MenuInflater # ═══════════════════════════════════════════════════════════════ class MenuInflater(Stub): """MenuInflater — 菜单填充器(桌面环境桩实现)""" def __init__(self, context=None): super().__init__() self._context = context def inflate(self, menu_res: int, menu: Any): return menu def toString(self) -> str: return f"MenuInflater@{hex(id(self))}" # ═══════════════════════════════════════════════════════════════ # Window # ═══════════════════════════════════════════════════════════════ class Window(Stub): """Window — 抽象窗口基类""" # 特性ID FEATURES_DEFAULT = -1 FEATURE_OPTIONS_PANEL = 0 FEATURE_NO_TITLE = 1 FEATURE_PROGRESS = 2 FEATURE_LEFT_ICON = 3 FEATURE_RIGHT_ICON = 4 FEATURE_INDETERMINATE_PROGRESS = 5 FEATURE_CONTEXT_MENU = 6 FEATURE_CUSTOM_TITLE = 7 FEATURE_ACTION_BAR = 8 FEATURE_ACTION_BAR_OVERLAY = 9 FEATURE_ACTION_MODE_OVERLAY = 10 FEATURE_SWIPE_TO_DISMISS = 11 FEATURE_CONTENT_TRANSITIONS = 12 FEATURE_WATCH = 13 # 本地特性 ID_ANDROID_CONTENT = 0x01020002 DECOR_CAPTION_SHORTCUT_DURATION_MS = 1000 # 标志 NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME = "navigationBarBackground" STATUS_BAR_BACKGROUND_TRANSITION_NAME = "statusBarBackground" def __init__(self, context=None): super().__init__() if context is None: context = android_context self._context = context self._features: Dict[int, bool] = {} self._title: str = "" self._decor_view: Optional[View] = None self._callback: Optional["Window.Callback"] = None self._window_manager: Optional[WindowManager] = None self._soft_input_mode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED self._flags: Dict[int, bool] = {} self._container: Optional[Window] = None self._local_features: List[int] = [] class Callback: def dispatchKeyEvent(self, event: KeyEvent) -> bool: return False def dispatchTouchEvent(self, event: MotionEvent) -> bool: return False def dispatchTrackballEvent(self, event: MotionEvent) -> bool: return False def dispatchGenericMotionEvent(self, event: MotionEvent) -> bool: return False def dispatchPopulateAccessibilityEvent(self, event: Any) -> bool: return False def onCreatePanelView(self, feature_id: int) -> Optional[View]: return None def onCreatePanelMenu(self, feature_id: int, menu: Any) -> bool: return False def onPreparePanel(self, feature_id: int, view: View, menu: Any) -> bool: return False def onMenuOpened(self, feature_id: int, menu: Any): pass def onMenuItemSelected(self, feature_id: int, item: Any) -> bool: return False def onWindowAttributesChanged(self, attrs: Any): pass def onContentChanged(self): pass def onWindowFocusChanged(self, has_focus: bool): pass def onAttachedToWindow(self): pass def onDetachedFromWindow(self): pass def onPanelClosed(self, feature_id: int, menu: Any): pass def onSearchRequested(self) -> bool: return False def onSearchRequested(self, search_event: Any) -> bool: return False def onActionModeStarted(self, mode: Any): pass def onActionModeFinished(self, mode: Any): pass def onWindowStartingActionMode(self, callback: Any) -> Optional[Any]: return None def onWindowStartingActionMode(self, callback: Any, type_: int) -> Optional[Any]: return None def setContentView(self, layout_res_id: int): pass def setContentView(self, view: View): pass def setContentView(self, view: View, params: ViewGroup.LayoutParams): pass def addContentView(self, view: View, params: ViewGroup.LayoutParams): pass def setTitle(self, title: str): self._title = title def getTitle(self) -> str: return self._title def setFeatureInt(self, feature_id: int, value: int): self._features[feature_id] = True def requestFeature(self, feature_id: int) -> bool: self._features[feature_id] = True return True def hasFeature(self, feature_id: int) -> bool: return self._features.get(feature_id, False) def setFlags(self, flags: int, mask: int): for flag_bit in range(32): if mask & (1 << flag_bit): self._flags[1 << flag_bit] = bool(flags & (1 << flag_bit)) def addFlags(self, flags: int): for flag_bit in range(32): if flags & (1 << flag_bit): self._flags[1 << flag_bit] = True def clearFlags(self, flags: int): for flag_bit in range(32): if flags & (1 << flag_bit): self._flags[1 << flag_bit] = False def setSoftInputMode(self, soft_input_mode: int): self._soft_input_mode = soft_input_mode def getSoftInputMode(self) -> int: return self._soft_input_mode def setLayout(self, width: int, height: int): pass def findViewById(self, id_: int) -> Optional[View]: if self._decor_view: return self._decor_view.findViewById(id_) return None def getDecorView(self) -> Optional[View]: return self._decor_view def setDecorView(self, view: View): self._decor_view = view def getWindowManager(self) -> Optional[WindowManager]: return self._window_manager def setWindowManager(self, wm: WindowManager, app_token: Any = None, app_name: str = ""): self._window_manager = wm def setCallback(self, callback: Callback): self._callback = callback def getCallback(self) -> Optional[Callback]: return self._callback def superDispatchTouchEvent(self, event: MotionEvent) -> bool: if self._decor_view: return self._decor_view.dispatchTouchEvent(event) return False def superDispatchKeyEvent(self, event: KeyEvent) -> bool: if self._decor_view: return self._decor_view.dispatchKeyEvent(event) return False def superDispatchTrackballEvent(self, event: MotionEvent) -> bool: return False def superDispatchGenericMotionEvent(self, event: MotionEvent) -> bool: return False def takeSurface(self, callback: Any): pass def takeInputQueue(self, callback: Any): pass def isActive(self) -> bool: return True def isFloating(self) -> bool: return False def setBackgroundDrawable(self, drawable: Any): pass def setBackgroundDrawableResource(self, res_id: int): pass def setDimAmount(self, amount: float): pass def getLayoutInflater(self): return None def setWindowAnimations(self, res_id: int): pass def getWindowStyle(self) -> Any: return None def setContainer(self, container: "Window"): self._container = container def getContainer(self) -> Optional["Window"]: return self._container def setLocalFeatures(self, local_features: int): pass def makeActive(self): pass def getContext(self): return self._context def getAttributes(self) -> WindowManager.LayoutParams: params = WindowManager.LayoutParams() params.softInputMode = self._soft_input_mode return params def setAttributes(self, attrs: WindowManager.LayoutParams): pass def reconfigure(self, attrs: WindowManager.LayoutParams): pass def onConfigurationChanged(self, new_config: Any): pass def getCurrentFocus(self) -> Optional[View]: return None def getVolumeControlStream(self) -> int: return -1 def setVolumeControlStream(self, stream_type: int): pass def getMediaController(self) -> Any: return None def setMediaController(self, controller: Any): pass def openGlEsVersion(self) -> int: return 2 def isShortcutKey(self, key_code: int, event: KeyEvent) -> bool: return False def setStatusBarColor(self, color: int): pass def setNavigationBarColor(self, color: int): pass def setStatusBarVisibility(self, visible: bool): pass def setNavigationBarVisibility(self, visible: bool): pass def getStatusBarColor(self) -> int: return 0 def getNavigationBarColor(self) -> int: return 0 # ═══════════════════════════════════════════════════════════════ # WindowInsets # ═══════════════════════════════════════════════════════════════ class WindowInsets(Stub): """WindowInsets — 窗口插入区域""" def __init__(self, left: int = 0, top: int = 0, right: int = 0, bottom: int = 0): super().__init__() self._system_window_insets = Rect(left, top, right, bottom) self._stable_insets = Rect(0, 0, 0, 0) self._display_cutout_insets = Rect(0, 0, 0, 0) self._consumed = False def getSystemWindowInsetLeft(self) -> int: return self._system_window_insets.left def getSystemWindowInsetTop(self) -> int: return self._system_window_insets.top def getSystemWindowInsetRight(self) -> int: return self._system_window_insets.right def getSystemWindowInsetBottom(self) -> int: return self._system_window_insets.bottom def getSystemWindowInsets(self) -> Rect: return self._system_window_insets def getStableInsetTop(self) -> int: return self._stable_insets.top def getStableInsetLeft(self) -> int: return self._stable_insets.left def getStableInsetRight(self) -> int: return self._stable_insets.right def getStableInsetBottom(self) -> int: return self._stable_insets.bottom def getDisplayCutoutInsets(self) -> Rect: return self._display_cutout_insets def consumeDisplayCutout(self) -> "WindowInsets": return WindowInsets() def consumeSystemWindowInsets(self) -> "WindowInsets": return WindowInsets() def replaceSystemWindowInsets(self, left: int, top: int, right: int, bottom: int) -> "WindowInsets": new_ins = WindowInsets(left, top, right, bottom) new_ins._stable_insets = self._stable_insets return new_ins def isConsumed(self) -> bool: return self._consumed def hasSystemWindowInsets(self) -> bool: return (self._system_window_insets.left > 0 or self._system_window_insets.top > 0 or self._system_window_insets.right > 0 or self._system_window_insets.bottom > 0) def hasInsets(self) -> bool: return self.hasSystemWindowInsets() def isRound(self) -> bool: return False def getDisplayCutout(self) -> Any: return None def consumeStableInsets(self) -> "WindowInsets": return WindowInsets() def toString(self) -> str: return f"WindowInsets(system={self._system_window_insets})" # ═══════════════════════════════════════════════════════════════ # WindowInsetsController # ═══════════════════════════════════════════════════════════════ class WindowInsetsController(Stub): """WindowInsetsController — 窗口插入控制器""" BEHAVIOR_SHOW_BARS_BY_TOUCH = 0 BEHAVIOR_SHOW_BARS_BY_SWIPE = 1 BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE = 2 APPEARANCE_OPAQUE_STATUS_BARS = 0x00000001 APPEARANCE_OPAQUE_NAVIGATION_BARS = 0x00000002 APPEARANCE_LOW_PROFILE_BARS = 0x00000004 APPEARANCE_LIGHT_STATUS_BARS = 0x00000008 APPEARANCE_LIGHT_NAVIGATION_BARS = 0x00000010 def __init__(self): super().__init__() self._appearance = 0 self._behavior = WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_TOUCH def show(self, types: int): pass def hide(self, types: int): pass def setSystemBarsAppearance(self, appearance: int, mask: int): self._appearance = (self._appearance & ~mask) | (appearance & mask) def getSystemBarsAppearance(self) -> int: return self._appearance def setSystemBarsBehavior(self, behavior: int): self._behavior = behavior def getSystemBarsBehavior(self) -> int: return self._behavior def setAnimationsDisabled(self, disabled: bool): pass # ═══════════════════════════════════════════════════════════════ # Display # ═══════════════════════════════════════════════════════════════ class Display(Stub): """Display — 显示信息""" DEFAULT_DISPLAY = 0 ROTATION_0 = 0 ROTATION_90 = 1 ROTATION_180 = 2 ROTATION_270 = 3 FLAG_PRESENTATION = 0x00000008 FLAG_PRIVATE = 0x00000004 FLAG_SECURE = 0x00000002 FLAG_SUPPORTS_PROTECTED_BUFFERS = 0x00000001 STATE_DOZE = 3 STATE_DOZE_SUSPEND = 4 STATE_OFF = 1 STATE_ON = 2 STATE_UNKNOWN = 0 def __init__(self, display_id: int = DEFAULT_DISPLAY): super().__init__() self._display_id = display_id self._width = 1080 self._height = 1920 self._rotation = Display.ROTATION_0 self._refresh_rate = 60.0 self._density = 2.0 self._density_dpi = 320 self._state = Display.STATE_ON self._flags = 0 self._name = "Built-in Screen" def getDisplayId(self) -> int: return self._display_id def getWidth(self) -> int: if _USE_LINUX: return LinuxDisplay.get_screen_size()[0] return self._width def getHeight(self) -> int: if _USE_LINUX: return LinuxDisplay.get_screen_size()[1] return self._height def getRotation(self) -> int: return self._rotation def getRefreshRate(self) -> float: return self._refresh_rate def getName(self) -> str: return self._name def getState(self) -> int: return self._state def getFlags(self) -> int: return self._flags def isHdr(self) -> bool: return False def isWideColorGamut(self) -> bool: return False def getSize(self, out_size: Any = None): if out_size is not None: out_size.width = self._width out_size.height = self._height return None def getRealSize(self, out_size: Any = None): if out_size is not None: out_size.width = self._width out_size.height = self._height return None def getMetrics(self, metrics: Any = None): if metrics is not None: metrics.widthPixels = self._width metrics.heightPixels = self._height metrics.density = self._density metrics.densityDpi = self._density_dpi metrics.scaledDensity = self._density metrics.xdpi = 320.0 metrics.ydpi = 320.0 return None def getRealMetrics(self, metrics: Any = None): return self.getMetrics(metrics) def getOrientation(self) -> int: return self._rotation def getDisplayAdjustments(self) -> Any: return None def getPixelFormat(self) -> int: return 1 # RGBA_8888 def isValid(self) -> bool: return True def toString(self) -> str: return f"Display({self._display_id}, {self._width}x{self._height})" # ═══════════════════════════════════════════════════════════════ # Surface # ═══════════════════════════════════════════════════════════════ class Surface(Stub): """Surface — 表面,用于绘制""" ROTATION_0 = 0 ROTATION_90 = 1 ROTATION_180 = 2 ROTATION_270 = 3 CHANGE_FRAME_RATE_ALWAYS = 1 CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS = 0 def __init__(self, surface_control: Any = None): super().__init__() self._is_valid = True self._is_locked = False self._width = 1080 self._height = 1920 self._hardware_accelerated = False def isValid(self) -> bool: return self._is_valid def isConsumerRunningBehind(self) -> bool: return False def lockCanvas(self, dirty_rect: Any = None) -> Any: if not self._is_valid: return None self._is_locked = True return None # 返回空 Canvas def lockHardwareCanvas(self) -> Any: if not self._is_valid: return None self._is_locked = True self._hardware_accelerated = True return None def unlockCanvasAndPost(self, canvas: Any): self._is_locked = False def getSurfaceControl(self) -> Any: return None def release(self): self._is_valid = False self._is_locked = False def destroy(self): self.release() def getWidth(self) -> int: return self._width def getHeight(self) -> int: return self._height def setFrameRate(self, frame_rate: float, compatibility: int): pass def toString(self) -> str: return f"Surface(valid={self._is_valid}, {self._width}x{self._height})" # ═══════════════════════════════════════════════════════════════ # Choreographer # ═══════════════════════════════════════════════════════════════ class Choreographer(Stub): """Choreographer — 帧渲染协调器""" CALLBACK_INPUT = 0 CALLBACK_ANIMATION = 1 CALLBACK_INSETS_ANIMATION = 2 CALLBACK_TRAVERSAL = 3 CALLBACK_COMMIT = 4 _instance: Optional["Choreographer"] = None class FrameCallback: def doFrame(self, frame_time_nanos: int): pass class FrameTimeline: pass def __init__(self): super().__init__() @staticmethod def getInstance() -> "Choreographer": if Choreographer._instance is None: Choreographer._instance = Choreographer() return Choreographer._instance def postFrameCallback(self, callback: "FrameCallback"): callback.doFrame(int(time.time() * 1_000_000_000)) def postFrameCallbackDelayed(self, callback: "FrameCallback", delay_millis: int): callback.doFrame(int((time.time() + delay_millis / 1000.0) * 1_000_000_000)) def removeFrameCallback(self, callback: "FrameCallback"): pass def postCallback(self, callback_type: int, action: Callable, token: Any): action() def postCallbackDelayed(self, callback_type: int, action: Callable, token: Any, delay_millis: int): import threading timer = threading.Timer(delay_millis / 1000.0, action) timer.daemon = True timer.start() def removeCallbacks(self, callback_type: int, action: Callable, token: Any): pass def getFrameTimeNanos(self) -> int: return int(time.time() * 1_000_000_000) def getLastFrameTimeNanos(self) -> int: return int(time.time() * 1_000_000_000) - 16_666_666 # ═══════════════════════════════════════════════════════════════ # ActionMode # ═══════════════════════════════════════════════════════════════ class ActionMode(Stub): """ActionMode — 操作模式""" TYPE_FLOATING = 1 TYPE_PRIMARY = 0 TYPE_TOOLBAR = 2 class Callback: def onCreateActionMode(self, mode: "ActionMode", menu: Any) -> bool: return False def onPrepareActionMode(self, mode: "ActionMode", menu: Any) -> bool: return False def onActionItemClicked(self, mode: "ActionMode", item: Any) -> bool: return False def onDestroyActionMode(self, mode: "ActionMode"): pass class Callback2(Callback): def onGetContentRect(self, mode: "ActionMode", view: View, out_rect: Any): pass def __init__(self): super().__init__() self._tag: Any = None self._title: str = "" self._subtitle: str = "" self._title_optional_hint: str = "" self._subtitle_optional_hint: str = "" self._type = ActionMode.TYPE_PRIMARY self._menu: Any = None def setTitle(self, title: str): self._title = title def getTitle(self) -> str: return self._title def setSubtitle(self, subtitle: str): self._subtitle = subtitle def getSubtitle(self) -> str: return self._subtitle def setTitleOptionalHint(self, hint: bool): self._title_optional_hint = hint def getTitleOptionalHint(self) -> bool: return bool(self._title_optional_hint) def setTag(self, tag: Any): self._tag = tag def getTag(self) -> Any: return self._tag def getMenu(self): return self._menu def setType(self, type_: int): self._type = type_ def getType(self) -> int: return self._type def invalidate(self): pass def invalidateContentRect(self): pass def finish(self): pass def getCustomView(self) -> Optional[View]: return None def setCustomView(self, view: View): pass def setImageDrawable(self, drawable: Any): pass def getMenuInflater(self): return None # ═══════════════════════════════════════════════════════════════ # ContextThemeWrapper # ═══════════════════════════════════════════════════════════════ class ContextThemeWrapper(Stub): """ContextThemeWrapper — 上下文主题包装器""" def __init__(self, base: Any = None, theme_res_id: int = 0): super().__init__() self._base = base if base is not None else android_context self._theme_res_id = theme_res_id self._theme: Any = None def attachBaseContext(self, new_base: Any): self._base = new_base def getBaseContext(self): return self._base def setTheme(self, resid: int): self._theme_res_id = resid def getThemeResId(self) -> int: return self._theme_res_id def getTheme(self) -> Any: return self._theme def getResources(self): if hasattr(self._base, "getResources"): return self._base.getResources() return None def getSystemService(self, name: str) -> Any: if hasattr(self._base, "getSystemService"): return self._base.getSystemService(name) return None def getPackageName(self) -> str: if hasattr(self._base, "getPackageName"): return self._base.getPackageName() return "" def getApplicationContext(self): return self._base # ═══════════════════════════════════════════════════════════════ # ViewOutlineProvider # ═══════════════════════════════════════════════════════════════ class ViewOutlineProvider(Stub): """ViewOutlineProvider — 视图轮廓提供者""" BACKGROUND = None BOUNDS = None PADDED_BOUNDS = None def getOutline(self, view: View, outline: Any): pass # 预定义实例 @staticmethod def _init_constants(): if ViewOutlineProvider.BACKGROUND is None: class _Background(ViewOutlineProvider): def getOutline(self, view, outline): if hasattr(outline, "setRect"): outline.setRect(0, 0, view.getWidth(), view.getHeight()) ViewOutlineProvider.BACKGROUND = _Background() if ViewOutlineProvider.BOUNDS is None: class _Bounds(ViewOutlineProvider): def getOutline(self, view, outline): if hasattr(outline, "setRect"): outline.setRect(0, 0, view.getWidth(), view.getHeight()) ViewOutlineProvider.BOUNDS = _Bounds() if ViewOutlineProvider.PADDED_BOUNDS is None: class _PaddedBounds(ViewOutlineProvider): def getOutline(self, view, outline): if hasattr(outline, "setRect"): outline.setRect( view.getPaddingLeft(), view.getPaddingTop(), view.getWidth() - view.getPaddingRight(), view.getHeight() - view.getPaddingBottom(), ) ViewOutlineProvider.PADDED_BOUNDS = _PaddedBounds() ViewOutlineProvider._init_constants() # ═══════════════════════════════════════════════════════════════ # RenderNode # ═══════════════════════════════════════════════════════════════ class RenderNode(Stub): """RenderNode — 渲染节点""" STATUS_DISPLAY_LIST_INFLATED = 1 STATUS_DISPLAY_LIST_READY = 2 STATUS_DISPLAY_LIST_PREPARE = 3 STATUS_DISPLAY_LIST_UNUSED = 0 def __init__(self, name: str = ""): super().__init__() self._name = name self._alpha = 1.0 self._translation_x = 0.0 self._translation_y = 0.0 self._translation_z = 0.0 self._elevation = 0.0 self._rotation = 0.0 self._rotation_x = 0.0 self._rotation_y = 0.0 self._scale_x = 1.0 self._scale_y = 1.0 self._pivot_x = 0.0 self._pivot_y = 0.0 self._left = 0 self._top = 0 self._right = 0 self._bottom = 0 self._clip_to_bounds = False self._clip_bounds: Optional[Rect] = None self._has_display_list = False self._status = RenderNode.STATUS_DISPLAY_LIST_UNUSED def setPosition(self, left: int, top: int, right: int, bottom: int) -> bool: old_left, old_top, old_right, old_bottom = self._left, self._top, self._right, self._bottom self._left, self._top, self._right, self._bottom = left, top, right, bottom return (old_left != left or old_top != top or old_right != right or old_bottom != bottom) def getLeft(self) -> int: return self._left def getTop(self) -> int: return self._top def getRight(self) -> int: return self._right def getBottom(self) -> int: return self._bottom def setAlpha(self, alpha: float) -> bool: changed = self._alpha != alpha self._alpha = alpha return changed def getAlpha(self) -> float: return self._alpha def setTranslationX(self, tx: float) -> bool: changed = self._translation_x != tx self._translation_x = tx return changed def getTranslationX(self) -> float: return self._translation_x def setTranslationY(self, ty: float) -> bool: changed = self._translation_y != ty self._translation_y = ty return changed def getTranslationY(self) -> float: return self._translation_y def setElevation(self, elevation: float) -> bool: changed = self._elevation != elevation self._elevation = elevation return changed def getElevation(self) -> float: return self._elevation def setRotation(self, rotation: float) -> bool: changed = self._rotation != rotation self._rotation = rotation return changed def getRotation(self) -> float: return self._rotation def setScaleX(self, scale_x: float) -> bool: changed = self._scale_x != scale_x self._scale_x = scale_x return changed def getScaleX(self) -> float: return self._scale_x def setScaleY(self, scale_y: float) -> bool: changed = self._scale_y != scale_y self._scale_y = scale_y return changed def getScaleY(self) -> float: return self._scale_y def setPivotX(self, pivot_x: float) -> bool: changed = self._pivot_x != pivot_x self._pivot_x = pivot_x return changed def getPivotX(self) -> float: return self._pivot_x def setPivotY(self, pivot_y: float) -> bool: changed = self._pivot_y != pivot_y self._pivot_y = pivot_y return changed def getPivotY(self) -> float: return self._pivot_y def setClipToBounds(self, clip_to_bounds: bool) -> bool: changed = self._clip_to_bounds != clip_to_bounds self._clip_to_bounds = clip_to_bounds return changed def getClipToBounds(self) -> bool: return self._clip_to_bounds def setClipBounds(self, clip_rect: Optional[Rect]): self._clip_bounds = clip_rect def getClipBounds(self) -> Optional[Rect]: return self._clip_bounds def setHasDisplayList(self, has_display_list: bool): self._has_display_list = has_display_list def hasDisplayList(self) -> bool: return self._has_display_list def getName(self) -> str: return self._name def discardDisplayList(self): self._has_display_list = False self._status = RenderNode.STATUS_DISPLAY_LIST_UNUSED def start(self) -> Any: return None def end(self): pass def offsetLeftAndRight(self, offset: int): self._left += offset self._right += offset def offsetTopAndBottom(self, offset: int): self._top += offset self._bottom += offset def getMatrix(self, out_matrix: Any): pass def getInverseMatrix(self, out_matrix: Any): pass def useLighting(self) -> bool: return False def setUseLighting(self, use_lighting: bool): pass def setLayerType(self, layer_type: int): pass def getLayerType(self) -> int: return 0 # ═══════════════════════════════════════════════════════════════ # ScrollCaptureCallback # ═══════════════════════════════════════════════════════════════ class ScrollCaptureCallback(Stub): """ScrollCaptureCallback — 滚动捕获回调""" def onScrollCaptureSearch(self, l: int, t: int, r: int, b: int) -> Optional[Any]: return None def onScrollCaptureStart(self, session: Any) -> Optional[Any]: return None def onScrollCaptureImageRequest(self, session: Any, request: Any) -> int: return 0 def onScrollCaptureEnd(self, session: Any): pass # ═══════════════════════════════════════════════════════════════ # KeyboardShortcutGroup # ═══════════════════════════════════════════════════════════════ class KeyboardShortcutGroup(Stub): """KeyboardShortcutGroup — 键盘快捷键分组""" def __init__(self, label: str, items: Optional[List[Any]] = None): super().__init__() self._label = label self._items: List[Any] = items if items is not None else [] self._system_group = False def getLabel(self) -> str: return self._label def getItems(self) -> List[Any]: return list(self._items) def addItem(self, item: Any): self._items.append(item) def setSystemGroup(self, is_system: bool): self._system_group = is_system def isSystemGroup(self) -> bool: return self._system_group # ═══════════════════════════════════════════════════════════════ # SoundEffectConstants # ═══════════════════════════════════════════════════════════════ class SoundEffectConstants: """SoundEffectConstants — 音效常量""" CLICK = 0 NAVIGATION_LEFT = 1 NAVIGATION_UP = 2 NAVIGATION_RIGHT = 3 NAVIGATION_DOWN = 4 NAVIGATION_REPEAT_LEFT = 5 NAVIGATION_REPEAT_UP = 6 NAVIGATION_REPEAT_RIGHT = 7 NAVIGATION_REPEAT_DOWN = 8 @staticmethod def getDirectionForNavigation(gravity: int) -> int: return SoundEffectConstants.NAVIGATION_UP # ═══════════════════════════════════════════════════════════════ # FocusFinder # ═══════════════════════════════════════════════════════════════ class FocusFinder(Stub): """FocusFinder — 焦点查找器""" _instance: Optional["FocusFinder"] = None def __init__(self): super().__init__() @staticmethod def getInstance() -> "FocusFinder": if FocusFinder._instance is None: FocusFinder._instance = FocusFinder() return FocusFinder._instance def findNextFocus(self, root: ViewGroup, focused: View, direction: int) -> Optional[View]: return None def findNearestTouchable(self, root: ViewGroup, x: int, y: int, direction: int, deltas: List[int]) -> Optional[View]: return None # ═══════════════════════════════════════════════════════════════ # ViewGroup override for children list # ═══════════════════════════════════════════════════════════════ # Ensure ViewGroup._children is separate from View._children ViewGroup._children = [] # Will be overridden in __init__ # ═══════════════════════════════════════════════════════════════ # __all__ # ═══════════════════════════════════════════════════════════════ __all__ = [ "View", "ViewGroup", "ViewParent", "ViewManager", "WindowManager", "MotionEvent", "KeyEvent", "GestureDetector", "ScaleGestureDetector", "VelocityTracker", "DragEvent", "AccessibilityDelegate", "ViewTreeObserver", "ViewConfiguration", "SurfaceView", "TextureView", "Window", "WindowInsets", "WindowInsetsController", "Display", "Surface", "Choreographer", "ActionMode", "ContextThemeWrapper", "Gravity", "ViewOutlineProvider", "RenderNode", "ScrollCaptureCallback", "KeyboardShortcutGroup", "MeasureSpec", "ViewPropertyAnimator", "SurfaceHolder", "SurfaceTexture", "FocusFinder", "SoundEffectConstants", "Rect", ]