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:
shuiyu1123 committed 2026-06-25 00:17:50 +00:00
1 parent 39b1da37c9
commit 4235f8692c
13 files changed
+2248 -163

No files matched your search

+285 -27
View File
@@ -4,6 +4,7 @@ Android Content 组件 — Context, ContentResolver, Uri, SharedPreferences 等
"""
import json
import mimetypes
import os
import sys
import threading
@@ -372,34 +373,134 @@ class ContentResolver(Stub):
super().__init__()
self._observers: Dict[str, List[Any]] = {}
@staticmethod
def _uri_to_file_path(uri) -> Optional[str]:
"""将 file:// 或纯路径 Uri 转换为本地文件系统路径"""
try:
uri_str = str(uri)
except Exception:
return None
# 处理字符串输入
if isinstance(uri, str):
if uri.startswith("file://"):
return uri[7:]
if "://" not in uri:
return uri if uri else None
return None
scheme = getattr(uri, "_scheme", "") or ""
path = getattr(uri, "_path", "") or ""
if scheme == "file":
return path if path else None
if scheme == "" and path:
return path
return None
def query(self, uri: Uri, projection: Optional[List[str]], selection: Optional[str],
selection_args: Optional[List[str]], sort_order: Optional[str],
cancellation_signal: Any = None) -> Any:
"""Query content provider"""
sys.stderr.write(f"[ContentResolver] query: uri={uri}\n")
"""Query content provider. 对 file:// URI 返回基于目录列表的 Cursor。"""
path = self._uri_to_file_path(uri)
if path is not None and os.path.isdir(path):
try:
from .database import Cursor, MatrixCursor
cols = projection or ["_id", "name", "size", "last_modified"]
cursor = MatrixCursor(cols)
for idx, name in enumerate(sorted(os.listdir(path))):
full = os.path.join(path, name)
try:
st = os.stat(full)
size = st.st_size
mtime = int(st.st_mtime * 1000)
except OSError:
size = 0
mtime = 0
cursor.addRow([idx, name, size, mtime])
return cursor
except Exception:
pass
return None
def insert(self, uri: Uri, values: ContentValues) -> Optional[Uri]:
sys.stderr.write(f"[ContentResolver] insert: uri={uri}\n")
"""Insert. 对 file:// URI 将 ContentValues 写为 JSON 文件。"""
path = self._uri_to_file_path(uri)
if path is not None and isinstance(values, ContentValues):
try:
os.makedirs(path if os.path.isdir(path) else os.path.dirname(path) or ".", exist_ok=True)
target = path if not os.path.isdir(path) else os.path.join(path, "insert.json")
data = values._values if hasattr(values, "_values") else {}
with open(target, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, default=str)
return Uri.withAppendedPath(uri, os.path.basename(target))
except OSError:
pass
return uri
def update(self, uri: Uri, values: ContentValues, selection: Optional[str],
selection_args: Optional[List[str]]) -> int:
sys.stderr.write(f"[ContentResolver] update: uri={uri}\n")
"""Update. 对 file:// URI 更新对应 JSON 文件。"""
path = self._uri_to_file_path(uri)
if path is not None and isinstance(values, ContentValues) and os.path.isfile(path):
try:
data = values._values if hasattr(values, "_values") else {}
with open(path, "r", encoding="utf-8") as f:
existing = json.load(f)
if isinstance(existing, dict):
existing.update(data)
with open(path, "w", encoding="utf-8") as f:
json.dump(existing, f, ensure_ascii=False, default=str)
return 1
except (OSError, ValueError):
pass
return 0
def delete(self, uri: Uri, selection: Optional[str],
selection_args: Optional[List[str]]) -> int:
sys.stderr.write(f"[ContentResolver] delete: uri={uri}\n")
"""Delete. 对 file:// URI 删除对应文件或目录。"""
path = self._uri_to_file_path(uri)
if path is not None:
try:
if os.path.isfile(path):
os.remove(path)
return 1
if os.path.isdir(path):
import shutil
shutil.rmtree(path)
return 1
except OSError:
pass
return 0
def applyBatch(self, authority: str, operations: List[Any]) -> List[Any]:
sys.stderr.write(f"[ContentResolver] applyBatch: authority={authority}\n")
return []
"""Apply batch operations, returning per-op results."""
results: List[Any] = []
for op in operations:
try:
op_type = getattr(op, "_type", None)
uri = getattr(op, "_uri", None)
values = getattr(op, "_values", None)
if op_type == 1 and uri is not None: # INSERT
results.append(ContentProviderResult(uri=self.insert(uri, values or ContentValues())))
elif op_type == 2 and uri is not None: # UPDATE
count = self.update(uri, values or ContentValues(),
getattr(op, "_selection", None),
getattr(op, "_selection_args", None))
results.append(ContentProviderResult(count=count))
elif op_type == 3 and uri is not None: # DELETE
count = self.delete(uri, getattr(op, "_selection", None),
getattr(op, "_selection_args", None))
results.append(ContentProviderResult(count=count))
else:
results.append(ContentProviderResult(count=0))
except Exception:
results.append(ContentProviderResult(count=0))
return results
def bulkInsert(self, uri: Uri, values: List[ContentValues]) -> int:
sys.stderr.write(f"[ContentResolver] bulkInsert: uri={uri}\n")
return len(values) if values else 0
count = 0
for v in values or []:
if self.insert(uri, v) is not None:
count += 1
return count
def call(self, uri: Uri, method: str, arg: Optional[str], extras: Optional[ContentValues]) -> Optional[ContentValues]:
return None
@@ -426,7 +527,17 @@ class ContentResolver(Stub):
if not self._observers[uri_str]:
del self._observers[uri_str]
def getType(self, uri: Uri) -> Optional[str]:
def getType(self, uri) -> Optional[str]:
"""返回 URI 指向资源的 MIME 类型。"""
path = self._uri_to_file_path(uri)
if path:
mime, _ = mimetypes.guess_type(path)
return mime or "application/octet-stream"
scheme = getattr(uri, "_scheme", "") or ""
if isinstance(uri, str) and "://" in uri:
scheme = uri.split("://")[0]
if scheme == "content":
return "vnd.android.cursor.dir/vnd.android.content"
return None
def acquireContentProviderClient(self, uri: Uri) -> Optional["ContentProviderClient"]:
@@ -436,12 +547,40 @@ class ContentResolver(Stub):
return ContentProviderClient(uri)
def getStreamTypes(self, uri: Uri, mime_type_filter: str) -> Optional[List[str]]:
return None
"""返回 URI 支持的流 MIME 类型列表。"""
mime = self.getType(uri)
if mime is None:
return None
if mime_type_filter and mime_type_filter.endswith("/*"):
prefix = mime_type_filter[:-1]
if mime.startswith(prefix):
return [mime]
return None
if mime == mime_type_filter:
return [mime]
return [mime] if mime else None
def openInputStream(self, uri: Uri) -> Any:
"""打开输入流。对 file:// URI 返回真实文件流。"""
path = self._uri_to_file_path(uri)
if path and os.path.isfile(path):
try:
return open(path, "rb")
except OSError:
return None
return None
def openOutputStream(self, uri: Uri) -> Any:
"""打开输出流。对 file:// URI 返回真实文件流。"""
path = self._uri_to_file_path(uri)
if path:
try:
d = os.path.dirname(path)
if d:
os.makedirs(d, exist_ok=True)
return open(path, "wb")
except OSError:
return None
return None
@@ -594,10 +733,16 @@ class Context(Stub):
return PackageManager()
def getResources(self):
return None
from .content_res import Resources, AssetManager
if not hasattr(self, "_resources") or self._resources is None:
self._resources = Resources(self.getAssets())
return self._resources
def getAssets(self):
return None
from .content_res import AssetManager
if not hasattr(self, "_assets") or self._assets is None:
self._assets = AssetManager()
return self._assets
def getMainLooper(self):
from .os import Looper
@@ -668,15 +813,34 @@ class Context(Stub):
pass
def getString(self, res_id: int, *args) -> str:
"""返回资源字符串。优先委托给 Resources,否则返回合理默认值。"""
try:
res = self.getResources()
val = res.getString(res_id, *args)
if val is not None:
return val
except Exception:
pass
return str(res_id)
def getColor(self, color_id: int) -> int:
return 0
"""返回资源颜色。优先委托给 Resources,否则返回不透明黑色。"""
try:
res = self.getResources()
return res.getColor(color_id)
except Exception:
return 0xFF000000
def getDrawable(self, drawable_id: int):
return None
"""返回 Drawable。优先委托给 Resources。"""
try:
res = self.getResources()
return res.getDrawable(drawable_id)
except Exception:
return None
def getWallpaper(self):
"""返回壁纸 Drawable。桌面端返回 None(无系统壁纸)。"""
return None
def getWallpaperDesiredMinimumWidth(self) -> int:
@@ -795,18 +959,55 @@ class Context(Stub):
self._theme = resid
def getText(self, resId):
"""返回资源文本。优先委托给 Resources。"""
try:
res = self.getResources()
val = res.getText(resId)
if val is not None:
return val
except Exception:
pass
return str(resId)
def getQuantityString(self, resId, quantity, *formatArgs):
return str(resId)
"""返回复数字符串。优先委托给 Resources。"""
try:
res = self.getResources()
return res.getQuantityString(resId, quantity, *formatArgs)
except Exception:
return str(resId)
def getStringArray(self, resId):
"""返回字符串数组。优先委托给 Resources。"""
try:
res = self.getResources()
val = res.getStringArray(resId)
if val is not None:
return val
except Exception:
pass
return []
def getTextArray(self, resId):
"""返回文本数组。优先委托给 Resources。"""
try:
res = self.getResources()
val = res.getTextArray(resId)
if val is not None:
return val
except Exception:
pass
return []
def getIntArray(self, resId):
"""返回整型数组。优先委托给 Resources。"""
try:
res = self.getResources()
val = res.getIntArray(resId)
if val is not None:
return val
except Exception:
pass
return []
# ─── Context 派生 / 回调 (扩展) ───────────────────────────
@@ -1386,29 +1587,55 @@ class ContentProvider(Stub):
def query(self, uri: Uri, projection: Optional[List[str]], selection: Optional[str],
selection_args: Optional[List[str]], sort_order: Optional[str]) -> Optional[Any]:
"""默认实现:委托给关联 Context 的 ContentResolver。"""
if self._context is not None:
return self._context.getContentResolver().query(
uri, projection, selection, selection_args, sort_order)
return None
def insert(self, uri: Uri, values: ContentValues) -> Optional[Uri]:
if self._context is not None:
return self._context.getContentResolver().insert(uri, values)
return None
def update(self, uri: Uri, values: ContentValues, selection: Optional[str],
selection_args: Optional[List[str]]) -> int:
if self._context is not None:
return self._context.getContentResolver().update(uri, values, selection, selection_args)
return 0
def delete(self, uri: Uri, selection: Optional[str], selection_args: Optional[List[str]]) -> int:
if self._context is not None:
return self._context.getContentResolver().delete(uri, selection, selection_args)
return 0
def getType(self, uri: Uri) -> Optional[str]:
"""返回 URI 的 MIME 类型。"""
path = ContentResolver._uri_to_file_path(uri)
if path:
mime, _ = mimetypes.guess_type(path)
return mime or "application/octet-stream"
return None
def getStreamTypes(self, uri: Uri, mime_type_filter: str) -> Optional[List[str]]:
return None
mime = self.getType(uri)
if mime is None:
return None
return [mime]
def openFile(self, uri: Uri, mode: str) -> Any:
"""打开文件。对 file:// URI 返回真实文件对象。"""
path = ContentResolver._uri_to_file_path(uri)
if path and os.path.exists(path):
try:
fmode = "rb" if "r" in mode else "wb"
return open(path, fmode)
except OSError:
return None
return None
def openAssetFile(self, uri: Uri, mode: str) -> Any:
return None
return self.openFile(uri, mode)
def attachInfo(self, context: Context, info: Any):
self._context = context
@@ -1444,29 +1671,44 @@ class ContentProviderClient(Stub):
def query(self, uri: Uri, projection: Optional[List[str]], selection: Optional[str],
selection_args: Optional[List[str]], sort_order: Optional[str]) -> Optional[Any]:
return None
"""委托给 ContentResolver 处理 file:// URI。"""
resolver = ContentResolver()
return resolver.query(uri, projection, selection, selection_args, sort_order)
def insert(self, uri: Uri, values: ContentValues) -> Optional[Uri]:
return uri
resolver = ContentResolver()
return resolver.insert(uri, values)
def update(self, uri: Uri, values: ContentValues, selection: Optional[str],
selection_args: Optional[List[str]]) -> int:
return 0
resolver = ContentResolver()
return resolver.update(uri, values, selection, selection_args)
def delete(self, uri: Uri, selection: Optional[str], selection_args: Optional[List[str]]) -> int:
return 0
resolver = ContentResolver()
return resolver.delete(uri, selection, selection_args)
def applyBatch(self, operations: List[Any]) -> List[Any]:
return []
resolver = ContentResolver()
return resolver.applyBatch("", operations)
def bulkInsert(self, uri: Uri, values: List[ContentValues]) -> int:
return len(values) if values else 0
resolver = ContentResolver()
return resolver.bulkInsert(uri, values)
def getType(self, uri: Uri) -> Optional[str]:
"""返回 URI 的 MIME 类型。"""
path = ContentResolver._uri_to_file_path(uri)
if path:
mime, _ = mimetypes.guess_type(path)
return mime or "application/octet-stream"
return None
def getStreamTypes(self, uri: Uri, mime_type_filter: str) -> Optional[List[str]]:
return None
mime = self.getType(uri)
if mime is None:
return None
return [mime]
def call(self, method: str, arg: Optional[str], extras: Optional[ContentValues]) -> Optional[ContentValues]:
return None
@@ -1478,13 +1720,21 @@ class ContentProviderClient(Stub):
pass
def openFile(self, uri: Uri, mode: str) -> Any:
"""打开文件。对 file:// URI 返回真实文件对象。"""
path = ContentResolver._uri_to_file_path(uri)
if path and os.path.exists(path):
try:
fmode = "rb" if "r" in mode else "wb"
return open(path, fmode)
except OSError:
return None
return None
def openAssetFile(self, uri: Uri, mode: str) -> Any:
return None
return self.openFile(uri, mode)
def openTypedAssetFile(self, uri: Uri, mime_type: str, opts: Optional[ContentValues]) -> Any:
return None
return self.openFile(uri, "r")
def getLocalContentProvider(self) -> Optional[ContentProvider]:
return None
@@ -1658,6 +1908,14 @@ class CursorLoader(Loader):
self._sort_order = sort_order
def loadInBackground(self):
"""在后台加载数据,委托给 ContentResolver.query。"""
if self._context is not None:
try:
resolver = self._context.getContentResolver()
return resolver.query(self._uri, self._projection, self._selection,
self._selection_args, self._sort_order)
except Exception:
pass
return None