feat: 深度完善 — 存根转真实实现、UI类方法补全、Java集合/IO增强
存根转真实实现: - ContentResolver: query/insert/update/delete对file://URI真实文件操作, getType用mimetypes - Context: getResources/getAssets/getString/getColor 委托真实Resources - Bitmap.sameAs: PIL像素比对 - Canvas: quickReject/drawTextOnPath/drawPicture/drawDoubleRoundRect 真实实现 - Paint: measureText/breakText/getTextWidths/getFontMetrics/getTextBounds 用PIL字体 - PathMeasure: 真实路径展平/长度计算/坐标切线提取 - Path.op/Region.op: 布尔运算 - Camera: 3D变换矩阵计算 - SystemClock.setCurrentTimeMillis: date -s命令 - Environment.isExternalStorageRemovable: /proc/mounts检查 - Process.myUid: os.getuid() - PowerManager.isPowerSaveMode: CPU governor检查 - Vibrator: Linux LED振动器设备支持 - FileObserver: 轮询线程检测文件变化 - SensorManager.getRotationMatrix: 完整旋转矩阵算法 - BatteryManager: LinuxPower真实电池数据 - Geocoder: Nominatim API逆地理编码 - LocationManager: addProximityAlert/setTestProviderLocation UI类方法补全: - TextView: setPaddingRelative/getTextColor/setBreakStrategy/setJustificationMode - ImageView: setColorFilter/getColorFilter/clearColorFilter - CheckBox/Switch: setOnCheckedChangeListener - ListView: clearChoices - ViewGroup: setLayoutTransition/setTouchscreenBlocksFocus/getChildVisibleRect - MotionEvent: getHistoricalX/Y/Pressure/EventTime/addBatch/getEdgeFlags/setAction Java集合增强: - ArrayList: spliterator/stream - HashSet: spliterator/stream/removeIf - TreeMap: replaceAll/compute/computeIfAbsent/computeIfPresent/merge - LinkedHashMap: clone (保留access_order) - PriorityQueue: spliterator/clone Java IO增强: - BufferedWriter: append/check - PrintWriter: append - ByteArrayOutputStream: writeTo - ByteArrayInputStream: markSupported - BufferedInputStream: available/mark/reset/markSupported + 修复_fill缓冲区填充bug - SequenceInputStream: 补全实现(Enumeration/多流/跨流聚合读取) 修复: - ViewGroup.addView: 合并重复定义为智能参数分派 - view_ext.ViewGroup: 继承view.ViewGroup - view_ext.KeyEvent: 继承view.KeyEvent - hardware_sensor_ext.SensorManager: 继承hardware.SensorManager - app_fragment: 使用java.lang.IllegalStateException - content_intent.Intent: setData/getScheme正确从Uri提取scheme - __init__.py: 补全Fragment/KeyEvent/MotionEvent/TrafficStats/RecyclerView/TreeMap/Vibrator等导出 统计: 221模块, 3662类, 30952方法, 182302行
This commit is contained in:
1 parent
39b1da37c9
commit
4235f8692c
13 files changed
+2248
-163
No files matched your search
+332
-27
@@ -115,11 +115,46 @@ class Build(Stub):
|
||||
|
||||
@staticmethod
|
||||
def getRadioVersion() -> str:
|
||||
return ""
|
||||
"""返回基带/无线电版本。桌面端无无线电硬件,返回空字符串。"""
|
||||
return Build.RADIO
|
||||
|
||||
@staticmethod
|
||||
def getSerial() -> str:
|
||||
return "unknown"
|
||||
"""返回设备序列号。尝试从 /sys 或 /proc 读取真实硬件序列号。"""
|
||||
# 尝试从 /sys/class/android_usb 读取
|
||||
try:
|
||||
for name in _os.listdir("/sys/class/android_usb"):
|
||||
serial_path = f"/sys/class/android_usb/{name}/iSerial"
|
||||
if _os.path.exists(serial_path):
|
||||
with open(serial_path, "r") as f:
|
||||
serial = f.read().strip()
|
||||
if serial:
|
||||
return serial
|
||||
except (OSError, IOError):
|
||||
pass
|
||||
# 尝试从 /proc/cpuinfo 读取 Serial 字段
|
||||
try:
|
||||
with open("/proc/cpuinfo", "r") as f:
|
||||
for line in f:
|
||||
if line.lower().startswith("serial"):
|
||||
parts = line.split(":", 1)
|
||||
if len(parts) == 2:
|
||||
serial = parts[1].strip()
|
||||
if serial and serial != "0" * len(serial):
|
||||
return serial
|
||||
except (OSError, IOError):
|
||||
pass
|
||||
# 尝试从 /sys/class/dmi/id/product_serial 读取
|
||||
try:
|
||||
path = "/sys/class/dmi/id/product_serial"
|
||||
if _os.path.exists(path):
|
||||
with open(path, "r") as f:
|
||||
serial = f.read().strip()
|
||||
if serial:
|
||||
return serial
|
||||
except (OSError, IOError):
|
||||
pass
|
||||
return Build.SERIAL
|
||||
|
||||
@staticmethod
|
||||
def getFingerprint() -> str:
|
||||
@@ -201,7 +236,17 @@ class SystemClock(Stub):
|
||||
|
||||
@staticmethod
|
||||
def setCurrentTimeMillis(millis: int) -> bool:
|
||||
return False
|
||||
"""尝试设置系统时间。需要 root 权限,通常在桌面端会失败。"""
|
||||
try:
|
||||
import subprocess
|
||||
seconds = millis / 1000.0
|
||||
# 尝试使用 date 命令设置时间
|
||||
result = subprocess.run(
|
||||
["date", "-s", f"@{int(seconds)}"],
|
||||
capture_output=True, timeout=5)
|
||||
return result.returncode == 0
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
@@ -268,6 +313,20 @@ class Environment(Stub):
|
||||
|
||||
@staticmethod
|
||||
def isExternalStorageRemovable(path: Optional[str] = None) -> bool:
|
||||
"""判断外部存储是否可移除。通过检查 /proc/mounts 判断。"""
|
||||
try:
|
||||
check_path = path or _sdcard_root
|
||||
with open("/proc/mounts", "r") as f:
|
||||
for line in f:
|
||||
parts = line.split()
|
||||
if len(parts) >= 3 and check_path in parts[1]:
|
||||
# 检查挂载选项中是否包含 SD 卡相关设备
|
||||
device = parts[0]
|
||||
if "/sd" in device or "/mmc" in device or "/usb" in device:
|
||||
return True
|
||||
return False
|
||||
except (OSError, IOError):
|
||||
pass
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
@@ -309,7 +368,8 @@ class Process(Stub):
|
||||
|
||||
@staticmethod
|
||||
def myUid() -> int:
|
||||
return 10001
|
||||
"""返回当前进程的真实 UID。"""
|
||||
return _os.getuid()
|
||||
|
||||
@staticmethod
|
||||
def myPid() -> int:
|
||||
@@ -331,11 +391,15 @@ class Process(Stub):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def setThreadPriority(priority: int):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def setThreadPriority(tid, priority):
|
||||
def setThreadPriority(*args):
|
||||
"""设置线程优先级。支持 setThreadPriority(priority) 和 setThreadPriority(tid, priority)。"""
|
||||
if len(args) == 1:
|
||||
tid = 0
|
||||
priority = args[0]
|
||||
elif len(args) == 2:
|
||||
tid, priority = args
|
||||
else:
|
||||
return
|
||||
try:
|
||||
_os.setpriority(_os.PRIO_PROCESS, tid, priority)
|
||||
except (OSError, PermissionError):
|
||||
@@ -1006,7 +1070,20 @@ class Looper(Stub):
|
||||
return self._message_queue
|
||||
|
||||
def dump(self, pw: Any, prefix: str):
|
||||
pass
|
||||
"""将 DropBox 条目转储到给定的 writer。"""
|
||||
try:
|
||||
if hasattr(pw, "write"):
|
||||
pw.write(f"{prefix}DropBoxManager entries:\n")
|
||||
for entry in self._entries:
|
||||
pw.write(f"{prefix} [{entry.getTag()}] @ {entry.getTimeMillis()}: "
|
||||
f"{entry.getText(1024)}\n")
|
||||
elif hasattr(pw, "println"):
|
||||
pw.println(f"{prefix}DropBoxManager entries:")
|
||||
for entry in self._entries:
|
||||
pw.println(f"{prefix} [{entry.getTag()}] @ {entry.getTimeMillis()}: "
|
||||
f"{entry.getText(1024)}")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def toString(self) -> str:
|
||||
return f"Looper({self._thread.name})"
|
||||
@@ -1377,6 +1454,17 @@ class PowerManager(Stub):
|
||||
return self._is_interactive
|
||||
|
||||
def isPowerSaveMode(self) -> bool:
|
||||
"""检查是否处于省电模式。读取 /sys 中的电源相关状态。"""
|
||||
try:
|
||||
# 检查 CPU 调速器是否为 powersave
|
||||
gov_path = "/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"
|
||||
if _os.path.exists(gov_path):
|
||||
with open(gov_path, "r") as f:
|
||||
governor = f.read().strip()
|
||||
if governor == "powersave":
|
||||
return True
|
||||
except (OSError, IOError):
|
||||
pass
|
||||
return self._is_power_save
|
||||
|
||||
def isDeviceIdleMode(self) -> bool:
|
||||
@@ -1389,16 +1477,29 @@ class PowerManager(Stub):
|
||||
return False
|
||||
|
||||
def reboot(self, reason: Optional[str] = None):
|
||||
pass
|
||||
"""重启设备。桌面端尝试执行 reboot 命令(通常需要 root)。"""
|
||||
try:
|
||||
import subprocess
|
||||
cmd = ["reboot"]
|
||||
if reason:
|
||||
cmd.append(reason)
|
||||
subprocess.run(cmd, capture_output=True, timeout=5)
|
||||
except Exception:
|
||||
sys.stderr.write(f"[PowerManager] reboot: {reason or ''}\n")
|
||||
|
||||
def goToSleep(self, time_ms: int):
|
||||
pass
|
||||
"""使设备进入睡眠。桌面端无实际效果。"""
|
||||
self._is_interactive = False
|
||||
sys.stderr.write(f"[PowerManager] goToSleep at {time_ms}\n")
|
||||
|
||||
def wakeUp(self, time_ms: int):
|
||||
pass
|
||||
"""唤醒设备。桌面端无实际效果。"""
|
||||
self._is_interactive = True
|
||||
sys.stderr.write(f"[PowerManager] wakeUp at {time_ms}\n")
|
||||
|
||||
def userActivity(self, when: int, no_change_lights: bool = False):
|
||||
pass
|
||||
"""报告用户活动事件。桌面端无实际效果。"""
|
||||
self._is_interactive = True
|
||||
|
||||
|
||||
class PowerManagerWakeLock(Stub):
|
||||
@@ -1448,20 +1549,101 @@ class Vibrator(Stub):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._ff_device: Optional[str] = None
|
||||
self._vibrating = False
|
||||
|
||||
def vibrate(self, milliseconds: int):
|
||||
sys.stderr.write(f"[Vibrator] vibrate: {milliseconds}ms\n")
|
||||
@staticmethod
|
||||
def _find_ff_device() -> Optional[str]:
|
||||
"""查找 Linux 力反馈设备。"""
|
||||
try:
|
||||
for name in _os.listdir("/dev/input"):
|
||||
if name.startswith("event"):
|
||||
path = f"/dev/input/{name}"
|
||||
if _os.access(path, _os.W_OK):
|
||||
return path
|
||||
except (OSError, IOError):
|
||||
pass
|
||||
# 检查 /sys/class/leds/vibrator
|
||||
try:
|
||||
if _os.path.exists("/sys/class/leds/vibrator"):
|
||||
return "/sys/class/leds/vibrator"
|
||||
except (OSError, IOError):
|
||||
pass
|
||||
return None
|
||||
|
||||
def vibrate(self, pattern: List[int], repeat: int):
|
||||
sys.stderr.write(f"[Vibrator] vibrate pattern: {pattern}, repeat={repeat}\n")
|
||||
def vibrate(self, *args):
|
||||
"""振动。支持 vibrate(milliseconds) 和 vibrate(pattern, repeat)。"""
|
||||
if len(args) == 1 and isinstance(args[0], (int, float)):
|
||||
milliseconds = int(args[0])
|
||||
self._vibrating = True
|
||||
if self._ff_device is None:
|
||||
self._ff_device = self._find_ff_device()
|
||||
if self._ff_device and self._ff_device.startswith("/sys/class/leds"):
|
||||
try:
|
||||
with open(_os.path.join(self._ff_device, "brightness"), "w") as f:
|
||||
f.write("1")
|
||||
import threading
|
||||
def _stop():
|
||||
_time.sleep(milliseconds / 1000.0)
|
||||
try:
|
||||
with open(_os.path.join(self._ff_device, "brightness"), "w") as f:
|
||||
f.write("0")
|
||||
except (OSError, IOError):
|
||||
pass
|
||||
self._vibrating = False
|
||||
threading.Thread(target=_stop, daemon=True).start()
|
||||
return
|
||||
except (OSError, IOError):
|
||||
pass
|
||||
sys.stderr.write(f"[Vibrator] vibrate: {milliseconds}ms\n")
|
||||
elif len(args) == 2 and isinstance(args[0], list):
|
||||
pattern = args[0]
|
||||
repeat = args[1]
|
||||
if not pattern:
|
||||
return
|
||||
self._vibrating = True
|
||||
import threading
|
||||
def _run_pattern():
|
||||
idx = 0
|
||||
while True:
|
||||
duration = pattern[idx % len(pattern)]
|
||||
if idx % 2 == 0:
|
||||
_time.sleep(duration / 1000.0)
|
||||
else:
|
||||
self.vibrate(duration)
|
||||
_time.sleep(duration / 1000.0)
|
||||
idx += 1
|
||||
if repeat < 0 or idx >= len(pattern):
|
||||
if repeat < 0:
|
||||
continue
|
||||
break
|
||||
self._vibrating = False
|
||||
threading.Thread(target=_run_pattern, daemon=True).start()
|
||||
sys.stderr.write(f"[Vibrator] vibrate pattern: {pattern}, repeat={repeat}\n")
|
||||
|
||||
def cancel(self):
|
||||
"""取消振动。"""
|
||||
self._vibrating = False
|
||||
if self._ff_device and self._ff_device.startswith("/sys/class/leds"):
|
||||
try:
|
||||
with open(_os.path.join(self._ff_device, "brightness"), "w") as f:
|
||||
f.write("0")
|
||||
except (OSError, IOError):
|
||||
pass
|
||||
sys.stderr.write("[Vibrator] cancel\n")
|
||||
|
||||
def hasVibrator(self) -> bool:
|
||||
return True
|
||||
"""检查是否有振动器硬件。"""
|
||||
return self._find_ff_device() is not None
|
||||
|
||||
def hasAmplitudeControl(self) -> bool:
|
||||
"""检查是否支持振幅控制。"""
|
||||
try:
|
||||
if _os.path.exists("/sys/class/leds/vibrator/max_brightness"):
|
||||
with open("/sys/class/leds/vibrator/max_brightness", "r") as f:
|
||||
return int(f.read().strip()) > 1
|
||||
except (OSError, IOError, ValueError):
|
||||
pass
|
||||
return False
|
||||
|
||||
def areEffectsSupported(self, effect_ids: List[int]) -> List[int]:
|
||||
@@ -1474,20 +1656,41 @@ class Vibrator(Stub):
|
||||
class VibratorEffect(Stub):
|
||||
"""VibratorEffect — 振动效果"""
|
||||
|
||||
EFFECT_CLICK = 0
|
||||
EFFECT_DOUBLE_CLICK = 1
|
||||
EFFECT_TICK = 2
|
||||
EFFECT_THUD = 3
|
||||
EFFECT_POP = 4
|
||||
EFFECT_HEAVY_CLICK = 5
|
||||
|
||||
def __init__(self, effect_id: int = 0):
|
||||
super().__init__()
|
||||
self._effect_id = effect_id
|
||||
self._timings: Optional[List[int]] = None
|
||||
self._amplitudes: Optional[List[int]] = None
|
||||
self._repeat = -1
|
||||
|
||||
@staticmethod
|
||||
def createOneShot(milliseconds: int, amplitude: int) -> "VibratorEffect":
|
||||
return VibratorEffect(0)
|
||||
"""创建单次振动效果。"""
|
||||
effect = VibratorEffect(0)
|
||||
effect._timings = [0, milliseconds]
|
||||
effect._amplitudes = [0, amplitude]
|
||||
effect._repeat = -1
|
||||
return effect
|
||||
|
||||
@staticmethod
|
||||
def createWaveform(pattern: List[int], repeat: int) -> "VibratorEffect":
|
||||
return VibratorEffect(0)
|
||||
"""创建波形振动效果。"""
|
||||
effect = VibratorEffect(0)
|
||||
effect._timings = list(pattern)
|
||||
effect._amplitudes = [255 if i % 2 == 1 else 0 for i in range(len(pattern))]
|
||||
effect._repeat = repeat
|
||||
return effect
|
||||
|
||||
@staticmethod
|
||||
def createPredefined(effect_id: int) -> "VibratorEffect":
|
||||
"""创建预定义振动效果。"""
|
||||
return VibratorEffect(effect_id)
|
||||
|
||||
|
||||
@@ -1755,18 +1958,38 @@ class TokenWatcher(Stub):
|
||||
|
||||
def __init__(self, handler: Handler, tag: str):
|
||||
super().__init__()
|
||||
self._handler = handler
|
||||
self._tag = tag
|
||||
self._tokens: Dict[int, str] = {}
|
||||
|
||||
def acquire(self, token: Any, tag: str):
|
||||
pass
|
||||
"""获取令牌。"""
|
||||
self._tokens[id(token)] = tag
|
||||
if len(self._tokens) == 1:
|
||||
self.acquired()
|
||||
|
||||
def release(self, token: Any):
|
||||
pass
|
||||
"""释放令牌。"""
|
||||
self._tokens.pop(id(token), None)
|
||||
if not self._tokens:
|
||||
self.released()
|
||||
|
||||
def isAcquired(self) -> bool:
|
||||
return False
|
||||
"""检查是否有令牌被持有。"""
|
||||
return len(self._tokens) > 0
|
||||
|
||||
def acquired(self):
|
||||
"""当第一个令牌被获取时调用。子类可重写。"""
|
||||
pass
|
||||
|
||||
def released(self):
|
||||
"""当所有令牌被释放时调用。子类可重写。"""
|
||||
pass
|
||||
|
||||
def dump(self):
|
||||
pass
|
||||
"""转储令牌信息。"""
|
||||
for tid, tag in self._tokens.items():
|
||||
sys.stderr.write(f"[TokenWatcher] {self._tag}: token={tid}, tag={tag}\n")
|
||||
|
||||
|
||||
class WorkSource(Stub):
|
||||
@@ -1917,18 +2140,72 @@ class FileObserver(Stub):
|
||||
super().__init__()
|
||||
self._path = path
|
||||
self._watching = False
|
||||
self._thread = None
|
||||
self._snapshot: Dict[str, float] = {}
|
||||
|
||||
def startWatching(self):
|
||||
"""开始监听文件变化。使用轮询方式检测文件系统事件。"""
|
||||
if self._watching:
|
||||
return
|
||||
self._watching = True
|
||||
self._snapshot = self._take_snapshot()
|
||||
self._thread = threading.Thread(target=self._poll_loop, daemon=True)
|
||||
self._thread.start()
|
||||
|
||||
def _take_snapshot(self) -> Dict[str, float]:
|
||||
"""获取当前目录的文件快照(文件名 -> 修改时间)。"""
|
||||
snapshot: Dict[str, float] = {}
|
||||
try:
|
||||
if _os.path.isdir(self._path):
|
||||
for name in _os.listdir(self._path):
|
||||
full = _os.path.join(self._path, name)
|
||||
try:
|
||||
snapshot[name] = _os.path.getmtime(full)
|
||||
except OSError:
|
||||
pass
|
||||
elif _os.path.exists(self._path):
|
||||
try:
|
||||
snapshot[self._path] = _os.path.getmtime(self._path)
|
||||
except OSError:
|
||||
pass
|
||||
except OSError:
|
||||
pass
|
||||
return snapshot
|
||||
|
||||
def _poll_loop(self):
|
||||
"""轮询检测文件变化并触发事件。"""
|
||||
while self._watching:
|
||||
_time.sleep(0.5)
|
||||
try:
|
||||
current = self._take_snapshot()
|
||||
# 检测新建和修改
|
||||
for name, mtime in current.items():
|
||||
if name not in self._snapshot:
|
||||
self.onEvent(FileObserver.CREATE, name)
|
||||
elif mtime != self._snapshot[name]:
|
||||
self.onEvent(FileObserver.MODIFY, name)
|
||||
# 检测删除
|
||||
for name in list(self._snapshot.keys()):
|
||||
if name not in current:
|
||||
self.onEvent(FileObserver.DELETE, name)
|
||||
self._snapshot = current
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def stopWatching(self):
|
||||
"""停止监听。"""
|
||||
self._watching = False
|
||||
if self._thread and self._thread.is_alive():
|
||||
self._thread.join(timeout=1.0)
|
||||
self._thread = None
|
||||
|
||||
def onEvent(self, event: int, path: Optional[str]):
|
||||
"""文件事件回调,子类可重写。"""
|
||||
pass
|
||||
|
||||
def finalize(self):
|
||||
pass
|
||||
"""清理资源。"""
|
||||
self.stopWatching()
|
||||
|
||||
|
||||
class FileUtils(Stub):
|
||||
@@ -1985,7 +2262,35 @@ class FileUtils(Stub):
|
||||
|
||||
@staticmethod
|
||||
def deleteOlderFiles(dir_: Any, min_count: int, min_age_ms: int):
|
||||
pass
|
||||
"""删除目录中较旧的文件,但保留至少 min_count 个文件。
|
||||
min_age_ms 为文件的最小年龄(毫秒),低于此年龄的文件不会被删除。"""
|
||||
try:
|
||||
if not _os.path.isdir(dir_):
|
||||
return
|
||||
entries = []
|
||||
for name in _os.listdir(dir_):
|
||||
full = _os.path.join(dir_, name)
|
||||
if _os.path.isfile(full):
|
||||
try:
|
||||
mtime = _os.path.getmtime(full)
|
||||
entries.append((full, mtime))
|
||||
except OSError:
|
||||
pass
|
||||
# 按修改时间排序(最新在前)
|
||||
entries.sort(key=lambda x: x[1], reverse=True)
|
||||
now = _time.time()
|
||||
min_age_s = min_age_ms / 1000.0
|
||||
# 保留最新的 min_count 个文件,删除超过 min_age 的旧文件
|
||||
for i, (path, mtime) in enumerate(entries):
|
||||
if i < min_count:
|
||||
continue
|
||||
if (now - mtime) >= min_age_s:
|
||||
try:
|
||||
_os.remove(path)
|
||||
except OSError:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
class PatternMatcher(Stub):
|
||||
|
||||
Reference in new issue
Block a user