核心类方法补全: - Activity: +20方法 (requestWindowFeature, setTitle/getTitle, onKeyDown/onKeyUp, enterPictureInPictureMode, getReferrer, isTaskRoot, navigateUpTo等) - Fragment: +15方法 (getChildFragmentManager, getParentFragment, requireContext/requireActivity, setMenuVisibility等) - Intent: +20方法 (getParcelableExtra, setComponent/getComponent, getDataString, getScheme, putExtras等) - View: +20方法 (setLabelFor, setRotationX/Y, setForeground, setBackgroundTintList等) - KeyEvent: +7方法 (getCharacters, isSystem, isPrintingKey, isModifierKey, startTracking等) - Paint: +12方法 (setStrokeCap/Join, getTextBounds, setBlendMode, setLetterSpacing等) - Notification.Builder: +10方法 (setProgress, setShowWhen, setOnlyAlertOnce, setSound等) 存根转真实实现: - TrafficStats: 读取 /proc/net/dev 返回真实网络流量 - RecyclerView.Adapter: 观察者模式触发通知 - BaseAdapter: 观察者模式触发通知 - ExifInterface: 使用PIL实现图片翻转/旋转 - Process: 使用os.kill实现进程信号 - FileUtils: 真实文件I/O - SharedPreferences: 监听器模式 - TelephonyManager.listen: 维护监听器列表 - ScrollView.smoothScrollTo: 更新滚动位置 Exception体系扩展: - 新增15个Exception/Error类 (ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchElementException, AssertionError, OutOfMemoryError, StackOverflowError, NoClassDefFoundError, UnsatisfiedLinkError, LinkageError, VerifyError, AbstractMethodError, IncompatibleClassChangeError, ExceptionInInitializerError) Math/System/Thread/StringBuilder/Integer增强: - Math: +20方法 (subtractExact, multiplyExact, toIntExact, floorDiv/floorMod, fma等) - System: getProperties/setProperties/mapLibraryName/getLogger - Thread: +10方法 (getUncaughtExceptionHandler, getAllStackTraces, getStackTrace, holdLock等) - StringBuilder: +7方法 (ensureCapacity, trimToSize, setLength, subSequence等) - Integer: +8静态方法 (compare, compareUnsigned, divideUnsigned, sum, max, min等) 修复: - app_fragment.py: 使用java.lang.IllegalStateException替代本地定义 - content_intent.py: Intent.setData/getScheme正确从Uri提取scheme - view_ext.py: KeyEvent继承view.KeyEvent获取所有KEYCODE常量 - __init__.py: 补全Fragment/KeyEvent/MotionEvent/TrafficStats/RecyclerView等导出 统计: 221模块, 3660类, 30826方法, 180217行
4110 lines
129 KiB
Python
4110 lines
129 KiB
Python
"""
|
|
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: int = -1,
|
|
params: Optional["ViewGroup.LayoutParams"] = None):
|
|
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 addView(self, child: View, params: Optional["ViewGroup.LayoutParams"] = None):
|
|
self.addView(child, -1, params)
|
|
|
|
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
|
|
|
|
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
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# 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",
|
|
] |