• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
resetMetaState(Spannable text)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      */
getMetaState(CharSequence text)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      */
getMetaState(CharSequence text, int meta)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 
getActive(CharSequence text, Object meta, int on, int lock)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      */
adjustMetaAfterKeypress(Spannable content)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      */
isMetaTracker(CharSequence text, Object what)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      */
isSelectingMetaTracker(CharSequence text, Object what)232     public static boolean isSelectingMetaTracker(CharSequence text, Object what) {
233         return what == SELECTING;
234     }
235 
adjust(Spannable content, Object what)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      */
resetLockedMeta(Spannable content)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 
resetLock(Spannable content, Object what)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      */
onKeyDown(View view, Editable content, int keyCode, KeyEvent event)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 
press(Editable content, Object what)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      */
startSelecting(View view, Spannable content)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      */
stopSelecting(View view, Spannable content)314     public static void stopSelecting(View view, Spannable content) {
315         content.removeSpan(SELECTING);
316     }
317 
318     /**
319      * Handles release of the meta keys.
320      */
onKeyUp(View view, Editable content, int keyCode, KeyEvent event)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 
release(Editable content, Object what, KeyEvent event)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 
clearMetaKeyState(View view, Editable content, int states)358     public void clearMetaKeyState(View view, Editable content, int states) {
359         clearMetaKeyState(content, states);
360     }
361 
clearMetaKeyState(Editable content, int states)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      */
resetLockedMeta(long state)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      */
getMetaState(long state)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      */
getMetaState(long state, int meta)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      */
adjustMetaAfterKeypress(long state)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      */
handleKeyDown(long state, int keyCode, KeyEvent event)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 
press(long state, int what, long mask, long locked, long pressed, long released, long used)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      */
handleKeyUp(long state, int keyCode, KeyEvent event)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 
release(long state, int what, long mask, long pressed, long released, long used, KeyEvent event)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      */
clearMetaKeyState(long state, int which)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