核心类方法补全: - 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行
2065 lines
62 KiB
Python
2065 lines
62 KiB
Python
"""
|
||
Android OS 组件 — Bundle, Handler, Looper, Message, Build 等
|
||
方法签名完全对齐 Android SDK
|
||
"""
|
||
|
||
import asyncio
|
||
import functools
|
||
import heapq
|
||
import os as _os
|
||
import sys
|
||
import threading
|
||
import time as _time
|
||
from datetime import datetime
|
||
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, TypeVar, Union
|
||
|
||
from .._core import (
|
||
Stub, AndroidOnlyError, android_context,
|
||
resolve_android_path, to_android_path,
|
||
_sdcard_root, _app_data_dir, _package_name,
|
||
HAS_PSUTIL,
|
||
)
|
||
|
||
# Linux 真实实现导入
|
||
try:
|
||
from .._linux_impl import IS_LINUX, LinuxPower, LinuxProcess, HAS_PSUTIL as _HAS_PSUTIL
|
||
_USE_LINUX = IS_LINUX
|
||
except ImportError:
|
||
_USE_LINUX = False
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════
|
||
# Build
|
||
# ═══════════════════════════════════════════════════════════════
|
||
|
||
class Build(Stub):
|
||
"""Build — 设备信息"""
|
||
|
||
class VERSION(Stub):
|
||
"""版本信息"""
|
||
SDK_INT = 34 # Android 14
|
||
RELEASE = "14"
|
||
INCREMENTAL = "20240301"
|
||
CODENAME = "REL"
|
||
SECURITY_PATCH = "2024-03-05"
|
||
BASE_OS = ""
|
||
PREVIEW_SDK_INT = 0
|
||
RESOURCES_SDK_INT = 34
|
||
MEDIA_PERFORMANCE_CLASS = 0
|
||
|
||
class VERSION_CODES(Stub):
|
||
"""版本代码常量"""
|
||
BASE = 1
|
||
BASE_1_1 = 2
|
||
CUPCAKE = 3
|
||
DONUT = 4
|
||
ECLAIR = 5
|
||
ECLAIR_0_1 = 6
|
||
ECLAIR_MR1 = 7
|
||
FROYO = 8
|
||
GINGERBREAD = 9
|
||
GINGERBREAD_MR1 = 10
|
||
HONEYCOMB = 11
|
||
HONEYCOMB_MR1 = 12
|
||
HONEYCOMB_MR2 = 13
|
||
ICE_CREAM_SANDWICH = 14
|
||
ICE_CREAM_SANDWICH_MR1 = 15
|
||
JELLY_BEAN = 16
|
||
JELLY_BEAN_MR1 = 17
|
||
JELLY_BEAN_MR2 = 18
|
||
KITKAT = 19
|
||
KITKAT_WATCH = 20
|
||
LOLLIPOP = 21
|
||
LOLLIPOP_MR1 = 22
|
||
M = 23
|
||
N = 24
|
||
N_MR1 = 25
|
||
O = 26
|
||
O_MR1 = 27
|
||
P = 28
|
||
Q = 29
|
||
R = 30
|
||
S = 31
|
||
S_V2 = 32
|
||
TIRAMISU = 33
|
||
UPSIDE_DOWN_CAKE = 34
|
||
|
||
VERSION = VERSION()
|
||
BOARD = "redfin"
|
||
BOOTLOADER = "r7.0-12345"
|
||
BRAND = "google"
|
||
CPU_ABI = "arm64-v8a"
|
||
CPU_ABI2 = ""
|
||
DEVICE = "redfin"
|
||
DISPLAY = "AP1A.240305.001"
|
||
FINGERPRINT = "google/redfin/redfin:14/AP1A.240305.001/12345678:user/release-keys"
|
||
HARDWARE = "redfin"
|
||
HOST = "build-test-01"
|
||
ID = "AP1A.240305.001"
|
||
MANUFACTURER = "Google"
|
||
MODEL = "Pixel 5"
|
||
ODM_SKU = None
|
||
PRODUCT = "redfin"
|
||
RADIO = ""
|
||
SERIAL = "unknown"
|
||
SKU = None
|
||
SOC_MANUFACTURER = "Qualcomm"
|
||
SOC_MODEL = "Snapdragon 765G"
|
||
SUPPORTED_32_BIT_ABIS = ["armeabi-v7a", "armeabi"]
|
||
SUPPORTED_64_BIT_ABIS = ["arm64-v8a"]
|
||
SUPPORTED_ABIS = ["arm64-v8a", "armeabi-v7a", "armeabi"]
|
||
TAGS = "release-keys"
|
||
TIME = int(_time.time() * 1000)
|
||
TYPE = "user"
|
||
USER = "build-user"
|
||
|
||
@staticmethod
|
||
def getRadioVersion() -> str:
|
||
return ""
|
||
|
||
@staticmethod
|
||
def getSerial() -> str:
|
||
return "unknown"
|
||
|
||
@staticmethod
|
||
def getFingerprint() -> str:
|
||
return Build.FINGERPRINT
|
||
|
||
@classmethod
|
||
def _populate_from_linux(cls):
|
||
"""从 LinuxProcess.get_system_info() 填充真实设备信息"""
|
||
try:
|
||
info = LinuxProcess.get_system_info() or {}
|
||
import platform as _platform
|
||
import socket as _socket
|
||
hostname = info.get("hostname") or _socket.gethostname()
|
||
system = info.get("platform") or _platform.system()
|
||
arch = info.get("arch") or _platform.machine()
|
||
kernel = info.get("kernel") or _platform.release()
|
||
cls.MODEL = hostname
|
||
cls.MANUFACTURER = "Linux"
|
||
cls.BRAND = system
|
||
cls.DEVICE = arch
|
||
cls.BOARD = arch
|
||
cls.HARDWARE = arch
|
||
cls.HOST = hostname
|
||
cls.PRODUCT = hostname
|
||
cls.DISPLAY = kernel
|
||
cls.ID = kernel
|
||
# FINGERPRINT: brand/device/device:version/id/build:user/release-keys
|
||
cls.FINGERPRINT = (
|
||
f"{system}/{hostname}/{arch}:Linux/{kernel}/{hostname}:user/release-keys"
|
||
)
|
||
# CPU ABI 用架构映射
|
||
_abi_map = {
|
||
"x86_64": "x86_64", "amd64": "x86_64",
|
||
"aarch64": "arm64-v8a", "arm64": "arm64-v8a",
|
||
"x86": "x86", "i386": "x86", "i686": "x86",
|
||
"armv7l": "armeabi-v7a", "arm": "armeabi-v7a",
|
||
}
|
||
cls.CPU_ABI = _abi_map.get(arch, arch)
|
||
cls.SUPPORTED_ABIS = [cls.CPU_ABI]
|
||
cls.SUPPORTED_64_BIT_ABIS = [cls.CPU_ABI] if "64" in cls.CPU_ABI else []
|
||
cls.SUPPORTED_32_BIT_ABIS = [cls.CPU_ABI] if "64" not in cls.CPU_ABI else []
|
||
except Exception:
|
||
pass
|
||
|
||
|
||
# 在 Linux 上用真实系统信息填充 Build
|
||
if _USE_LINUX:
|
||
Build._populate_from_linux()
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════
|
||
# SystemClock
|
||
# ═══════════════════════════════════════════════════════════════
|
||
|
||
class SystemClock(Stub):
|
||
"""SystemClock — 系统时钟"""
|
||
|
||
@staticmethod
|
||
def elapsedRealtime() -> int:
|
||
"""返回自启动以来的毫秒数"""
|
||
return int(_time.monotonic() * 1000)
|
||
|
||
@staticmethod
|
||
def elapsedRealtimeNanos() -> int:
|
||
return int(_time.monotonic_ns())
|
||
|
||
@staticmethod
|
||
def uptimeMillis() -> int:
|
||
"""返回自启动以来(不含深度睡眠)的毫秒数"""
|
||
return int(_time.monotonic() * 1000)
|
||
|
||
@staticmethod
|
||
def currentThreadTimeMillis() -> int:
|
||
return int(_time.clock_gettime(_time.CLOCK_THREAD_CPUTIME_ID) * 1000)
|
||
|
||
@staticmethod
|
||
def sleep(ms: int):
|
||
_time.sleep(ms / 1000.0)
|
||
|
||
@staticmethod
|
||
def setCurrentTimeMillis(millis: int) -> bool:
|
||
return False
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════
|
||
# Environment
|
||
# ═══════════════════════════════════════════════════════════════
|
||
|
||
class Environment(Stub):
|
||
"""Environment — 环境目录"""
|
||
|
||
MEDIA_BAD_REMOVAL = "bad_removal"
|
||
MEDIA_CHECKING = "checking"
|
||
MEDIA_EJECTING = "ejecting"
|
||
MEDIA_MOUNTED = "mounted"
|
||
MEDIA_MOUNTED_READ_ONLY = "mounted_ro"
|
||
MEDIA_NOFS = "nofs"
|
||
MEDIA_REMOVED = "removed"
|
||
MEDIA_SHARED = "shared"
|
||
MEDIA_UNKNOWN = "unknown"
|
||
MEDIA_UNMOUNTABLE = "unmountable"
|
||
MEDIA_UNMOUNTED = "unmounted"
|
||
|
||
DIRECTORY_ALARMS = "Alarms"
|
||
DIRECTORY_DCIM = "DCIM"
|
||
DIRECTORY_DOCUMENTS = "Documents"
|
||
DIRECTORY_DOWNLOADS = "Download"
|
||
DIRECTORY_MOVIES = "Movies"
|
||
DIRECTORY_MUSIC = "Music"
|
||
DIRECTORY_NOTIFICATIONS = "Notifications"
|
||
DIRECTORY_PICTURES = "Pictures"
|
||
DIRECTORY_PODCASTS = "Podcasts"
|
||
DIRECTORY_RINGTONES = "Ringtones"
|
||
DIRECTORY_AUDIOBOOKS = "Audiobooks"
|
||
DIRECTORY_SCREENSHOTS = "Screenshots"
|
||
|
||
@staticmethod
|
||
def getExternalStorageDirectory() -> str:
|
||
return _sdcard_root
|
||
|
||
@staticmethod
|
||
def getExternalStoragePublicDirectory(type_: str) -> str:
|
||
path = _os.path.join(_sdcard_root, type_)
|
||
_os.makedirs(path, exist_ok=True)
|
||
return path
|
||
|
||
@staticmethod
|
||
def getDataDirectory() -> str:
|
||
return _os.path.join(_app_data_dir, "data")
|
||
|
||
@staticmethod
|
||
def getDownloadCacheDirectory() -> str:
|
||
return _os.path.join(_app_data_dir, "cache", "download")
|
||
|
||
@staticmethod
|
||
def getRootDirectory() -> str:
|
||
return _os.path.abspath(_os.sep)
|
||
|
||
@staticmethod
|
||
def getStorageDirectory(user_handle: Any = None) -> str:
|
||
return _sdcard_root
|
||
|
||
@staticmethod
|
||
def getExternalStorageState(path: Optional[str] = None) -> str:
|
||
return Environment.MEDIA_MOUNTED
|
||
|
||
@staticmethod
|
||
def isExternalStorageRemovable(path: Optional[str] = None) -> bool:
|
||
return False
|
||
|
||
@staticmethod
|
||
def isExternalStorageEmulated(path: Optional[str] = None) -> bool:
|
||
return True
|
||
|
||
@staticmethod
|
||
def isExternalStorageLegacy(path: Optional[str] = None) -> bool:
|
||
return True
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════
|
||
# Process
|
||
# ═══════════════════════════════════════════════════════════════
|
||
|
||
class Process(Stub):
|
||
"""Process — 进程信息"""
|
||
|
||
# 进程优先级
|
||
THREAD_PRIORITY_DEFAULT = 0
|
||
THREAD_PRIORITY_LOWEST = -19
|
||
THREAD_PRIORITY_BACKGROUND = 10
|
||
THREAD_PRIORITY_FOREGROUND = -2
|
||
THREAD_PRIORITY_DISPLAY = -4
|
||
THREAD_PRIORITY_URGENT_DISPLAY = -8
|
||
THREAD_PRIORITY_AUDIO = -16
|
||
THREAD_PRIORITY_URGENT_AUDIO = -19
|
||
THREAD_PRIORITY_MORE_FAVORABLE = -1
|
||
THREAD_PRIORITY_LESS_FAVORABLE = 1
|
||
|
||
SIGNAL_KILL = 9
|
||
SIGNAL_QUIT = 3
|
||
SIGNAL_USR1 = 10
|
||
SIGNAL_USR2 = 12
|
||
|
||
@staticmethod
|
||
def myTid() -> int:
|
||
return threading.get_ident()
|
||
|
||
@staticmethod
|
||
def myUid() -> int:
|
||
return 10001
|
||
|
||
@staticmethod
|
||
def myPid() -> int:
|
||
return _os.getpid()
|
||
|
||
@staticmethod
|
||
def killProcess(pid):
|
||
import signal
|
||
try:
|
||
_os.kill(pid, signal.SIGKILL)
|
||
except (OSError, ProcessLookupError):
|
||
pass
|
||
|
||
@staticmethod
|
||
def sendSignal(pid, signal_num):
|
||
try:
|
||
_os.kill(pid, signal_num)
|
||
except (OSError, ProcessLookupError):
|
||
pass
|
||
|
||
@staticmethod
|
||
def setThreadPriority(priority: int):
|
||
pass
|
||
|
||
@staticmethod
|
||
def setThreadPriority(tid, priority):
|
||
try:
|
||
_os.setpriority(_os.PRIO_PROCESS, tid, priority)
|
||
except (OSError, PermissionError):
|
||
pass
|
||
|
||
@staticmethod
|
||
def getElapsedCpuTime() -> int:
|
||
return int(_time.clock_gettime(_time.CLOCK_PROCESS_CPUTIME_ID) * 1000)
|
||
|
||
@staticmethod
|
||
def supportsProcesses() -> bool:
|
||
return True
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════
|
||
# BaseBundle / PersistableBundle
|
||
# ═══════════════════════════════════════════════════════════════
|
||
|
||
class BaseBundle(Stub):
|
||
"""BaseBundle — Bundle 基类"""
|
||
|
||
def __init__(self):
|
||
super().__init__()
|
||
self._map: Dict[str, Any] = {}
|
||
self._class_loader = None
|
||
|
||
def getString(self, key: str, default: Optional[str] = None) -> Optional[str]:
|
||
v = self._map.get(key, default)
|
||
return str(v) if v is not None else default
|
||
|
||
def getInt(self, key: str, default: int = 0) -> int:
|
||
v = self._map.get(key, default)
|
||
try:
|
||
return int(v) if v is not None else default
|
||
except (ValueError, TypeError):
|
||
return default
|
||
|
||
def getLong(self, key: str, default: int = 0) -> int:
|
||
return self.getInt(key, default)
|
||
|
||
def getFloat(self, key: str, default: float = 0.0) -> float:
|
||
v = self._map.get(key, default)
|
||
try:
|
||
return float(v) if v is not None else default
|
||
except (ValueError, TypeError):
|
||
return default
|
||
|
||
def getDouble(self, key: str, default: float = 0.0) -> float:
|
||
return self.getFloat(key, default)
|
||
|
||
def getBoolean(self, key: str, default: bool = False) -> bool:
|
||
v = self._map.get(key, default)
|
||
if isinstance(v, bool):
|
||
return v
|
||
if isinstance(v, str):
|
||
return v.lower() in ("true", "1", "yes")
|
||
return bool(v) if v is not None else default
|
||
|
||
def getByte(self, key: str, default: int = 0) -> int:
|
||
return self.getInt(key, default)
|
||
|
||
def getChar(self, key: str, default: str = "") -> str:
|
||
return str(self._map.get(key, default))
|
||
|
||
def getShort(self, key: str, default: int = 0) -> int:
|
||
return self.getInt(key, default)
|
||
|
||
def getCharSequence(self, key: str, default: Optional[str] = None) -> Optional[str]:
|
||
return self.getString(key, default)
|
||
|
||
def getSerializable(self, key: str, default: Any = None) -> Any:
|
||
return self._map.get(key, default)
|
||
|
||
def getStringArrayList(self, key: str) -> Optional[List[str]]:
|
||
v = self._map.get(key)
|
||
if isinstance(v, list):
|
||
return [str(x) for x in v]
|
||
return None
|
||
|
||
def getIntegerArrayList(self, key: str) -> Optional[List[int]]:
|
||
v = self._map.get(key)
|
||
if isinstance(v, list):
|
||
return [int(x) for x in v if isinstance(x, (int, float))]
|
||
return None
|
||
|
||
def containsKey(self, key: str) -> bool:
|
||
return key in self._map
|
||
|
||
def isEmpty(self) -> bool:
|
||
return len(self._map) == 0
|
||
|
||
def size(self) -> int:
|
||
return len(self._map)
|
||
|
||
def clear(self):
|
||
self._map.clear()
|
||
|
||
def keySet(self) -> Set[str]:
|
||
return set(self._map.keys())
|
||
|
||
def hasKey(self, key: str) -> bool:
|
||
return key in self._map
|
||
|
||
def toString(self) -> str:
|
||
return f"Bundle({self._map})"
|
||
|
||
def __len__(self) -> int:
|
||
return len(self._map)
|
||
|
||
def __contains__(self, key: str) -> bool:
|
||
return key in self._map
|
||
|
||
def __getitem__(self, key: str):
|
||
return self._map[key]
|
||
|
||
def __setitem__(self, key: str, value: Any):
|
||
self._map[key] = value
|
||
|
||
def __delitem__(self, key: str):
|
||
self._map.pop(key, None)
|
||
|
||
|
||
class PersistableBundle(BaseBundle):
|
||
"""PersistableBundle — 可持久化的 Bundle"""
|
||
|
||
def __init__(self, capacity: int = 0):
|
||
super().__init__()
|
||
|
||
def putPersistableBundle(self, key: str, value: "PersistableBundle"):
|
||
self._map[key] = value
|
||
|
||
def getPersistableBundle(self, key: str) -> Optional["PersistableBundle"]:
|
||
v = self._map.get(key)
|
||
return v if isinstance(v, PersistableBundle) else None
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════
|
||
# Bundle
|
||
# ═══════════════════════════════════════════════════════════════
|
||
|
||
class Bundle(BaseBundle):
|
||
"""Bundle — 键值对数据容器"""
|
||
|
||
EMPTY: "Bundle" = None # 在类加载后初始化
|
||
|
||
def __init__(self, capacity: int = 0):
|
||
super().__init__()
|
||
self._capacity = capacity
|
||
|
||
def putString(self, key: str, value: Optional[str]):
|
||
self._map[key] = value
|
||
|
||
def putInt(self, key: str, value: int):
|
||
self._map[key] = value
|
||
|
||
def putLong(self, key: str, value: int):
|
||
self._map[key] = value
|
||
|
||
def putFloat(self, key: str, value: float):
|
||
self._map[key] = value
|
||
|
||
def putDouble(self, key: str, value: float):
|
||
self._map[key] = value
|
||
|
||
def putBoolean(self, key: str, value: bool):
|
||
self._map[key] = value
|
||
|
||
def putByte(self, key: str, value: int):
|
||
self._map[key] = value
|
||
|
||
def putChar(self, key: str, value: str):
|
||
self._map[key] = value
|
||
|
||
def putShort(self, key: str, value: int):
|
||
self._map[key] = value
|
||
|
||
def putCharSequence(self, key: str, value: Optional[str]):
|
||
self._map[key] = value
|
||
|
||
def putSerializable(self, key: str, value: Any):
|
||
self._map[key] = value
|
||
|
||
def putParcelable(self, key: str, value: Any):
|
||
self._map[key] = value
|
||
|
||
def putStringArrayList(self, key: str, value: Optional[List[str]]):
|
||
self._map[key] = value
|
||
|
||
def putIntegerArrayList(self, key: str, value: Optional[List[int]]):
|
||
self._map[key] = value
|
||
|
||
# ---- 数组类型 ----
|
||
def putIntArray(self, key: str, value: Optional[List[int]]):
|
||
self._map[key] = list(value) if value is not None else None
|
||
|
||
def getIntArray(self, key: str) -> Optional[List[int]]:
|
||
v = self._map.get(key)
|
||
if isinstance(v, list):
|
||
return [int(x) for x in v]
|
||
return None
|
||
|
||
def putStringArray(self, key: str, value: Optional[List[str]]):
|
||
self._map[key] = list(value) if value is not None else None
|
||
|
||
def getStringArray(self, key: str) -> Optional[List[str]]:
|
||
v = self._map.get(key)
|
||
if isinstance(v, list):
|
||
return [str(x) for x in v]
|
||
return None
|
||
|
||
def putLongArray(self, key: str, value: Optional[List[int]]):
|
||
self._map[key] = list(value) if value is not None else None
|
||
|
||
def getLongArray(self, key: str) -> Optional[List[int]]:
|
||
v = self._map.get(key)
|
||
if isinstance(v, list):
|
||
return [int(x) for x in v]
|
||
return None
|
||
|
||
def putBooleanArray(self, key: str, value: Optional[List[bool]]):
|
||
self._map[key] = list(value) if value is not None else None
|
||
|
||
def getBooleanArray(self, key: str) -> Optional[List[bool]]:
|
||
v = self._map.get(key)
|
||
if isinstance(v, list):
|
||
return [bool(x) for x in v]
|
||
return None
|
||
|
||
def putDoubleArray(self, key: str, value: Optional[List[float]]):
|
||
self._map[key] = list(value) if value is not None else None
|
||
|
||
def getDoubleArray(self, key: str) -> Optional[List[float]]:
|
||
v = self._map.get(key)
|
||
if isinstance(v, list):
|
||
return [float(x) for x in v]
|
||
return None
|
||
|
||
def putByteArray(self, key: str, value: Optional[List[int]]):
|
||
self._map[key] = list(value) if value is not None else None
|
||
|
||
def getByteArray(self, key: str) -> Optional[List[int]]:
|
||
v = self._map.get(key)
|
||
if isinstance(v, list):
|
||
return [int(x) & 0xFF for x in v]
|
||
return None
|
||
|
||
def putAll(self, bundle: "Bundle"):
|
||
if bundle:
|
||
self._map.update(bundle._map)
|
||
|
||
def getParcelable(self, key: str, default: Any = None) -> Any:
|
||
return self._map.get(key, default)
|
||
|
||
def getBundle(self, key: str) -> Optional["Bundle"]:
|
||
v = self._map.get(key)
|
||
return v if isinstance(v, Bundle) else None
|
||
|
||
def putBundle(self, key: str, value: Optional["Bundle"]):
|
||
self._map[key] = value
|
||
|
||
def describeContents(self) -> int:
|
||
return 0
|
||
|
||
def clone(self) -> "Bundle":
|
||
b = Bundle()
|
||
b._map = dict(self._map)
|
||
return b
|
||
|
||
def deepCopy(self) -> "Bundle":
|
||
import copy
|
||
b = Bundle()
|
||
b._map = copy.deepcopy(self._map)
|
||
return b
|
||
|
||
def filterValues(self):
|
||
pass
|
||
|
||
def getPairValue(self) -> Optional[str]:
|
||
return None
|
||
|
||
def setClassLoader(self, loader: Any):
|
||
self._class_loader = loader
|
||
|
||
def getClassLoader(self) -> Any:
|
||
return self._class_loader
|
||
|
||
@staticmethod
|
||
def forPair(key: str, value: str) -> "Bundle":
|
||
b = Bundle()
|
||
b.putString(key, value)
|
||
return b
|
||
|
||
|
||
Bundle.EMPTY = Bundle()
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════
|
||
# Parcel / Parcelable
|
||
# ═══════════════════════════════════════════════════════════════
|
||
|
||
class Parcelable(Stub):
|
||
"""Parcelable 接口"""
|
||
|
||
CONTENTS_FILE_DESCRIPTOR = "CONTENTS_FILE_DESCRIPTOR"
|
||
PARCELABLE_WRITE_RETURN_VALUE = 1
|
||
PARCELABLE_ELIDE_DUPLICATES = 2
|
||
|
||
def describeContents(self) -> int:
|
||
return 0
|
||
|
||
def writeToParcel(self, dest: "Parcel", flags: int):
|
||
pass
|
||
|
||
class Creator:
|
||
def createFromParcel(self, source: "Parcel"):
|
||
return None
|
||
def newArray(self, size: int):
|
||
return [None] * size
|
||
|
||
class ClassLoaderCreator:
|
||
def createFromParcel(self, source: "Parcel", loader: Any):
|
||
return None
|
||
def newArray(self, size: int):
|
||
return [None] * size
|
||
|
||
|
||
class Parcel(Stub):
|
||
"""Parcel — 序列化数据容器"""
|
||
|
||
STRING_CREATOR = "STRING_CREATOR"
|
||
STRING_LIST_CREATOR = "STRING_LIST_CREATOR"
|
||
INT_CREATOR = "INT_CREATOR"
|
||
|
||
def __init__(self):
|
||
super().__init__()
|
||
self._data: Dict[str, Any] = {}
|
||
self._position = 0
|
||
self._size = 0
|
||
|
||
@staticmethod
|
||
def obtain() -> "Parcel":
|
||
return Parcel()
|
||
|
||
def recycle(self):
|
||
self._data.clear()
|
||
self._position = 0
|
||
self._size = 0
|
||
|
||
def writeInt(self, value: int):
|
||
self._data.setdefault("_ints", []).append(value)
|
||
self._size += 4
|
||
|
||
def writeLong(self, value: int):
|
||
self._data.setdefault("_longs", []).append(value)
|
||
self._size += 8
|
||
|
||
def writeFloat(self, value: float):
|
||
self._data.setdefault("_floats", []).append(value)
|
||
self._size += 4
|
||
|
||
def writeDouble(self, value: float):
|
||
self._data.setdefault("_doubles", []).append(value)
|
||
self._size += 8
|
||
|
||
def writeString(self, value: Optional[str]):
|
||
strs = self._data.setdefault("_strings", [])
|
||
strs.append(value)
|
||
self._size += len(value) + 4 if value else 4
|
||
|
||
def writeBoolean(self, value: bool):
|
||
self.writeInt(1 if value else 0)
|
||
|
||
def writeByte(self, value: int):
|
||
self.writeInt(value & 0xFF)
|
||
|
||
def writeParcelable(self, p: Parcelable, flags: int):
|
||
pass
|
||
|
||
def writeStringArray(self, values: Optional[List[str]]):
|
||
self._data["_string_array"] = values
|
||
|
||
def writeIntArray(self, values: Optional[List[int]]):
|
||
self._data["_int_array"] = values
|
||
|
||
def writeLongArray(self, values: Optional[List[int]]):
|
||
self._data["_long_array"] = values
|
||
|
||
def readInt(self) -> int:
|
||
ints = self._data.get("_ints", [])
|
||
if ints:
|
||
return ints.pop(0)
|
||
return 0
|
||
|
||
def readLong(self) -> int:
|
||
longs = self._data.get("_longs", [])
|
||
if longs:
|
||
return longs.pop(0)
|
||
return 0
|
||
|
||
def readFloat(self) -> float:
|
||
floats = self._data.get("_floats", [])
|
||
if floats:
|
||
return floats.pop(0)
|
||
return 0.0
|
||
|
||
def readDouble(self) -> float:
|
||
doubles = self._data.get("_doubles", [])
|
||
if doubles:
|
||
return doubles.pop(0)
|
||
return 0.0
|
||
|
||
def readString(self) -> Optional[str]:
|
||
strs = self._data.get("_strings", [])
|
||
if strs:
|
||
return strs.pop(0)
|
||
return None
|
||
|
||
def readBoolean(self) -> bool:
|
||
return self.readInt() != 0
|
||
|
||
def readByte(self) -> int:
|
||
return self.readInt() & 0xFF
|
||
|
||
def dataAvail(self) -> int:
|
||
return self._size - self._position
|
||
|
||
def dataPosition(self) -> int:
|
||
return self._position
|
||
|
||
def setDataPosition(self, pos: int):
|
||
self._position = pos
|
||
|
||
def dataSize(self) -> int:
|
||
return self._size
|
||
|
||
def dataCapacity(self) -> int:
|
||
return self._size
|
||
|
||
def setDataCapacity(self, capacity: int):
|
||
pass
|
||
|
||
def appendFrom(self, parcel: "Parcel", offset: int, length: int):
|
||
pass
|
||
|
||
def marshall(self) -> bytes:
|
||
import json
|
||
return json.dumps(self._data).encode("utf-8")
|
||
|
||
@staticmethod
|
||
def unmarshall(data: bytes) -> "Parcel":
|
||
import json
|
||
p = Parcel()
|
||
try:
|
||
p._data = json.loads(data.decode("utf-8"))
|
||
except Exception:
|
||
pass
|
||
return p
|
||
|
||
def writeValue(self, v: Any):
|
||
self._data["_value"] = v
|
||
|
||
def readValue(self) -> Any:
|
||
return self._data.get("_value")
|
||
|
||
def hasFileDescriptors(self) -> bool:
|
||
return False
|
||
|
||
def toString(self) -> str:
|
||
return f"Parcel({self._size} bytes)"
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════
|
||
# Message
|
||
# ═══════════════════════════════════════════════════════════════
|
||
|
||
class Message(Stub):
|
||
"""Message — Handler 消息"""
|
||
|
||
def __init__(self):
|
||
super().__init__()
|
||
self.what = 0
|
||
self.arg1 = 0
|
||
self.arg2 = 0
|
||
self.obj: Any = None
|
||
self.data: Optional[Bundle] = None
|
||
self.replyTo: Optional["Messenger"] = None
|
||
self._target: Optional["Handler"] = None
|
||
self._callback: Optional[Callable] = None
|
||
self.when: int = 0
|
||
self._sending = False
|
||
|
||
@staticmethod
|
||
def obtain(handler: Optional["Handler"] = None, what: int = 0) -> "Message":
|
||
msg = Message()
|
||
if handler:
|
||
msg._target = handler
|
||
msg.what = what
|
||
return msg
|
||
|
||
def sendToTarget(self):
|
||
if self._target:
|
||
self._target.sendMessage(self)
|
||
|
||
def getTarget(self) -> Optional["Handler"]:
|
||
return self._target
|
||
|
||
def setTarget(self, handler: "Handler"):
|
||
self._target = handler
|
||
|
||
@property
|
||
def target(self) -> Optional["Handler"]:
|
||
return self._target
|
||
|
||
@target.setter
|
||
def target(self, handler: "Handler"):
|
||
self._target = handler
|
||
|
||
def getCallback(self) -> Optional[Callable]:
|
||
return self._callback
|
||
|
||
def copyFrom(self, other: "Message"):
|
||
self.what = other.what
|
||
self.arg1 = other.arg1
|
||
self.arg2 = other.arg2
|
||
self.obj = other.obj
|
||
self.data = other.data
|
||
self.replyTo = other.replyTo
|
||
|
||
def recycle(self):
|
||
self.what = 0
|
||
self.arg1 = 0
|
||
self.arg2 = 0
|
||
self.obj = None
|
||
self.data = None
|
||
self._target = None
|
||
self._callback = None
|
||
|
||
def isAsynchronous(self) -> bool:
|
||
return False
|
||
|
||
def setAsynchronous(self, async_: bool):
|
||
pass
|
||
|
||
def toString(self) -> str:
|
||
return f"Message(what={self.what}, arg1={self.arg1}, arg2={self.arg2})"
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════
|
||
# Looper
|
||
# ═══════════════════════════════════════════════════════════════
|
||
|
||
class Looper(Stub):
|
||
"""Looper — 消息循环"""
|
||
|
||
_main_looper: Optional["Looper"] = None
|
||
_thread_local = threading.local()
|
||
|
||
def __init__(self, quit_allowed: bool = True):
|
||
super().__init__()
|
||
self._thread = threading.current_thread()
|
||
self._running = False
|
||
self._message_queue: List[Message] = []
|
||
self._queue_lock = threading.Lock()
|
||
self._quit_allowed = quit_allowed
|
||
|
||
@staticmethod
|
||
def prepare():
|
||
"""为当前线程准备一个 Looper"""
|
||
looper = Looper()
|
||
Looper._thread_local.looper = looper
|
||
return looper
|
||
|
||
@staticmethod
|
||
def prepareMainLooper():
|
||
if Looper._main_looper is None:
|
||
Looper._main_looper = Looper.prepare()
|
||
|
||
@staticmethod
|
||
def getMainLooper() -> "Looper":
|
||
if Looper._main_looper is None:
|
||
Looper.prepareMainLooper()
|
||
return Looper._main_looper
|
||
|
||
@staticmethod
|
||
def myLooper() -> Optional["Looper"]:
|
||
return getattr(Looper._thread_local, "looper", None)
|
||
|
||
@staticmethod
|
||
def loop():
|
||
"""运行消息循环,处理到期的消息"""
|
||
looper = Looper.myLooper()
|
||
if looper is None:
|
||
raise RuntimeError("No Looper prepared")
|
||
looper._running = True
|
||
while looper._running:
|
||
msg = looper._next()
|
||
if msg is not None:
|
||
target = msg._target
|
||
if target is not None:
|
||
target.dispatchMessage(msg)
|
||
msg.recycle()
|
||
else:
|
||
# 无到期消息,短暂 sleep 避免忙等
|
||
_time.sleep(0.001)
|
||
|
||
def _next(self) -> Optional[Message]:
|
||
"""非阻塞地取出一条到期消息(when <= now)"""
|
||
with self._queue_lock:
|
||
now = SystemClock.uptimeMillis()
|
||
for i, msg in enumerate(self._message_queue):
|
||
if msg.when <= now:
|
||
return self._message_queue.pop(i)
|
||
return None
|
||
|
||
def _enqueue(self, msg: Message):
|
||
"""将消息按 when 时间顺序入队"""
|
||
with self._queue_lock:
|
||
inserted = False
|
||
for i, m in enumerate(self._message_queue):
|
||
if msg.when < m.when:
|
||
self._message_queue.insert(i, msg)
|
||
inserted = True
|
||
break
|
||
if not inserted:
|
||
self._message_queue.append(msg)
|
||
|
||
def _remove_callbacks(self, callback):
|
||
"""从队列中移除匹配 callback 的消息"""
|
||
with self._queue_lock:
|
||
self._message_queue = [
|
||
m for m in self._message_queue
|
||
if m._callback is not callback
|
||
]
|
||
|
||
def _remove_messages(self, what: int):
|
||
"""从队列中移除匹配 what 的消息"""
|
||
with self._queue_lock:
|
||
self._message_queue = [
|
||
m for m in self._message_queue
|
||
if m.what != what
|
||
]
|
||
|
||
def _has_messages(self, what: int) -> bool:
|
||
"""检查队列中是否有匹配 what 的消息"""
|
||
with self._queue_lock:
|
||
return any(m.what == what for m in self._message_queue)
|
||
|
||
def quit(self):
|
||
"""退出消息循环"""
|
||
if not self._quit_allowed:
|
||
raise RuntimeError("Main Looper is not allowed to quit")
|
||
with self._queue_lock:
|
||
self._running = False
|
||
self._message_queue.clear()
|
||
|
||
def quitSafely(self):
|
||
"""安全退出,仅停止循环(已入队消息不再处理)"""
|
||
with self._queue_lock:
|
||
self._running = False
|
||
|
||
def isCurrentThread(self) -> bool:
|
||
return threading.current_thread() == self._thread
|
||
|
||
def getThread(self) -> threading.Thread:
|
||
return self._thread
|
||
|
||
def getQueue(self):
|
||
return self._message_queue
|
||
|
||
def dump(self, pw: Any, prefix: str):
|
||
pass
|
||
|
||
def toString(self) -> str:
|
||
return f"Looper({self._thread.name})"
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════
|
||
# Handler
|
||
# ═══════════════════════════════════════════════════════════════
|
||
|
||
class Handler(Stub):
|
||
"""Handler — 消息处理器"""
|
||
|
||
def __init__(self, looper: Optional[Looper] = None, callback: Optional[Callable] = None):
|
||
super().__init__()
|
||
self._looper = looper or Looper.getMainLooper()
|
||
self._callback = callback
|
||
self._async = False
|
||
|
||
def handleMessage(self, msg: Message):
|
||
pass
|
||
|
||
def dispatchMessage(self, msg: Message):
|
||
if msg._callback:
|
||
msg._callback()
|
||
elif self._callback:
|
||
self._callback(msg)
|
||
else:
|
||
self.handleMessage(msg)
|
||
|
||
def post(self, r: Callable) -> bool:
|
||
msg = Message.obtain(self)
|
||
msg._callback = r
|
||
return self.sendMessage(msg)
|
||
|
||
def postDelayed(self, r: Callable, delay_ms: int) -> bool:
|
||
msg = Message.obtain(self)
|
||
msg._callback = r
|
||
return self.sendMessageDelayed(msg, delay_ms)
|
||
|
||
def postAtTime(self, r: Callable, uptime_ms: int) -> bool:
|
||
msg = Message.obtain(self)
|
||
msg._callback = r
|
||
msg.when = uptime_ms
|
||
return self.sendMessageAtTime(msg, uptime_ms)
|
||
|
||
def sendMessage(self, msg: Message) -> bool:
|
||
msg._target = self
|
||
looper = self._looper
|
||
if looper is not None and looper._running:
|
||
looper._enqueue(msg)
|
||
return True
|
||
# 当前线程无运行中的 Looper,回退到同步执行(保持兼容性)
|
||
self.dispatchMessage(msg)
|
||
return True
|
||
|
||
def sendEmptyMessage(self, what: int) -> bool:
|
||
msg = Message.obtain(self, what)
|
||
return self.sendMessage(msg)
|
||
|
||
def sendMessageDelayed(self, msg: Message, delay_ms: int) -> bool:
|
||
msg._target = self
|
||
msg.when = SystemClock.uptimeMillis() + delay_ms
|
||
looper = self._looper
|
||
if looper is not None and looper._running:
|
||
looper._enqueue(msg)
|
||
return True
|
||
# 回退到同步执行
|
||
self.dispatchMessage(msg)
|
||
return True
|
||
|
||
def sendMessageAtTime(self, msg: Message, uptime_ms: int) -> bool:
|
||
msg._target = self
|
||
msg.when = uptime_ms
|
||
looper = self._looper
|
||
if looper is not None and looper._running:
|
||
looper._enqueue(msg)
|
||
return True
|
||
self.dispatchMessage(msg)
|
||
return True
|
||
|
||
def sendMessageAtFrontOfQueue(self, msg: Message) -> bool:
|
||
msg._target = self
|
||
msg.when = 0
|
||
looper = self._looper
|
||
if looper is not None and looper._running:
|
||
with looper._queue_lock:
|
||
looper._message_queue.insert(0, msg)
|
||
return True
|
||
self.dispatchMessage(msg)
|
||
return True
|
||
|
||
def removeCallbacks(self, r: Callable):
|
||
looper = self._looper
|
||
if looper is not None:
|
||
looper._remove_callbacks(r)
|
||
|
||
def removeMessages(self, what: int):
|
||
looper = self._looper
|
||
if looper is not None:
|
||
looper._remove_messages(what)
|
||
|
||
def obtainMessage(self, what: int = 0) -> Message:
|
||
return Message.obtain(self, what)
|
||
|
||
def hasMessages(self, what: int) -> bool:
|
||
looper = self._looper
|
||
if looper is not None:
|
||
return looper._has_messages(what)
|
||
return False
|
||
|
||
def getLooper(self) -> Looper:
|
||
return self._looper
|
||
|
||
def toString(self) -> str:
|
||
return f"Handler({self.__class__.__name__})"
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════
|
||
# HandlerThread
|
||
# ═══════════════════════════════════════════════════════════════
|
||
|
||
class HandlerThread(threading.Thread, Stub):
|
||
"""HandlerThread — 带 Looper 的工作线程"""
|
||
|
||
def __init__(self, name: str, priority: int = Process.THREAD_PRIORITY_DEFAULT):
|
||
super().__init__()
|
||
Stub.__init__(self)
|
||
self._name = name
|
||
self._priority = priority
|
||
self._looper: Optional[Looper] = None
|
||
self._started = threading.Event()
|
||
|
||
def run(self):
|
||
Looper.prepare()
|
||
self._looper = Looper.myLooper()
|
||
self._started.set()
|
||
Looper.loop()
|
||
|
||
def getLooper(self) -> Optional[Looper]:
|
||
self._started.wait()
|
||
return self._looper
|
||
|
||
def getThreadId(self) -> int:
|
||
return self.ident or 0
|
||
|
||
def quit(self) -> bool:
|
||
if self._looper:
|
||
self._looper.quit()
|
||
return True
|
||
return False
|
||
|
||
def quitSafely(self) -> bool:
|
||
return self.quit()
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════
|
||
# AsyncTask Executor (守护线程池)
|
||
# ═══════════════════════════════════════════════════════════════
|
||
|
||
class _AsyncTaskExecutor:
|
||
"""守护线程池执行器 — 用于 AsyncTask 后台执行,不阻塞程序退出。
|
||
|
||
使用 BoundedSemaphore 限制并发数,每个任务在独立的守护线程中运行。
|
||
"""
|
||
|
||
def __init__(self, max_workers: int = 4):
|
||
self._max_workers = max_workers
|
||
self._semaphore = threading.BoundedSemaphore(max_workers)
|
||
self._shutdown_flag = False
|
||
|
||
def submit(self, fn, *args, **kwargs):
|
||
if self._shutdown_flag:
|
||
raise RuntimeError("Executor has been shut down")
|
||
self._semaphore.acquire()
|
||
|
||
def _run():
|
||
try:
|
||
fn(*args, **kwargs)
|
||
finally:
|
||
self._semaphore.release()
|
||
|
||
t = threading.Thread(target=_run, daemon=True)
|
||
t.start()
|
||
|
||
def execute(self, command):
|
||
self.submit(command)
|
||
|
||
def shutdown(self, wait: bool = True):
|
||
self._shutdown_flag = True
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════
|
||
# AsyncTask
|
||
# ═══════════════════════════════════════════════════════════════
|
||
|
||
class AsyncTask(Stub):
|
||
"""AsyncTask — 异步任务"""
|
||
|
||
Status = type("Status", (), {"PENDING": 0, "RUNNING": 1, "FINISHED": 2})
|
||
|
||
SERIAL_EXECUTOR = None
|
||
THREAD_POOL_EXECUTOR = None
|
||
|
||
def __init__(self):
|
||
super().__init__()
|
||
self._status = AsyncTask.Status.PENDING
|
||
self._result: Any = None
|
||
self._cancelled = False
|
||
self._handler = Handler(Looper.getMainLooper())
|
||
|
||
def doInBackground(self, *params) -> Any:
|
||
raise NotImplementedError
|
||
|
||
def onPreExecute(self):
|
||
pass
|
||
|
||
def onPostExecute(self, result: Any):
|
||
pass
|
||
|
||
def onProgressUpdate(self, *values):
|
||
pass
|
||
|
||
def onCancelled(self, result: Any = None):
|
||
pass
|
||
|
||
def execute(self, *params) -> "AsyncTask":
|
||
return self.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, *params)
|
||
|
||
def executeOnExecutor(self, exec: Any, *params) -> "AsyncTask":
|
||
self._status = AsyncTask.Status.RUNNING
|
||
self.onPreExecute()
|
||
exec.submit(self._run_background, params)
|
||
return self
|
||
|
||
def _run_background(self, params):
|
||
"""在后台线程中执行 doInBackground,完成后通过 Handler 回调主线程"""
|
||
try:
|
||
self._result = self.doInBackground(*params)
|
||
except Exception as e:
|
||
self._result = e
|
||
self._status = AsyncTask.Status.FINISHED
|
||
if self._cancelled:
|
||
self._handler.post(lambda: self.onCancelled(self._result))
|
||
else:
|
||
self._handler.post(lambda: self.onPostExecute(self._result))
|
||
|
||
def get(self) -> Any:
|
||
return self._result
|
||
|
||
def getStatus(self) -> int:
|
||
return self._status
|
||
|
||
def isCancelled(self) -> bool:
|
||
return self._cancelled
|
||
|
||
def cancel(self, may_interrupt_if_running: bool) -> bool:
|
||
self._cancelled = True
|
||
return True
|
||
|
||
def publishProgress(self, *values):
|
||
self._handler.post(lambda: self.onProgressUpdate(*values))
|
||
|
||
|
||
# 初始化默认执行器
|
||
AsyncTask.THREAD_POOL_EXECUTOR = _AsyncTaskExecutor(max_workers=4)
|
||
AsyncTask.SERIAL_EXECUTOR = _AsyncTaskExecutor(max_workers=1)
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════
|
||
# CountDownTimer
|
||
# ═══════════════════════════════════════════════════════════════
|
||
|
||
class CountDownTimer(Stub):
|
||
"""CountDownTimer — 倒计时"""
|
||
|
||
def __init__(self, millis_in_future: int, count_down_interval: int):
|
||
super().__init__()
|
||
self._millis_in_future = millis_in_future
|
||
self._interval = count_down_interval
|
||
self._cancelled = False
|
||
|
||
def onTick(self, millis_until_finished: int):
|
||
pass
|
||
|
||
def onFinish(self):
|
||
pass
|
||
|
||
def start(self) -> "CountDownTimer":
|
||
self._cancelled = False
|
||
remaining = self._millis_in_future
|
||
while remaining > 0 and not self._cancelled:
|
||
self.onTick(remaining)
|
||
_time.sleep(self._interval / 1000.0)
|
||
remaining -= self._interval
|
||
if not self._cancelled:
|
||
self.onFinish()
|
||
return self
|
||
|
||
def cancel(self):
|
||
self._cancelled = True
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════
|
||
# Messenger
|
||
# ═══════════════════════════════════════════════════════════════
|
||
|
||
class Messenger(Stub):
|
||
"""Messenger — 跨进程消息传递"""
|
||
|
||
def __init__(self, handler: Handler):
|
||
super().__init__()
|
||
self._handler = handler
|
||
|
||
def send(self, message: Message):
|
||
self._handler.sendMessage(message)
|
||
|
||
def getBinder(self):
|
||
return None
|
||
|
||
@staticmethod
|
||
def obtain(handler: Handler) -> "Messenger":
|
||
return Messenger(handler)
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════
|
||
# PowerManager / PowerManagerWakeLock
|
||
# ═══════════════════════════════════════════════════════════════
|
||
|
||
class PowerManager(Stub):
|
||
"""PowerManager — 电源管理"""
|
||
|
||
ACQUIRE_CAUSES_WAKEUP = 0x10000000
|
||
DOZE_WAKE_LOCK = 0x00000040
|
||
DRAW_WAKE_LOCK = 0x00000080
|
||
FULL_WAKE_LOCK = 0x0000001A
|
||
LOCATION_HARDWARE_POWER_MODE = 0x00000080
|
||
ON_AFTER_RELEASE = 0x20000000
|
||
PARTIAL_WAKE_LOCK = 0x00000001
|
||
PROXIMITY_SCREEN_OFF_WAKE_LOCK = 0x00000020
|
||
SCREEN_BRIGHT_WAKE_LOCK = 0x0000000A
|
||
SCREEN_DIM_WAKE_LOCK = 0x00000006
|
||
WAKE_LOCK_LEVEL_MASK = 0x0000FFFF
|
||
|
||
def __init__(self):
|
||
super().__init__()
|
||
self._is_interactive = True
|
||
self._is_power_save = False
|
||
self._is_device_idle = False
|
||
|
||
def newWakeLock(self, level_and_flags: int, tag: str) -> "PowerManagerWakeLock":
|
||
return PowerManagerWakeLock(level_and_flags, tag)
|
||
|
||
def isInteractive(self) -> bool:
|
||
if _USE_LINUX:
|
||
return self.isScreenOn()
|
||
return self._is_interactive
|
||
|
||
def isScreenOn(self) -> bool:
|
||
"""屏幕是否点亮 — 在 Linux 上检查 DISPLAY 环境变量"""
|
||
if _USE_LINUX:
|
||
import os as _os_mod
|
||
display = _os_mod.environ.get("DISPLAY", "")
|
||
if display:
|
||
return True
|
||
# WAYLAND_DISPLAY 作为回退
|
||
wayland = _os_mod.environ.get("WAYLAND_DISPLAY", "")
|
||
return bool(wayland)
|
||
return self._is_interactive
|
||
|
||
def isPowerSaveMode(self) -> bool:
|
||
return self._is_power_save
|
||
|
||
def isDeviceIdleMode(self) -> bool:
|
||
return self._is_device_idle
|
||
|
||
def isIgnoringBatteryOptimizations(self, package_name: str) -> bool:
|
||
return True
|
||
|
||
def isSustainedPerformanceModeSupported(self) -> bool:
|
||
return False
|
||
|
||
def reboot(self, reason: Optional[str] = None):
|
||
pass
|
||
|
||
def goToSleep(self, time_ms: int):
|
||
pass
|
||
|
||
def wakeUp(self, time_ms: int):
|
||
pass
|
||
|
||
def userActivity(self, when: int, no_change_lights: bool = False):
|
||
pass
|
||
|
||
|
||
class PowerManagerWakeLock(Stub):
|
||
"""PowerManagerWakeLock — 唤醒锁"""
|
||
|
||
def __init__(self, level_and_flags: int, tag: str):
|
||
super().__init__()
|
||
self._level_and_flags = level_and_flags
|
||
self._tag = tag
|
||
self._held = False
|
||
self._work_source = None
|
||
self._history_tag = None
|
||
|
||
def acquire(self, timeout_ms: int = 0):
|
||
self._held = True
|
||
sys.stderr.write(f"[PowerManager] acquire wakelock: {self._tag}\n")
|
||
|
||
def acquireLocked(self):
|
||
self._held = True
|
||
|
||
def release(self, flags: int = 0):
|
||
self._held = False
|
||
sys.stderr.write(f"[PowerManager] release wakelock: {self._tag}\n")
|
||
|
||
def isHeld(self) -> bool:
|
||
return self._held
|
||
|
||
def setWorkSource(self, work_source: Any):
|
||
self._work_source = work_source
|
||
|
||
def setReferenceCounted(self, value: bool):
|
||
pass
|
||
|
||
def setHistoryTag(self, tag: str):
|
||
self._history_tag = tag
|
||
|
||
def toString(self) -> str:
|
||
return f"WakeLock({self._tag}, held={self._held})"
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════
|
||
# Vibrator / VibratorEffect
|
||
# ═══════════════════════════════════════════════════════════════
|
||
|
||
class Vibrator(Stub):
|
||
"""Vibrator — 振动器"""
|
||
|
||
def __init__(self):
|
||
super().__init__()
|
||
|
||
def vibrate(self, milliseconds: int):
|
||
sys.stderr.write(f"[Vibrator] vibrate: {milliseconds}ms\n")
|
||
|
||
def vibrate(self, pattern: List[int], repeat: int):
|
||
sys.stderr.write(f"[Vibrator] vibrate pattern: {pattern}, repeat={repeat}\n")
|
||
|
||
def cancel(self):
|
||
sys.stderr.write("[Vibrator] cancel\n")
|
||
|
||
def hasVibrator(self) -> bool:
|
||
return True
|
||
|
||
def hasAmplitudeControl(self) -> bool:
|
||
return False
|
||
|
||
def areEffectsSupported(self, effect_ids: List[int]) -> List[int]:
|
||
return [0] * len(effect_ids)
|
||
|
||
def areAllEffectsSupported(self, effect_ids: List[int]) -> List[int]:
|
||
return [0] * len(effect_ids)
|
||
|
||
|
||
class VibratorEffect(Stub):
|
||
"""VibratorEffect — 振动效果"""
|
||
|
||
def __init__(self, effect_id: int = 0):
|
||
super().__init__()
|
||
self._effect_id = effect_id
|
||
|
||
@staticmethod
|
||
def createOneShot(milliseconds: int, amplitude: int) -> "VibratorEffect":
|
||
return VibratorEffect(0)
|
||
|
||
@staticmethod
|
||
def createWaveform(pattern: List[int], repeat: int) -> "VibratorEffect":
|
||
return VibratorEffect(0)
|
||
|
||
@staticmethod
|
||
def createPredefined(effect_id: int) -> "VibratorEffect":
|
||
return VibratorEffect(effect_id)
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════
|
||
# BatteryManager
|
||
# ═══════════════════════════════════════════════════════════════
|
||
|
||
class BatteryManager(Stub):
|
||
"""BatteryManager — 电池管理"""
|
||
|
||
BATTERY_PROPERTY_CHARGE_COUNTER = 1
|
||
BATTERY_PROPERTY_CURRENT_NOW = 2
|
||
BATTERY_PROPERTY_CURRENT_AVERAGE = 3
|
||
BATTERY_PROPERTY_CAPACITY = 4
|
||
BATTERY_PROPERTY_STATUS = 5
|
||
BATTERY_PROPERTY_ENERGY_COUNTER = 6
|
||
BATTERY_PROPERTY_BATTERY_NOW = 7
|
||
BATTERY_PROPERTY_BATTERY_LOW = 8
|
||
|
||
BATTERY_STATUS_UNKNOWN = 1
|
||
BATTERY_STATUS_CHARGING = 2
|
||
BATTERY_STATUS_DISCHARGING = 3
|
||
BATTERY_STATUS_NOT_CHARGING = 4
|
||
BATTERY_STATUS_FULL = 5
|
||
|
||
BATTERY_HEALTH_UNKNOWN = 1
|
||
BATTERY_HEALTH_GOOD = 2
|
||
BATTERY_HEALTH_OVERHEAT = 3
|
||
BATTERY_HEALTH_DEAD = 4
|
||
BATTERY_HEALTH_OVER_VOLTAGE = 5
|
||
BATTERY_HEALTH_UNSPECIFIED_FAILURE = 6
|
||
BATTERY_HEALTH_COLD = 7
|
||
|
||
EXTRA_STATUS = "status"
|
||
EXTRA_HEALTH = "health"
|
||
EXTRA_PRESENT = "present"
|
||
EXTRA_LEVEL = "level"
|
||
EXTRA_SCALE = "scale"
|
||
EXTRA_ICON_SMALL = "icon-small"
|
||
EXTRA_PLUGGED = "plugged"
|
||
EXTRA_VOLTAGE = "voltage"
|
||
EXTRA_TEMPERATURE = "temperature"
|
||
EXTRA_TECHNOLOGY = "technology"
|
||
EXTRA_BATTERY_LOW = "battery_low"
|
||
EXTRA_CHARGE_COUNTER = "charge_counter"
|
||
|
||
BATTERY_PLUGGED_AC = 1
|
||
BATTERY_PLUGGED_USB = 2
|
||
BATTERY_PLUGGED_WIRELESS = 4
|
||
|
||
def __init__(self):
|
||
super().__init__()
|
||
self._capacity = 85
|
||
self._status = self.BATTERY_STATUS_CHARGING
|
||
self._health = self.BATTERY_HEALTH_GOOD
|
||
self._present = True
|
||
self._level = 85
|
||
self._plugged = self.BATTERY_PLUGGED_AC
|
||
self._voltage = 4200
|
||
self._temperature = 250
|
||
self._technology = "Li-ion"
|
||
self._charge_counter = 3500000
|
||
self._current_now = -500000
|
||
self._current_avg = -450000
|
||
self._energy_counter = 15000000
|
||
if _USE_LINUX:
|
||
self._refresh_from_linux()
|
||
|
||
_LINUX_STATUS_MAP = {
|
||
"charging": 2, # BATTERY_STATUS_CHARGING
|
||
"discharging": 3, # BATTERY_STATUS_DISCHARGING
|
||
"not charging": 4, # BATTERY_STATUS_NOT_CHARGING
|
||
"full": 5, # BATTERY_STATUS_FULL
|
||
"unknown": 1, # BATTERY_STATUS_UNKNOWN
|
||
}
|
||
|
||
def _get_linux_battery_info(self) -> dict:
|
||
"""从 LinuxPower 获取真实电池信息"""
|
||
try:
|
||
return LinuxPower.get_battery_info() or {}
|
||
except Exception:
|
||
return {}
|
||
|
||
def _refresh_from_linux(self):
|
||
"""用 Linux 真实电池信息刷新内部状态"""
|
||
info = self._get_linux_battery_info()
|
||
if not info:
|
||
return
|
||
self._present = bool(info.get("present", False))
|
||
level = info.get("level", -1)
|
||
if self._present and isinstance(level, (int, float)) and level >= 0:
|
||
self._level = int(level)
|
||
self._capacity = int(level)
|
||
else:
|
||
# 无电池或电量未知
|
||
self._level = -1
|
||
self._capacity = -1
|
||
status_str = str(info.get("status", "unknown")).lower()
|
||
self._status = self._LINUX_STATUS_MAP.get(status_str, self.BATTERY_STATUS_UNKNOWN)
|
||
voltage = info.get("voltage", 0)
|
||
if isinstance(voltage, (int, float)):
|
||
# LinuxPower 返回伏特, Android 期望毫伏
|
||
self._voltage = int(voltage * 1000)
|
||
temperature = info.get("temperature", 0)
|
||
if isinstance(temperature, (int, float)):
|
||
# LinuxPower 返回摄氏度, Android 期望十分之一度
|
||
self._temperature = int(temperature * 10)
|
||
self._technology = info.get("technology", "") or "Li-ion"
|
||
# 充电状态决定 plugged
|
||
if self._status == self.BATTERY_STATUS_CHARGING:
|
||
self._plugged = self.BATTERY_PLUGGED_AC
|
||
else:
|
||
self._plugged = 0
|
||
|
||
def getIntProperty(self, prop_id: int) -> int:
|
||
if _USE_LINUX:
|
||
self._refresh_from_linux()
|
||
if prop_id == self.BATTERY_PROPERTY_CAPACITY:
|
||
return self._capacity
|
||
if prop_id == self.BATTERY_PROPERTY_STATUS:
|
||
return self._status
|
||
if prop_id == self.BATTERY_PROPERTY_CHARGE_COUNTER:
|
||
return self._charge_counter
|
||
if prop_id == self.BATTERY_PROPERTY_CURRENT_NOW:
|
||
return self._current_now
|
||
if prop_id == self.BATTERY_PROPERTY_CURRENT_AVERAGE:
|
||
return self._current_avg
|
||
if prop_id == self.BATTERY_PROPERTY_ENERGY_COUNTER:
|
||
return self._energy_counter
|
||
return -1
|
||
|
||
def getLongProperty(self, prop_id: int) -> int:
|
||
return self.getIntProperty(prop_id)
|
||
|
||
def isCharging(self) -> bool:
|
||
if _USE_LINUX:
|
||
self._refresh_from_linux()
|
||
return self._status == self.BATTERY_STATUS_CHARGING
|
||
|
||
def isPresent(self) -> bool:
|
||
"""电池是否存在"""
|
||
if _USE_LINUX:
|
||
self._refresh_from_linux()
|
||
return self._present
|
||
|
||
def getLevel(self) -> int:
|
||
"""电池电量 (0-100, -1 表示未知)"""
|
||
if _USE_LINUX:
|
||
self._refresh_from_linux()
|
||
return self._level
|
||
|
||
def getVoltage(self) -> int:
|
||
"""电池电压 (毫伏)"""
|
||
if _USE_LINUX:
|
||
self._refresh_from_linux()
|
||
return self._voltage
|
||
|
||
def getTemperature(self) -> int:
|
||
"""电池温度 (十分之一摄氏度)"""
|
||
if _USE_LINUX:
|
||
self._refresh_from_linux()
|
||
return self._temperature
|
||
|
||
def getTechnology(self) -> str:
|
||
"""电池技术"""
|
||
if _USE_LINUX:
|
||
self._refresh_from_linux()
|
||
return self._technology
|
||
|
||
def getPlugType(self) -> int:
|
||
"""充电类型"""
|
||
if _USE_LINUX:
|
||
self._refresh_from_linux()
|
||
return self._plugged
|
||
|
||
def getStatus(self) -> int:
|
||
"""电池状态"""
|
||
if _USE_LINUX:
|
||
self._refresh_from_linux()
|
||
return self._status
|
||
|
||
def computeChargeTimeRemaining(self) -> int:
|
||
if _USE_LINUX:
|
||
# 无电池或已充满时返回 -1
|
||
if not self._present or self._status == self.BATTERY_STATUS_FULL:
|
||
return -1
|
||
return 1800000 # 30 minutes
|
||
|
||
def toString(self) -> str:
|
||
return f"BatteryManager(level={self._level}%, charging={self.isCharging()})"
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════
|
||
# 异常类
|
||
# ═══════════════════════════════════════════════════════════════
|
||
|
||
class NetworkOnMainThreadException(RuntimeError):
|
||
"""网络操作在主线程上执行"""
|
||
pass
|
||
|
||
|
||
class TransactionTooLargeException(RuntimeError):
|
||
"""事务数据太大"""
|
||
pass
|
||
|
||
|
||
class OperationCanceledException(RuntimeError):
|
||
"""操作被取消"""
|
||
pass
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════
|
||
# 其他工具类
|
||
# ═══════════════════════════════════════════════════════════════
|
||
|
||
class ConditionVariable(Stub):
|
||
"""ConditionVariable — 条件变量"""
|
||
|
||
def __init__(self):
|
||
super().__init__()
|
||
self._cond = threading.Condition()
|
||
self._open = False
|
||
|
||
def block(self, timeout_ms: int = 0):
|
||
with self._cond:
|
||
if timeout_ms <= 0:
|
||
while not self._open:
|
||
self._cond.wait()
|
||
else:
|
||
if not self._open:
|
||
self._cond.wait(timeout_ms / 1000.0)
|
||
|
||
def open(self):
|
||
with self._cond:
|
||
self._open = True
|
||
self._cond.notify_all()
|
||
|
||
def close(self):
|
||
with self._cond:
|
||
self._open = False
|
||
|
||
|
||
class Semaphore(Stub):
|
||
"""Semaphore — 信号量"""
|
||
|
||
def __init__(self, permits: int):
|
||
super().__init__()
|
||
self._sem = threading.Semaphore(permits)
|
||
|
||
def acquire(self):
|
||
self._sem.acquire()
|
||
|
||
def tryAcquire(self) -> bool:
|
||
return self._sem.acquire(blocking=False)
|
||
|
||
def release(self):
|
||
self._sem.release()
|
||
|
||
def availablePermits(self) -> int:
|
||
return self._sem._value
|
||
|
||
|
||
class TokenWatcher(Stub):
|
||
"""TokenWatcher — 令牌监视器"""
|
||
|
||
def __init__(self, handler: Handler, tag: str):
|
||
super().__init__()
|
||
|
||
def acquire(self, token: Any, tag: str):
|
||
pass
|
||
|
||
def release(self, token: Any):
|
||
pass
|
||
|
||
def isAcquired(self) -> bool:
|
||
return False
|
||
|
||
def dump(self):
|
||
pass
|
||
|
||
|
||
class WorkSource(Stub):
|
||
"""WorkSource — 工作来源"""
|
||
|
||
def __init__(self, uid: int = 0):
|
||
super().__init__()
|
||
self._uids: List[int] = [uid] if uid else []
|
||
|
||
def add(self, uid: int):
|
||
if uid not in self._uids:
|
||
self._uids.append(uid)
|
||
|
||
def remove(self, uid: int):
|
||
if uid in self._uids:
|
||
self._uids.remove(uid)
|
||
|
||
def get(self, index: int) -> int:
|
||
if 0 <= index < len(self._uids):
|
||
return self._uids[index]
|
||
return 0
|
||
|
||
def size(self) -> int:
|
||
return len(self._uids)
|
||
|
||
def clear(self):
|
||
self._uids.clear()
|
||
|
||
def set(self, ws: "WorkSource"):
|
||
if ws:
|
||
self._uids = list(ws._uids)
|
||
|
||
def toString(self) -> str:
|
||
return f"WorkSource({self._uids})"
|
||
|
||
|
||
class UserHandle(Stub):
|
||
"""UserHandle — 用户句柄"""
|
||
|
||
USER_ALL = -1
|
||
USER_CURRENT = -2
|
||
USER_CURRENT_OR_SELF = -3
|
||
USER_NULL = -10000
|
||
USER_OWNER = 0
|
||
USER_SYSTEM = 0
|
||
|
||
def __init__(self, user_id: int = 0):
|
||
super().__init__()
|
||
self._user_id = user_id
|
||
|
||
@staticmethod
|
||
def myUserId() -> int:
|
||
return 0
|
||
|
||
@staticmethod
|
||
def getUserHandleForUid(uid: int) -> "UserHandle":
|
||
return UserHandle(uid // 100000)
|
||
|
||
@staticmethod
|
||
def getUserId(uid: int) -> int:
|
||
return uid // 100000
|
||
|
||
@staticmethod
|
||
def getUid(user_id: int, app_id: int) -> int:
|
||
return user_id * 100000 + app_id
|
||
|
||
@staticmethod
|
||
def getAppId(uid: int) -> int:
|
||
return uid % 100000
|
||
|
||
def getIdentifier(self) -> int:
|
||
return self._user_id
|
||
|
||
def hashCode(self) -> int:
|
||
return self._user_id
|
||
|
||
def toString(self) -> str:
|
||
return f"UserHandle({self._user_id})"
|
||
|
||
|
||
class DropBoxManager(Stub):
|
||
"""DropBoxManager — 系统日志收集"""
|
||
|
||
IS_EMPTY = 0
|
||
IS_TEXT = 1
|
||
IS_GZIPPED = 2
|
||
|
||
class Entry(Stub):
|
||
def __init__(self, tag: str, milliseconds: int):
|
||
super().__init__()
|
||
self._tag = tag
|
||
self._millis = milliseconds
|
||
self._text = ""
|
||
|
||
def getTag(self) -> str:
|
||
return self._tag
|
||
|
||
def getTimeMillis(self) -> int:
|
||
return self._millis
|
||
|
||
def getText(self, max_bytes: int) -> Optional[str]:
|
||
return self._text
|
||
|
||
def getFlags(self, max_bytes: int) -> int:
|
||
return DropBoxManager.IS_TEXT
|
||
|
||
def close(self):
|
||
pass
|
||
|
||
def __init__(self):
|
||
super().__init__()
|
||
self._entries: List[DropBoxManager.Entry] = []
|
||
|
||
def addText(self, tag: str, text: str):
|
||
entry = DropBoxManager.Entry(tag, int(_time.time() * 1000))
|
||
entry._text = text
|
||
self._entries.append(entry)
|
||
|
||
def getNextEntry(self, tag: str, milliseconds: int) -> Optional[Entry]:
|
||
for entry in self._entries:
|
||
if entry.getTimeMillis() > milliseconds:
|
||
if not tag or entry.getTag() == tag:
|
||
return entry
|
||
return None
|
||
|
||
def dump(self, pw: Any, prefix: str):
|
||
pass
|
||
|
||
|
||
class FileObserver(Stub):
|
||
"""FileObserver — 文件监听"""
|
||
|
||
ACCESS = 0x00000001
|
||
MODIFY = 0x00000002
|
||
ATTRIB = 0x00000004
|
||
CLOSE_WRITE = 0x00000008
|
||
CLOSE_NOWRITE = 0x00000010
|
||
OPEN = 0x00000020
|
||
MOVED_FROM = 0x00000040
|
||
MOVED_TO = 0x00000080
|
||
CREATE = 0x00000100
|
||
DELETE = 0x00000200
|
||
DELETE_SELF = 0x00000400
|
||
MOVE_SELF = 0x00000800
|
||
ALL_EVENTS = 0x00000FFF
|
||
|
||
def __init__(self, path: str):
|
||
super().__init__()
|
||
self._path = path
|
||
self._watching = False
|
||
|
||
def startWatching(self):
|
||
self._watching = True
|
||
|
||
def stopWatching(self):
|
||
self._watching = False
|
||
|
||
def onEvent(self, event: int, path: Optional[str]):
|
||
pass
|
||
|
||
def finalize(self):
|
||
pass
|
||
|
||
|
||
class FileUtils(Stub):
|
||
"""FileUtils — 文件工具"""
|
||
|
||
@staticmethod
|
||
def readFileToString(path):
|
||
try:
|
||
with open(path, "r", encoding="utf-8") as f:
|
||
return f.read()
|
||
except Exception:
|
||
return ""
|
||
|
||
@staticmethod
|
||
def stringToFile(path, content):
|
||
try:
|
||
with open(path, "w", encoding="utf-8") as f:
|
||
f.write(content)
|
||
return True
|
||
except Exception:
|
||
return False
|
||
|
||
@staticmethod
|
||
def copyFile(src, dst):
|
||
try:
|
||
import shutil
|
||
shutil.copy2(src, dst)
|
||
return True
|
||
except Exception:
|
||
return False
|
||
|
||
@staticmethod
|
||
def deleteDirectory(path):
|
||
try:
|
||
import shutil
|
||
shutil.rmtree(path)
|
||
return True
|
||
except Exception:
|
||
return False
|
||
|
||
@staticmethod
|
||
def deleteContents(path):
|
||
try:
|
||
import shutil
|
||
for entry in _os.listdir(path):
|
||
full = _os.path.join(path, entry)
|
||
if _os.path.isdir(full):
|
||
shutil.rmtree(full)
|
||
else:
|
||
_os.remove(full)
|
||
return True
|
||
except Exception:
|
||
return False
|
||
|
||
@staticmethod
|
||
def deleteOlderFiles(dir_: Any, min_count: int, min_age_ms: int):
|
||
pass
|
||
|
||
|
||
class PatternMatcher(Stub):
|
||
"""PatternMatcher — 模式匹配器"""
|
||
|
||
PATTERN_LITERAL = 0
|
||
PATTERN_PREFIX = 1
|
||
PATTERN_SIMPLE_GLOB = 2
|
||
PATTERN_ADVANCED_GLOB = 3
|
||
PATTERN_SUFFIX = 4
|
||
|
||
def __init__(self, pattern: str, type_: int):
|
||
super().__init__()
|
||
self._pattern = pattern
|
||
self._type = type_
|
||
|
||
def getPath(self) -> str:
|
||
return self._pattern
|
||
|
||
def getType(self) -> int:
|
||
return self._type
|
||
|
||
def match(self, str_: str) -> bool:
|
||
if self._type == self.PATTERN_LITERAL:
|
||
return str_ == self._pattern
|
||
if self._type == self.PATTERN_PREFIX:
|
||
return str_.startswith(self._pattern)
|
||
if self._type == self.PATTERN_SUFFIX:
|
||
return str_.endswith(self._pattern)
|
||
if self._type == self.PATTERN_SIMPLE_GLOB:
|
||
import fnmatch
|
||
return fnmatch.fnmatch(str_, self._pattern)
|
||
return str_ == self._pattern
|
||
|
||
def toString(self) -> str:
|
||
return f"PatternMatcher({self._pattern}, type={self._type})"
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════
|
||
# __all__
|
||
# ═══════════════════════════════════════════════════════════════
|
||
|
||
__all__ = [
|
||
"Bundle",
|
||
"BaseBundle",
|
||
"PersistableBundle",
|
||
"Handler",
|
||
"Looper",
|
||
"Message",
|
||
"HandlerThread",
|
||
"PowerManager",
|
||
"PowerManagerWakeLock",
|
||
"Vibrator",
|
||
"VibratorEffect",
|
||
"Build",
|
||
"SystemClock",
|
||
"Environment",
|
||
"Parcel",
|
||
"Parcelable",
|
||
"Process",
|
||
"AsyncTask",
|
||
"CountDownTimer",
|
||
"Messenger",
|
||
"BatteryManager",
|
||
"PatternMatcher",
|
||
"ConditionVariable",
|
||
"Semaphore",
|
||
"TokenWatcher",
|
||
"WorkSource",
|
||
"UserHandle",
|
||
"DropBoxManager",
|
||
"FileObserver",
|
||
"FileUtils",
|
||
"NetworkOnMainThreadException",
|
||
"TransactionTooLargeException",
|
||
"OperationCanceledException",
|
||
] |