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 any 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    /**
170     * Returns true if this object is one that this class would use to
171     * keep track of the selecting meta state in the specified text.
172     */
173    public static boolean isSelectingMetaTracker(CharSequence text, Object what) {
174        return what == SELECTING;
175    }
176
177    private static void adjust(Spannable content, Object what) {
178        int current = content.getSpanFlags(what);
179
180        if (current == PRESSED)
181            content.setSpan(what, 0, 0, USED);
182        else if (current == RELEASED)
183            content.removeSpan(what);
184    }
185
186    /**
187     * Call this if you are a method that ignores the locked meta state
188     * (arrow keys, for example) and you handle a key.
189     */
190    protected static void resetLockedMeta(Spannable content) {
191        resetLock(content, CAP);
192        resetLock(content, ALT);
193        resetLock(content, SYM);
194        resetLock(content, SELECTING);
195    }
196
197    private static void resetLock(Spannable content, Object what) {
198        int current = content.getSpanFlags(what);
199
200        if (current == LOCKED)
201            content.removeSpan(what);
202    }
203
204    /**
205     * Handles presses of the meta keys.
206     */
207    public boolean onKeyDown(View view, Editable content,
208                             int keyCode, KeyEvent event) {
209        if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
210            press(content, CAP);
211            return true;
212        }
213
214        if (keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT
215                || keyCode == KeyEvent.KEYCODE_NUM) {
216            press(content, ALT);
217            return true;
218        }
219
220        if (keyCode == KeyEvent.KEYCODE_SYM) {
221            press(content, SYM);
222            return true;
223        }
224
225        return false; // no super to call through to
226    }
227
228    private void press(Editable content, Object what) {
229        int state = content.getSpanFlags(what);
230
231        if (state == PRESSED)
232            ; // repeat before use
233        else if (state == RELEASED)
234            content.setSpan(what, 0, 0, LOCKED);
235        else if (state == USED)
236            ; // repeat after use
237        else if (state == LOCKED)
238            content.removeSpan(what);
239        else
240            content.setSpan(what, 0, 0, PRESSED);
241    }
242
243    /**
244     * Start selecting text.
245     * @hide pending API review
246     */
247    public static void startSelecting(View view, Spannable content) {
248        content.setSpan(SELECTING, 0, 0, PRESSED);
249    }
250
251    /**
252     * Stop selecting text.  This does not actually collapse the selection;
253     * call {@link android.text.Selection#setSelection} too.
254     * @hide pending API review
255     */
256    public static void stopSelecting(View view, Spannable content) {
257        content.removeSpan(SELECTING);
258    }
259
260    /**
261     * Handles release of the meta keys.
262     */
263    public boolean onKeyUp(View view, Editable content, int keyCode,
264                                    KeyEvent event) {
265        if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
266            release(content, CAP);
267            return true;
268        }
269
270        if (keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT
271                || keyCode == KeyEvent.KEYCODE_NUM) {
272            release(content, ALT);
273            return true;
274        }
275
276        if (keyCode == KeyEvent.KEYCODE_SYM) {
277            release(content, SYM);
278            return true;
279        }
280
281        return false; // no super to call through to
282    }
283
284    private void release(Editable content, Object what) {
285        int current = content.getSpanFlags(what);
286
287        if (current == USED)
288            content.removeSpan(what);
289        else if (current == PRESSED)
290            content.setSpan(what, 0, 0, RELEASED);
291    }
292
293    public void clearMetaKeyState(View view, Editable content, int states) {
294        clearMetaKeyState(content, states);
295    }
296
297    public static void clearMetaKeyState(Editable content, int states) {
298        if ((states&META_SHIFT_ON) != 0) content.removeSpan(CAP);
299        if ((states&META_ALT_ON) != 0) content.removeSpan(ALT);
300        if ((states&META_SYM_ON) != 0) content.removeSpan(SYM);
301        if ((states&META_SELECTING) != 0) content.removeSpan(SELECTING);
302    }
303
304    /**
305     * Call this if you are a method that ignores the locked meta state
306     * (arrow keys, for example) and you handle a key.
307     */
308    public static long resetLockedMeta(long state) {
309        state = resetLock(state, META_SHIFT_ON, META_SHIFT_MASK);
310        state = resetLock(state, META_ALT_ON, META_ALT_MASK);
311        state = resetLock(state, META_SYM_ON, META_SYM_MASK);
312        return state;
313    }
314
315    private static long resetLock(long state, int what, long mask) {
316        if ((state&(((long)what)<<LOCKED_SHIFT)) != 0) {
317            state &= ~mask;
318        }
319        return state;
320    }
321
322    // ---------------------------------------------------------------------
323    // Version of API that operates on a state bit mask
324    // ---------------------------------------------------------------------
325
326    /**
327     * Gets the state of the meta keys.
328     *
329     * @param state the current meta state bits.
330     *
331     * @return an integer in which each bit set to one represents a pressed
332     *         or locked meta key.
333     */
334    public static final int getMetaState(long state) {
335        return getActive(state, META_SHIFT_ON, META_SHIFT_ON, META_CAP_LOCKED) |
336               getActive(state, META_ALT_ON, META_ALT_ON, META_ALT_LOCKED) |
337               getActive(state, META_SYM_ON, META_SYM_ON, META_SYM_LOCKED);
338    }
339
340    /**
341     * Gets the state of a particular meta key.
342     *
343     * @param state the current state bits.
344     * @param meta META_SHIFT_ON, META_ALT_ON, or META_SYM_ON
345     *
346     * @return 0 if inactive, 1 if active, 2 if locked.
347     */
348    public static final int getMetaState(long state, int meta) {
349        switch (meta) {
350            case META_SHIFT_ON:
351                return getActive(state, meta, 1, 2);
352
353            case META_ALT_ON:
354                return getActive(state, meta, 1, 2);
355
356            case META_SYM_ON:
357                return getActive(state, meta, 1, 2);
358
359            default:
360                return 0;
361        }
362    }
363
364    private static int getActive(long state, int meta, int on, int lock) {
365        if ((state&(meta<<LOCKED_SHIFT)) != 0) {
366            return lock;
367        } else if ((state&meta) != 0) {
368            return on;
369        } else {
370            return 0;
371        }
372    }
373
374    /**
375     * Call this method after you handle a keypress so that the meta
376     * state will be reset to unshifted (if it is not still down)
377     * or primed to be reset to unshifted (once it is released).  Takes
378     * the current state, returns the new state.
379     */
380    public static long adjustMetaAfterKeypress(long state) {
381        state = adjust(state, META_SHIFT_ON, META_SHIFT_MASK);
382        state = adjust(state, META_ALT_ON, META_ALT_MASK);
383        state = adjust(state, META_SYM_ON, META_SYM_MASK);
384        return state;
385    }
386
387    private static long adjust(long state, int what, long mask) {
388        if ((state&(((long)what)<<PRESSED_SHIFT)) != 0)
389            return (state&~mask) | what | ((long)what)<<USED_SHIFT;
390        else if ((state&(((long)what)<<RELEASED_SHIFT)) != 0)
391            return state & ~mask;
392        return state;
393    }
394
395    /**
396     * Handles presses of the meta keys.
397     */
398    public static long handleKeyDown(long state, int keyCode, KeyEvent event) {
399        if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
400            return press(state, META_SHIFT_ON, META_SHIFT_MASK);
401        }
402
403        if (keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT
404                || keyCode == KeyEvent.KEYCODE_NUM) {
405            return press(state, META_ALT_ON, META_ALT_MASK);
406        }
407
408        if (keyCode == KeyEvent.KEYCODE_SYM) {
409            return press(state, META_SYM_ON, META_SYM_MASK);
410        }
411
412        return state;
413    }
414
415    private static long press(long state, int what, long mask) {
416        if ((state&(((long)what)<<PRESSED_SHIFT)) != 0)
417            ; // repeat before use
418        else if ((state&(((long)what)<<RELEASED_SHIFT)) != 0)
419            state = (state&~mask) | what | (((long)what) << LOCKED_SHIFT);
420        else if ((state&(((long)what)<<USED_SHIFT)) != 0)
421            ; // repeat after use
422        else if ((state&(((long)what)<<LOCKED_SHIFT)) != 0)
423            state = state&~mask;
424        else
425            state = state | what | (((long)what)<<PRESSED_SHIFT);
426        return state;
427    }
428
429    /**
430     * Handles release of the meta keys.
431     */
432    public static long handleKeyUp(long state, int keyCode, KeyEvent event) {
433        if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
434            return release(state, META_SHIFT_ON, META_SHIFT_MASK);
435        }
436
437        if (keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT
438                || keyCode == KeyEvent.KEYCODE_NUM) {
439            return release(state, META_ALT_ON, META_ALT_MASK);
440        }
441
442        if (keyCode == KeyEvent.KEYCODE_SYM) {
443            return release(state, META_SYM_ON, META_SYM_MASK);
444        }
445
446        return state;
447    }
448
449    private static long release(long state, int what, long mask) {
450        if ((state&(((long)what)<<USED_SHIFT)) != 0)
451            state = state&~mask;
452        else if ((state&(((long)what)<<PRESSED_SHIFT)) != 0)
453            state = state | what | (((long)what)<<RELEASED_SHIFT);
454        return state;
455    }
456
457    public long clearMetaKeyState(long state, int which) {
458        if ((which&META_SHIFT_ON) != 0)
459            state = resetLock(state, META_SHIFT_ON, META_SHIFT_MASK);
460        if ((which&META_ALT_ON) != 0)
461            state = resetLock(state, META_ALT_ON, META_ALT_MASK);
462        if ((which&META_SYM_ON) != 0)
463            state = resetLock(state, META_SYM_ON, META_SYM_MASK);
464        return state;
465    }
466
467    /**
468     * The meta key has been pressed but has not yet been used.
469     */
470    private static final int PRESSED =
471        Spannable.SPAN_MARK_MARK | (1 << Spannable.SPAN_USER_SHIFT);
472
473    /**
474     * The meta key has been pressed and released but has still
475     * not yet been used.
476     */
477    private static final int RELEASED =
478        Spannable.SPAN_MARK_MARK | (2 << Spannable.SPAN_USER_SHIFT);
479
480    /**
481     * The meta key has been pressed and used but has not yet been released.
482     */
483    private static final int USED =
484        Spannable.SPAN_MARK_MARK | (3 << Spannable.SPAN_USER_SHIFT);
485
486    /**
487     * The meta key has been pressed and released without use, and then
488     * pressed again; it may also have been released again.
489     */
490    private static final int LOCKED =
491        Spannable.SPAN_MARK_MARK | (4 << Spannable.SPAN_USER_SHIFT);
492}
493
494