""" 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 _USE_LINUX = IS_LINUX except ImportError: _USE_LINUX = False # ═══════════════════════════════════════════════════════════════ # 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: if len(R) < 9: R.extend([0] * (9 - len(R))) if len(R) >= 9: R[0] = 1.0 R[1] = 0.0 R[2] = 0.0 R[3] = 0.0 R[4] = 1.0 R[5] = 0.0 R[6] = 0.0 R[7] = 0.0 R[8] = 1.0 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: return 0.0 @staticmethod def remapCoordinateSystem(inR: List[float], X: int, Y: int, outR: List[float]) -> bool: if len(inR) >= 9 and len(outR) >= 9: for i in range(9): outR[i] = inR[i] return True def getDeviceId(self) -> str: 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: 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: return 0 # ═══════════════════════════════════════════════════════════════ # 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", ]