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 */ 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 */ 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 */ 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 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 */ 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 */ 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 */ 232 public static boolean isSelectingMetaTracker(CharSequence text, Object what) { 233 return what == SELECTING; 234 } 235 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 */ 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 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 */ 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 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 */ 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 */ 314 public static void stopSelecting(View view, Spannable content) { 315 content.removeSpan(SELECTING); 316 } 317 318 /** 319 * Handles release of the meta keys. 320 */ 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 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 358 public void clearMetaKeyState(View view, Editable content, int states) { 359 clearMetaKeyState(content, states); 360 } 361 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 */ 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 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 */ 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