- 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关键字冲突
545 lines
19 KiB
Python
545 lines
19 KiB
Python
"""
|
|
Android gesture 包 — Gesture, GestureOverlayView, GesturePoint, GestureStroke,
|
|
GestureLibrary, GestureStore, Prediction, OrientedBoundingBox, GestureUtils
|
|
方法签名完全对齐 Android SDK
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import math
|
|
import sys
|
|
from typing import Any, Dict, List, Optional, Tuple
|
|
|
|
from .._core import Stub, AndroidOnlyError
|
|
from ..java.lang import RuntimeException
|
|
|
|
IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {})
|
|
|
|
__all__ = [
|
|
"Gesture",
|
|
"GestureOverlayView",
|
|
"GesturePoint",
|
|
"GestureStroke",
|
|
"GestureLibrary",
|
|
"GestureStore",
|
|
"Prediction",
|
|
"OrientedBoundingBox",
|
|
"GestureUtils",
|
|
]
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# GesturePoint
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class GesturePoint(Stub):
|
|
"""GesturePoint — 手势点"""
|
|
|
|
def __init__(self, x: float = 0.0, y: float = 0.0, timestamp: int = 0):
|
|
super().__init__()
|
|
self.x = x
|
|
self.y = y
|
|
self.timestamp = timestamp
|
|
|
|
def toString(self) -> str:
|
|
return f"GesturePoint({self.x}, {self.y})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# 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
|
|
|
|
def toRect(self) -> Tuple[float, float, float, float]:
|
|
"""转换为矩形"""
|
|
hw = self.width / 2.0
|
|
hh = self.height / 2.0
|
|
return (self.centerX - hw, self.centerY - hh,
|
|
self.centerX + hw, self.centerY + hh)
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# Prediction
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class Prediction(Stub):
|
|
"""Prediction — 预测结果"""
|
|
|
|
def __init__(self, name: str = "", score: float = 0.0):
|
|
super().__init__()
|
|
self.name = name
|
|
self.score = score
|
|
|
|
def toString(self) -> str:
|
|
return f"Prediction(name={self.name}, score={self.score})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# GestureStroke
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class GestureStroke(Stub):
|
|
"""GestureStroke — 手势笔划"""
|
|
|
|
def __init__(self, points: Optional[List[GesturePoint]] = None):
|
|
super().__init__()
|
|
self._points = points or []
|
|
self._path: Optional[Any] = None
|
|
self._length = self._compute_length()
|
|
self._bounding_box = OrientedBoundingBox()
|
|
|
|
def _compute_length(self) -> float:
|
|
length = 0.0
|
|
for i in range(1, len(self._points)):
|
|
dx = self._points[i].x - self._points[i - 1].x
|
|
dy = self._points[i].y - self._points[i - 1].y
|
|
length += math.sqrt(dx * dx + dy * dy)
|
|
return length
|
|
|
|
def getLength(self) -> float:
|
|
"""获取长度"""
|
|
return self._length
|
|
|
|
def getPoints(self) -> List[GesturePoint]:
|
|
"""获取点列表"""
|
|
return self._points.copy()
|
|
|
|
def getPath(self) -> Optional[Any]:
|
|
"""获取路径"""
|
|
return self._path
|
|
|
|
def toPath(self, width: float = 1.0, height: float = 1.0, num_sample: int = 0) -> Any:
|
|
"""转换为路径"""
|
|
return self._path
|
|
|
|
def clearPath(self) -> None:
|
|
"""清除路径"""
|
|
self._path = None
|
|
|
|
def computeOrientedBoundingBox(self) -> OrientedBoundingBox:
|
|
"""计算有向包围盒"""
|
|
return self._bounding_box
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# Gesture
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class Gesture(Stub):
|
|
"""Gesture — 手势"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._strokes: List[GestureStroke] = []
|
|
self._id = 0
|
|
self._bounding_box: Optional[Tuple[float, float, float, float]] = None
|
|
self._length = 0.0
|
|
|
|
def getStrokes(self) -> List[GestureStroke]:
|
|
"""获取笔划列表"""
|
|
return self._strokes.copy()
|
|
|
|
def getLength(self) -> float:
|
|
"""获取手势长度"""
|
|
return sum(s.getLength() for s in self._strokes)
|
|
|
|
def getBoundingBox(self) -> Tuple[float, float, float, float]:
|
|
"""获取包围盒"""
|
|
if self._bounding_box:
|
|
return self._bounding_box
|
|
if not self._strokes:
|
|
return (0.0, 0.0, 0.0, 0.0)
|
|
min_x = min(p.x for s in self._strokes for p in s._points) if self._strokes else 0
|
|
min_y = min(p.y for s in self._strokes for p in s._points) if self._strokes else 0
|
|
max_x = max(p.x for s in self._strokes for p in s._points) if self._strokes else 0
|
|
max_y = max(p.y for s in self._strokes for p in s._points) if self._strokes else 0
|
|
return (min_x, min_y, max_x, max_y)
|
|
|
|
def getID(self) -> int:
|
|
"""获取 ID"""
|
|
return self._id
|
|
|
|
def setID(self, id_: int) -> None:
|
|
"""设置 ID"""
|
|
self._id = id_
|
|
|
|
def toPath(self) -> Optional[Any]:
|
|
"""转换为路径"""
|
|
return None
|
|
|
|
def toTempPath(self) -> Optional[Any]:
|
|
"""转换为临时路径"""
|
|
return None
|
|
|
|
def toBitmap(self) -> Optional[Any]:
|
|
"""转换为位图"""
|
|
return None
|
|
|
|
def getStrokesCount(self) -> int:
|
|
"""获取笔划数量"""
|
|
return len(self._strokes)
|
|
|
|
def getGestureStroke(self, index: int) -> Optional[GestureStroke]:
|
|
"""获取指定索引的笔划"""
|
|
if 0 <= index < len(self._strokes):
|
|
return self._strokes[index]
|
|
return None
|
|
|
|
def addStroke(self, stroke: GestureStroke) -> None:
|
|
"""添加笔划"""
|
|
self._strokes.append(stroke)
|
|
|
|
def toString(self) -> str:
|
|
return f"Gesture(id={self._id}, strokes={len(self._strokes)})"
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
if not isinstance(other, Gesture):
|
|
return False
|
|
return self._id == other._id
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self._id) & 0x7FFFFFFF
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# GestureOverlayView
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class GestureOverlayView(Stub):
|
|
"""GestureOverlayView — 手势覆盖视图 (桌面存根)"""
|
|
|
|
GESTURE_STROKE_TYPE_SINGLE = 0
|
|
GESTURE_STROKE_TYPE_MULTIPLE = 1
|
|
ORIENTATION_HORIZONTAL = 0
|
|
ORIENTATION_VERTICAL = 1
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__()
|
|
self._context = context
|
|
self._gesture: Optional[Gesture] = None
|
|
self._gesture_color = 0xFFFFFF00
|
|
self._uncertain_gesture_color = 0xFFFF0000
|
|
self._gesture_stroke_width = 4.0
|
|
self._gesture_stroke_type = GestureOverlayView.GESTURE_STROKE_TYPE_SINGLE
|
|
self._gesture_stroke_length_threshold = 50.0
|
|
self._gesture_stroke_squareness_threshold = 0.5
|
|
self._fade_enabled = True
|
|
self._fade_offset = 0
|
|
self._fade_duration = 150
|
|
self._events_interception_enabled = True
|
|
self._gesture_visible = True
|
|
self._gesture_listeners: List[Any] = []
|
|
self._gesture_performed_listeners: List[Any] = []
|
|
self._gesturing_listeners: List[Any] = []
|
|
|
|
def getGesture(self) -> Optional[Gesture]:
|
|
return self._gesture
|
|
|
|
def setGesture(self, gesture: Gesture) -> None:
|
|
self._gesture = gesture
|
|
|
|
def setGestureColor(self, color: int) -> None:
|
|
self._gesture_color = color
|
|
|
|
def getGestureColor(self) -> int:
|
|
return self._gesture_color
|
|
|
|
def setUncertainGestureColor(self, color: int) -> None:
|
|
self._uncertain_gesture_color = color
|
|
|
|
def getUncertainGestureColor(self) -> int:
|
|
return self._uncertain_gesture_color
|
|
|
|
def setGestureStrokeWidth(self, width: float) -> None:
|
|
self._gesture_stroke_width = width
|
|
|
|
def getGestureStrokeWidth(self) -> float:
|
|
return self._gesture_stroke_width
|
|
|
|
def setGestureStrokeType(self, type_: int) -> None:
|
|
self._gesture_stroke_type = type_
|
|
|
|
def getGestureStrokeType(self) -> int:
|
|
return self._gesture_stroke_type
|
|
|
|
def setGestureStrokeLengthThreshold(self, threshold: float) -> None:
|
|
self._gesture_stroke_length_threshold = threshold
|
|
|
|
def getGestureStrokeLengthThreshold(self) -> float:
|
|
return self._gesture_stroke_length_threshold
|
|
|
|
def setGestureStrokeSquarenessThreshold(self, threshold: float) -> None:
|
|
self._gesture_stroke_squareness_threshold = threshold
|
|
|
|
def getGestureStrokeSquarenessThreshold(self) -> float:
|
|
return self._gesture_stroke_squareness_threshold
|
|
|
|
def setFadeEnabled(self, enabled: bool) -> None:
|
|
self._fade_enabled = enabled
|
|
|
|
def isFadeEnabled(self) -> bool:
|
|
return self._fade_enabled
|
|
|
|
def setFadeOffset(self, offset: int) -> None:
|
|
self._fade_offset = offset
|
|
|
|
def getFadeOffset(self) -> int:
|
|
return self._fade_offset
|
|
|
|
def setFadeDuration(self, duration: int) -> None:
|
|
self._fade_duration = duration
|
|
|
|
def getFadeDuration(self) -> int:
|
|
return self._fade_duration
|
|
|
|
def setEventsInterceptionEnabled(self, enabled: bool) -> None:
|
|
self._events_interception_enabled = enabled
|
|
|
|
def isEventsInterceptionEnabled(self) -> bool:
|
|
return self._events_interception_enabled
|
|
|
|
def setGestureVisible(self, visible: bool) -> None:
|
|
self._gesture_visible = visible
|
|
|
|
def isGestureVisible(self) -> bool:
|
|
return self._gesture_visible
|
|
|
|
def addOnGestureListener(self, listener: Any) -> None:
|
|
self._gesture_listeners.append(listener)
|
|
|
|
def removeOnGestureListener(self, listener: Any) -> None:
|
|
if listener in self._gesture_listeners:
|
|
self._gesture_listeners.remove(listener)
|
|
|
|
def removeAllOnGestureListeners(self) -> None:
|
|
self._gesture_listeners.clear()
|
|
|
|
def addOnGesturePerformedListener(self, listener: Any) -> None:
|
|
self._gesture_performed_listeners.append(listener)
|
|
|
|
def removeOnGesturePerformedListener(self, listener: Any) -> None:
|
|
if listener in self._gesture_performed_listeners:
|
|
self._gesture_performed_listeners.remove(listener)
|
|
|
|
def removeAllOnGesturePerformedListeners(self) -> None:
|
|
self._gesture_performed_listeners.clear()
|
|
|
|
def addOnGesturingListener(self, listener: Any) -> None:
|
|
self._gesturing_listeners.append(listener)
|
|
|
|
def removeOnGesturingListener(self, listener: Any) -> None:
|
|
if listener in self._gesturing_listeners:
|
|
self._gesturing_listeners.remove(listener)
|
|
|
|
def removeAllOnGesturingListeners(self) -> None:
|
|
self._gesturing_listeners.clear()
|
|
|
|
def cancelGesture(self) -> None:
|
|
self._gesture = None
|
|
|
|
def clear(self) -> None:
|
|
self._gesture = None
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# GestureLibrary
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class GestureLibrary(Stub):
|
|
"""GestureLibrary — 手势库 (桌面存根)"""
|
|
|
|
ORIENTATION_INVARIANT = 0
|
|
ORIENTATION_SENSITIVE = 1
|
|
ORIENTATION_SEQUENTIAL_SENSITIVE = 2
|
|
SEQUENCE_INVARIANT = 0
|
|
SEQUENCE_SENSITIVE = 1
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._gestures: Dict[str, List[Gesture]] = {}
|
|
self._read_only = False
|
|
self._orientation_style = GestureLibrary.ORIENTATION_SENSITIVE
|
|
self._sequence_type = GestureLibrary.SEQUENCE_SENSITIVE
|
|
|
|
def store(self) -> bool:
|
|
"""存储手势库"""
|
|
return True
|
|
|
|
def load(self) -> bool:
|
|
"""加载手势库"""
|
|
return True
|
|
|
|
def getGestureEntries(self) -> List[str]:
|
|
"""获取手势条目"""
|
|
return list(self._gestures.keys())
|
|
|
|
def getGestures(self, entry_name: str) -> List[Gesture]:
|
|
"""获取手势列表"""
|
|
return self._gestures.get(entry_name, [])
|
|
|
|
def recognize(self, gesture: Gesture) -> List[Prediction]:
|
|
"""识别手势"""
|
|
return []
|
|
|
|
def removeGesture(self, entry_name: str, gesture: Gesture) -> None:
|
|
"""移除手势"""
|
|
if entry_name in self._gestures and gesture in self._gestures[entry_name]:
|
|
self._gestures[entry_name].remove(gesture)
|
|
|
|
def removeEntry(self, entry_name: str) -> None:
|
|
"""移除条目"""
|
|
self._gestures.pop(entry_name, None)
|
|
|
|
def addGesture(self, entry_name: str, gesture: Gesture) -> None:
|
|
"""添加手势"""
|
|
if entry_name not in self._gestures:
|
|
self._gestures[entry_name] = []
|
|
self._gestures[entry_name].append(gesture)
|
|
|
|
def isReadOnly(self) -> bool:
|
|
"""检查是否只读"""
|
|
return self._read_only
|
|
|
|
def setOrientationStyle(self, style: int) -> None:
|
|
self._orientation_style = style
|
|
|
|
def getOrientationStyle(self) -> int:
|
|
return self._orientation_style
|
|
|
|
def setSequenceType(self, type_: int) -> None:
|
|
self._sequence_type = type_
|
|
|
|
def getSequenceType(self) -> int:
|
|
return self._sequence_type
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# GestureStore
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class GestureStore(Stub):
|
|
"""GestureStore — 手势存储 (桌面存根)"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._gestures: Dict[str, List[Gesture]] = {}
|
|
self._orientation_style = GestureLibrary.ORIENTATION_SENSITIVE
|
|
self._sequence_type = GestureLibrary.SEQUENCE_SENSITIVE
|
|
self._change_listener: Optional[Any] = None
|
|
|
|
def addGesture(self, entry_name: str, gesture: Gesture) -> None:
|
|
if entry_name not in self._gestures:
|
|
self._gestures[entry_name] = []
|
|
self._gestures[entry_name].append(gesture)
|
|
|
|
def removeGesture(self, entry_name: str, gesture: Gesture) -> None:
|
|
if entry_name in self._gestures and gesture in self._gestures[entry_name]:
|
|
self._gestures[entry_name].remove(gesture)
|
|
|
|
def removeEntry(self, entry_name: str) -> None:
|
|
self._gestures.pop(entry_name, None)
|
|
|
|
def getGestureEntries(self) -> List[str]:
|
|
return list(self._gestures.keys())
|
|
|
|
def getGestures(self, entry_name: str) -> List[Gesture]:
|
|
return self._gestures.get(entry_name, [])
|
|
|
|
def recognize(self, gesture: Gesture) -> List[Prediction]:
|
|
return []
|
|
|
|
def recognizeAsync(self, gesture: Gesture) -> None:
|
|
pass
|
|
|
|
def setOrientationStyle(self, style: int) -> None:
|
|
self._orientation_style = style
|
|
|
|
def getOrientationStyle(self) -> int:
|
|
return self._orientation_style
|
|
|
|
def setSequenceType(self, type_: int) -> None:
|
|
self._sequence_type = type_
|
|
|
|
def getSequenceType(self) -> int:
|
|
return self._sequence_type
|
|
|
|
def load(self, store_file: Any) -> None:
|
|
pass
|
|
|
|
def save(self, store_file: Any) -> None:
|
|
pass
|
|
|
|
def setChangeListener(self, listener: Any) -> None:
|
|
self._change_listener = listener
|
|
|
|
def getChangeListener(self) -> Any:
|
|
return self._change_listener
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# GestureUtils
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class GestureUtils(Stub):
|
|
"""GestureUtils — 手势工具类"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
|
|
@staticmethod
|
|
def copy(gesture: Gesture) -> Gesture:
|
|
"""复制手势"""
|
|
new_gesture = Gesture()
|
|
for stroke in gesture.getStrokes():
|
|
new_stroke = GestureStroke(stroke.getPoints())
|
|
new_gesture.addStroke(new_stroke)
|
|
return new_gesture
|
|
|
|
@staticmethod
|
|
def boundingBox(gesture: Gesture) -> OrientedBoundingBox:
|
|
"""计算手势包围盒"""
|
|
bbox = gesture.getBoundingBox()
|
|
cx = (bbox[0] + bbox[2]) / 2.0 if bbox[2] != bbox[0] else 0.0
|
|
cy = (bbox[1] + bbox[3]) / 2.0 if bbox[3] != bbox[1] else 0.0
|
|
w = bbox[2] - bbox[0]
|
|
h = bbox[3] - bbox[1]
|
|
return OrientedBoundingBox(cx, cy, w, h, 0.0)
|
|
|
|
@staticmethod
|
|
def boundingBox_stroke(stroke: GestureStroke) -> OrientedBoundingBox:
|
|
"""计算笔划包围盒"""
|
|
return stroke.computeOrientedBoundingBox()
|
|
|
|
@staticmethod
|
|
def spatialSampling(gesture: Gesture, num_samples: int) -> List[float]:
|
|
"""空间采样"""
|
|
return []
|
|
|
|
@staticmethod
|
|
def temporalSampling(gesture: Gesture, num_samples: int) -> List[float]:
|
|
"""时间采样"""
|
|
return []
|
|
|
|
@staticmethod
|
|
def computeOrientedBoundingBox(points: List[GesturePoint]) -> OrientedBoundingBox:
|
|
"""计算有向包围盒"""
|
|
if not points:
|
|
return OrientedBoundingBox()
|
|
xs = [p.x for p in points]
|
|
ys = [p.y for p in points]
|
|
cx = sum(xs) / len(xs)
|
|
cy = sum(ys) / len(ys)
|
|
w = max(xs) - min(xs)
|
|
h = max(ys) - min(ys)
|
|
return OrientedBoundingBox(cx, cy, w, h, 0.0) |