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