MetaKeyKeyListener.java revision 9066cfe9886ac131c34d59ed0e2d287b0e3c0087
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 17package android.text.method; 18 19import android.view.KeyEvent; 20import android.view.View; 21import android.text.*; 22 23/** 24 * This base class encapsulates the behavior for handling the meta keys 25 * (shift and alt) and the pseudo-meta state of selecting text. 26 * Key listeners that care about meta state should 27 * inherit from it; you should not instantiate this class directly in a client. 28 */ 29 30public abstract class MetaKeyKeyListener { 31 public static final int META_SHIFT_ON = KeyEvent.META_SHIFT_ON; 32 public static final int META_ALT_ON = KeyEvent.META_ALT_ON; 33 public static final int META_SYM_ON = KeyEvent.META_SYM_ON; 34 35 private static final int LOCKED_SHIFT = 8; 36 37 public static final int META_CAP_LOCKED = KeyEvent.META_SHIFT_ON << LOCKED_SHIFT; 38 public static final int META_ALT_LOCKED = KeyEvent.META_ALT_ON << LOCKED_SHIFT; 39 public static final int META_SYM_LOCKED = KeyEvent.META_SYM_ON << LOCKED_SHIFT; 40 41 /** 42 * @hide pending API review 43 */ 44 public static final int META_SELECTING = 1 << 16; 45 46 private static final int USED_SHIFT = 24; 47 48 private static final long META_CAP_USED = ((long)KeyEvent.META_SHIFT_ON) << USED_SHIFT; 49 private static final long META_ALT_USED = ((long)KeyEvent.META_ALT_ON) << USED_SHIFT; 50 private static final long META_SYM_USED = ((long)KeyEvent.META_SYM_ON) << USED_SHIFT; 51 52 private static final int PRESSED_SHIFT = 32; 53 54 private static final long META_CAP_PRESSED = ((long)KeyEvent.META_SHIFT_ON) << PRESSED_SHIFT; 55 private static final long META_ALT_PRESSED = ((long)KeyEvent.META_ALT_ON) << PRESSED_SHIFT; 56 private static final long META_SYM_PRESSED = ((long)KeyEvent.META_SYM_ON) << PRESSED_SHIFT; 57 58 private static final int RELEASED_SHIFT = 40; 59 60 private static final long META_CAP_RELEASED = ((long)KeyEvent.META_SHIFT_ON) << RELEASED_SHIFT; 61 private static final long META_ALT_RELEASED = ((long)KeyEvent.META_ALT_ON) << RELEASED_SHIFT; 62 private static final long META_SYM_RELEASED = ((long)KeyEvent.META_SYM_ON) << RELEASED_SHIFT; 63 64 private static final long META_SHIFT_MASK = META_SHIFT_ON 65 | META_CAP_LOCKED | META_CAP_USED 66 | META_CAP_PRESSED | META_CAP_RELEASED; 67 private static final long META_ALT_MASK = META_ALT_ON 68 | META_ALT_LOCKED | META_ALT_USED 69 | META_ALT_PRESSED | META_ALT_RELEASED; 70 private static final long META_SYM_MASK = META_SYM_ON 71 | META_SYM_LOCKED | META_SYM_USED 72 | META_SYM_PRESSED | META_SYM_RELEASED; 73 74 private static final Object CAP = new NoCopySpan.Concrete(); 75 private static final Object ALT = new NoCopySpan.Concrete(); 76 private static final Object SYM = new NoCopySpan.Concrete(); 77 private static final Object SELECTING = new NoCopySpan.Concrete(); 78 79 /** 80 * Resets all meta state to inactive. 81 */ 82 public static void resetMetaState(Spannable text) { 83 text.removeSpan(CAP); 84 text.removeSpan(ALT); 85 text.removeSpan(SYM); 86 text.removeSpan(SELECTING); 87 } 88 89 /** 90 * Gets the state of the meta keys. 91 * 92 * @param text the buffer in which the meta key would have been pressed. 93 * 94 * @return an integer in which each bit set to one represents a pressed 95 * or locked meta key. 96 */ 97 public static final int getMetaState(CharSequence text) { 98 return getActive(text, CAP, META_SHIFT_ON, META_CAP_LOCKED) | 99 getActive(text, ALT, META_ALT_ON, META_ALT_LOCKED) | 100 getActive(text, SYM, META_SYM_ON, META_SYM_LOCKED) | 101 getActive(text, SELECTING, META_SELECTING, META_SELECTING); 102 } 103 104 /** 105 * Gets the state of a particular meta key. 106 * 107 * @param meta META_SHIFT_ON, META_ALT_ON, META_SYM_ON, or META_SELECTING 108 * @param text the buffer in which the meta key would have been pressed. 109 * 110 * @return 0 if inactive, 1 if active, 2 if locked. 111 */ 112 public static final int getMetaState(CharSequence text, int meta) { 113 switch (meta) { 114 case META_SHIFT_ON: 115 return getActive(text, CAP, 1, 2); 116 117 case META_ALT_ON: 118 return getActive(text, ALT, 1, 2); 119 120 case META_SYM_ON: 121 return getActive(text, SYM, 1, 2); 122 123 case META_SELECTING: 124 return getActive(text, SELECTING, 1, 2); 125 126 default: 127 return 0; 128 } 129 } 130 131 private static int getActive(CharSequence text, Object meta, 132 int on, int lock) { 133 if (!(text instanceof Spanned)) { 134 return 0; 135 } 136 137 Spanned sp = (Spanned) text; 138 int flag = sp.getSpanFlags(meta); 139 140 if (flag == LOCKED) { 141 return lock; 142 } else if (flag != 0) { 143 return on; 144 } else { 145 return 0; 146 } 147 } 148 149 /** 150 * Call this method after you handle a keypress so that the meta 151 * state will be reset to unshifted (if it is not still down) 152 * or primed to be reset to unshifted (once it is released). 153 */ 154 public static void adjustMetaAfterKeypress(Spannable content) { 155 adjust(content, CAP); 156 adjust(content, ALT); 157 adjust(content, SYM); 158 } 159 160 /** 161 * Returns true if this object is one that this class would use to 162 * keep track of meta state in the specified text. 163 */ 164 public static boolean isMetaTracker(CharSequence text, Object what) { 165 return what == CAP || what == ALT || what == SYM || 166 what == SELECTING; 167 } 168 169 private static void adjust(Spannable content, Object what) { 170 int current = content.getSpanFlags(what); 171 172 if (current == PRESSED) 173 content.setSpan(what, 0, 0, USED); 174 else if (current == RELEASED) 175 content.removeSpan(what); 176 } 177 178 /** 179 * Call this if you are a method that ignores the locked meta state 180 * (arrow keys, for example) and you handle a key. 181 */ 182 protected static void resetLockedMeta(Spannable content) { 183 resetLock(content, CAP); 184 resetLock(content, ALT); 185 resetLock(content, SYM); 186 resetLock(content, SELECTING); 187 } 188 189 private static void resetLock(Spannable content, Object what) { 190 int current = content.getSpanFlags(what); 191 192 if (current == LOCKED) 193 content.removeSpan(what); 194 } 195 196 /** 197 * Handles presses of the meta keys. 198 */ 199 public boolean onKeyDown(View view, Editable content, 200 int keyCode, KeyEvent event) { 201 if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) { 202 press(content, CAP); 203 return true; 204 } 205 206 if (keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT 207 || keyCode == KeyEvent.KEYCODE_NUM) { 208 press(content, ALT); 209 return true; 210 } 211 212 if (keyCode == KeyEvent.KEYCODE_SYM) { 213 press(content, SYM); 214 return true; 215 } 216 217 return false; // no super to call through to 218 } 219 220 private void press(Editable content, Object what) { 221 int state = content.getSpanFlags(what); 222 223 if (state == PRESSED) 224 ; // repeat before use 225 else if (state == RELEASED) 226 content.setSpan(what, 0, 0, LOCKED); 227 else if (state == USED) 228 ; // repeat after use 229 else if (state == LOCKED) 230 content.removeSpan(what); 231 else 232 content.setSpan(what, 0, 0, PRESSED); 233 } 234 235 /** 236 * Start selecting text. 237 * @hide pending API review 238 */ 239 public static void startSelecting(View view, Spannable content) { 240 content.setSpan(SELECTING, 0, 0, PRESSED); 241 } 242 243 /** 244 * Stop selecting text. This does not actually collapse the selection; 245 * call {@link android.text.Selection#setSelection} too. 246 * @hide pending API review 247 */ 248 public static void stopSelecting(View view, Spannable content) { 249 content.removeSpan(SELECTING); 250 } 251 252 /** 253 * Handles release of the meta keys. 254 */ 255 public boolean onKeyUp(View view, Editable content, int keyCode, 256 KeyEvent event) { 257 if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) { 258 release(content, CAP); 259 return true; 260 } 261 262 if (keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT 263 || keyCode == KeyEvent.KEYCODE_NUM) { 264 release(content, ALT); 265 return true; 266 } 267 268 if (keyCode == KeyEvent.KEYCODE_SYM) { 269 release(content, SYM); 270 return true; 271 } 272 273 return false; // no super to call through to 274 } 275 276 private void release(Editable content, Object what) { 277 int current = content.getSpanFlags(what); 278 279 if (current == USED) 280 content.removeSpan(what); 281 else if (current == PRESSED) 282 content.setSpan(what, 0, 0, RELEASED); 283 } 284 285 public void clearMetaKeyState(View view, Editable content, int states) { 286 clearMetaKeyState(content, states); 287 } 288 289 public static void clearMetaKeyState(Editable content, int states) { 290 if ((states&META_SHIFT_ON) != 0) content.removeSpan(CAP); 291 if ((states&META_ALT_ON) != 0) content.removeSpan(ALT); 292 if ((states&META_SYM_ON) != 0) content.removeSpan(SYM); 293 if ((states&META_SELECTING) != 0) content.removeSpan(SELECTING); 294 } 295 296 /** 297 * Call this if you are a method that ignores the locked meta state 298 * (arrow keys, for example) and you handle a key. 299 */ 300 public static long resetLockedMeta(long state) { 301 state = resetLock(state, META_SHIFT_ON, META_SHIFT_MASK); 302 state = resetLock(state, META_ALT_ON, META_ALT_MASK); 303 state = resetLock(state, META_SYM_ON, META_SYM_MASK); 304 return state; 305 } 306 307 private static long resetLock(long state, int what, long mask) { 308 if ((state&(((long)what)<<LOCKED_SHIFT)) != 0) { 309 state &= ~mask; 310 } 311 return state; 312 } 313 314 // --------------------------------------------------------------------- 315 // Version of API that operates on a state bit mask 316 // --------------------------------------------------------------------- 317 318 /** 319 * Gets the state of the meta keys. 320 * 321 * @param state the current meta state bits. 322 * 323 * @return an integer in which each bit set to one represents a pressed 324 * or locked meta key. 325 */ 326 public static final int getMetaState(long state) { 327 return getActive(state, META_SHIFT_ON, META_SHIFT_ON, META_CAP_LOCKED) | 328 getActive(state, META_ALT_ON, META_ALT_ON, META_ALT_LOCKED) | 329 getActive(state, META_SYM_ON, META_SYM_ON, META_SYM_LOCKED); 330 } 331 332 /** 333 * Gets the state of a particular meta key. 334 * 335 * @param state the current state bits. 336 * @param meta META_SHIFT_ON, META_ALT_ON, or META_SYM_ON 337 * 338 * @return 0 if inactive, 1 if active, 2 if locked. 339 */ 340 public static final int getMetaState(long state, int meta) { 341 switch (meta) { 342 case META_SHIFT_ON: 343 return getActive(state, meta, 1, 2); 344 345 case META_ALT_ON: 346 return getActive(state, meta, 1, 2); 347 348 case META_SYM_ON: 349 return getActive(state, meta, 1, 2); 350 351 default: 352 return 0; 353 } 354 } 355 356 private static int getActive(long state, int meta, int on, int lock) { 357 if ((state&(meta<<LOCKED_SHIFT)) != 0) { 358 return lock; 359 } else if ((state&meta) != 0) { 360 return on; 361 } else { 362 return 0; 363 } 364 } 365 366 /** 367 * Call this method after you handle a keypress so that the meta 368 * state will be reset to unshifted (if it is not still down) 369 * or primed to be reset to unshifted (once it is released). Takes 370 * the current state, returns the new state. 371 */ 372 public static long adjustMetaAfterKeypress(long state) { 373 state = adjust(state, META_SHIFT_ON, META_SHIFT_MASK); 374 state = adjust(state, META_ALT_ON, META_ALT_MASK); 375 state = adjust(state, META_SYM_ON, META_SYM_MASK); 376 return state; 377 } 378 379 private static long adjust(long state, int what, long mask) { 380 if ((state&(((long)what)<<PRESSED_SHIFT)) != 0) 381 return (state&~mask) | what | ((long)what)<<USED_SHIFT; 382 else if ((state&(((long)what)<<RELEASED_SHIFT)) != 0) 383 return state & ~mask; 384 return state; 385 } 386 387 /** 388 * Handles presses of the meta keys. 389 */ 390 public static long handleKeyDown(long state, int keyCode, KeyEvent event) { 391 if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) { 392 return press(state, META_SHIFT_ON, META_SHIFT_MASK); 393 } 394 395 if (keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT 396 || keyCode == KeyEvent.KEYCODE_NUM) { 397 return press(state, META_ALT_ON, META_ALT_MASK); 398 } 399 400 if (keyCode == KeyEvent.KEYCODE_SYM) { 401 return press(state, META_SYM_ON, META_SYM_MASK); 402 } 403 404 return state; 405 } 406 407 private static long press(long state, int what, long mask) { 408 if ((state&(((long)what)<<PRESSED_SHIFT)) != 0) 409 ; // repeat before use 410 else if ((state&(((long)what)<<RELEASED_SHIFT)) != 0) 411 state = (state&~mask) | what | (((long)what) << LOCKED_SHIFT); 412 else if ((state&(((long)what)<<USED_SHIFT)) != 0) 413 ; // repeat after use 414 else if ((state&(((long)what)<<LOCKED_SHIFT)) != 0) 415 state = state&~mask; 416 else 417 state = state | what | (((long)what)<<PRESSED_SHIFT); 418 return state; 419 } 420 421 /** 422 * Handles release of the meta keys. 423 */ 424 public static long handleKeyUp(long state, int keyCode, KeyEvent event) { 425 if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) { 426 return release(state, META_SHIFT_ON, META_SHIFT_MASK); 427 } 428 429 if (keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT 430 || keyCode == KeyEvent.KEYCODE_NUM) { 431 return release(state, META_ALT_ON, META_ALT_MASK); 432 } 433 434 if (keyCode == KeyEvent.KEYCODE_SYM) { 435 return release(state, META_SYM_ON, META_SYM_MASK); 436 } 437 438 return state; 439 } 440 441 private static long release(long state, int what, long mask) { 442 if ((state&(((long)what)<<USED_SHIFT)) != 0) 443 state = state&~mask; 444 else if ((state&(((long)what)<<PRESSED_SHIFT)) != 0) 445 state = state | what | (((long)what)<<RELEASED_SHIFT); 446 return state; 447 } 448 449 public long clearMetaKeyState(long state, int which) { 450 if ((which&META_SHIFT_ON) != 0) 451 state = resetLock(state, META_SHIFT_ON, META_SHIFT_MASK); 452 if ((which&META_ALT_ON) != 0) 453 state = resetLock(state, META_ALT_ON, META_ALT_MASK); 454 if ((which&META_SYM_ON) != 0) 455 state = resetLock(state, META_SYM_ON, META_SYM_MASK); 456 return state; 457 } 458 459 /** 460 * The meta key has been pressed but has not yet been used. 461 */ 462 private static final int PRESSED = 463 Spannable.SPAN_MARK_MARK | (1 << Spannable.SPAN_USER_SHIFT); 464 465 /** 466 * The meta key has been pressed and released but has still 467 * not yet been used. 468 */ 469 private static final int RELEASED = 470 Spannable.SPAN_MARK_MARK | (2 << Spannable.SPAN_USER_SHIFT); 471 472 /** 473 * The meta key has been pressed and used but has not yet been released. 474 */ 475 private static final int USED = 476 Spannable.SPAN_MARK_MARK | (3 << Spannable.SPAN_USER_SHIFT); 477 478 /** 479 * The meta key has been pressed and released without use, and then 480 * pressed again; it may also have been released again. 481 */ 482 private static final int LOCKED = 483 Spannable.SPAN_MARK_MARK | (4 << Spannable.SPAN_USER_SHIFT); 484} 485 486