存根转真实实现: - 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行
746 lines
29 KiB
Python
746 lines
29 KiB
Python
"""
|
|
Android Hardware 组件 — Sensor, SensorManager, SensorEvent, 等
|
|
方法签名完全对齐 Android SDK
|
|
"""
|
|
|
|
import math
|
|
import time
|
|
import random
|
|
from typing import Any, Callable, Dict, List, Optional, Tuple
|
|
|
|
from .._core import Stub, AndroidOnlyError, android_context, HAS_PIL
|
|
|
|
# Linux 真实实现导入
|
|
try:
|
|
from .._linux_impl import IS_LINUX, LinuxBluetooth, LinuxSensors, HAS_BLUETOOTHCTL, LinuxPower
|
|
_USE_LINUX = IS_LINUX
|
|
except ImportError:
|
|
try:
|
|
from .._linux_impl import IS_LINUX, LinuxBluetooth, LinuxSensors, HAS_BLUETOOTHCTL
|
|
_USE_LINUX = IS_LINUX
|
|
LinuxPower = None
|
|
except ImportError:
|
|
_USE_LINUX = False
|
|
LinuxPower = None
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# Sensor
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class Sensor(Stub):
|
|
"""Sensor — 传感器"""
|
|
|
|
TYPE_ACCELEROMETER = 1
|
|
TYPE_MAGNETIC_FIELD = 2
|
|
TYPE_ORIENTATION = 3
|
|
TYPE_GYROSCOPE = 4
|
|
TYPE_LIGHT = 5
|
|
TYPE_PRESSURE = 6
|
|
TYPE_PROXIMITY = 8
|
|
TYPE_GRAVITY = 9
|
|
TYPE_LINEAR_ACCELERATION = 10
|
|
TYPE_ROTATION_VECTOR = 11
|
|
TYPE_RELATIVE_HUMIDITY = 12
|
|
TYPE_AMBIENT_TEMPERATURE = 13
|
|
TYPE_STEP_COUNTER = 19
|
|
TYPE_STEP_DETECTOR = 18
|
|
TYPE_HEART_RATE = 21
|
|
TYPE_LOW_LATENCY_OFFBODY_DETECT = 34
|
|
TYPE_ACCELEROMETER_LIMITED_AXES = 35
|
|
TYPE_DEVICE_ORIENTATION = 27
|
|
TYPE_STATIONARY_DETECT = 28
|
|
|
|
REPORTING_MODE_CONTINUOUS = 0
|
|
REPORTING_MODE_ON_CHANGE = 1
|
|
REPORTING_MODE_ONE_SHOT = 2
|
|
REPORTING_MODE_SPECIAL_TRIGGER = 3
|
|
|
|
def __init__(self, type_: int, name: str = "Simulated Sensor",
|
|
vendor: str = "Android-pyApi", version: int = 1,
|
|
resolution: float = 1.0, max_range: float = 100.0,
|
|
power: float = 0.5, min_delay: int = 10000):
|
|
super().__init__()
|
|
self._type = type_
|
|
self._name = name
|
|
self._vendor = vendor
|
|
self._version = version
|
|
self._resolution = resolution
|
|
self._max_range = max_range
|
|
self._power = power
|
|
self._min_delay = min_delay
|
|
|
|
# Assign default values based on sensor type
|
|
type_defaults = {
|
|
Sensor.TYPE_ACCELEROMETER: ("Accelerometer", 0.01, 39.24, 3.0, 10000),
|
|
Sensor.TYPE_GYROSCOPE: ("Gyroscope", 0.001, 34.91, 6.1, 10000),
|
|
Sensor.TYPE_MAGNETIC_FIELD: ("Magnetic Field", 0.01, 2000.0, 4.0, 10000),
|
|
Sensor.TYPE_LIGHT: ("Light", 1.0, 40000.0, 0.5, 10000),
|
|
Sensor.TYPE_PROXIMITY: ("Proximity", 1.0, 10.0, 0.5, 10000),
|
|
Sensor.TYPE_PRESSURE: ("Pressure", 0.01, 1100.0, 1.0, 10000),
|
|
Sensor.TYPE_GRAVITY: ("Gravity", 0.01, 39.24, 3.0, 10000),
|
|
Sensor.TYPE_LINEAR_ACCELERATION: ("Linear Acceleration", 0.01, 39.24, 3.0, 10000),
|
|
Sensor.TYPE_ROTATION_VECTOR: ("Rotation Vector", 0.0001, 1.0, 5.0, 10000),
|
|
Sensor.TYPE_RELATIVE_HUMIDITY: ("Relative Humidity", 0.1, 100.0, 0.5, 10000),
|
|
Sensor.TYPE_AMBIENT_TEMPERATURE: ("Ambient Temperature", 0.01, 80.0, 0.5, 10000),
|
|
Sensor.TYPE_STEP_COUNTER: ("Step Counter", 1.0, 100000.0, 0.1, 100000),
|
|
Sensor.TYPE_STEP_DETECTOR: ("Step Detector", 1.0, 1.0, 0.1, 100000),
|
|
Sensor.TYPE_HEART_RATE: ("Heart Rate", 1.0, 220.0, 0.5, 100000),
|
|
}
|
|
|
|
if type_ in type_defaults:
|
|
n, res, mr, p, md = type_defaults[type_]
|
|
self._name = name if name != "Simulated Sensor" else n
|
|
self._resolution = resolution if resolution != 1.0 else res
|
|
self._max_range = max_range if max_range != 100.0 else mr
|
|
self._power = power if power != 0.5 else p
|
|
self._min_delay = min_delay if min_delay != 10000 else md
|
|
|
|
def getType(self) -> int:
|
|
return self._type
|
|
|
|
def getName(self) -> str:
|
|
return self._name
|
|
|
|
def getVendor(self) -> str:
|
|
return self._vendor
|
|
|
|
def getVersion(self) -> int:
|
|
return self._version
|
|
|
|
def getMaximumRange(self) -> float:
|
|
return self._max_range
|
|
|
|
def getMinDelay(self) -> int:
|
|
return self._min_delay
|
|
|
|
def getPower(self) -> float:
|
|
return self._power
|
|
|
|
def getResolution(self) -> float:
|
|
return self._resolution
|
|
|
|
def getFifoMaxEventCount(self) -> int:
|
|
return 0
|
|
|
|
def getFifoReservedEventCount(self) -> int:
|
|
return 0
|
|
|
|
def getReportingMode(self) -> int:
|
|
return Sensor.REPORTING_MODE_CONTINUOUS
|
|
|
|
def isWakeUpSensor(self) -> bool:
|
|
return False
|
|
|
|
def getStringType(self) -> str:
|
|
type_names = {
|
|
Sensor.TYPE_ACCELEROMETER: "android.sensor.accelerometer",
|
|
Sensor.TYPE_MAGNETIC_FIELD: "android.sensor.magnetic_field",
|
|
Sensor.TYPE_GYROSCOPE: "android.sensor.gyroscope",
|
|
Sensor.TYPE_LIGHT: "android.sensor.light",
|
|
Sensor.TYPE_PRESSURE: "android.sensor.pressure",
|
|
Sensor.TYPE_PROXIMITY: "android.sensor.proximity",
|
|
Sensor.TYPE_GRAVITY: "android.sensor.gravity",
|
|
Sensor.TYPE_LINEAR_ACCELERATION: "android.sensor.linear_acceleration",
|
|
Sensor.TYPE_ROTATION_VECTOR: "android.sensor.rotation_vector",
|
|
Sensor.TYPE_RELATIVE_HUMIDITY: "android.sensor.relative_humidity",
|
|
Sensor.TYPE_AMBIENT_TEMPERATURE: "android.sensor.ambient_temperature",
|
|
Sensor.TYPE_STEP_COUNTER: "android.sensor.step_counter",
|
|
Sensor.TYPE_STEP_DETECTOR: "android.sensor.step_detector",
|
|
Sensor.TYPE_HEART_RATE: "android.sensor.heart_rate",
|
|
}
|
|
return type_names.get(self._type, "android.sensor.unknown")
|
|
|
|
def toString(self) -> str:
|
|
return f"{self._name} (type={self._type}, vendor={self._vendor}, ver={self._version})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# SensorEvent
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class SensorEvent(Stub):
|
|
"""SensorEvent — 传感器事件"""
|
|
|
|
def __init__(self, sensor: Sensor, accuracy: int = 3):
|
|
super().__init__()
|
|
self.sensor = sensor
|
|
self.accuracy = accuracy
|
|
self.timestamp = int(time.time() * 1_000_000_000) # nanoseconds
|
|
self.values: List[float] = []
|
|
|
|
# Generate appropriate values based on sensor type
|
|
if sensor.getType() == Sensor.TYPE_ACCELEROMETER:
|
|
self.values = [random.uniform(-1, 1), random.uniform(-1, 1),
|
|
9.8 + random.uniform(-0.5, 0.5)]
|
|
elif sensor.getType() == Sensor.TYPE_GYROSCOPE:
|
|
self.values = [random.uniform(-0.1, 0.1)] * 3
|
|
elif sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD:
|
|
self.values = [random.uniform(-50, 50)] * 3
|
|
elif sensor.getType() == Sensor.TYPE_LIGHT:
|
|
self.values = [random.uniform(100, 1000)]
|
|
elif sensor.getType() == Sensor.TYPE_PROXIMITY:
|
|
self.values = [5.0 if random.random() > 0.5 else 0.0]
|
|
elif sensor.getType() == Sensor.TYPE_PRESSURE:
|
|
self.values = [random.uniform(980, 1050)]
|
|
elif sensor.getType() in (Sensor.TYPE_GRAVITY, Sensor.TYPE_LINEAR_ACCELERATION):
|
|
self.values = [random.uniform(-1, 1)] * 3
|
|
elif sensor.getType() == Sensor.TYPE_ROTATION_VECTOR:
|
|
self.values = [random.uniform(-1, 1)] * 5
|
|
elif sensor.getType() == Sensor.TYPE_RELATIVE_HUMIDITY:
|
|
self.values = [random.uniform(30, 70)]
|
|
elif sensor.getType() == Sensor.TYPE_AMBIENT_TEMPERATURE:
|
|
self.values = [random.uniform(15, 35)]
|
|
elif sensor.getType() == Sensor.TYPE_STEP_COUNTER:
|
|
self.values = [float(random.randint(0, 10000))]
|
|
elif sensor.getType() == Sensor.TYPE_STEP_DETECTOR:
|
|
self.values = [1.0 if random.random() > 0.95 else 0.0]
|
|
elif sensor.getType() == Sensor.TYPE_HEART_RATE:
|
|
self.values = [random.uniform(60, 100)]
|
|
else:
|
|
self.values = [0.0]
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# SensorEventListener
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class SensorEventListener(Stub):
|
|
"""SensorEventListener — 传感器事件监听器"""
|
|
|
|
def onSensorChanged(self, event: SensorEvent):
|
|
pass
|
|
|
|
def onAccuracyChanged(self, sensor: Sensor, accuracy: int):
|
|
pass
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# SensorDelay
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class SensorDelay(Stub):
|
|
"""SensorDelay — 传感器采样延迟常量"""
|
|
FASTEST = 0
|
|
GAME = 1
|
|
UI = 2
|
|
NORMAL = 3
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# SensorManager
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class SensorManager(Stub):
|
|
"""SensorManager — 传感器管理器"""
|
|
|
|
SENSOR_DELAY_FASTEST = 0
|
|
SENSOR_DELAY_GAME = 1
|
|
SENSOR_DELAY_UI = 2
|
|
SENSOR_DELAY_NORMAL = 3
|
|
|
|
AXIS_X = 1
|
|
AXIS_Y = 2
|
|
AXIS_Z = 3
|
|
AXIS_MINUS_X = 129
|
|
AXIS_MINUS_Y = 130
|
|
AXIS_MINUS_Z = 131
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._sensors: Dict[int, Sensor] = {}
|
|
self._listeners: Dict[int, List[Tuple[SensorEventListener, Sensor, int]]] = {}
|
|
|
|
# Create default sensors
|
|
for type_ in [
|
|
Sensor.TYPE_ACCELEROMETER, Sensor.TYPE_GYROSCOPE,
|
|
Sensor.TYPE_MAGNETIC_FIELD, Sensor.TYPE_LIGHT,
|
|
Sensor.TYPE_PROXIMITY, Sensor.TYPE_PRESSURE,
|
|
Sensor.TYPE_GRAVITY, Sensor.TYPE_LINEAR_ACCELERATION,
|
|
Sensor.TYPE_ROTATION_VECTOR, Sensor.TYPE_RELATIVE_HUMIDITY,
|
|
Sensor.TYPE_AMBIENT_TEMPERATURE, Sensor.TYPE_STEP_COUNTER,
|
|
Sensor.TYPE_STEP_DETECTOR, Sensor.TYPE_HEART_RATE,
|
|
]:
|
|
self._sensors[type_] = Sensor(type_)
|
|
|
|
def getDefaultSensor(self, type_: int) -> Optional[Sensor]:
|
|
if _USE_LINUX:
|
|
sensor = self._build_linux_sensor(type_)
|
|
if sensor is not None:
|
|
return sensor
|
|
return self._sensors.get(type_)
|
|
|
|
def _build_linux_sensor(self, type_: int) -> Optional[Sensor]:
|
|
"""根据 Linux 真实传感器数据构造 Sensor 对象"""
|
|
try:
|
|
if type_ == Sensor.TYPE_AMBIENT_TEMPERATURE:
|
|
temps = LinuxSensors.get_temperatures()
|
|
if temps:
|
|
t = temps[0]
|
|
return Sensor(Sensor.TYPE_AMBIENT_TEMPERATURE,
|
|
name=t.get("name", "Linux Temperature"),
|
|
vendor="Linux", resolution=0.01,
|
|
max_range=125.0, power=0.0, min_delay=1000)
|
|
return None
|
|
if type_ == Sensor.TYPE_LIGHT:
|
|
light = LinuxSensors.get_light_level()
|
|
if light is not None:
|
|
return Sensor(Sensor.TYPE_LIGHT, name="Linux Light Sensor",
|
|
vendor="Linux", resolution=0.001,
|
|
max_range=40000.0, power=0.0, min_delay=1000)
|
|
return None
|
|
except Exception:
|
|
return None
|
|
return None
|
|
|
|
def getSensorList(self, type_: int) -> List[Sensor]:
|
|
if _USE_LINUX:
|
|
return self._get_linux_sensor_list(type_)
|
|
if type_ == -1: # ALL
|
|
return list(self._sensors.values())
|
|
sensor = self._sensors.get(type_)
|
|
return [sensor] if sensor else []
|
|
|
|
def _get_linux_sensor_list(self, type_: int) -> List[Sensor]:
|
|
"""从 Linux 真实传感器数据生成传感器列表"""
|
|
sensors: List[Sensor] = []
|
|
try:
|
|
if type_ == -1 or type_ == Sensor.TYPE_AMBIENT_TEMPERATURE:
|
|
for t in LinuxSensors.get_temperatures():
|
|
sensors.append(Sensor(
|
|
Sensor.TYPE_AMBIENT_TEMPERATURE,
|
|
name=t.get("name", "Linux Temperature"),
|
|
vendor="Linux", resolution=0.01,
|
|
max_range=125.0, power=0.0, min_delay=1000))
|
|
if type_ == -1 or type_ == Sensor.TYPE_LIGHT:
|
|
light = LinuxSensors.get_light_level()
|
|
if light is not None:
|
|
sensors.append(Sensor(
|
|
Sensor.TYPE_LIGHT, name="Linux Light Sensor",
|
|
vendor="Linux", resolution=0.001,
|
|
max_range=40000.0, power=0.0, min_delay=1000))
|
|
# CPU 频率作为附加传感器 (映射到压力传感器类型以承载真实数据)
|
|
if type_ == -1 or type_ == Sensor.TYPE_PRESSURE:
|
|
for f in LinuxSensors.get_cpu_frequency():
|
|
sensors.append(Sensor(
|
|
Sensor.TYPE_PRESSURE,
|
|
name=f"CPU{f.get('cpu', 0)} Frequency",
|
|
vendor="Linux", resolution=1.0,
|
|
max_range=10000.0, power=0.0, min_delay=1000))
|
|
# 风扇转速作为附加传感器 (映射到相对湿度类型以承载真实数据)
|
|
if type_ == -1 or type_ == Sensor.TYPE_RELATIVE_HUMIDITY:
|
|
for fan in LinuxSensors.get_fan_speeds():
|
|
sensors.append(Sensor(
|
|
Sensor.TYPE_RELATIVE_HUMIDITY,
|
|
name=f"Fan {fan.get('name', '')}",
|
|
vendor="Linux", resolution=1.0,
|
|
max_range=10000.0, power=0.0, min_delay=1000))
|
|
except Exception:
|
|
pass
|
|
# 若未取到真实数据则回退到模拟传感器
|
|
if not sensors:
|
|
if type_ == -1:
|
|
sensors = list(self._sensors.values())
|
|
else:
|
|
sensor = self._sensors.get(type_)
|
|
if sensor:
|
|
sensors = [sensor]
|
|
return sensors
|
|
|
|
def getSensors(self) -> List[Sensor]:
|
|
return list(self._sensors.values())
|
|
|
|
def registerListener(self, listener: SensorEventListener, sensor: Sensor,
|
|
samplingPeriodUs: int, maxReportLatencyUs: int = 0) -> bool:
|
|
if sensor.getType() not in self._sensors:
|
|
return False
|
|
if listener not in self._listeners:
|
|
self._listeners[id(listener)] = []
|
|
self._listeners[id(listener)].append((listener, sensor, samplingPeriodUs))
|
|
return True
|
|
|
|
def unregisterListener(self, listener: SensorEventListener, sensor: Sensor = None) -> bool:
|
|
lid = id(listener)
|
|
if lid in self._listeners:
|
|
if sensor:
|
|
self._listeners[lid] = [
|
|
(l, s, d) for l, s, d in self._listeners[lid]
|
|
if s.getType() != sensor.getType()
|
|
]
|
|
if not self._listeners[lid]:
|
|
del self._listeners[lid]
|
|
else:
|
|
del self._listeners[lid]
|
|
return True
|
|
return False
|
|
|
|
@staticmethod
|
|
def getOrientation(R: List[float], values: List[float]) -> List[float]:
|
|
if len(R) >= 9 and len(values) >= 3:
|
|
values[0] = math.degrees(math.atan2(R[1], R[4])) # azimuth
|
|
values[1] = math.degrees(math.asin(-R[7])) # pitch
|
|
values[2] = math.degrees(math.atan2(-R[6], R[8])) # roll
|
|
return values
|
|
|
|
@staticmethod
|
|
def getRotationMatrix(R: List[float], I: List[float],
|
|
gravity: List[float], geomagnetic: List[float]) -> bool:
|
|
"""根据重力和地磁向量计算旋转矩阵 R 和倾斜矩阵 I。
|
|
算法对齐 Android SensorManager.getRotationMatrix。"""
|
|
if len(R) < 9:
|
|
R.extend([0.0] * (9 - len(R)))
|
|
gx, gy, gz = gravity[0], gravity[1], gravity[2]
|
|
mx, my, mz = geomagnetic[0], geomagnetic[1], geomagnetic[2]
|
|
# 归一化重力向量 -> H
|
|
g_len = math.sqrt(gx * gx + gy * gy + gz * gz)
|
|
if g_len < 1e-10:
|
|
return False
|
|
hx = gx / g_len
|
|
hy = gy / g_len
|
|
hz = gz / g_len
|
|
# 叉积 A = H x M (东方向)
|
|
ax = hy * mz - hz * my
|
|
ay = hz * mx - hx * mz
|
|
az = hx * my - hy * mx
|
|
a_len = math.sqrt(ax * ax + ay * ay + az * az)
|
|
if a_len < 1e-10:
|
|
return False
|
|
ax /= a_len
|
|
ay /= a_len
|
|
az /= a_len
|
|
# 重新计算 M = A x H
|
|
ex = ay * hz - az * hy
|
|
ey = az * hx - ax * hz
|
|
ez = ax * hy - ay * hx
|
|
# 填充旋转矩阵 R (列优先,Android 格式)
|
|
R[0] = ax
|
|
R[1] = ay
|
|
R[2] = az
|
|
R[3] = ex
|
|
R[4] = ey
|
|
R[5] = ez
|
|
R[6] = hx
|
|
R[7] = hy
|
|
R[8] = hz
|
|
# 填充倾斜矩阵 I
|
|
if I is not None and len(I) >= 9:
|
|
inv_h = 1.0 / g_len
|
|
inv_m = 1.0 / math.sqrt(mx * mx + my * my + mz * mz) if (mx * mx + my * my + mz * mz) > 0 else 0
|
|
I[0] = inv_h * hx
|
|
I[1] = inv_h * hy
|
|
I[2] = inv_h * hz
|
|
I[3] = inv_m * mx
|
|
I[4] = inv_m * my
|
|
I[5] = inv_m * mz
|
|
I[6] = inv_h * ex
|
|
I[7] = inv_h * ey
|
|
I[8] = inv_h * ez
|
|
return True
|
|
|
|
@staticmethod
|
|
def getAltitude(p0: float, p: float) -> float:
|
|
return 44330.0 * (1.0 - math.pow(p / p0, 1.0 / 5.255))
|
|
|
|
@staticmethod
|
|
def getInclination(I: List[float]) -> float:
|
|
"""根据倾斜矩阵 I 计算地磁倾角(弧度)。"""
|
|
if len(I) >= 9:
|
|
# I[5] = cos(el), I[2] = sin(el)
|
|
# 倾角 = atan2(I[2], I[5])
|
|
return math.atan2(I[2], I[5])
|
|
return 0.0
|
|
|
|
@staticmethod
|
|
def remapCoordinateSystem(inR: List[float], X: int, Y: int,
|
|
outR: List[float]) -> bool:
|
|
"""根据指定的 X/Y 轴重映射坐标系。"""
|
|
if len(inR) < 9 or len(outR) < 9:
|
|
return False
|
|
# Android 轴常量
|
|
AXIS_X = 1
|
|
AXIS_Y = 2
|
|
AXIS_Z = 3
|
|
AXIS_MINUS_X = -1
|
|
AXIS_MINUS_Y = -2
|
|
AXIS_MINUS_Z = -3
|
|
# 将轴常量映射到列索引和符号
|
|
axis_map = {
|
|
AXIS_X: (0, 1), AXIS_Y: (1, 1), AXIS_Z: (2, 1),
|
|
AXIS_MINUS_X: (0, -1), AXIS_MINUS_Y: (1, -1), AXIS_MINUS_Z: (2, -1),
|
|
}
|
|
if X not in axis_map or Y not in axis_map:
|
|
return False
|
|
x_col, x_sign = axis_map[X]
|
|
y_col, y_sign = axis_map[Y]
|
|
# Z = X x Y
|
|
z_col = 3 - x_col - y_col
|
|
z_sign = x_sign * y_sign
|
|
# 如果 X 和 Y 不正交
|
|
if abs(x_col - y_col) != 1 and not (x_col == 0 and y_col == 2) and not (x_col == 2 and y_col == 0):
|
|
z_sign = -z_sign
|
|
# 填充输出矩阵(列优先)
|
|
for row in range(3):
|
|
outR[row * 3 + 0] = inR[row * 3 + x_col] * x_sign
|
|
outR[row * 3 + 1] = inR[row * 3 + y_col] * y_sign
|
|
outR[row * 3 + 2] = inR[row * 3 + z_col] * z_sign
|
|
return True
|
|
|
|
def getDeviceId(self) -> str:
|
|
"""返回传感器设备 ID。尝试从 /sys 读取真实设备标识。"""
|
|
import os as _os
|
|
# 尝试从 /sys/class/sensor 读取
|
|
try:
|
|
base = "/sys/class/sensor"
|
|
if _os.path.isdir(base):
|
|
names = _os.listdir(base)
|
|
if names:
|
|
return names[0]
|
|
except (OSError, IOError):
|
|
pass
|
|
# 尝试从 /sys/bus/iio/devices 读取
|
|
try:
|
|
base = "/sys/bus/iio/devices"
|
|
if _os.path.isdir(base):
|
|
names = _os.listdir(base)
|
|
if names:
|
|
return names[0]
|
|
except (OSError, IOError):
|
|
pass
|
|
return "sensor_hub_0"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# HardwareBuffer
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class HardwareBuffer(Stub):
|
|
"""HardwareBuffer — 硬件缓冲 (API 26+)"""
|
|
|
|
USAGE_CPU_READ_OFTEN = 0x00000003
|
|
USAGE_CPU_WRITE_OFTEN = 0x00000030
|
|
USAGE_GPU_SAMPLED_IMAGE = 0x00000100
|
|
USAGE_GPU_COLOR_OUTPUT = 0x00000200
|
|
USAGE_GPU_DATA_BUFFER = 0x00001000
|
|
USAGE_SENSOR_DIRECT_DATA = 0x00400000
|
|
USAGE_PROTECTED_CONTENT = 0x00004000
|
|
USAGE_VIDEO_ENCODE = 0x00010000
|
|
USAGE_GPU_FRAMEBUFFER = 0x00020000
|
|
USAGE_COMPOSER_OVERLAY = 0x00000800
|
|
USAGE_FRONT_BUFFER_QUEUE = 0x00008000
|
|
|
|
BLOB = 0x21
|
|
RGBA_8888 = 1
|
|
RGBA_FP16 = 22
|
|
RGBA_1010102 = 43
|
|
RGBX_8888 = 2
|
|
RGB_888 = 3
|
|
RGB_565 = 4
|
|
YCBCR_420_888 = 35
|
|
|
|
def __init__(self, width: int, height: int, format: int, layers: int,
|
|
usage: int, buffer_size: int = 0):
|
|
super().__init__()
|
|
self._width = width
|
|
self._height = height
|
|
self._format = format
|
|
self._layers = layers
|
|
self._usage = usage
|
|
self._buffer_size = buffer_size
|
|
self._closed = False
|
|
|
|
@property
|
|
def width(self) -> int:
|
|
return self._width
|
|
|
|
@property
|
|
def height(self) -> int:
|
|
return self._height
|
|
|
|
@property
|
|
def format(self) -> int:
|
|
return self._format
|
|
|
|
@property
|
|
def layers(self) -> int:
|
|
return self._layers
|
|
|
|
@property
|
|
def usage(self) -> int:
|
|
return self._usage
|
|
|
|
def destroy(self):
|
|
self.close()
|
|
|
|
def close(self):
|
|
self._closed = True
|
|
|
|
def isClosed(self) -> bool:
|
|
return self._closed
|
|
|
|
def isEmpty(self) -> bool:
|
|
return self._width == 0 or self._height == 0
|
|
|
|
def getWidth(self) -> int:
|
|
return self._width
|
|
|
|
def getHeight(self) -> int:
|
|
return self._height
|
|
|
|
def getFormat(self) -> int:
|
|
return self._format
|
|
|
|
def getLayers(self) -> int:
|
|
return self._layers
|
|
|
|
def getUsage(self) -> int:
|
|
return self._usage
|
|
|
|
def getStride(self) -> int:
|
|
return self._width
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# BatteryManager (Constants)
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class BatteryManager(Stub):
|
|
"""BatteryManager — 电池管理器常量 (实际实现在 android.os)"""
|
|
|
|
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_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
|
|
|
|
BATTERY_PLUGGED_AC = 1
|
|
BATTERY_PLUGGED_USB = 2
|
|
BATTERY_PLUGGED_WIRELESS = 4
|
|
|
|
ACTION_CHARGING = "android.intent.action.ACTION_CHARGING"
|
|
ACTION_DISCHARGING = "android.intent.action.ACTION_DISCHARGING"
|
|
EXTRA_STATUS = "status"
|
|
EXTRA_HEALTH = "health"
|
|
EXTRA_LEVEL = "level"
|
|
EXTRA_SCALE = "scale"
|
|
EXTRA_PLUGGED = "plugged"
|
|
EXTRA_TEMPERATURE = "temperature"
|
|
EXTRA_VOLTAGE = "voltage"
|
|
EXTRA_TECHNOLOGY = "technology"
|
|
EXTRA_CHARGE_COUNTER = "charge_counter"
|
|
EXTRA_PRESENT = "present"
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
|
|
def getIntProperty(self, id: int) -> int:
|
|
"""返回电池整型属性。优先使用 LinuxPower 读取真实数据。"""
|
|
if _USE_LINUX and LinuxPower is not None:
|
|
try:
|
|
info = LinuxPower.get_battery_info() or {}
|
|
if id == BatteryManager.BATTERY_PROPERTY_CAPACITY:
|
|
level = info.get("level", -1)
|
|
return int(level) if isinstance(level, (int, float)) and level >= 0 else -1
|
|
if id == BatteryManager.BATTERY_PROPERTY_STATUS:
|
|
status_map = {
|
|
"charging": BatteryManager.BATTERY_STATUS_CHARGING,
|
|
"discharging": BatteryManager.BATTERY_STATUS_DISCHARGING,
|
|
"not charging": BatteryManager.BATTERY_STATUS_NOT_CHARGING,
|
|
"full": BatteryManager.BATTERY_STATUS_FULL,
|
|
"unknown": BatteryManager.BATTERY_STATUS_UNKNOWN,
|
|
}
|
|
return status_map.get(str(info.get("status", "unknown")).lower(),
|
|
BatteryManager.BATTERY_STATUS_UNKNOWN)
|
|
if id == BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER:
|
|
voltage = info.get("voltage", 0)
|
|
return int(voltage * 1000) if isinstance(voltage, (int, float)) else 0
|
|
except Exception:
|
|
pass
|
|
# 回退到默认值
|
|
id_values = {
|
|
BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER: 3500,
|
|
BatteryManager.BATTERY_PROPERTY_CAPACITY: 85,
|
|
BatteryManager.BATTERY_PROPERTY_STATUS: BatteryManager.BATTERY_STATUS_DISCHARGING,
|
|
}
|
|
return id_values.get(id, 0)
|
|
|
|
def getLongProperty(self, id: int) -> int:
|
|
"""返回电池长整型属性。优先使用 LinuxPower 读取真实数据。"""
|
|
if _USE_LINUX and LinuxPower is not None:
|
|
try:
|
|
info = LinuxPower.get_battery_info() or {}
|
|
if id == BatteryManager.BATTERY_PROPERTY_CURRENT_NOW:
|
|
current = info.get("current_now", 0)
|
|
return int(current) if isinstance(current, (int, float)) else 0
|
|
if id == BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER:
|
|
voltage = info.get("voltage", 0)
|
|
return int(voltage * 3600000) if isinstance(voltage, (int, float)) else 0
|
|
except Exception:
|
|
pass
|
|
return self.getIntProperty(id)
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# BiometricManager (lightweight)
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class BiometricManager(Stub):
|
|
"""BiometricManager — 生物识别管理器 (轻量)"""
|
|
|
|
BIOMETRIC_SUCCESS = 0
|
|
BIOMETRIC_ERROR_HW_UNAVAILABLE = 1
|
|
BIOMETRIC_ERROR_NONE_ENROLLED = 11
|
|
BIOMETRIC_ERROR_NO_HARDWARE = 12
|
|
BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
|
|
def canAuthenticate(self) -> int:
|
|
return BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# Flashlight support
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class CameraFlashlight(Stub):
|
|
"""CameraFlashlight — 手电筒支持"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._on = False
|
|
|
|
def setTorchMode(self, cameraId: str, enabled: bool):
|
|
self._on = enabled
|
|
|
|
def isTorchModeEnabled(self, cameraId: str) -> bool:
|
|
return self._on
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# __all__
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
__all__ = [
|
|
"Sensor", "SensorManager", "SensorEvent", "SensorEventListener",
|
|
"SensorDelay",
|
|
"HardwareBuffer",
|
|
"BatteryManager",
|
|
"BiometricManager",
|
|
"CameraFlashlight",
|
|
] |