Home | History | Annotate | Download | only in method
      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.view.KeyEvent;
     20 import android.view.View;
     21 import 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 
     30 public 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