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 private static final int PRESSED_RETURN_VALUE = 1; 139 private static final int LOCKED_RETURN_VALUE = 2; 140 141 /** 142 * Resets all meta state to inactive. 143 */ resetMetaState(Spannable text)144 public static void resetMetaState(Spannable text) { 145 text.removeSpan(CAP); 146 text.removeSpan(ALT); 147 text.removeSpan(SYM); 148 text.removeSpan(SELECTING); 149 } 150 151 /** 152 * Gets the state of the meta keys. 153 * 154 * @param text the buffer in which the meta key would have been pressed. 155 * 156 * @return an integer in which each bit set to one represents a pressed 157 * or locked meta key. 158 */ getMetaState(CharSequence text)159 public static final int getMetaState(CharSequence text) { 160 return getActive(text, CAP, META_SHIFT_ON, META_CAP_LOCKED) | 161 getActive(text, ALT, META_ALT_ON, META_ALT_LOCKED) | 162 getActive(text, SYM, META_SYM_ON, META_SYM_LOCKED) | 163 getActive(text, SELECTING, META_SELECTING, META_SELECTING); 164 } 165 166 /** 167 * Gets the state of the meta keys for a specific key event. 168 * 169 * For input devices that use toggled key modifiers, the `toggled' state 170 * is stored into the text buffer. This method retrieves the meta state 171 * for this event, accounting for the stored state. If the event has been 172 * created by a device that does not support toggled key modifiers, like 173 * a virtual device for example, the stored state is ignored. 174 * 175 * @param text the buffer in which the meta key would have been pressed. 176 * @param event the event for which to evaluate the meta state. 177 * @return an integer in which each bit set to one represents a pressed 178 * or locked meta key. 179 */ getMetaState(final CharSequence text, final KeyEvent event)180 public static final int getMetaState(final CharSequence text, final KeyEvent event) { 181 int metaState = event.getMetaState(); 182 if (event.getKeyCharacterMap().getModifierBehavior() 183 == KeyCharacterMap.MODIFIER_BEHAVIOR_CHORDED_OR_TOGGLED) { 184 metaState |= getMetaState(text); 185 } 186 return metaState; 187 } 188 189 // As META_SELECTING is @hide we should not mention it in public comments, hence the 190 // omission in @param meta 191 /** 192 * Gets the state of a particular meta key. 193 * 194 * @param meta META_SHIFT_ON, META_ALT_ON, META_SYM_ON 195 * @param text the buffer in which the meta key would have been pressed. 196 * 197 * @return 0 if inactive, 1 if active, 2 if locked. 198 */ getMetaState(CharSequence text, int meta)199 public static final int getMetaState(CharSequence text, int meta) { 200 switch (meta) { 201 case META_SHIFT_ON: 202 return getActive(text, CAP, PRESSED_RETURN_VALUE, LOCKED_RETURN_VALUE); 203 204 case META_ALT_ON: 205 return getActive(text, ALT, PRESSED_RETURN_VALUE, LOCKED_RETURN_VALUE); 206 207 case META_SYM_ON: 208 return getActive(text, SYM, PRESSED_RETURN_VALUE, LOCKED_RETURN_VALUE); 209 210 case META_SELECTING: 211 return getActive(text, SELECTING, PRESSED_RETURN_VALUE, LOCKED_RETURN_VALUE); 212 213 default: 214 return 0; 215 } 216 } 217 218 /** 219 * Gets the state of a particular meta key to use with a particular key event. 220 * 221 * If the key event has been created by a device that does not support toggled 222 * key modifiers, like a virtual keyboard for example, only the meta state in 223 * the key event is considered. 224 * 225 * @param meta META_SHIFT_ON, META_ALT_ON, META_SYM_ON 226 * @param text the buffer in which the meta key would have been pressed. 227 * @param event the event for which to evaluate the meta state. 228 * @return 0 if inactive, 1 if active, 2 if locked. 229 */ getMetaState(final CharSequence text, final int meta, final KeyEvent event)230 public static final int getMetaState(final CharSequence text, final int meta, 231 final KeyEvent event) { 232 int metaState = event.getMetaState(); 233 if (event.getKeyCharacterMap().getModifierBehavior() 234 == KeyCharacterMap.MODIFIER_BEHAVIOR_CHORDED_OR_TOGGLED) { 235 metaState |= getMetaState(text); 236 } 237 if (META_SELECTING == meta) { 238 // #getMetaState(long, int) does not support META_SELECTING, but we want the same 239 // behavior as #getMetaState(CharSequence, int) so we need to do it here 240 if ((metaState & META_SELECTING) != 0) { 241 // META_SELECTING is only ever set to PRESSED and can't be LOCKED, so return 1 242 return 1; 243 } 244 return 0; 245 } 246 return getMetaState(metaState, meta); 247 } 248 getActive(CharSequence text, Object meta, int on, int lock)249 private static int getActive(CharSequence text, Object meta, 250 int on, int lock) { 251 if (!(text instanceof Spanned)) { 252 return 0; 253 } 254 255 Spanned sp = (Spanned) text; 256 int flag = sp.getSpanFlags(meta); 257 258 if (flag == LOCKED) { 259 return lock; 260 } else if (flag != 0) { 261 return on; 262 } else { 263 return 0; 264 } 265 } 266 267 /** 268 * Call this method after you handle a keypress so that the meta 269 * state will be reset to unshifted (if it is not still down) 270 * or primed to be reset to unshifted (once it is released). 271 */ adjustMetaAfterKeypress(Spannable content)272 public static void adjustMetaAfterKeypress(Spannable content) { 273 adjust(content, CAP); 274 adjust(content, ALT); 275 adjust(content, SYM); 276 } 277 278 /** 279 * Returns true if this object is one that this class would use to 280 * keep track of any meta state in the specified text. 281 */ isMetaTracker(CharSequence text, Object what)282 public static boolean isMetaTracker(CharSequence text, Object what) { 283 return what == CAP || what == ALT || what == SYM || 284 what == SELECTING; 285 } 286 287 /** 288 * Returns true if this object is one that this class would use to 289 * keep track of the selecting meta state in the specified text. 290 */ isSelectingMetaTracker(CharSequence text, Object what)291 public static boolean isSelectingMetaTracker(CharSequence text, Object what) { 292 return what == SELECTING; 293 } 294 adjust(Spannable content, Object what)295 private static void adjust(Spannable content, Object what) { 296 int current = content.getSpanFlags(what); 297 298 if (current == PRESSED) 299 content.setSpan(what, 0, 0, USED); 300 else if (current == RELEASED) 301 content.removeSpan(what); 302 } 303 304 /** 305 * Call this if you are a method that ignores the locked meta state 306 * (arrow keys, for example) and you handle a key. 307 */ resetLockedMeta(Spannable content)308 protected static void resetLockedMeta(Spannable content) { 309 resetLock(content, CAP); 310 resetLock(content, ALT); 311 resetLock(content, SYM); 312 resetLock(content, SELECTING); 313 } 314 resetLock(Spannable content, Object what)315 private static void resetLock(Spannable content, Object what) { 316 int current = content.getSpanFlags(what); 317 318 if (current == LOCKED) 319 content.removeSpan(what); 320 } 321 322 /** 323 * Handles presses of the meta keys. 324 */ onKeyDown(View view, Editable content, int keyCode, KeyEvent event)325 public boolean onKeyDown(View view, Editable content, int keyCode, KeyEvent event) { 326 if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) { 327 press(content, CAP); 328 return true; 329 } 330 331 if (keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT 332 || keyCode == KeyEvent.KEYCODE_NUM) { 333 press(content, ALT); 334 return true; 335 } 336 337 if (keyCode == KeyEvent.KEYCODE_SYM) { 338 press(content, SYM); 339 return true; 340 } 341 342 return false; // no super to call through to 343 } 344 press(Editable content, Object what)345 private void press(Editable content, Object what) { 346 int state = content.getSpanFlags(what); 347 348 if (state == PRESSED) 349 ; // repeat before use 350 else if (state == RELEASED) 351 content.setSpan(what, 0, 0, LOCKED); 352 else if (state == USED) 353 ; // repeat after use 354 else if (state == LOCKED) 355 content.removeSpan(what); 356 else 357 content.setSpan(what, 0, 0, PRESSED); 358 } 359 360 /** 361 * Start selecting text. 362 * @hide pending API review 363 */ startSelecting(View view, Spannable content)364 public static void startSelecting(View view, Spannable content) { 365 content.setSpan(SELECTING, 0, 0, PRESSED); 366 } 367 368 /** 369 * Stop selecting text. This does not actually collapse the selection; 370 * call {@link android.text.Selection#setSelection} too. 371 * @hide pending API review 372 */ stopSelecting(View view, Spannable content)373 public static void stopSelecting(View view, Spannable content) { 374 content.removeSpan(SELECTING); 375 } 376 377 /** 378 * Handles release of the meta keys. 379 */ onKeyUp(View view, Editable content, int keyCode, KeyEvent event)380 public boolean onKeyUp(View view, Editable content, int keyCode, KeyEvent event) { 381 if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) { 382 release(content, CAP, event); 383 return true; 384 } 385 386 if (keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT 387 || keyCode == KeyEvent.KEYCODE_NUM) { 388 release(content, ALT, event); 389 return true; 390 } 391 392 if (keyCode == KeyEvent.KEYCODE_SYM) { 393 release(content, SYM, event); 394 return true; 395 } 396 397 return false; // no super to call through to 398 } 399 release(Editable content, Object what, KeyEvent event)400 private void release(Editable content, Object what, KeyEvent event) { 401 int current = content.getSpanFlags(what); 402 403 switch (event.getKeyCharacterMap().getModifierBehavior()) { 404 case KeyCharacterMap.MODIFIER_BEHAVIOR_CHORDED_OR_TOGGLED: 405 if (current == USED) 406 content.removeSpan(what); 407 else if (current == PRESSED) 408 content.setSpan(what, 0, 0, RELEASED); 409 break; 410 411 default: 412 content.removeSpan(what); 413 break; 414 } 415 } 416 clearMetaKeyState(View view, Editable content, int states)417 public void clearMetaKeyState(View view, Editable content, int states) { 418 clearMetaKeyState(content, states); 419 } 420 clearMetaKeyState(Editable content, int states)421 public static void clearMetaKeyState(Editable content, int states) { 422 if ((states&META_SHIFT_ON) != 0) content.removeSpan(CAP); 423 if ((states&META_ALT_ON) != 0) content.removeSpan(ALT); 424 if ((states&META_SYM_ON) != 0) content.removeSpan(SYM); 425 if ((states&META_SELECTING) != 0) content.removeSpan(SELECTING); 426 } 427 428 /** 429 * Call this if you are a method that ignores the locked meta state 430 * (arrow keys, for example) and you handle a key. 431 */ resetLockedMeta(long state)432 public static long resetLockedMeta(long state) { 433 if ((state & META_CAP_LOCKED) != 0) { 434 state &= ~META_SHIFT_MASK; 435 } 436 if ((state & META_ALT_LOCKED) != 0) { 437 state &= ~META_ALT_MASK; 438 } 439 if ((state & META_SYM_LOCKED) != 0) { 440 state &= ~META_SYM_MASK; 441 } 442 return state; 443 } 444 445 // --------------------------------------------------------------------- 446 // Version of API that operates on a state bit mask 447 // --------------------------------------------------------------------- 448 449 /** 450 * Gets the state of the meta keys. 451 * 452 * @param state the current meta state bits. 453 * 454 * @return an integer in which each bit set to one represents a pressed 455 * or locked meta key. 456 */ getMetaState(long state)457 public static final int getMetaState(long state) { 458 int result = 0; 459 460 if ((state & META_CAP_LOCKED) != 0) { 461 result |= META_CAP_LOCKED; 462 } else if ((state & META_SHIFT_ON) != 0) { 463 result |= META_SHIFT_ON; 464 } 465 466 if ((state & META_ALT_LOCKED) != 0) { 467 result |= META_ALT_LOCKED; 468 } else if ((state & META_ALT_ON) != 0) { 469 result |= META_ALT_ON; 470 } 471 472 if ((state & META_SYM_LOCKED) != 0) { 473 result |= META_SYM_LOCKED; 474 } else if ((state & META_SYM_ON) != 0) { 475 result |= META_SYM_ON; 476 } 477 478 return result; 479 } 480 481 /** 482 * Gets the state of a particular meta key. 483 * 484 * @param state the current state bits. 485 * @param meta META_SHIFT_ON, META_ALT_ON, or META_SYM_ON 486 * 487 * @return 0 if inactive, 1 if active, 2 if locked. 488 */ getMetaState(long state, int meta)489 public static final int getMetaState(long state, int meta) { 490 switch (meta) { 491 case META_SHIFT_ON: 492 if ((state & META_CAP_LOCKED) != 0) return LOCKED_RETURN_VALUE; 493 if ((state & META_SHIFT_ON) != 0) return PRESSED_RETURN_VALUE; 494 return 0; 495 496 case META_ALT_ON: 497 if ((state & META_ALT_LOCKED) != 0) return LOCKED_RETURN_VALUE; 498 if ((state & META_ALT_ON) != 0) return PRESSED_RETURN_VALUE; 499 return 0; 500 501 case META_SYM_ON: 502 if ((state & META_SYM_LOCKED) != 0) return LOCKED_RETURN_VALUE; 503 if ((state & META_SYM_ON) != 0) return PRESSED_RETURN_VALUE; 504 return 0; 505 506 default: 507 return 0; 508 } 509 } 510 511 /** 512 * Call this method after you handle a keypress so that the meta 513 * state will be reset to unshifted (if it is not still down) 514 * or primed to be reset to unshifted (once it is released). Takes 515 * the current state, returns the new state. 516 */ adjustMetaAfterKeypress(long state)517 public static long adjustMetaAfterKeypress(long state) { 518 if ((state & META_CAP_PRESSED) != 0) { 519 state = (state & ~META_SHIFT_MASK) | META_SHIFT_ON | META_CAP_USED; 520 } else if ((state & META_CAP_RELEASED) != 0) { 521 state &= ~META_SHIFT_MASK; 522 } 523 524 if ((state & META_ALT_PRESSED) != 0) { 525 state = (state & ~META_ALT_MASK) | META_ALT_ON | META_ALT_USED; 526 } else if ((state & META_ALT_RELEASED) != 0) { 527 state &= ~META_ALT_MASK; 528 } 529 530 if ((state & META_SYM_PRESSED) != 0) { 531 state = (state & ~META_SYM_MASK) | META_SYM_ON | META_SYM_USED; 532 } else if ((state & META_SYM_RELEASED) != 0) { 533 state &= ~META_SYM_MASK; 534 } 535 return state; 536 } 537 538 /** 539 * Handles presses of the meta keys. 540 */ handleKeyDown(long state, int keyCode, KeyEvent event)541 public static long handleKeyDown(long state, int keyCode, KeyEvent event) { 542 if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) { 543 return press(state, META_SHIFT_ON, META_SHIFT_MASK, 544 META_CAP_LOCKED, META_CAP_PRESSED, META_CAP_RELEASED, META_CAP_USED); 545 } 546 547 if (keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT 548 || keyCode == KeyEvent.KEYCODE_NUM) { 549 return press(state, META_ALT_ON, META_ALT_MASK, 550 META_ALT_LOCKED, META_ALT_PRESSED, META_ALT_RELEASED, META_ALT_USED); 551 } 552 553 if (keyCode == KeyEvent.KEYCODE_SYM) { 554 return press(state, META_SYM_ON, META_SYM_MASK, 555 META_SYM_LOCKED, META_SYM_PRESSED, META_SYM_RELEASED, META_SYM_USED); 556 } 557 return state; 558 } 559 press(long state, int what, long mask, long locked, long pressed, long released, long used)560 private static long press(long state, int what, long mask, 561 long locked, long pressed, long released, long used) { 562 if ((state & pressed) != 0) { 563 // repeat before use 564 } else if ((state & released) != 0) { 565 state = (state &~ mask) | what | locked; 566 } else if ((state & used) != 0) { 567 // repeat after use 568 } else if ((state & locked) != 0) { 569 state &= ~mask; 570 } else { 571 state |= what | pressed; 572 } 573 return state; 574 } 575 576 /** 577 * Handles release of the meta keys. 578 */ handleKeyUp(long state, int keyCode, KeyEvent event)579 public static long handleKeyUp(long state, int keyCode, KeyEvent event) { 580 if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) { 581 return release(state, META_SHIFT_ON, META_SHIFT_MASK, 582 META_CAP_PRESSED, META_CAP_RELEASED, META_CAP_USED, event); 583 } 584 585 if (keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT 586 || keyCode == KeyEvent.KEYCODE_NUM) { 587 return release(state, META_ALT_ON, META_ALT_MASK, 588 META_ALT_PRESSED, META_ALT_RELEASED, META_ALT_USED, event); 589 } 590 591 if (keyCode == KeyEvent.KEYCODE_SYM) { 592 return release(state, META_SYM_ON, META_SYM_MASK, 593 META_SYM_PRESSED, META_SYM_RELEASED, META_SYM_USED, event); 594 } 595 return state; 596 } 597 release(long state, int what, long mask, long pressed, long released, long used, KeyEvent event)598 private static long release(long state, int what, long mask, 599 long pressed, long released, long used, KeyEvent event) { 600 switch (event.getKeyCharacterMap().getModifierBehavior()) { 601 case KeyCharacterMap.MODIFIER_BEHAVIOR_CHORDED_OR_TOGGLED: 602 if ((state & used) != 0) { 603 state &= ~mask; 604 } else if ((state & pressed) != 0) { 605 state |= what | released; 606 } 607 break; 608 609 default: 610 state &= ~mask; 611 break; 612 } 613 return state; 614 } 615 616 /** 617 * Clears the state of the specified meta key if it is locked. 618 * @param state the meta key state 619 * @param which meta keys to clear, may be a combination of {@link #META_SHIFT_ON}, 620 * {@link #META_ALT_ON} or {@link #META_SYM_ON}. 621 */ clearMetaKeyState(long state, int which)622 public long clearMetaKeyState(long state, int which) { 623 if ((which & META_SHIFT_ON) != 0 && (state & META_CAP_LOCKED) != 0) { 624 state &= ~META_SHIFT_MASK; 625 } 626 if ((which & META_ALT_ON) != 0 && (state & META_ALT_LOCKED) != 0) { 627 state &= ~META_ALT_MASK; 628 } 629 if ((which & META_SYM_ON) != 0 && (state & META_SYM_LOCKED) != 0) { 630 state &= ~META_SYM_MASK; 631 } 632 return state; 633 } 634 635 /** 636 * The meta key has been pressed but has not yet been used. 637 */ 638 private static final int PRESSED = 639 Spannable.SPAN_MARK_MARK | (1 << Spannable.SPAN_USER_SHIFT); 640 641 /** 642 * The meta key has been pressed and released but has still 643 * not yet been used. 644 */ 645 private static final int RELEASED = 646 Spannable.SPAN_MARK_MARK | (2 << Spannable.SPAN_USER_SHIFT); 647 648 /** 649 * The meta key has been pressed and used but has not yet been released. 650 */ 651 private static final int USED = 652 Spannable.SPAN_MARK_MARK | (3 << Spannable.SPAN_USER_SHIFT); 653 654 /** 655 * The meta key has been pressed and released without use, and then 656 * pressed again; it may also have been released again. 657 */ 658 private static final int LOCKED = 659 Spannable.SPAN_MARK_MARK | (4 << Spannable.SPAN_USER_SHIFT); 660 } 661