""" Android Util 组件 — Log, SparseArray, LruCache, DisplayMetrics 等 方法签名完全对齐 Android SDK """ import math import re as _re import struct import sys from datetime import datetime from typing import Any, Callable, Dict, Generic, List, Optional, Set, Tuple, TypeVar, Union from .._core import ( Stub, AndroidOnlyError, android_context, resolve_android_path, to_android_path, _sdcard_root, _app_data_dir, ) # ═══════════════════════════════════════════════════════════════ # Log # ═══════════════════════════════════════════════════════════════ class Log(Stub): """Log — 日志输出""" ASSERT = 7 DEBUG = 3 ERROR = 6 INFO = 4 VERBOSE = 2 WARN = 5 _min_log_level = VERBOSE @staticmethod def v(tag: str, msg: str) -> int: if Log._min_log_level <= Log.VERBOSE: sys.stderr.write(f"V/{tag}: {msg}\n") return 0 @staticmethod def d(tag: str, msg: str) -> int: if Log._min_log_level <= Log.DEBUG: sys.stderr.write(f"D/{tag}: {msg}\n") return 0 @staticmethod def i(tag: str, msg: str) -> int: if Log._min_log_level <= Log.INFO: sys.stderr.write(f"I/{tag}: {msg}\n") return 0 @staticmethod def w(tag: str, msg: str = None, tr: Exception = None) -> int: if Log._min_log_level <= Log.WARN: # 支持 w(tag, Throwable) 调用形式 if msg is None and tr is not None: msg = Log.getStackTraceString(tr) elif tr is not None: msg = f"{msg}\n{Log.getStackTraceString(tr)}" sys.stderr.write(f"W/{tag}: {msg}\n") return 0 @staticmethod def e(tag: str, msg: str = None, tr: Exception = None) -> int: if Log._min_log_level <= Log.ERROR: # 支持 e(tag, Throwable) 调用形式 if msg is None and tr is not None: msg = Log.getStackTraceString(tr) elif tr is not None: msg = f"{msg}\n{Log.getStackTraceString(tr)}" sys.stderr.write(f"E/{tag}: {msg}\n") return 0 @staticmethod def wtf(tag: str, msg=None, tr: Exception = None) -> int: """What a Terrible Failure。支持以下调用形式: - wtf(tag, msg) - wtf(tag, Throwable) - wtf(tag, msg, Throwable) """ # 如果 msg 是异常对象,则当作 tr 处理 if isinstance(msg, BaseException): tr = msg msg = None if msg is None and tr is not None: msg = Log.getStackTraceString(tr) elif tr is not None: msg = f"{msg}\n{Log.getStackTraceString(tr)}" if msg is None: msg = "" sys.stderr.write(f"WTF/{tag}: {msg}\n") return 0 @staticmethod def println(log_level: int, tag: str, msg: str) -> int: level_map = { Log.VERBOSE: "V", Log.DEBUG: "D", Log.INFO: "I", Log.WARN: "W", Log.ERROR: "E", Log.ASSERT: "A", } prefix = level_map.get(log_level, "?") sys.stderr.write(f"{prefix}/{tag}: {msg}\n") return 0 @staticmethod def isLoggable(tag: str, level: int) -> bool: return level >= Log._min_log_level @staticmethod def getStackTraceString(tr: Exception) -> str: import traceback if tr is None: return "" if isinstance(tr, str): return tr return "".join(traceback.format_exception(type(tr), tr, tr.__traceback__)) @staticmethod def setMinLogLevel(level: int): Log._min_log_level = level # ═══════════════════════════════════════════════════════════════ # SparseArray # ═══════════════════════════════════════════════════════════════ T = TypeVar("T") class SparseArray(Generic[T], Stub): """SparseArray — 稀疏数组(int 键映射)""" def __init__(self, initial_capacity: int = 10): super().__init__() self._keys: List[int] = [] self._values: List[Optional[T]] = [] self._size = 0 def put(self, key: int, value: T): """如果 key 已存在则替换""" try: idx = self._keys.index(key) self._values[idx] = value except ValueError: self._keys.append(key) self._values.append(value) self._size += 1 def get(self, key: int, default: Optional[T] = None) -> Optional[T]: try: idx = self._keys.index(key) return self._values[idx] except ValueError: return default def delete(self, key: int): try: idx = self._keys.index(key) self._keys.pop(idx) self._values.pop(idx) self._size -= 1 except ValueError: pass def remove(self, key: int): self.delete(key) def removeAt(self, index: int): if 0 <= index < self._size: self._keys.pop(index) self._values.pop(index) self._size -= 1 def size(self) -> int: return self._size def keyAt(self, index: int) -> int: if 0 <= index < self._size: return self._keys[index] return 0 def valueAt(self, index: int) -> Optional[T]: if 0 <= index < self._size: return self._values[index] return None def setValueAt(self, index: int, value: T): if 0 <= index < self._size: self._values[index] = value def indexOfKey(self, key: int) -> int: try: return self._keys.index(key) except ValueError: return -1 def indexOfValue(self, value: T) -> int: try: return self._values.index(value) except ValueError: return -1 def clear(self): self._keys.clear() self._values.clear() self._size = 0 def append(self, key: int, value: T): self.put(key, value) def containsKey(self, key: int) -> bool: return key in self._keys def putAll(self, other: "SparseArray[T]"): for i in range(other.size()): self.put(other.keyAt(i), other.valueAt(i)) def clone(self) -> "SparseArray[T]": sa = SparseArray[T]() sa._keys = list(self._keys) sa._values = list(self._values) sa._size = self._size return sa def toString(self) -> str: pairs = ", ".join( f"{k}={v}" for k, v in zip(self._keys, self._values) ) return f"SparseArray{{{pairs}}}" def __getitem__(self, key: int) -> Optional[T]: return self.get(key) def __setitem__(self, key: int, value: T): self.put(key, value) def __delitem__(self, key: int): self.delete(key) def __len__(self) -> int: return self._size def __contains__(self, key: int) -> bool: return key in self._keys class SparseIntArray(Stub): """SparseIntArray — int 到 int 的映射""" def __init__(self, initial_capacity: int = 10): super().__init__() self._keys: List[int] = [] self._values: List[int] = [] def put(self, key: int, value: int): try: idx = self._keys.index(key) self._values[idx] = value except ValueError: self._keys.append(key) self._values.append(value) def get(self, key: int, default: int = 0) -> int: try: idx = self._keys.index(key) return self._values[idx] except ValueError: return default def delete(self, key: int): try: idx = self._keys.index(key) self._keys.pop(idx) self._values.pop(idx) except ValueError: pass def removeAt(self, index: int): if 0 <= index < len(self._keys): self._keys.pop(index) self._values.pop(index) def size(self) -> int: return len(self._keys) def keyAt(self, index: int) -> int: if 0 <= index < len(self._keys): return self._keys[index] return 0 def valueAt(self, index: int) -> int: if 0 <= index < len(self._values): return self._values[index] return 0 def indexOfKey(self, key: int) -> int: try: return self._keys.index(key) except ValueError: return -1 def indexOfValue(self, value: int) -> int: try: return self._values.index(value) except ValueError: return -1 def clear(self): self._keys.clear() self._values.clear() def append(self, key: int, value: int): self.put(key, value) def toString(self) -> str: pairs = ", ".join(f"{k}={v}" for k, v in zip(self._keys, self._values)) return f"SparseIntArray{{{pairs}}}" class SparseBooleanArray(Stub): """SparseBooleanArray — int 到 bool 的映射""" def __init__(self, initial_capacity: int = 10): super().__init__() self._keys: List[int] = [] self._values: List[bool] = [] def put(self, key: int, value: bool): try: idx = self._keys.index(key) self._values[idx] = value except ValueError: self._keys.append(key) self._values.append(value) def get(self, key: int, default: bool = False) -> bool: try: idx = self._keys.index(key) return self._values[idx] except ValueError: return default def delete(self, key: int): try: idx = self._keys.index(key) self._keys.pop(idx) self._values.pop(idx) except ValueError: pass def size(self) -> int: return len(self._keys) def keyAt(self, index: int) -> int: if 0 <= index < len(self._keys): return self._keys[index] return 0 def valueAt(self, index: int) -> bool: if 0 <= index < len(self._values): return self._values[index] return False def indexOfKey(self, key: int) -> int: try: return self._keys.index(key) except ValueError: return -1 def indexOfValue(self, value: bool) -> int: try: return self._values.index(value) except ValueError: return -1 def clear(self): self._keys.clear() self._values.clear() def append(self, key: int, value: bool): self.put(key, value) def toString(self) -> str: pairs = ", ".join(f"{k}={v}" for k, v in zip(self._keys, self._values)) return f"SparseBooleanArray{{{pairs}}}" # ═══════════════════════════════════════════════════════════════ # LruCache # ═══════════════════════════════════════════════════════════════ class LruCache(Stub): """LruCache — LRU 缓存""" def __init__(self, max_size: int): super().__init__() self._max_size = max_size self._size = 0 self._map: Dict[Any, Any] = {} def get(self, key: Any) -> Optional[Any]: return self._map.get(key) def put(self, key: Any, value: Any) -> Optional[Any]: old = self._map.get(key) self._map[key] = value self._size += self._safe_size_of(key, value) if old is not None: self._size -= self._safe_size_of(key, old) self._trim_to_size(self._max_size) return old def remove(self, key: Any) -> Optional[Any]: value = self._map.pop(key, None) if value is not None: self._size -= self._safe_size_of(key, value) return value def size(self) -> int: return self._size def maxSize(self) -> int: return self._max_size def trimToSize(self, max_size: int): self._trim_to_size(max_size) def _trim_to_size(self, max_size: int): while self._size > max_size and self._map: # Remove oldest (first key) oldest_key = next(iter(self._map)) self.remove(oldest_key) def snapshot(self) -> Dict[Any, Any]: return dict(self._map) def evictAll(self): self._map.clear() self._size = 0 def _safe_size_of(self, key: Any, value: Any) -> int: try: return self.sizeOf(key, value) except Exception: return 1 def sizeOf(self, key: Any, value: Any) -> int: return 1 def entryRemoved(self, evicted: bool, key: Any, old_value: Any, new_value: Optional[Any]): pass def create(self, key: Any) -> Optional[Any]: return None def getCount(self) -> int: return len(self._map) def putCount(self) -> int: return len(self._map) def evictionCount(self) -> int: return 0 def hitCount(self) -> int: return 0 def missCount(self) -> int: return 0 def toString(self) -> str: return f"LruCache(maxSize={self._max_size}, size={self._size})" # ═══════════════════════════════════════════════════════════════ # DisplayMetrics # ═══════════════════════════════════════════════════════════════ class DisplayMetrics(Stub): """DisplayMetrics — 屏幕显示度量""" DENSITY_LOW = 120 DENSITY_MEDIUM = 160 DENSITY_TV = 213 DENSITY_HIGH = 240 DENSITY_XHIGH = 320 DENSITY_XXHIGH = 480 DENSITY_XXXHIGH = 640 DENSITY_DEFAULT = DENSITY_MEDIUM DENSITY_DEVICE_STABLE = DENSITY_XXXHIGH def __init__(self): super().__init__() self.widthPixels = 1080 self.heightPixels = 1920 self.density = 2.75 self.densityDpi = 440 self.scaledDensity = 2.75 self.xdpi = 440.0 self.ydpi = 440.0 self.noncompatWidthPixels = 1080 self.noncompatHeightPixels = 1920 self.noncompatDensity = 2.75 self.noncompatDensityDpi = 440 self.noncompatScaledDensity = 2.75 self.noncompatXdpi = 440.0 self.noncompatYdpi = 440.0 def setTo(self, other: "DisplayMetrics"): self.widthPixels = other.widthPixels self.heightPixels = other.heightPixels self.density = other.density self.densityDpi = other.densityDpi self.scaledDensity = other.scaledDensity self.xdpi = other.xdpi self.ydpi = other.ydpi def setToDefaults(self): self.__init__() def toString(self) -> str: return (f"DisplayMetrics({self.widthPixels}x{self.heightPixels}, " f"density={self.density}, dpi={self.densityDpi})") # ═══════════════════════════════════════════════════════════════ # TypedValue # ═══════════════════════════════════════════════════════════════ class TypedValue(Stub): """TypedValue — 类型化值的容器""" COMPLEX_UNIT_PX = 0 COMPLEX_UNIT_DIP = 1 COMPLEX_UNIT_SP = 2 COMPLEX_UNIT_PT = 3 COMPLEX_UNIT_IN = 4 COMPLEX_UNIT_MM = 5 COMPLEX_UNIT_FRACTION = 6 COMPLEX_UNIT_FRACTION_PARENT = 7 COMPLEX_UNIT_SHIFT = 0 COMPLEX_UNIT_MASK = 0xF COMPLEX_RADIX_SHIFT = 4 COMPLEX_RADIX_MASK = 0x3 COMPLEX_MANTISSA_SHIFT = 8 COMPLEX_MANTISSA_MASK = 0x00ffffff # 复数值的基数乘数(与 AOSP TypedValue.RADIX_MULTS 一致) _RADIX_MULTS = ( 0.00390625, # 1/256, radix 0 3.0517578e-5, # 1/32768, radix 1 1.1920929e-7, # 1/8388608, radix 2 4.6566129e-10, # 1/2147483648, radix 3 ) TYPE_NULL = 0x00 TYPE_REFERENCE = 0x01 TYPE_ATTRIBUTE = 0x02 TYPE_STRING = 0x03 TYPE_FLOAT = 0x04 TYPE_DIMENSION = 0x05 TYPE_FRACTION = 0x06 TYPE_FIRST_INT = 0x10 TYPE_INT_DEC = 0x10 TYPE_INT_HEX = 0x11 TYPE_INT_BOOLEAN = 0x12 TYPE_INT_COLOR_ARGB8 = 0x1C TYPE_INT_COLOR_RGB8 = 0x1D TYPE_INT_COLOR_ARGB4 = 0x1E TYPE_INT_COLOR_RGB4 = 0x1F TYPE_LAST_COLOR_INT = 0x1F TYPE_LAST_INT = 0x1F DATA_NULL_UNDEFINED = 0 DATA_NULL_EMPTY = 1 def __init__(self): super().__init__() self.type = self.TYPE_NULL self.data = 0 self.assetCookie = 0 self.resourceId = 0 self.changingConfigurations = 0 self.string = None self.density = 0 @staticmethod def applyDimension(unit: int, value: float, metrics: DisplayMetrics) -> float: if unit == TypedValue.COMPLEX_UNIT_PX: return value if unit == TypedValue.COMPLEX_UNIT_DIP: return value * metrics.density if unit == TypedValue.COMPLEX_UNIT_SP: return value * metrics.scaledDensity if unit == TypedValue.COMPLEX_UNIT_PT: return value * metrics.xdpi * (1.0 / 72) if unit == TypedValue.COMPLEX_UNIT_IN: return value * metrics.xdpi if unit == TypedValue.COMPLEX_UNIT_MM: return value * metrics.xdpi * (1.0 / 25.4) return value @staticmethod def complexToDimension(data: int, metrics: DisplayMetrics) -> float: """将复数维度值转换为像素值。 复数值格式:bits 0-3=unit, bits 4-5=radix, bits 8-31=mantissa(有符号24位)""" unit = (data >> TypedValue.COMPLEX_UNIT_SHIFT) & TypedValue.COMPLEX_UNIT_MASK radix = (data >> TypedValue.COMPLEX_RADIX_SHIFT) & TypedValue.COMPLEX_RADIX_MASK mantissa = (data >> TypedValue.COMPLEX_MANTISSA_SHIFT) & TypedValue.COMPLEX_MANTISSA_MASK # 24 位有符号数符号扩展 if mantissa & 0x800000: mantissa = mantissa - 0x1000000 value = mantissa * TypedValue._RADIX_MULTS[radix] return TypedValue.applyDimension(unit, value, metrics) @staticmethod def complexToFloat(data: int) -> float: """将复数值转换为浮点数(与 AOSP complexToFloat 一致)。""" radix = (data >> TypedValue.COMPLEX_RADIX_SHIFT) & TypedValue.COMPLEX_RADIX_MASK # mantissa 保持在原始位位置(左移 8 位),与 AOSP 一致 mantissa = data & (TypedValue.COMPLEX_MANTISSA_MASK << TypedValue.COMPLEX_MANTISSA_SHIFT) # 处理符号(Java int 是有符号的,bit 31 为符号位) if mantissa & 0x80000000: mantissa = mantissa - 0x100000000 return float(mantissa) * TypedValue._RADIX_MULTS[radix] @staticmethod def complexToDimensionPixelOffset(data: int, metrics: DisplayMetrics) -> int: return int(TypedValue.complexToDimension(data, metrics)) @staticmethod def complexToDimensionPixelSize(data: int, metrics: DisplayMetrics) -> int: val = TypedValue.complexToDimension(data, metrics) if val >= 0: return int(val + 0.5) return int(val - 0.5) def getFloat(self) -> float: """将 data 字段按 IEEE 754 单精度浮点数解读(与 Float.intBitsToFloat 一致)。""" bits = self.data & 0xFFFFFFFF return struct.unpack('f', struct.pack('I', bits))[0] def toString(self) -> str: return f"TypedValue(type={self.type}, data={self.data})" # ═══════════════════════════════════════════════════════════════ # Pair # ═══════════════════════════════════════════════════════════════ class Pair(Stub): """Pair — 有序对""" def __init__(self, first: Any, second: Any): super().__init__() self.first = first self.second = second @staticmethod def create(a: Any, b: Any) -> "Pair": return Pair(a, b) def hashCode(self) -> int: return hash((self.first, self.second)) def equals(self, other: Any) -> bool: if not isinstance(other, Pair): return False return self.first == other.first and self.second == other.second def toString(self) -> str: return f"Pair({self.first}, {self.second})" def __eq__(self, other): return self.equals(other) def __hash__(self): return self.hashCode() def __repr__(self): return self.toString() def __iter__(self): return iter((self.first, self.second)) # ═══════════════════════════════════════════════════════════════ # Size / SizeF # ═══════════════════════════════════════════════════════════════ class Size(Stub): """Size — 整数尺寸""" def __init__(self, width: int, height: int): super().__init__() self._width = width self._height = height def getWidth(self) -> int: return self._width def getHeight(self) -> int: return self._height def equals(self, other: Any) -> bool: if not isinstance(other, Size): return False return self._width == other._width and self._height == other._height def hashCode(self) -> int: return (self._height << 16) ^ self._width def toString(self) -> str: return f"{self._width}x{self._height}" @staticmethod def parseSize(string: str) -> "Size": parts = string.split("x") if len(parts) == 2: return Size(int(parts[0]), int(parts[1])) raise ValueError(f"Invalid size: {string}") def __eq__(self, other): return self.equals(other) def __hash__(self): return self.hashCode() def __repr__(self): return self.toString() class SizeF(Stub): """SizeF — 浮点数尺寸""" def __init__(self, width: float, height: float): super().__init__() self._width = width self._height = height def getWidth(self) -> float: return self._width def getHeight(self) -> float: return self._height def equals(self, other: Any) -> bool: if not isinstance(other, SizeF): return False return abs(self._width - other._width) < 0.001 and abs(self._height - other._height) < 0.001 def hashCode(self) -> int: return hash((self._width, self._height)) def toString(self) -> str: return f"{self._width:.1f}x{self._height:.1f}" @staticmethod def parseSizeF(string: str) -> "SizeF": parts = string.split("x") if len(parts) == 2: return SizeF(float(parts[0]), float(parts[1])) raise ValueError(f"Invalid size: {string}") def __eq__(self, other): return self.equals(other) def __hash__(self): return self.hashCode() def __repr__(self): return self.toString() # ═══════════════════════════════════════════════════════════════ # StateSet # ═══════════════════════════════════════════════════════════════ class StateSet(Stub): """StateSet — 状态集合""" VIEW_STATE_WINDOW_FOCUSED = 1 VIEW_STATE_SELECTED = 1 << 1 VIEW_STATE_FOCUSED = 1 << 2 VIEW_STATE_ENABLED = 1 << 3 VIEW_STATE_PRESSED = 1 << 4 VIEW_STATE_ACTIVATED = 1 << 5 VIEW_STATE_ACCELERATED = 1 << 6 VIEW_STATE_HOVERED = 1 << 7 VIEW_STATE_DRAG_CAN_ACCEPT = 1 << 8 VIEW_STATE_DRAG_HOVERED = 1 << 9 WILD_CARD = 0x00000000 @staticmethod def isWildCard(state_spec_or_mask: Any) -> bool: if isinstance(state_spec_or_mask, list): return len(state_spec_or_mask) == 0 return state_spec_or_mask == StateSet.WILD_CARD @staticmethod def trimStateSet(states: List[int], new_size: int) -> List[int]: return states[:new_size] @staticmethod def dump(states: List[int]) -> str: if not states: return "[]" return "[" + ", ".join(str(s) for s in states) + "]" # ═══════════════════════════════════════════════════════════════ # MathUtils # ═══════════════════════════════════════════════════════════════ class MathUtils(Stub): """MathUtils — 数学工具""" DEG_TO_RAD = math.pi / 180.0 RAD_TO_DEG = 180.0 / math.pi @staticmethod def abs(v: float) -> float: return abs(v) @staticmethod def constrain(amount: float, low: float, high: float) -> float: return max(low, min(high, amount)) @staticmethod def constrainInt(amount: int, low: int, high: int) -> int: return max(low, min(high, amount)) @staticmethod def lerp(start: float, stop: float, amount: float) -> float: return start + (stop - start) * amount @staticmethod def lerpInv(a: float, b: float, value: float) -> float: if a != b: return (value - a) / (b - a) return 0.0 @staticmethod def norm(start: float, stop: float, value: float) -> float: return MathUtils.lerpInv(start, stop, value) @staticmethod def map(min_start: float, min_stop: float, max_start: float, max_stop: float, value: float) -> float: return max_start + (value - min_start) * (max_stop - max_start) / (min_stop - min_start) @staticmethod def dist(x1: float, y1: float, x2: float, y2: float) -> float: return math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) @staticmethod def max(var1: float, var2: float) -> float: return max(var1, var2) @staticmethod def min(var1: float, var2: float) -> float: return min(var1, var2) @staticmethod def degrees(radians: float) -> float: return math.degrees(radians) @staticmethod def radians(degrees: float) -> float: return math.radians(degrees) @staticmethod def exp(a: float) -> float: return math.exp(a) @staticmethod def log(a: float) -> float: return math.log(a) @staticmethod def pow(a: float, b: float) -> float: return a ** b @staticmethod def sqrt(a: float) -> float: return math.sqrt(a) @staticmethod def sin(angle: float) -> float: return math.sin(angle) @staticmethod def cos(angle: float) -> float: return math.cos(angle) @staticmethod def tan(angle: float) -> float: return math.tan(angle) @staticmethod def atan2(a: float, b: float) -> float: return math.atan2(a, b) @staticmethod def random() -> float: import random return random.random() @staticmethod def isNaN(v: float) -> bool: return math.isnan(v) @staticmethod def nanToZero(value: float) -> float: return 0.0 if math.isnan(value) else value # ═══════════════════════════════════════════════════════════════ # Pattern (re) # ═══════════════════════════════════════════════════════════════ class Pattern(Stub): """Pattern — 正则表达式模式""" CASE_INSENSITIVE = 2 MULTILINE = 8 DOTALL = 32 UNICODE_CASE = 64 CANON_EQ = 128 UNIX_LINES = 1 LITERAL = 16 COMMENTS = 4 def __init__(self, pattern: str, flags: int = 0): super().__init__() self._pattern_str = pattern self._flags = flags re_flags = 0 if flags & self.CASE_INSENSITIVE: re_flags |= _re.IGNORECASE if flags & self.MULTILINE: re_flags |= _re.MULTILINE if flags & self.DOTALL: re_flags |= _re.DOTALL if flags & self.UNICODE_CASE: re_flags |= _re.UNICODE if flags & self.UNIX_LINES: pass # no re equivalent self._compiled = _re.compile(pattern, re_flags) @staticmethod def compile(pattern: str, flags: int = 0) -> "Pattern": return Pattern(pattern, flags) def matcher(self, input_str: str) -> "Matcher": return Matcher(self._compiled, input_str) def split(self, input_str: str, limit: int = 0) -> List[str]: if limit < 0: return self._compiled.split(input_str, maxsplit=0) return self._compiled.split(input_str, maxsplit=limit - 1 if limit > 0 else 0) def pattern(self) -> str: return self._pattern_str def flags(self) -> int: return self._flags @staticmethod def matches(regex: str, input_str: str) -> bool: return bool(_re.fullmatch(regex, input_str)) @staticmethod def quote(s: str) -> str: return _re.escape(s) def toString(self) -> str: return self._pattern_str class Matcher(Stub): """Matcher — 正则匹配器""" def __init__(self, compiled_pattern: "Pattern", input_str: str): super().__init__() self._pattern = compiled_pattern self._input = input_str self._region_start = 0 self._region_end = len(input_str) self._match: Optional[Any] = None def matches(self) -> bool: self._match = self._pattern.fullmatch(self._input, self._region_start, self._region_end) return self._match is not None def find(self, start: int = 0) -> bool: self._match = self._pattern.search(self._input, start or self._region_start, self._region_end) return self._match is not None def lookingAt(self) -> bool: self._match = self._pattern.match(self._input, self._region_start, self._region_end) return self._match is not None def group(self, group: int = 0) -> Optional[str]: if self._match: return self._match.group(group) return None def groupCount(self) -> int: if self._match: return len(self._match.groups()) return 0 def start(self, group: int = 0) -> int: if self._match: return self._match.start(group) return -1 def end(self, group: int = 0) -> int: if self._match: return self._match.end(group) return -1 def replaceAll(self, replacement: str) -> str: return self._pattern.sub(replacement, self._input) def replaceFirst(self, replacement: str) -> str: return self._pattern.sub(replacement, self._input, count=1) def region(self, start: int, end: int) -> "Matcher": self._region_start = start self._region_end = end return self def regionStart(self) -> int: return self._region_start def regionEnd(self) -> int: return self._region_end def reset(self, input_str: Optional[str] = None) -> "Matcher": if input_str is not None: self._input = input_str self._match = None return self def hitEnd(self) -> bool: return False def requireEnd(self) -> bool: return False def usePattern(self, new_pattern: "Pattern") -> "Matcher": self._pattern = new_pattern return self def appendReplacement(self, sb: Any, replacement: str): pass def appendTail(self, sb: Any): pass def toString(self) -> str: return f"Matcher({self._pattern.pattern()})" # ═══════════════════════════════════════════════════════════════ # TYPED_VALUE 常量 # ═══════════════════════════════════════════════════════════════ TYPED_VALUE = TypedValue # 别名 # ═══════════════════════════════════════════════════════════════ # Pair 反向兼容 (android.util.Pair) # ═══════════════════════════════════════════════════════════════ # Pair 已在上面定义 # ═══════════════════════════════════════════════════════════════ # __all__ # ═══════════════════════════════════════════════════════════════ __all__ = [ "Log", "SparseArray", "SparseIntArray", "SparseBooleanArray", "LruCache", "DisplayMetrics", "TypedValue", "Pair", "Size", "SizeF", "StateSet", "MathUtils", "Pattern", "Matcher", "TYPED_VALUE", ]