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