""" Android View Haptic 组件 — HapticFeedbackConstants, View 触觉扩展 方法签名完全对齐 Android SDK """ from __future__ import annotations import threading from typing import Any, Callable, Dict, List, Optional from .._core import Stub, AndroidOnlyError from ..java.lang import RuntimeException # ═══════════════════════════════════════════════════════════════ # HapticFeedbackConstants # ═══════════════════════════════════════════════════════════════ class HapticFeedbackConstants(Stub): """HapticFeedbackConstants — 触觉反馈常量""" LONG_PRESS = 0 TEXT_HANDLE_MOVE = 12 VIRTUAL_KEY = 1 VIRTUAL_KEY_RELEASE = 2 CLOCK_TICK = 4 KEYBOARD_PRESS = 3 KEYBOARD_RELEASE = 13 KEYBOARD_TAP = 3 CONFIRM = 5 REJECT = 6 GESTURE_START = 7 GESTURE_END = 8 EDGE_REJECT = 9 DRAG_CROSS = 10 SEGMENT_FREQUENT_TICK = 11 SEGMENT_TICK = 14 SEGMENT_TICK_VERY_SMALL = 15 SEGMENT_TICK_SMALL = 16 SEGMENT_TICK_MEDIUM = 17 SEGMENT_TICK_LARGE = 18 ENTRY_BUMP = 19 EXIT_BUMP = 20 REORDER = 21 CONTEXT_CLICK = 22 FLAG_IGNORE_VIEW_SETTING = 1 FLAG_IGNORE_GLOBAL_SETTING = 2 _is_enabled = True _global_enabled = True _lock = threading.Lock() def __init__(self): super().__init__() @classmethod def isEnabled(cls) -> bool: with cls._lock: return cls._is_enabled and cls._global_enabled @classmethod def setEnabled(cls, enabled: bool) -> None: with cls._lock: cls._is_enabled = enabled @classmethod def setGlobalEnabled(cls, enabled: bool) -> None: with cls._lock: cls._global_enabled = enabled @classmethod def isGlobalEnabled(cls) -> bool: with cls._lock: return cls._global_enabled @classmethod def toString(cls) -> str: return (f"HapticFeedbackConstants(enabled={cls._is_enabled}, " f"global={cls._global_enabled})") @classmethod def getFeedbackConstantName(cls, feedback_constant: int) -> str: names = { 0: "LONG_PRESS", 12: "TEXT_HANDLE_MOVE", 1: "VIRTUAL_KEY", 2: "VIRTUAL_KEY_RELEASE", 4: "CLOCK_TICK", 3: "KEYBOARD_PRESS", 13: "KEYBOARD_RELEASE", 5: "CONFIRM", 6: "REJECT", 7: "GESTURE_START", 8: "GESTURE_END", 9: "EDGE_REJECT", 10: "DRAG_CROSS", 11: "SEGMENT_FREQUENT_TICK", 14: "SEGMENT_TICK", 15: "SEGMENT_TICK_VERY_SMALL", 16: "SEGMENT_TICK_SMALL", 17: "SEGMENT_TICK_MEDIUM", 18: "SEGMENT_TICK_LARGE", 19: "ENTRY_BUMP", 20: "EXIT_BUMP", 21: "REORDER", 22: "CONTEXT_CLICK", } return names.get(feedback_constant, f"UNKNOWN({feedback_constant})") # ═══════════════════════════════════════════════════════════════ # View Haptic 方法扩展 # ═══════════════════════════════════════════════════════════════ class ViewHapticExtensions(Stub): """ViewHapticExtensions — 视图触觉扩展方法""" def __init__(self, view: Any = None): super().__init__() self._view = view self._haptic_enabled = True self._haptic_feedback_enabled = True self._exclude_gesture_region = False self._lock = threading.Lock() def performHapticFeedback(self, feedback_constant: int, flags: int = 0) -> bool: """执行触觉反馈""" if not self._haptic_enabled: return False if flags & HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING: pass if flags & HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING: pass if not HapticFeedbackConstants.isEnabled(): return False return self._do_haptic(feedback_constant) def _do_haptic(self, feedback_constant: int) -> bool: """实际执行触觉反馈效果 (桌面模拟)""" return True def setHapticFeedbackEnabled(self, enabled: bool) -> None: with self._lock: self._haptic_feedback_enabled = enabled def isHapticFeedbackEnabled(self) -> bool: return self._haptic_feedback_enabled def setHapticEnabled(self, enabled: bool) -> None: with self._lock: self._haptic_enabled = enabled def isHapticEnabled(self) -> bool: return self._haptic_enabled def setExcludeFromExcludeGestureRegion(self, exclude: bool) -> None: self._exclude_gesture_region = exclude def isExcludedFromExcludeGestureRegion(self) -> bool: return self._exclude_gesture_region def toString(self) -> str: return (f"ViewHapticExtensions(enabled={self._haptic_enabled}, " f"feedback={self._haptic_feedback_enabled})") # ═══════════════════════════════════════════════════════════════ # HapticPlayer (触觉播放器) # ═══════════════════════════════════════════════════════════════ class HapticPlayer(Stub): """HapticPlayer — 触觉播放器""" PLAY_STATUS_IDLE = 0 PLAY_STATUS_PLAYING = 1 PLAY_STATUS_PAUSED = 2 PLAY_STATUS_STOPPED = 3 def __init__(self): super().__init__() self._status = self.PLAY_STATUS_IDLE self._duration: int = 0 self._amplitude: float = 1.0 self._frequency: float = 150.0 self._on_completion: Optional[Callable] = None self._lock = threading.Lock() def play(self) -> None: with self._lock: self._status = self.PLAY_STATUS_PLAYING def pause(self) -> None: with self._lock: self._status = self.PLAY_STATUS_PAUSED def stop(self) -> None: with self._lock: self._status = self.PLAY_STATUS_STOPPED if self._on_completion: self._on_completion() def setDuration(self, duration_ms: int) -> None: self._duration = max(0, duration_ms) def getDuration(self) -> int: return self._duration def setAmplitude(self, amplitude: float) -> None: self._amplitude = max(0.0, min(1.0, amplitude)) def getAmplitude(self) -> float: return self._amplitude def setFrequency(self, frequency_hz: float) -> None: self._frequency = max(20.0, min(1000.0, frequency_hz)) def getFrequency(self) -> float: return self._frequency def setOnCompletionListener(self, listener: Optional[Callable]) -> None: self._on_completion = listener def getStatus(self) -> int: return self._status def isPlaying(self) -> bool: return self._status == self.PLAY_STATUS_PLAYING def toString(self) -> str: return (f"HapticPlayer(duration={self._duration}ms, " f"amplitude={self._amplitude:.2f}, " f"freq={self._frequency:.0f}Hz)") # ═══════════════════════════════════════════════════════════════ # HapticFeedbackProvider # ═══════════════════════════════════════════════════════════════ class HapticFeedbackProvider(Stub): """HapticFeedbackProvider — 触觉反馈提供者""" def __init__(self): super().__init__() self._players: Dict[int, HapticPlayer] = {} self._default_duration: Dict[int, int] = { HapticFeedbackConstants.LONG_PRESS: 50, HapticFeedbackConstants.VIRTUAL_KEY: 10, HapticFeedbackConstants.KEYBOARD_TAP: 5, HapticFeedbackConstants.CONFIRM: 30, HapticFeedbackConstants.REJECT: 40, HapticFeedbackConstants.GESTURE_START: 20, HapticFeedbackConstants.GESTURE_END: 15, HapticFeedbackConstants.CLOCK_TICK: 5, } self._enabled = True def getPlayer(self, feedback_constant: int) -> HapticPlayer: if feedback_constant not in self._players: player = HapticPlayer() duration = self._default_duration.get(feedback_constant, 20) player.setDuration(duration) self._players[feedback_constant] = player return self._players[feedback_constant] def perform(self, feedback_constant: int, flags: int = 0) -> bool: if not self._enabled: return False player = self.getPlayer(feedback_constant) player.play() return True def cancel(self, feedback_constant: int) -> None: if feedback_constant in self._players: self._players[feedback_constant].stop() def cancelAll(self) -> None: for player in self._players.values(): player.stop() def setEnabled(self, enabled: bool) -> None: self._enabled = enabled def isEnabled(self) -> bool: return self._enabled def toString(self) -> str: return f"HapticFeedbackProvider(players={len(self._players)})" # ═══════════════════════════════════════════════════════════════ # HapticPattern / HapticComposer # ═══════════════════════════════════════════════════════════════ class HapticPattern(Stub): """HapticPattern — 触觉模式""" def __init__(self): super().__init__() self._segments: List[HapticPattern.Segment] = [] class Segment(Stub): """HapticPattern.Segment — 触觉片段""" TYPE_PAUSE = 0 TYPE_VIBRATION = 1 def __init__(self, type_val: int = TYPE_VIBRATION, duration_ms: int = 0, amplitude: float = 1.0): super().__init__() self._type = type_val self._duration = duration_ms self._amplitude = amplitude def getType(self) -> int: return self._type def getDuration(self) -> int: return self._duration def getAmplitude(self) -> float: return self._amplitude def toString(self) -> str: return (f"Segment(type={'VIBRATION' if self._type == 1 else 'PAUSE'}, " f"duration={self._duration}, amplitude={self._amplitude:.2f})") def addSegment(self, segment: Segment) -> None: self._segments.append(segment) def addVibration(self, duration_ms: int, amplitude: float = 1.0) -> None: self._segments.append(self.Segment(self.Segment.TYPE_VIBRATION, duration_ms, amplitude)) def addPause(self, duration_ms: int) -> None: self._segments.append(self.Segment(self.Segment.TYPE_PAUSE, duration_ms, 0.0)) def getSegments(self) -> List[Segment]: return list(self._segments) def getTotalDuration(self) -> int: return sum(s.getDuration() for s in self._segments) def clear(self) -> None: self._segments.clear() def toString(self) -> str: return f"HapticPattern(segments={len(self._segments)}, total={self.getTotalDuration()}ms)" class HapticComposer(Stub): """HapticComposer — 触觉合成器""" def __init__(self): super().__init__() self._pattern = HapticPattern() def addContinuous(self, duration_ms: int, amplitude: float = 1.0) -> "HapticComposer": self._pattern.addVibration(duration_ms, amplitude) return self def addPause(self, duration_ms: int) -> "HapticComposer": self._pattern.addPause(duration_ms) return self def addRamp(self, duration_ms: int, start_amplitude: float, end_amplitude: float, steps: int = 10) -> "HapticComposer": step_duration = duration_ms // max(1, steps) for i in range(steps): amp = start_amplitude + (end_amplitude - start_amplitude) * (i / max(1, steps - 1)) self._pattern.addVibration(step_duration, amp) return self def addPulse(self, duration_ms: int, amplitude: float = 1.0, count: int = 1, interval_ms: int = 100) -> "HapticComposer": for i in range(count): if i > 0: self._pattern.addPause(interval_ms) self._pattern.addVibration(duration_ms, amplitude) return self def addClick(self) -> "HapticComposer": return self.addPulse(10, 1.0, 1, 0) def addTick(self) -> "HapticComposer": return self.addPulse(5, 0.5, 1, 0) def build(self) -> HapticPattern: return self._pattern def reset(self) -> None: self._pattern = HapticPattern() def toString(self) -> str: return f"HapticComposer(pattern={self._pattern})"