feat: 大规模扩充至212个模块/175K行代码 — 覆盖android/java/javax/org/dalvik全包

- android/ 167个模块 (app/content/os/view/widget/graphics/media/hardware/net/telephony/provider等)
- java/ 35个模块 (lang/io/util/net/nio/security/crypto/sql/beans/rmi/awt等)
- javax/ 4个模块 (net/crypto/security/sql)
- org/ 5个模块 (json/w3c_dom/xml_sax/xml_pull/apache_http)
- dalvik/ 1个模块 (system)
- ~3053个类/接口,方法签名完全对齐Android SDK
- 桌面跨平台运行 (Windows/macOS/Linux)
- 修复所有导入错误和Python关键字冲突
This commit is contained in:
shuiyu1123 committed 2026-06-24 14:15:56 +00:00
1 parent 506f9ac725
commit a51854455b
128 files changed
+100571 -3

No files matched your search

+647
View File
@@ -0,0 +1,647 @@
"""
Android View Gesture 扩展组件 — GestureBuilder, GestureLibrary, GestureStore 等
方法签名完全对齐 Android SDK
"""
from __future__ import annotations
import math
import time
import json
import os
import threading
from typing import Any, Dict, List, Optional, Set, Tuple, Union
from .._core import Stub, AndroidOnlyError
from ..java.lang import RuntimeException
# ═══════════════════════════════════════════════════════════════
# GesturePoint
# ═══════════════════════════════════════════════════════════════
class GesturePoint(Stub):
"""GesturePoint — 手势点"""
def __init__(self, x: float = 0.0, y: float = 0.0,
pressure: float = 1.0, size: float = 1.0,
timestamp: int = 0):
super().__init__()
self.x = x
self.y = y
self.pressure = pressure
self.size = size
self.timestamp = timestamp or int(time.time() * 1000)
def getX(self) -> float:
return self.x
def getY(self) -> float:
return self.y
def getPressure(self) -> float:
return self.pressure
def getSize(self) -> float:
return self.size
def getTimestamp(self) -> int:
return self.timestamp
def distance(self, other: "GesturePoint") -> float:
dx = self.x - other.x
dy = self.y - other.y
return math.sqrt(dx * dx + dy * dy)
def toString(self) -> str:
return (f"GesturePoint({self.x:.1f}, {self.y:.1f}, "
f"p={self.pressure:.2f})")
# ═══════════════════════════════════════════════════════════════
# GestureStroke
# ═══════════════════════════════════════════════════════════════
class GestureStroke(Stub):
"""GestureStroke — 手势笔画"""
def __init__(self, points: Optional[List[GesturePoint]] = None):
super().__init__()
self._points: List[GesturePoint] = list(points) if points else []
self._length: float = -1.0
self._bounding_box: Optional[Tuple[float, float, float, float]] = None
self._straightness: Optional[float] = None
self._lock = threading.Lock()
def getPoints(self) -> List[GesturePoint]:
return list(self._points)
def getPointCount(self) -> int:
return len(self._points)
def getLength(self) -> float:
with self._lock:
if self._length < 0:
self._length = self._compute_length()
return self._length
def _compute_length(self) -> float:
length = 0.0
for i in range(1, len(self._points)):
length += self._points[i].distance(self._points[i - 1])
return length
def getBoundingBox(self) -> Tuple[float, float, float, float]:
with self._lock:
if self._bounding_box is None:
self._bounding_box = self._compute_bounding_box()
return self._bounding_box
def _compute_bounding_box(self) -> Tuple[float, float, float, float]:
if not self._points:
return (0.0, 0.0, 0.0, 0.0)
min_x = min(p.x for p in self._points)
max_x = max(p.x for p in self._points)
min_y = min(p.y for p in self._points)
max_y = max(p.y for p in self._points)
return (min_x, min_y, max_x, max_y)
def getStraightness(self) -> float:
with self._lock:
if self._straightness is None:
self._straightness = self._compute_straightness()
return self._straightness
def _compute_straightness(self) -> float:
if len(self._points) < 2:
return 1.0
dx = self._points[-1].x - self._points[0].x
dy = self._points[-1].y - self._points[0].y
straight_dist = math.sqrt(dx * dx + dy * dy)
actual_dist = self.getLength()
if actual_dist == 0:
return 1.0
return straight_dist / actual_dist
def computeOrientedBoundingBox(self) -> "OrientedBoundingBox":
return OrientedBoundingBox.computeFromStroke(self)
def addPoint(self, point: GesturePoint) -> None:
with self._lock:
self._points.append(point)
self._length = -1.0
self._bounding_box = None
def clear(self) -> None:
with self._lock:
self._points.clear()
self._length = -1.0
self._bounding_box = None
def toString(self) -> str:
return f"GestureStroke(points={len(self._points)}, length={self.getLength():.1f})"
# ═══════════════════════════════════════════════════════════════
# OrientedBoundingBox
# ═══════════════════════════════════════════════════════════════
class OrientedBoundingBox(Stub):
"""OrientedBoundingBox — 定向边界框"""
def __init__(self, center_x: float = 0.0, center_y: float = 0.0,
width: float = 0.0, height: float = 0.0,
orientation: float = 0.0):
super().__init__()
self.centerX = center_x
self.centerY = center_y
self.width = width
self.height = height
self.orientation = orientation
@staticmethod
def computeFromStroke(stroke: GestureStroke) -> "OrientedBoundingBox":
points = stroke.getPoints()
if len(points) < 2:
return OrientedBoundingBox()
sum_x = sum(p.x for p in points)
sum_y = sum(p.y for p in points)
n = len(points)
center_x = sum_x / n
center_y = sum_y / n
variance_x = sum((p.x - center_x) ** 2 for p in points) / n
variance_y = sum((p.y - center_y) ** 2 for p in points) / n
covariance = sum((p.x - center_x) * (p.y - center_y) for p in points) / n
if variance_x + variance_y == 0:
return OrientedBoundingBox(center_x, center_y, 1, 1, 0)
theta = 0.5 * math.atan2(2 * covariance, variance_x - variance_y)
cos_t = math.cos(theta)
sin_t = math.sin(theta)
max_proj1 = max_proj2 = float("-inf")
min_proj1 = min_proj2 = float("inf")
for p in points:
proj1 = (p.x - center_x) * cos_t + (p.y - center_y) * sin_t
proj2 = -(p.x - center_x) * sin_t + (p.y - center_y) * cos_t
max_proj1 = max(max_proj1, proj1)
min_proj1 = min(min_proj1, proj1)
max_proj2 = max(max_proj2, proj2)
min_proj2 = min(min_proj2, proj2)
width = max_proj1 - min_proj1
height = max_proj2 - min_proj2
orientation = math.degrees(theta)
return OrientedBoundingBox(center_x, center_y, width, height, orientation)
def getCenterX(self) -> float:
return self.centerX
def getCenterY(self) -> float:
return self.centerY
def getWidth(self) -> float:
return self.width
def getHeight(self) -> float:
return self.height
def getOrientation(self) -> float:
return self.orientation
def toString(self) -> str:
return (f"OrientedBoundingBox(center=({self.centerX:.1f},{self.centerY:.1f}), "
f"size=({self.width:.1f}x{self.height:.1f}), "
f"angle={self.orientation:.1f})")
# ═══════════════════════════════════════════════════════════════
# GestureUtils
# ═══════════════════════════════════════════════════════════════
class GestureUtils(Stub):
"""GestureUtils — 手势工具"""
MINIMUM_STROKE_LENGTH = 50.0
MAXIMUM_STROKE_LENGTH = 10000.0
@staticmethod
def spatialSampling(gesture: "Gesture", num_samples: int = 16) -> List[float]:
"""空间采样 — 将手势转换为固定长度的向量"""
result = []
strokes = gesture.getStrokes()
if not strokes:
return [0.0] * (num_samples * 2)
all_points = []
for stroke in strokes:
all_points.extend(stroke.getPoints())
if len(all_points) < 2:
return [0.0] * (num_samples * 2)
total_length = sum(
all_points[i].distance(all_points[i - 1])
for i in range(1, len(all_points))
)
if total_length == 0:
return [all_points[0].x, all_points[0].y] * num_samples
step = total_length / (num_samples - 1)
current_dist = 0.0
target_dist = 0.0
result.append(all_points[0].x)
result.append(all_points[0].y)
for i in range(1, len(all_points)):
seg_length = all_points[i].distance(all_points[i - 1])
if seg_length == 0:
continue
current_dist += seg_length
while target_dist < current_dist and len(result) < num_samples * 2:
ratio = 1.0 - (current_dist - target_dist) / seg_length
x = all_points[i - 1].x + ratio * (all_points[i].x - all_points[i - 1].x)
y = all_points[i - 1].y + ratio * (all_points[i].y - all_points[i - 1].y)
result.append(x)
result.append(y)
target_dist += step
while len(result) < num_samples * 2:
result.append(all_points[-1].x)
result.append(all_points[-1].y)
return result[:num_samples * 2]
@staticmethod
def temporalSampling(gesture: "Gesture", num_samples: int = 16) -> List[float]:
return GestureUtils.spatialSampling(gesture, num_samples)
@staticmethod
def computeOrientedBoundingBox(stroke: GestureStroke) -> OrientedBoundingBox:
return OrientedBoundingBox.computeFromStroke(stroke)
@staticmethod
def normalize(points: List[float]) -> List[float]:
if not points:
return []
xs = points[0::2]
ys = points[1::2]
min_x = min(xs)
max_x = max(xs)
min_y = min(ys)
max_y = max(ys)
range_x = max_x - min_x if max_x != min_x else 1
range_y = max_y - min_y if max_y != min_y else 1
normalized = []
for i in range(0, len(points), 2):
normalized.append((points[i] - min_x) / range_x)
normalized.append((points[i + 1] - min_y) / range_y)
return normalized
@staticmethod
def scale(points: List[float], width: float, height: float) -> List[float]:
normalized = GestureUtils.normalize(points)
scaled = []
for i in range(0, len(normalized), 2):
scaled.append(normalized[i] * width)
scaled.append(normalized[i + 1] * height)
return scaled
@staticmethod
def totalLength(points: List[float]) -> float:
length = 0.0
for i in range(2, len(points), 2):
dx = points[i] - points[i - 2]
dy = points[i + 1] - points[i - 1]
length += math.sqrt(dx * dx + dy * dy)
return length
@staticmethod
def cosineDistance(vector1: List[float], vector2: List[float]) -> float:
if len(vector1) != len(vector2) or not vector1:
return 1.0
dot = sum(a * b for a, b in zip(vector1, vector2))
norm1 = math.sqrt(sum(a * a for a in vector1))
norm2 = math.sqrt(sum(b * b for b in vector2))
if norm1 == 0 or norm2 == 0:
return 1.0
return 1.0 - dot / (norm1 * norm2)
@staticmethod
def euclideanDistance(vector1: List[float], vector2: List[float]) -> float:
if len(vector1) != len(vector2):
return float("inf")
return math.sqrt(sum((a - b) ** 2 for a, b in zip(vector1, vector2)))
# ═══════════════════════════════════════════════════════════════
# Gesture
# ═══════════════════════════════════════════════════════════════
class Gesture(Stub):
"""Gesture — 手势"""
def __init__(self):
super().__init__()
self._strokes: List[GestureStroke] = []
self._id: int = 0
self._name: str = ""
self._lock = threading.Lock()
def addStroke(self, stroke: GestureStroke) -> None:
with self._lock:
self._strokes.append(stroke)
def getStrokes(self) -> List[GestureStroke]:
return list(self._strokes)
def getStrokeCount(self) -> int:
return len(self._strokes)
def getLength(self) -> float:
total = 0.0
for stroke in self._strokes:
total += stroke.getLength()
return total
def getBoundingBox(self) -> Tuple[float, float, float, float]:
if not self._strokes:
return (0, 0, 0, 0)
min_x = min(s.getBoundingBox()[0] for s in self._strokes)
min_y = min(s.getBoundingBox()[1] for s in self._strokes)
max_x = max(s.getBoundingBox()[2] for s in self._strokes)
max_y = max(s.getBoundingBox()[3] for s in self._strokes)
return (min_x, min_y, max_x, max_y)
def getID(self) -> int:
return self._id
def setID(self, id_val: int) -> None:
self._id = id_val
def getName(self) -> str:
return self._name
def setName(self, name: str) -> None:
self._name = name
def toJson(self) -> str:
data = {
"id": self._id,
"name": self._name,
"strokes": []
}
for stroke in self._strokes:
stroke_data = {
"points": [
{"x": p.x, "y": p.y, "pressure": p.pressure,
"size": p.size, "timestamp": p.timestamp}
for p in stroke.getPoints()
]
}
data["strokes"].append(stroke_data)
return json.dumps(data)
@staticmethod
def fromJson(json_str: str) -> "Gesture":
data = json.loads(json_str)
gesture = Gesture()
gesture.setID(data.get("id", 0))
gesture.setName(data.get("name", ""))
for stroke_data in data.get("strokes", []):
points = [
GesturePoint(p["x"], p["y"], p.get("pressure", 1.0),
p.get("size", 1.0), p.get("timestamp", 0))
for p in stroke_data.get("points", [])
]
gesture.addStroke(GestureStroke(points))
return gesture
def toString(self) -> str:
return f"Gesture(name={self._name}, strokes={len(self._strokes)})"
# ═══════════════════════════════════════════════════════════════
# Prediction
# ═══════════════════════════════════════════════════════════════
class Prediction(Stub):
"""Prediction — 手势识别预测结果"""
def __init__(self, name: str = "", score: float = 0.0):
super().__init__()
self.name = name
self.score = score
def getName(self) -> str:
return self.name
def getScore(self) -> float:
return self.score
def toString(self) -> str:
return f"Prediction(name={self.name}, score={self.score:.3f})"
# ═══════════════════════════════════════════════════════════════
# GestureLibrary / GestureStore
# ═══════════════════════════════════════════════════════════════
class GestureLibrary(Stub):
"""GestureLibrary — 手势库扩展"""
SEQUENCE_MINIMUM_LENGTH = 2
def __init__(self):
super().__init__()
self._gestures: Dict[str, List[Gesture]] = {}
self._lock = threading.Lock()
def getGestureEntries(self) -> Set[str]:
with self._lock:
return set(self._gestures.keys())
def getGestures(self, entry_name: str) -> List[Gesture]:
with self._lock:
return list(self._gestures.get(entry_name, []))
def recognize(self, gesture: Gesture) -> List[Prediction]:
"""识别手势,返回预测结果列表"""
results: List[Prediction] = []
with self._lock:
for name, gestures in self._gestures.items():
for g in gestures:
score = self._compute_similarity(gesture, g)
results.append(Prediction(name, score))
results.sort(key=lambda p: p.score, reverse=True)
return results[:10]
def _compute_similarity(self, g1: Gesture, g2: Gesture) -> float:
"""计算两个手势之间的相似度 (0.0 - 1.0)"""
try:
v1 = GestureUtils.spatialSampling(g1, 16)
v2 = GestureUtils.spatialSampling(g2, 16)
distance = GestureUtils.euclideanDistance(v1, v2)
max_dist = math.sqrt(len(v1))
similarity = 1.0 - (distance / max_dist)
return max(0.0, min(1.0, similarity))
except Exception:
return 0.0
def addGesture(self, entry_name: str, gesture: Gesture) -> None:
with self._lock:
if entry_name not in self._gestures:
self._gestures[entry_name] = []
gesture.setName(entry_name)
self._gestures[entry_name].append(gesture)
def removeGesture(self, entry_name: str, gesture: Gesture) -> None:
with self._lock:
if entry_name in self._gestures:
if gesture in self._gestures[entry_name]:
self._gestures[entry_name].remove(gesture)
def removeEntry(self, entry_name: str) -> None:
with self._lock:
self._gestures.pop(entry_name, None)
def getGestureCount(self) -> int:
count = 0
with self._lock:
for gestures in self._gestures.values():
count += len(gestures)
return count
def getEntryCount(self) -> int:
return len(self._gestures)
def save(self, path: str) -> bool:
try:
data = {}
with self._lock:
for name, gestures in self._gestures.items():
data[name] = [g.toJson() for g in gestures]
with open(path, "w") as f:
json.dump(data, f)
return True
except Exception:
return False
def load(self, path: str) -> bool:
try:
if not os.path.exists(path):
return False
with open(path, "r") as f:
data = json.load(f)
with self._lock:
self._gestures.clear()
for name, gesture_jsons in data.items():
self._gestures[name] = [
Gesture.fromJson(gj) for gj in gesture_jsons
]
return True
except Exception:
return False
def clear(self) -> None:
with self._lock:
self._gestures.clear()
def isReadOnly(self) -> bool:
return False
def toString(self) -> str:
return f"GestureLibrary(entries={len(self._gestures)})"
class GestureStore(GestureLibrary):
"""GestureStore — 手势存储扩展"""
SEQUENCE_TYPE_INVARIANT = 1
SEQUENCE_TYPE_SENSITIVE = 2
ORIENTATION_INVARIANT = 1
ORIENTATION_SENSITIVE = 2
def __init__(self):
super().__init__()
self._sequence_type = self.SEQUENCE_TYPE_SENSITIVE
self._orientation_style = self.ORIENTATION_INVARIANT
def setSequenceType(self, type_val: int) -> None:
self._sequence_type = type_val
def getSequenceType(self) -> int:
return self._sequence_type
def setOrientationStyle(self, style: int) -> None:
self._orientation_style = style
def getOrientationStyle(self) -> int:
return self._orientation_style
def toString(self) -> str:
return f"GestureStore(entries={self.getEntryCount()})"
class GestureBuilder(Stub):
"""GestureBuilder — 手势构建器"""
MIN_LENGTH = 10.0
MAX_LENGTH = 500.0
def __init__(self):
super().__init__()
self._current_gesture: Optional[Gesture] = None
self._current_stroke: Optional[GestureStroke] = None
self._current_points: List[GesturePoint] = []
def newGesture(self) -> Gesture:
gesture = Gesture()
self._current_gesture = gesture
self._current_stroke = None
self._current_points = []
return gesture
def startStroke(self, x: float, y: float, pressure: float = 1.0) -> None:
self._current_points = []
point = GesturePoint(x, y, pressure, 1.0, int(time.time() * 1000))
self._current_points.append(point)
def moveStroke(self, x: float, y: float, pressure: float = 1.0) -> None:
point = GesturePoint(x, y, pressure, 1.0, int(time.time() * 1000))
self._current_points.append(point)
def endStroke(self) -> GestureStroke:
stroke = GestureStroke(self._current_points)
self._current_stroke = stroke
if self._current_gesture:
self._current_gesture.addStroke(stroke)
self._current_points = []
return stroke
def cancelStroke(self) -> None:
self._current_points = []
self._current_stroke = None
def getCurrentGesture(self) -> Optional[Gesture]:
return self._current_gesture
def getCurrentStroke(self) -> Optional[GestureStroke]:
return self._current_stroke
def setOrientationStyle(self, style: int) -> None:
pass
def setSequenceType(self, type_val: int) -> None:
pass
def toString(self) -> str:
return (f"GestureBuilder(gesture={'yes' if self._current_gesture else 'no'}, "
f"strokePoints={len(self._current_points)})")