""" Android InputMethod Service 组件 — InputMethodService, AbstractInputMethodService, Keyboard, Key, KeyboardView 等 方法签名完全对齐 Android SDK (android.inputmethodservice 包) """ from __future__ import annotations import time from typing import Any, Callable, Dict, List, Optional, Set, Tuple from .._core import Stub, AndroidOnlyError from ..java.lang import RuntimeException # ═══════════════════════════════════════════════════════════════ # 常量 # ═══════════════════════════════════════════════════════════════ KEYCODE_UNKNOWN = 0 KEYCODE_SOFT_LEFT = 1 KEYCODE_SOFT_RIGHT = 2 KEYCODE_HOME = 3 KEYCODE_BACK = 4 KEYCODE_CALL = 5 KEYCODE_ENDCALL = 6 KEYCODE_0 = 7 KEYCODE_1 = 8 KEYCODE_2 = 9 KEYCODE_3 = 10 KEYCODE_4 = 11 KEYCODE_5 = 12 KEYCODE_6 = 13 KEYCODE_7 = 14 KEYCODE_8 = 15 KEYCODE_9 = 16 KEYCODE_STAR = 17 KEYCODE_POUND = 18 KEYCODE_DPAD_UP = 19 KEYCODE_DPAD_DOWN = 20 KEYCODE_DPAD_LEFT = 21 KEYCODE_DPAD_RIGHT = 22 KEYCODE_DPAD_CENTER = 23 KEYCODE_VOLUME_UP = 24 KEYCODE_VOLUME_DOWN = 25 KEYCODE_POWER = 26 KEYCODE_CAMERA = 27 KEYCODE_CLEAR = 28 KEYCODE_A = 29 KEYCODE_B = 30 KEYCODE_C = 31 KEYCODE_D = 32 KEYCODE_E = 33 KEYCODE_F = 34 KEYCODE_G = 35 KEYCODE_H = 36 KEYCODE_I = 37 KEYCODE_J = 38 KEYCODE_K = 39 KEYCODE_L = 40 KEYCODE_M = 41 KEYCODE_N = 42 KEYCODE_O = 43 KEYCODE_P = 44 KEYCODE_Q = 45 KEYCODE_R = 46 KEYCODE_S = 47 KEYCODE_T = 48 KEYCODE_U = 49 KEYCODE_V = 50 KEYCODE_W = 51 KEYCODE_X = 52 KEYCODE_Y = 53 KEYCODE_Z = 54 KEYCODE_COMMA = 55 KEYCODE_PERIOD = 56 KEYCODE_ALT_LEFT = 57 KEYCODE_ALT_RIGHT = 58 KEYCODE_SHIFT_LEFT = 59 KEYCODE_SHIFT_RIGHT = 60 KEYCODE_TAB = 61 KEYCODE_SPACE = 62 KEYCODE_SYM = 63 KEYCODE_EXPLORER = 64 KEYCODE_ENVELOPE = 65 KEYCODE_ENTER = 66 KEYCODE_DEL = 67 KEYCODE_GRAVE = 68 KEYCODE_MINUS = 69 KEYCODE_EQUALS = 70 KEYCODE_LEFT_BRACKET = 71 KEYCODE_RIGHT_BRACKET = 72 KEYCODE_BACKSLASH = 73 KEYCODE_SEMICOLON = 74 KEYCODE_APOSTROPHE = 75 KEYCODE_SLASH = 76 KEYCODE_AT = 77 KEYCODE_NUM = 78 KEYCODE_HEADSETHOOK = 79 KEYCODE_FOCUS = 80 KEYCODE_PLUS = 81 KEYCODE_MENU = 82 KEYCODE_NOTIFICATION = 83 KEYCODE_SEARCH = 84 KEYCODE_MEDIA_PLAY_PAUSE = 85 KEYCODE_MEDIA_STOP = 86 KEYCODE_MEDIA_NEXT = 87 KEYCODE_MEDIA_PREVIOUS = 88 KEYCODE_MEDIA_REWIND = 89 KEYCODE_MEDIA_FAST_FORWARD = 90 KEYCODE_MUTE = 91 KEYCODE_PAGE_UP = 92 KEYCODE_PAGE_DOWN = 93 KEYCODE_PICTSYMBOLS = 94 KEYCODE_SWITCH_CHARSET = 95 KEYCODE_BUTTON_A = 96 KEYCODE_BUTTON_B = 97 KEYCODE_BUTTON_C = 98 KEYCODE_BUTTON_X = 99 KEYCODE_BUTTON_Y = 100 KEYCODE_BUTTON_Z = 101 KEYCODE_BUTTON_L1 = 102 KEYCODE_BUTTON_R1 = 103 KEYCODE_BUTTON_L2 = 104 KEYCODE_BUTTON_R2 = 105 KEYCODE_BUTTON_THUMBL = 106 KEYCODE_BUTTON_THUMBR = 107 KEYCODE_BUTTON_START = 108 KEYCODE_BUTTON_SELECT = 109 KEYCODE_BUTTON_MODE = 110 KEYCODE_ESCAPE = 111 KEYCODE_FORWARD_DEL = 112 KEYCODE_CTRL_LEFT = 113 KEYCODE_CTRL_RIGHT = 114 KEYCODE_CAPS_LOCK = 115 KEYCODE_SCROLL_LOCK = 116 KEYCODE_META_LEFT = 117 KEYCODE_META_RIGHT = 118 KEYCODE_FUNCTION = 119 KEYCODE_SYSRQ = 120 KEYCODE_BREAK = 121 KEYCODE_MOVE_HOME = 122 KEYCODE_MOVE_END = 123 KEYCODE_INSERT = 124 KEYCODE_FORWARD = 125 KEYCODE_MEDIA_PLAY = 126 KEYCODE_MEDIA_PAUSE = 127 KEYCODE_MEDIA_CLOSE = 128 KEYCODE_MEDIA_EJECT = 129 KEYCODE_MEDIA_RECORD = 130 KEYCODE_F1 = 131 KEYCODE_F2 = 132 KEYCODE_F3 = 133 KEYCODE_F4 = 134 KEYCODE_F5 = 135 KEYCODE_F6 = 136 KEYCODE_F7 = 137 KEYCODE_F8 = 138 KEYCODE_F9 = 139 KEYCODE_F10 = 140 KEYCODE_F11 = 141 KEYCODE_F12 = 142 KEYCODE_NUM_LOCK = 143 KEYCODE_NUMPAD_0 = 144 KEYCODE_NUMPAD_1 = 145 KEYCODE_NUMPAD_2 = 146 KEYCODE_NUMPAD_3 = 147 KEYCODE_NUMPAD_4 = 148 KEYCODE_NUMPAD_5 = 149 KEYCODE_NUMPAD_6 = 150 KEYCODE_NUMPAD_7 = 151 KEYCODE_NUMPAD_8 = 152 KEYCODE_NUMPAD_9 = 153 KEYCODE_NUMPAD_DIVIDE = 154 KEYCODE_NUMPAD_MULTIPLY = 155 KEYCODE_NUMPAD_SUBTRACT = 156 KEYCODE_NUMPAD_ADD = 157 KEYCODE_NUMPAD_DOT = 158 KEYCODE_NUMPAD_COMMA = 159 KEYCODE_NUMPAD_ENTER = 160 KEYCODE_NUMPAD_EQUALS = 161 KEYCODE_NUMPAD_LEFT_PAREN = 162 KEYCODE_NUMPAD_RIGHT_PAREN = 163 KEYCODE_VOLUME_MUTE = 164 KEYCODE_INFO = 165 KEYCODE_CHANNEL_UP = 166 KEYCODE_CHANNEL_DOWN = 167 KEYCODE_ZOOM_IN = 168 KEYCODE_ZOOM_OUT = 169 KEYCODE_TV = 170 KEYCODE_WINDOW = 171 KEYCODE_GUIDE = 172 KEYCODE_DVR = 173 KEYCODE_BOOKMARK = 174 KEYCODE_CAPTIONS = 175 KEYCODE_SETTINGS = 176 KEYCODE_TV_POWER = 177 KEYCODE_TV_INPUT = 178 KEYCODE_STB_POWER = 179 KEYCODE_STB_INPUT = 180 KEYCODE_AVR_POWER = 181 KEYCODE_AVR_INPUT = 182 KEYCODE_PROG_RED = 183 KEYCODE_PROG_GREEN = 184 KEYCODE_PROG_YELLOW = 185 KEYCODE_PROG_BLUE = 186 KEYCODE_APP_SWITCH = 187 KEYCODE_BUTTON_1 = 188 KEYCODE_BUTTON_2 = 189 KEYCODE_BUTTON_3 = 190 KEYCODE_BUTTON_4 = 191 KEYCODE_BUTTON_5 = 192 KEYCODE_BUTTON_6 = 193 KEYCODE_BUTTON_7 = 194 KEYCODE_BUTTON_8 = 195 KEYCODE_BUTTON_9 = 196 KEYCODE_BUTTON_10 = 197 KEYCODE_BUTTON_11 = 198 KEYCODE_BUTTON_12 = 199 KEYCODE_BUTTON_13 = 200 KEYCODE_BUTTON_14 = 201 KEYCODE_BUTTON_15 = 202 KEYCODE_BUTTON_16 = 203 KEYCODE_LANGUAGE_SWITCH = 204 KEYCODE_MANNER_MODE = 205 KEYCODE_3D_MODE = 206 KEYCODE_CONTACTS = 207 KEYCODE_CALENDAR = 208 KEYCODE_MUSIC = 209 KEYCODE_CALCULATOR = 210 KEYCODE_ZENKAKU_HANKAKU = 211 KEYCODE_EISU = 212 KEYCODE_MUHENKAN = 213 KEYCODE_HENKAN = 214 KEYCODE_KATAKANA_HIRAGANA = 215 KEYCODE_YEN = 216 KEYCODE_RO = 217 KEYCODE_KANA = 218 KEYCODE_ASSIST = 219 KEYCODE_BRIGHTNESS_DOWN = 220 KEYCODE_BRIGHTNESS_UP = 221 KEYCODE_MEDIA_AUDIO_TRACK = 222 KEYCODE_SLEEP = 223 KEYCODE_WAKEUP = 224 KEYCODE_PAIRING = 225 KEYCODE_MEDIA_TOP_MENU = 226 KEYCODE_11 = 227 KEYCODE_12 = 228 KEYCODE_LAST_CHANNEL = 229 KEYCODE_TV_DATA_SERVICE = 230 KEYCODE_VOICE_ASSIST = 231 KEYCODE_TV_RADIO_SERVICE = 232 KEYCODE_TV_TELETEXT = 233 KEYCODE_TV_NUMBER_ENTRY = 234 KEYCODE_TV_TERRESTRIAL_ANALOG = 235 KEYCODE_TV_TERRESTRIAL_DIGITAL = 236 KEYCODE_TV_SATELLITE = 237 KEYCODE_TV_SATELLITE_BS = 238 KEYCODE_TV_SATELLITE_CS = 239 KEYCODE_TV_SATELLITE_SERVICE = 240 KEYCODE_TV_NETWORK = 241 KEYCODE_TV_ANTENNA_CABLE = 242 KEYCODE_TV_INPUT_HDMI_1 = 243 KEYCODE_TV_INPUT_HDMI_2 = 244 KEYCODE_TV_INPUT_HDMI_3 = 245 KEYCODE_TV_INPUT_HDMI_4 = 246 KEYCODE_TV_INPUT_COMPOSITE_1 = 247 KEYCODE_TV_INPUT_COMPOSITE_2 = 248 KEYCODE_TV_INPUT_COMPONENT_1 = 249 KEYCODE_TV_INPUT_COMPONENT_2 = 250 KEYCODE_TV_INPUT_VGA_1 = 251 KEYCODE_TV_AUDIO_DESCRIPTION = 252 KEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP = 253 KEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN = 254 KEYCODE_TV_ZOOM_MODE = 255 KEYCODE_TV_CONTENTS_MENU = 256 KEYCODE_TV_MEDIA_CONTEXT_MENU = 257 KEYCODE_TV_TIMER_PROGRAMMING = 258 KEYCODE_HELP = 259 KEYCODE_NAVIGATE_PREVIOUS = 260 KEYCODE_NAVIGATE_NEXT = 261 KEYCODE_NAVIGATE_IN = 262 KEYCODE_NAVIGATE_OUT = 263 KEYCODE_STEM_PRIMARY = 264 KEYCODE_STEM_1 = 265 KEYCODE_STEM_2 = 266 KEYCODE_STEM_3 = 267 KEYCODE_DPAD_UP_LEFT = 268 KEYCODE_DPAD_DOWN_LEFT = 269 KEYCODE_DPAD_UP_RIGHT = 270 KEYCODE_DPAD_DOWN_RIGHT = 271 KEYCODE_MEDIA_SKIP_FORWARD = 272 KEYCODE_MEDIA_SKIP_BACKWARD = 273 KEYCODE_MEDIA_STEP_FORWARD = 274 KEYCODE_MEDIA_STEP_BACKWARD = 275 KEYCODE_MEDIA_TOP_MENU_2 = 276 KEYCODE_SOFT_SLEEP = 277 KEYCODE_CUT = 278 KEYCODE_COPY = 279 KEYCODE_PASTE = 280 KEYCODE_SYSTEM_NAVIGATION_UP = 281 KEYCODE_SYSTEM_NAVIGATION_DOWN = 282 KEYCODE_SYSTEM_NAVIGATION_LEFT = 283 KEYCODE_SYSTEM_NAVIGATION_RIGHT = 284 KEYCODE_ALL_APPS = 285 KEYCODE_REFRESH = 286 KEYCODE_THUMBS_UP = 287 KEYCODE_THUMBS_DOWN = 288 KEYCODE_PROFILE_SWITCH = 289 KEYCODE_VIDEO_APP_1 = 290 KEYCODE_VIDEO_APP_2 = 291 KEYCODE_VIDEO_APP_3 = 292 KEYCODE_VIDEO_APP_4 = 293 KEYCODE_VIDEO_APP_5 = 294 KEYCODE_VIDEO_APP_6 = 295 KEYCODE_VIDEO_APP_7 = 296 KEYCODE_VIDEO_APP_8 = 297 KEYCODE_FEATURED_APP_1 = 298 KEYCODE_FEATURED_APP_2 = 299 KEYCODE_FEATURED_APP_3 = 300 KEYCODE_FEATURED_APP_4 = 301 KEYCODE_DEMO_APP_1 = 302 KEYCODE_DEMO_APP_2 = 303 KEYCODE_DEMO_APP_3 = 304 KEYCODE_DEMO_APP_4 = 305 # ═══════════════════════════════════════════════════════════════ # Key # ═══════════════════════════════════════════════════════════════ class Key(Stub): """Key — 键盘按键""" KEY_FLAG_ALT = 1 KEY_FLAG_SHIFT = 2 KEY_FLAG_CAPS_LOCK = 4 KEY_FLAG_CTRL = 8 KEY_FLAG_META = 16 KEY_FLAG_FUNCTION = 32 KEY_FLAG_MODIFIER = 64 KEY_FLAG_REPEAT = 128 KEY_FLAG_LONGPRESS = 256 KEY_FLAG_CLUSTER = 512 KEY_FLAG_CANCELED = 1024 KEY_FLAG_EDGE = 2048 KEY_FLAG_GESTURE = 4096 KEY_FLAG_VIRTUAL = 8192 KEY_TYPE_NORMAL = 0 KEY_TYPE_MODIFIER = 1 KEY_TYPE_SPACE = 2 KEY_TYPE_DONE = 3 KEY_TYPE_DELETE = 4 KEY_TYPE_ACTION = 5 KEY_TYPE_CANCEL = 6 KEY_TYPE_MODE_CHANGE = 7 KEY_TYPE_SHIFT = 8 KEY_TYPE_ALT = 9 KEY_TYPE_CTRL = 10 KEY_TYPE_FUNCTION = 11 KEY_TYPE_EMOJI = 12 KEY_TYPE_LANGUAGE = 13 def __init__(self, key_code: int = KEYCODE_UNKNOWN, label: str = "", type: int = KEY_TYPE_NORMAL): super().__init__() self._key_code = key_code self._label = label self._type = type self._text: str = "" self._icon: Any = None self._x: int = 0 self._y: int = 0 self._width: int = 0 self._height: int = 0 self._gap: int = 0 self._sticky: bool = False self._modifier: bool = False self._enabled: bool = True self._repeating: bool = False self._edge_flags: int = 0 self._popup_characters: str = "" self._secondary_labels: List[str] = [] def getKeyCode(self) -> int: return self._key_code def setKeyCode(self, key_code: int): self._key_code = key_code def getLabel(self) -> str: return self._label def setLabel(self, label: str): self._label = label def getType(self) -> int: return self._type def setType(self, type_: int): self._type = type_ def getText(self) -> str: return self._text def setText(self, text: str): self._text = text def getIcon(self) -> Any: return self._icon def setIcon(self, icon: Any): self._icon = icon def getX(self) -> int: return self._x def setX(self, x: int): self._x = x def getY(self) -> int: return self._y def setY(self, y: int): self._y = y def getWidth(self) -> int: return self._width def setWidth(self, width: int): self._width = width def getHeight(self) -> int: return self._height def setHeight(self, height: int): self._height = height def getGap(self) -> int: return self._gap def setGap(self, gap: int): self._gap = gap def isSticky(self) -> bool: return self._sticky def setSticky(self, sticky: bool): self._sticky = sticky def isModifier(self) -> bool: return self._modifier def setModifier(self, modifier: bool): self._modifier = modifier def isEnabled(self) -> bool: return self._enabled def setEnabled(self, enabled: bool): self._enabled = enabled def isRepeating(self) -> bool: return self._repeating def setRepeating(self, repeating: bool): self._repeating = repeating def getPopupCharacters(self) -> str: return self._popup_characters def setPopupCharacters(self, chars: str): self._popup_characters = chars def getSecondaryLabels(self) -> List[str]: return list(self._secondary_labels) def setSecondaryLabels(self, labels: List[str]): self._secondary_labels = list(labels) def isShiftKey(self) -> bool: return self._type == Key.KEY_TYPE_SHIFT def isAltKey(self) -> bool: return self._type == Key.KEY_TYPE_ALT def isCtrlKey(self) -> bool: return self._type == Key.KEY_TYPE_CTRL def isFunctionKey(self) -> bool: return self._type == Key.KEY_TYPE_FUNCTION def toString(self) -> str: return f"Key(code={self._key_code}, label={self._label}, type={self._type})" # ═══════════════════════════════════════════════════════════════ # Keyboard # ═══════════════════════════════════════════════════════════════ class Keyboard(Stub): """Keyboard — 键盘布局""" KEYBOARD_TYPE_ALPHA = 0 KEYBOARD_TYPE_NUMERIC = 1 KEYBOARD_TYPE_PHONE = 2 KEYBOARD_TYPE_SYMBOLS = 3 KEYBOARD_TYPE_EMOJI = 4 KEYBOARD_TYPE_URL = 5 KEYBOARD_TYPE_EMAIL = 6 KEYBOARD_TYPE_IME = 7 EDGE_LEFT = 1 EDGE_RIGHT = 2 EDGE_TOP = 4 EDGE_BOTTOM = 8 def __init__(self, context: Any = None, layout_id: int = 0, keyboard_type: int = KEYBOARD_TYPE_ALPHA): super().__init__() self._context = context self._layout_id = layout_id self._keyboard_type = keyboard_type self._keys: List[Key] = [] self._shift_keys: List[Key] = [] self._alt_keys: List[Key] = [] self._ctrl_keys: List[Key] = [] self._function_keys: List[Key] = [] self._modifier_keys: List[Key] = [] self._key_width: int = 0 self._key_height: int = 0 self._horizontal_gap: int = 0 self._vertical_gap: int = 0 self._default_row_height: int = 0 self._rows: int = 0 self._columns: int = 0 self._label: str = "" self._is_shifted: bool = False self._is_alt: bool = False self._is_ctrl: bool = False self._is_function: bool = False def getKeys(self) -> List[Key]: return list(self._keys) def setKeys(self, keys: List[Key]): self._keys = list(keys) def addKey(self, key: Key): self._keys.append(key) def removeKey(self, key: Key): self._keys = [k for k in self._keys if k is not key] def getKeyByCode(self, key_code: int) -> Optional[Key]: for k in self._keys: if k.getKeyCode() == key_code: return k return None def getShiftKeys(self) -> List[Key]: return list(self._shift_keys) def getAltKeys(self) -> List[Key]: return list(self._alt_keys) def getModifierKeys(self) -> List[Key]: return list(self._modifier_keys) def getKeyWidth(self) -> int: return self._key_width def setKeyWidth(self, width: int): self._key_width = width def getKeyHeight(self) -> int: return self._key_height def setKeyHeight(self, height: int): self._key_height = height def getHorizontalGap(self) -> int: return self._horizontal_gap def setHorizontalGap(self, gap: int): self._horizontal_gap = gap def getVerticalGap(self) -> int: return self._vertical_gap def setVerticalGap(self, gap: int): self._vertical_gap = gap def getRows(self) -> int: return self._rows def setRows(self, rows: int): self._rows = rows def getColumns(self) -> int: return self._columns def setColumns(self, cols: int): self._columns = cols def getLabel(self) -> str: return self._label def setLabel(self, label: str): self._label = label def isShifted(self) -> bool: return self._is_shifted def setShifted(self, shifted: bool): self._is_shifted = shifted def getKeyboardType(self) -> int: return self._keyboard_type def setKeyboardType(self, type_: int): self._keyboard_type = type_ def toString(self) -> str: return f"Keyboard(keys={len(self._keys)}, type={self._keyboard_type})" # ═══════════════════════════════════════════════════════════════ # KeyboardView # ═══════════════════════════════════════════════════════════════ class KeyboardView(Stub): """KeyboardView — 键盘视图""" def __init__(self, context: Any = None, keyboard: Keyboard = None): super().__init__() self._context = context self._keyboard = keyboard self._is_visible: bool = False self._is_preview_enabled: bool = True self._is_proximity_correction_enabled: bool = True self._key_text_size: float = 20.0 self._key_text_color: int = 0xFF000000 self._background_color: int = 0xFFEEEEEE self._preview_background_color: int = 0xFFCCCCCC self._on_key_listener: Optional[Callable] = None self._on_key_action_listener: Optional[Callable] = None self._pressed_keys: Set[Key] = set() self._keyboard_width: int = 0 self._keyboard_height: int = 0 def setKeyboard(self, keyboard: Keyboard): self._keyboard = keyboard def getKeyboard(self) -> Optional[Keyboard]: return self._keyboard def setVisibility(self, visible: bool): self._is_visible = visible def isVisible(self) -> bool: return self._is_visible def setPreviewEnabled(self, enabled: bool): self._is_preview_enabled = enabled def isPreviewEnabled(self) -> bool: return self._is_preview_enabled def setProximityCorrectionEnabled(self, enabled: bool): self._is_proximity_correction_enabled = enabled def isProximityCorrectionEnabled(self) -> bool: return self._is_proximity_correction_enabled def setKeyTextSize(self, size: float): self._key_text_size = size def getKeyTextSize(self) -> float: return self._key_text_size def setKeyTextColor(self, color: int): self._key_text_color = color def getKeyTextColor(self) -> int: return self._key_text_color def setBackgroundColor(self, color: int): self._background_color = color def getBackgroundColor(self) -> int: return self._background_color def setOnKeyboardActionListener(self, listener: Callable): self._on_key_listener = listener def setOnKeyListener(self, listener: Callable): self._on_key_action_listener = listener def simulateKeyPress(self, key: Key): """模拟按键按下""" self._pressed_keys.add(key) if self._on_key_listener: self._on_key_listener.onKey(key.getKeyCode(), key) def simulateKeyRelease(self, key: Key): """模拟按键释放""" self._pressed_keys.discard(key) def isKeyPressed(self, key: Key) -> bool: return key in self._pressed_keys def invalidate(self): """重绘键盘""" pass def setKeyboardWidth(self, width: int): self._keyboard_width = width def setKeyboardHeight(self, height: int): self._keyboard_height = height def getKeyboardWidth(self) -> int: return self._keyboard_width def getKeyboardHeight(self) -> int: return self._keyboard_height def clampY(self, y: int) -> int: return max(0, min(y, self._keyboard_height)) def toString(self) -> str: return f"KeyboardView(visible={self._is_visible})" # ═══════════════════════════════════════════════════════════════ # AbstractInputMethodService # ═══════════════════════════════════════════════════════════════ class AbstractInputMethodService(Stub): """AbstractInputMethodService — 抽象输入法服务基类""" SERVICE_INTERFACE = "android.inputmethodservice.AbstractInputMethodService" SERVICE_META_DATA = "android.inputmethod" def __init__(self): super().__init__() self._keyboard_view: Optional[KeyboardView] = None self._keyboard: Optional[Keyboard] = None self._is_fullscreen: bool = False self._is_extracted_view_visible: bool = False self._is_input_view_visible: bool = False self._is_candidates_view_visible: bool = False self._input_view_started: bool = False def onCreate(self): pass def onDestroy(self): self._keyboard = None self._keyboard_view = None def onCreateInputView(self) -> Optional[KeyboardView]: return self._keyboard_view def onCreateCandidatesView(self) -> Any: return None def onStartInputView(self, info: Any, restarting: bool): self._is_input_view_visible = True self._input_view_started = True def onFinishInputView(self, finishing_input: bool): self._is_input_view_visible = False def onStartCandidatesView(self, info: Any, restarting: bool): self._is_candidates_view_visible = True def onFinishCandidatesView(self, finishing_input: bool): self._is_candidates_view_visible = False def onStartInput(self, input_connection: Any, editor_info: Any, restarting: bool): pass def onFinishInput(self): pass def setKeyboard(self, keyboard: Keyboard): self._keyboard = keyboard if self._keyboard_view: self._keyboard_view.setKeyboard(keyboard) def getKeyboard(self) -> Optional[Keyboard]: return self._keyboard def setKeyboardView(self, keyboard_view: KeyboardView): self._keyboard_view = keyboard_view def getKeyboardView(self) -> Optional[KeyboardView]: return self._keyboard_view def sendKeyChar(self, char: str): pass def sendDownUpKeyEvents(self, key_event_code: int): pass def switchToNextKeyboard(self): pass def switchToPreviousKeyboard(self): pass def hideWindow(self): self._is_input_view_visible = False self._is_candidates_view_visible = False def showWindow(self, show_flags: int): pass def requestHideSelf(self, flags: int): pass def requestShowSelf(self, flags: int): pass def isFullscreen(self) -> bool: return self._is_fullscreen def setFullscreen(self, fullscreen: bool): self._is_fullscreen = fullscreen def toString(self) -> str: return f"AbstractInputMethodService(fullscreen={self._is_fullscreen})" # ═══════════════════════════════════════════════════════════════ # InputMethodService # ═══════════════════════════════════════════════════════════════ class InputMethodService(AbstractInputMethodService): """InputMethodService — 输入法服务""" BACK_DISPOSITION_DEFAULT = 0 BACK_DISPOSITION_WILL_DISMISS = 1 BACK_DISPOSITION_WILL_NOT_DISMISS = 2 SHOW_INPUT_METHOD_PICKER = 1 SHOW_EXPLICIT = 2 SHOW_FORCED = 4 HIDE_EXPLICIT = 1 HIDE_NOT_ALWAYS = 2 HIDE_IMPLICIT_ONLY = 4 HIDE_BY_COMMAND = 8 KEYGUARD_DISABLE_NONE = 0 KEYGUARD_DISABLE_FEATURES_NONE = 0 KEYGUARD_DISABLE_FEATURES_ALL = 0x7FFFFFFF def __init__(self): super().__init__() self._back_disposition: int = InputMethodService.BACK_DISPOSITION_DEFAULT self._is_input_view_shown: bool = False self._is_candidates_view_shown: bool = False self._extract_view: Any = None self._input_connection: Any = None self._current_input_editor_info: Any = None self._settings_observer: Any = None def onInitializeInterface(self): pass def onBindInput(self): pass def onUnbindInput(self): pass def onStartInput(self, attribute: Any, restarting: bool): super().onStartInput(attribute, attribute, restarting) def onFinishInput(self): super().onFinishInput() def onDisplayCompletions(self, completions: List[Any]): pass def onUpdateSelection(self, old_sel_start: int, old_sel_end: int, new_sel_start: int, new_sel_end: int, candidates_start: int, candidates_end: int): pass def onUpdateExtractedText(self, token: int, extracted_text: Any): pass def onExtractedTextClicked(self): pass def onExtractingInputChanged(self, editor_info: Any): pass def onConfigureWindow(self, win: Any, is_fullscreen: bool, is_obscured: bool): pass def onEvaluateFullscreenMode(self) -> bool: return self._is_fullscreen def onEvaluateInputViewShown(self) -> bool: return True def onComputeInsets(self, out_insets: Any): pass def onKeyDown(self, key_code: int, event: Any) -> bool: return False def onKeyUp(self, key_code: int, event: Any) -> bool: return False def onKeyLongPress(self, key_code: int, event: Any) -> bool: return False def onKeyMultiple(self, key_code: int, count: int, event: Any) -> bool: return False def onTrackballEvent(self, event: Any) -> bool: return False def onGenericMotionEvent(self, event: Any) -> bool: return False def onWindowShown(self): pass def onWindowHidden(self): pass def setBackDisposition(self, disposition: int): self._back_disposition = disposition def getBackDisposition(self) -> int: return self._back_disposition def switchInputMethod(self, id: str): pass def changeInputMethodSubtype(self, subtype: Any): pass def getCurrentInputBinding(self) -> Any: return None def getCurrentInputConnection(self) -> Any: return self._input_connection def getCurrentInputEditorInfo(self) -> Any: return self._current_input_editor_info def getCurrentInputStarted(self) -> bool: return self._input_view_started def getLayoutInflater(self) -> Any: return None def getWindow(self) -> Any: return None def isInputViewShown(self) -> bool: return self._is_input_view_shown def setInputView(self, view: Any): self._is_input_view_shown = view is not None def showStatusIcon(self, icon_id: int): pass def hideStatusIcon(self): pass def sendDefaultEditorAction(self, from_enter_key: bool): pass def sendDownUpKeyEvents(self, key_event_code: int): pass def sendKeyChar(self, char: str): pass def updateFullscreenMode(self): pass def updateInputViewShown(self): pass def exposeCompletions(self, completions: List[Any]): self.onDisplayCompletions(completions) def toString(self) -> str: return f"InputMethodService(backDisposition={self._back_disposition})" # ═══════════════════════════════════════════════════════════════ # InputMethodServiceKeyboardHelper # ═══════════════════════════════════════════════════════════════ class InputMethodServiceKeyboardHelper(Stub): """InputMethodServiceKeyboardHelper — 输入法键盘辅助类""" @staticmethod def createDefaultQWERTYKeyboard(context: Any) -> Keyboard: kb = Keyboard(context, 0, Keyboard.KEYBOARD_TYPE_ALPHA) row1_chars = "qwertyuiop" row2_chars = "asdfghjkl" row3_chars = "zxcvbnm" for i, c in enumerate(row1_chars): k = Key(ord(c.upper()), c, Key.KEY_TYPE_NORMAL) k.setWidth(50) k.setHeight(60) k.setX(i * 55) kb.addKey(k) shift_key = Key(KEYCODE_SHIFT_LEFT, "Shift", Key.KEY_TYPE_SHIFT) shift_key.setWidth(70) shift_key.setHeight(60) kb.addKey(shift_key) space_key = Key(KEYCODE_SPACE, " ", Key.KEY_TYPE_SPACE) space_key.setWidth(200) space_key.setHeight(60) kb.addKey(space_key) del_key = Key(KEYCODE_DEL, "Del", Key.KEY_TYPE_DELETE) del_key.setWidth(70) del_key.setHeight(60) kb.addKey(del_key) enter_key = Key(KEYCODE_ENTER, "Enter", Key.KEY_TYPE_DONE) enter_key.setWidth(80) enter_key.setHeight(60) kb.addKey(enter_key) return kb @staticmethod def createNumericKeyboard(context: Any) -> Keyboard: kb = Keyboard(context, 0, Keyboard.KEYBOARD_TYPE_NUMERIC) for i in range(10): k = Key(KEYCODE_0 + i, str(i), Key.KEY_TYPE_NORMAL) k.setWidth(60) k.setHeight(60) kb.addKey(k) return kb @staticmethod def createPhoneKeyboard(context: Any) -> Keyboard: kb = Keyboard(context, 0, Keyboard.KEYBOARD_TYPE_PHONE) layout = [["1", "2", "3"], ["4", "5", "6"], ["7", "8", "9"], ["*", "0", "#"]] for row_idx, row in enumerate(layout): for col_idx, c in enumerate(row): k = Key(KEYCODE_0 + int(c) if c.isdigit() else ( KEYCODE_STAR if c == "*" else KEYCODE_POUND), c, Key.KEY_TYPE_NORMAL) k.setWidth(80) k.setHeight(60) k.setX(col_idx * 85) k.setY(row_idx * 65) kb.addKey(k) return kb @staticmethod def createSymbolKeyboard(context: Any) -> Keyboard: kb = Keyboard(context, 0, Keyboard.KEYBOARD_TYPE_SYMBOLS) symbols = "~!@#$%^&*()_+-=[]{}|;':\",./<>?" for i, c in enumerate(symbols): k = Key(KEYCODE_UNKNOWN, c, Key.KEY_TYPE_NORMAL) k.setWidth(45) k.setHeight(55) kb.addKey(k) return kb @staticmethod def createKeyboardView(context: Any, keyboard: Keyboard) -> KeyboardView: return KeyboardView(context, keyboard)