1 /* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.text.method; 18 19 import android.text.Editable; 20 import android.text.NoCopySpan; 21 import android.text.Spannable; 22 import android.text.Spanned; 23 import android.view.KeyEvent; 24 import android.view.View; 25 import android.view.KeyCharacterMap; 26 27 /** 28 * This base class encapsulates the behavior for tracking the state of 29 * meta keys such as SHIFT, ALT and SYM as well as the pseudo-meta state of selecting text. 30 * <p> 31 * Key listeners that care about meta state should inherit from this class; 32 * you should not instantiate this class directly in a client. 33 * </p><p> 34 * This class provides two mechanisms for tracking meta state that can be used 35 * together or independently. 36 * </p> 37 * <ul> 38 * <li>Methods such as {@link #handleKeyDown(long, int, KeyEvent)} and 39 * {@link #getMetaState(long)} operate on a meta key state bit mask.</li> 40 * <li>Methods such as {@link #onKeyDown(View, Editable, int, KeyEvent)} and 41 * {@link #getMetaState(CharSequence, int)} operate on meta key state flags stored 42 * as spans in an {@link Editable} text buffer. The spans only describe the current 43 * meta key state of the text editor; they do not carry any positional information.</li> 44 * </ul> 45 * <p> 46 * The behavior of this class varies according to the keyboard capabilities 47 * described by the {@link KeyCharacterMap} of the keyboard device such as 48 * the {@link KeyCharacterMap#getModifierBehavior() key modifier behavior}. 49 * </p><p> 50 * {@link MetaKeyKeyListener} implements chorded and toggled key modifiers. 51 * When key modifiers are toggled into a latched or locked state, the state 52 * of the modifier is stored in the {@link Editable} text buffer or in a 53 * meta state integer managed by the client. These latched or locked modifiers 54 * should be considered to be held <b>in addition to</b> those that the 55 * keyboard already reported as being pressed in {@link KeyEvent#getMetaState()}. 56 * In other words, the {@link MetaKeyKeyListener} augments the meta state 57 * provided by the keyboard; it does not replace it. This distinction is important 58 * to ensure that meta keys not handled by {@link MetaKeyKeyListener} such as 59 * {@link KeyEvent#KEYCODE_CAPS_LOCK} or {@link KeyEvent#KEYCODE_NUM_LOCK} are 60 * taken into consideration. 61 * </p><p> 62 * To ensure correct meta key behavior, the following pattern should be used 63 * when mapping key codes to characters: 64 * </p> 65 * <code> 66 * private char getUnicodeChar(TextKeyListener listener, KeyEvent event, Editable textBuffer) { 67 * // Use the combined meta states from the event and the key listener. 68 * int metaState = event.getMetaState() | listener.getMetaState(textBuffer); 69 * return event.getUnicodeChar(metaState); 70 * } 71 * </code> 72 */ 73 public abstract class MetaKeyKeyListener { 74 /** 75 * Flag that indicates that the SHIFT key is on. 76 * Value equals {@link KeyEvent#META_SHIFT_ON}. 77 */ 78 public static final int META_SHIFT_ON = KeyEvent.META_SHIFT_ON; 79 /** 80 * Flag that indicates that the ALT key is on. 81 * Value equals {@link KeyEvent#META_ALT_ON}. 82 */ 83 public static final int META_ALT_ON = KeyEvent.META_ALT_ON; 84 /** 85 * Flag that indicates that the SYM key is on. 86 * Value equals {@link KeyEvent#META_SYM_ON}. 87 */ 88 public static final int META_SYM_ON = KeyEvent.META_SYM_ON; 89 90 /** 91 * Flag that indicates that the SHIFT key is locked in CAPS mode. 92 */ 93 public static final int META_CAP_LOCKED = KeyEvent.META_CAP_LOCKED; 94 /** 95 * Flag that indicates that the ALT key is locked. 96 */ 97 public static final int META_ALT_LOCKED = KeyEvent.META_ALT_LOCKED; 98 /** 99 * Flag that indicates that the SYM key is locked. 100 */ 101 public static final int META_SYM_LOCKED = KeyEvent.META_SYM_LOCKED; 102 103 /** 104 * @hide pending API review 105 */ 106 public static final int META_SELECTING = KeyEvent.META_SELECTING; 107 108 // These bits are privately used by the meta key key listener. 109 // They are deliberately assigned values outside of the representable range of an 'int' 110 // so as not to conflict with any meta key states publicly defined by KeyEvent. 111 private static final long META_CAP_USED = 1L << 32; 112 private static final long META_ALT_USED = 1L << 33; 113 private static final long META_SYM_USED = 1L << 34; 114 115 private static final long META_CAP_PRESSED = 1L << 40; 116 private static final long META_ALT_PRESSED = 1L << 41; 117 private static final long META_SYM_PRESSED = 1L << 42; 118 119 private static final long META_CAP_RELEASED = 1L << 48; 120 private static final long META_ALT_RELEASED = 1L << 49; 121 private static final long META_SYM_RELEASED = 1L << 50; 122 123 private static final long META_SHIFT_MASK = META_SHIFT_ON 124 | META_CAP_LOCKED | META_CAP_USED 125 | META_CAP_PRESSED | META_CAP_RELEASED; 126 private static final long META_ALT_MASK = META_ALT_ON 127 | META_ALT_LOCKED | META_ALT_USED 128 | META_ALT_PRESSED | META_ALT_RELEASED; 129 private static final long META_SYM_MASK = META_SYM_ON 130 | META_SYM_LOCKED | META_SYM_USED 131 | META_SYM_PRESSED | META_SYM_RELEASED; 132 133 private static final Object CAP = new NoCopySpan.Concrete(); 134 private static final Object ALT = new NoCopySpan.Concrete(); 135 private static final Object SYM = new NoCopySpan.Concrete(); 136 private static final Object SELECTING = new NoCopySpan.Concrete(); 137 138 /** 139 * Resets all meta state to inactive. 140 */ resetMetaState(Spannable text)141 public static void resetMetaState(Spannable text) { 142 text.removeSpan(CAP); 143 text.removeSpan(ALT); 144 text.removeSpan(SYM); 145 text.removeSpan(SELECTING); 146 } 147 148 /** 149 * Gets the state of the meta keys. 150 * 151 * @param text the buffer in which the meta key would have been pressed. 152 * 153 * @return an integer in which each bit set to one represents a pressed 154 * or locked meta key. 155 */ getMetaState(CharSequence text)156 public static final int getMetaState(CharSequence text) { 157 return getActive(text, CAP, META_SHIFT_ON, META_CAP_LOCKED) | 158 getActive(text, ALT, META_ALT_ON, META_ALT_LOCKED) | 159 getActive(text, SYM, META_SYM_ON, META_SYM_LOCKED) | 160 getActive(text, SELECTING, META_SELECTING, META_SELECTING); 161 } 162 163 /** 164 * Gets the state of a particular meta key. 165 * 166 * @param meta META_SHIFT_ON, META_ALT_ON, META_SYM_ON, or META_SELECTING 167 * @param text the buffer in which the meta key would have been pressed. 168 * 169 * @return 0 if inactive, 1 if active, 2 if locked. 170 */ getMetaState(CharSequence text, int meta)171 public static final int getMetaState(CharSequence text, int meta) { 172 switch (meta) { 173 case META_SHIFT_ON: 174 return getActive(text, CAP, 1, 2); 175 176 case META_ALT_ON: 177 return getActive(text, ALT, 1, 2); 178 179 case META_SYM_ON: 180 return getActive(text, SYM, 1, 2); 181 182 case META_SELECTING: 183 return getActive(text, SELECTING, 1, 2); 184 185 default: 186 return 0; 187 } 188 } 189 getActive(CharSequence text, Object meta, int on, int lock)190 private static int getActive(CharSequence text, Object meta, 191 int on, int lock) { 192 if (!(text instanceof Spanned)) { 193 return 0; 194 } 195 196 Spanned sp = (Spanned) text; 197 int flag = sp.getSpanFlags(meta); 198 199 if (flag == LOCKED) { 200 return lock; 201 } else if (flag != 0) { 202 return on; 203 } else { 204 return 0; 205 } 206 } 207 208 /** 209 * Call this method after you handle a keypress so that the meta 210 * state will be reset to unshifted (if it is not still down) 211 * or primed to be reset to unshifted (once it is released). 212 */ adjustMetaAfterKeypress(Spannable content)213 public static void adjustMetaAfterKeypress(Spannable content) { 214 adjust(content, CAP); 215 adjust(content, ALT); 216 adjust(content, SYM); 217 } 218 219 /** 220 * Returns true if this object is one that this class would use to 221 * keep track of any meta state in the specified text. 222 */ isMetaTracker(CharSequence text, Object what)223 public static boolean isMetaTracker(CharSequence text, Object what) { 224 return what == CAP || what == ALT || what == SYM || 225 what == SELECTING; 226 } 227 228 /** 229 * Returns true if this object is one that this class would use to 230 * keep track of the selecting meta state in the specified text. 231 */ isSelectingMetaTracker(CharSequence text, Object what)232 public static boolean isSelectingMetaTracker(CharSequence text, Object what) { 233 return what == SELECTING; 234 } 235 adjust(Spannable content, Object what)236 private static void adjust(Spannable content, Object what) { 237 int current = content.getSpanFlags(what); 238 239 if (current == PRESSED) 240 content.setSpan(what, 0, 0, USED); 241 else if (current == RELEASED) 242 content.removeSpan(what); 243 } 244 245 /** 246 * Call this if you are a method that ignores the locked meta state 247 * (arrow keys, for example) and you handle a key. 248 */ resetLockedMeta(Spannable content)249 protected static void resetLockedMeta(Spannable content) { 250 resetLock(content, CAP); 251 resetLock(content, ALT); 252 resetLock(content, SYM); 253 resetLock(content, SELECTING); 254 } 255 resetLock(Spannable content, Object what)256 private static void resetLock(Spannable content, Object what) { 257 int current = content.getSpanFlags(what); 258 259 if (current == LOCKED) 260 content.removeSpan(what); 261 } 262 263 /** 264 * Handles presses of the meta keys. 265 */ onKeyDown(View view, Editable content, int keyCode, KeyEvent event)266 public boolean onKeyDown(View view, Editable content, int keyCode, KeyEvent event) { 267 if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) { 268 press(content, CAP); 269 return true; 270 } 271 272 if (keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT 273 || keyCode == KeyEvent.KEYCODE_NUM) { 274 press(content, ALT); 275 return true; 276 } 277 278 if (keyCode == KeyEvent.KEYCODE_SYM) { 279 press(content, SYM); 280 return true; 281 } 282 283 return false; // no super to call through to 284 } 285 press(Editable content, Object what)286 private void press(Editable content, Object what) { 287 int state = content.getSpanFlags(what); 288 289 if (state == PRESSED) 290 ; // repeat before use 291 else if (state == RELEASED) 292 content.setSpan(what, 0, 0, LOCKED); 293 else if (state == USED) 294 ; // repeat after use 295 else if (state == LOCKED) 296 content.removeSpan(what); 297 else 298 content.setSpan(what, 0, 0, PRESSED); 299 } 300 301 /** 302 * Start selecting text. 303 * @hide pending API review 304 */ startSelecting(View view, Spannable content)305 public static void startSelecting(View view, Spannable content) { 306 content.setSpan(SELECTING, 0, 0, PRESSED); 307 } 308 309 /** 310 * Stop selecting text. This does not actually collapse the selection; 311 * call {@link android.text.Selection#setSelection} too. 312 * @hide pending API review 313 */ stopSelecting(View view, Spannable content)314 public static void stopSelecting(View view, Spannable content) { 315 content.removeSpan(SELECTING); 316 } 317 318 /** 319 * Handles release of the meta keys. 320 */ onKeyUp(View view, Editable content, int keyCode, KeyEvent event)321 public boolean onKeyUp(View view, Editable content, int keyCode, KeyEvent event) { 322 if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) { 323 release(content, CAP, event); 324 return true; 325 } 326 327 if (keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT 328 || keyCode == KeyEvent.KEYCODE_NUM) { 329 release(content, ALT, event); 330 return true; 331 } 332 333 if (keyCode == KeyEvent.KEYCODE_SYM) { 334 release(content, SYM, event); 335 return true; 336 } 337 338 return false; // no super to call through to 339 } 340 release(Editable content, Object what, KeyEvent event)341 private void release(Editable content, Object what, KeyEvent event) { 342 int current = content.getSpanFlags(what); 343 344 switch (event.getKeyCharacterMap().getModifierBehavior()) { 345 case KeyCharacterMap.MODIFIER_BEHAVIOR_CHORDED_OR_TOGGLED: 346 if (current == USED) 347 content.removeSpan(what); 348 else if (current == PRESSED) 349 content.setSpan(what, 0, 0, RELEASED); 350 break; 351 352 default: 353 content.removeSpan(what); 354 break; 355 } 356 } 357 clearMetaKeyState(View view, Editable content, int states)358 public void clearMetaKeyState(View view, Editable content, int states) { 359 clearMetaKeyState(content, states); 360 } 361 clearMetaKeyState(Editable content, int states)362 public static void clearMetaKeyState(Editable content, int states) { 363 if ((states&META_SHIFT_ON) != 0) content.removeSpan(CAP); 364 if ((states&META_ALT_ON) != 0) content.removeSpan(ALT); 365 if ((states&META_SYM_ON) != 0) content.removeSpan(SYM); 366 if ((states&META_SELECTING) != 0) content.removeSpan(SELECTING); 367 } 368 369 /** 370 * Call this if you are a method that ignores the locked meta state 371 * (arrow keys, for example) and you handle a key. 372 */ resetLockedMeta(long state)373 public static long resetLockedMeta(long state) { 374 if ((state & META_CAP_LOCKED) != 0) { 375 state &= ~META_SHIFT_MASK; 376 } 377 if ((state & META_ALT_LOCKED) != 0) { 378 state &= ~META_ALT_MASK; 379 } 380 if ((state & META_SYM_LOCKED) != 0) { 381 state &= ~META_SYM_MASK; 382 } 383 return state; 384 } 385 386 // --------------------------------------------------------------------- 387 // Version of API that operates on a state bit mask 388 // --------------------------------------------------------------------- 389 390 /** 391 * Gets the state of the meta keys. 392 * 393 * @param state the current meta state bits. 394 * 395 * @return an integer in which each bit set to one represents a pressed 396 * or locked meta key. 397 */ getMetaState(long state)398 public static final int getMetaState(long state) { 399 int result = 0; 400 401 if ((state & META_CAP_LOCKED) != 0) { 402 result |= META_CAP_LOCKED; 403 } else if ((state & META_SHIFT_ON) != 0) { 404 result |= META_SHIFT_ON; 405 } 406 407 if ((state & META_ALT_LOCKED) != 0) { 408 result |= META_ALT_LOCKED; 409 } else if ((state & META_ALT_ON) != 0) { 410 result |= META_ALT_ON; 411 } 412 413 if ((state & META_SYM_LOCKED) != 0) { 414 result |= META_SYM_LOCKED; 415 } else if ((state & META_SYM_ON) != 0) { 416 result |= META_SYM_ON; 417 } 418 419 return result; 420 } 421 422 /** 423 * Gets the state of a particular meta key. 424 * 425 * @param state the current state bits. 426 * @param meta META_SHIFT_ON, META_ALT_ON, or META_SYM_ON 427 * 428 * @return 0 if inactive, 1 if active, 2 if locked. 429 */ getMetaState(long state, int meta)430 public static final int getMetaState(long state, int meta) { 431 switch (meta) { 432 case META_SHIFT_ON: 433 if ((state & META_CAP_LOCKED) != 0) return 2; 434 if ((state & META_SHIFT_ON) != 0) return 1; 435 return 0; 436 437 case META_ALT_ON: 438 if ((state & META_ALT_LOCKED) != 0) return 2; 439 if ((state & META_ALT_ON) != 0) return 1; 440 return 0; 441 442 case META_SYM_ON: 443 if ((state & META_SYM_LOCKED) != 0) return 2; 444 if ((state & META_SYM_ON) != 0) return 1; 445 return 0; 446 447 default: 448 return 0; 449 } 450 } 451 452 /** 453 * Call this method after you handle a keypress so that the meta 454 * state will be reset to unshifted (if it is not still down) 455 * or primed to be reset to unshifted (once it is released). Takes 456 * the current state, returns the new state. 457 */ adjustMetaAfterKeypress(long state)458 public static long adjustMetaAfterKeypress(long state) { 459 if ((state & META_CAP_PRESSED) != 0) { 460 state = (state & ~META_SHIFT_MASK) | META_SHIFT_ON | META_CAP_USED; 461 } else if ((state & META_CAP_RELEASED) != 0) { 462 state &= ~META_SHIFT_MASK; 463 } 464 465 if ((state & META_ALT_PRESSED) != 0) { 466 state = (state & ~META_ALT_MASK) | META_ALT_ON | META_ALT_USED; 467 } else if ((state & META_ALT_RELEASED) != 0) { 468 state &= ~META_ALT_MASK; 469 } 470 471 if ((state & META_SYM_PRESSED) != 0) { 472 state = (state & ~META_SYM_MASK) | META_SYM_ON | META_SYM_USED; 473 } else if ((state & META_SYM_RELEASED) != 0) { 474 state &= ~META_SYM_MASK; 475 } 476 return state; 477 } 478 479 /** 480 * Handles presses of the meta keys. 481 */ handleKeyDown(long state, int keyCode, KeyEvent event)482 public static long handleKeyDown(long state, int keyCode, KeyEvent event) { 483 if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) { 484 return press(state, META_SHIFT_ON, META_SHIFT_MASK, 485 META_CAP_LOCKED, META_CAP_PRESSED, META_CAP_RELEASED, META_CAP_USED); 486 } 487 488 if (keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT 489 || keyCode == KeyEvent.KEYCODE_NUM) { 490 return press(state, META_ALT_ON, META_ALT_MASK, 491 META_ALT_LOCKED, META_ALT_PRESSED, META_ALT_RELEASED, META_ALT_USED); 492 } 493 494 if (keyCode == KeyEvent.KEYCODE_SYM) { 495 return press(state, META_SYM_ON, META_SYM_MASK, 496 META_SYM_LOCKED, META_SYM_PRESSED, META_SYM_RELEASED, META_SYM_USED); 497 } 498 return state; 499 } 500 press(long state, int what, long mask, long locked, long pressed, long released, long used)501 private static long press(long state, int what, long mask, 502 long locked, long pressed, long released, long used) { 503 if ((state & pressed) != 0) { 504 // repeat before use 505 } else if ((state & released) != 0) { 506 state = (state &~ mask) | what | locked; 507 } else if ((state & used) != 0) { 508 // repeat after use 509 } else if ((state & locked) != 0) { 510 state &= ~mask; 511 } else { 512 state |= what | pressed; 513 } 514 return state; 515 } 516 517 /** 518 * Handles release of the meta keys. 519 */ handleKeyUp(long state, int keyCode, KeyEvent event)520 public static long handleKeyUp(long state, int keyCode, KeyEvent event) { 521 if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) { 522 return release(state, META_SHIFT_ON, META_SHIFT_MASK, 523 META_CAP_PRESSED, META_CAP_RELEASED, META_CAP_USED, event); 524 } 525 526 if (keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT 527 || keyCode == KeyEvent.KEYCODE_NUM) { 528 return release(state, META_ALT_ON, META_ALT_MASK, 529 META_ALT_PRESSED, META_ALT_RELEASED, META_ALT_USED, event); 530 } 531 532 if (keyCode == KeyEvent.KEYCODE_SYM) { 533 return release(state, META_SYM_ON, META_SYM_MASK, 534 META_SYM_PRESSED, META_SYM_RELEASED, META_SYM_USED, event); 535 } 536 return state; 537 } 538 release(long state, int what, long mask, long pressed, long released, long used, KeyEvent event)539 private static long release(long state, int what, long mask, 540 long pressed, long released, long used, KeyEvent event) { 541 switch (event.getKeyCharacterMap().getModifierBehavior()) { 542 case KeyCharacterMap.MODIFIER_BEHAVIOR_CHORDED_OR_TOGGLED: 543 if ((state & used) != 0) { 544 state &= ~mask; 545 } else if ((state & pressed) != 0) { 546 state |= what | released; 547 } 548 break; 549 550 default: 551 state &= ~mask; 552 break; 553 } 554 return state; 555 } 556 557 /** 558 * Clears the state of the specified meta key if it is locked. 559 * @param state the meta key state 560 * @param which meta keys to clear, may be a combination of {@link #META_SHIFT_ON}, 561 * {@link #META_ALT_ON} or {@link #META_SYM_ON}. 562 */ clearMetaKeyState(long state, int which)563 public long clearMetaKeyState(long state, int which) { 564 if ((which & META_SHIFT_ON) != 0 && (state & META_CAP_LOCKED) != 0) { 565 state &= ~META_SHIFT_MASK; 566 } 567 if ((which & META_ALT_ON) != 0 && (state & META_ALT_LOCKED) != 0) { 568 state &= ~META_ALT_MASK; 569 } 570 if ((which & META_SYM_ON) != 0 && (state & META_SYM_LOCKED) != 0) { 571 state &= ~META_SYM_MASK; 572 } 573 return state; 574 } 575 576 /** 577 * The meta key has been pressed but has not yet been used. 578 */ 579 private static final int PRESSED = 580 Spannable.SPAN_MARK_MARK | (1 << Spannable.SPAN_USER_SHIFT); 581 582 /** 583 * The meta key has been pressed and released but has still 584 * not yet been used. 585 */ 586 private static final int RELEASED = 587 Spannable.SPAN_MARK_MARK | (2 << Spannable.SPAN_USER_SHIFT); 588 589 /** 590 * The meta key has been pressed and used but has not yet been released. 591 */ 592 private static final int USED = 593 Spannable.SPAN_MARK_MARK | (3 << Spannable.SPAN_USER_SHIFT); 594 595 /** 596 * The meta key has been pressed and released without use, and then 597 * pressed again; it may also have been released again. 598 */ 599 private static final int LOCKED = 600 Spannable.SPAN_MARK_MARK | (4 << Spannable.SPAN_USER_SHIFT); 601 } 602 603