MetaKeyKeyListener.java revision 54b6cfa9a9e5b861a9930af873580d6dc20f773c
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 * (caps, fn, sym). Key listener that care about meta state should 26 * inherit from it; you should not instantiate this class directly in a client. 27 */ 28 29public abstract class MetaKeyKeyListener { 30 public static final int META_SHIFT_ON = KeyEvent.META_SHIFT_ON; 31 public static final int META_ALT_ON = KeyEvent.META_ALT_ON; 32 public static final int META_SYM_ON = KeyEvent.META_SYM_ON; 33 34 public static final int META_CAP_LOCKED = KeyEvent.META_SHIFT_ON << 8; 35 public static final int META_ALT_LOCKED = KeyEvent.META_ALT_ON << 8; 36 public static final int META_SYM_LOCKED = KeyEvent.META_SYM_ON << 8; 37 38 private static final Object CAP = new Object(); 39 private static final Object ALT = new Object(); 40 private static final Object SYM = new Object(); 41 42 /** 43 * Resets all meta state to inactive. 44 */ 45 public static void resetMetaState(Spannable text) { 46 text.removeSpan(CAP); 47 text.removeSpan(ALT); 48 text.removeSpan(SYM); 49 } 50 51 /** 52 * Gets the state of the meta keys. 53 * 54 * @param text the buffer in which the meta key would have been pressed. 55 * 56 * @return an integer in which each bit set to one represents a pressed 57 * or locked meta key. 58 */ 59 public static final int getMetaState(CharSequence text) { 60 return getActive(text, CAP, META_SHIFT_ON, META_CAP_LOCKED) | 61 getActive(text, ALT, META_ALT_ON, META_ALT_LOCKED) | 62 getActive(text, SYM, META_SYM_ON, META_SYM_LOCKED); 63 } 64 65 /** 66 * Gets the state of a particular meta key. 67 * 68 * @param meta META_SHIFT_ON, META_ALT_ON, or META_SYM_ON 69 * @param text the buffer in which the meta key would have been pressed. 70 * 71 * @return 0 if inactive, 1 if active, 2 if locked. 72 */ 73 public static final int getMetaState(CharSequence text, int meta) { 74 switch (meta) { 75 case META_SHIFT_ON: 76 return getActive(text, CAP, 1, 2); 77 78 case META_ALT_ON: 79 return getActive(text, ALT, 1, 2); 80 81 case META_SYM_ON: 82 return getActive(text, SYM, 1, 2); 83 84 default: 85 return 0; 86 } 87 } 88 89 private static int getActive(CharSequence text, Object meta, 90 int on, int lock) { 91 if (!(text instanceof Spanned)) { 92 return 0; 93 } 94 95 Spanned sp = (Spanned) text; 96 int flag = sp.getSpanFlags(meta); 97 98 if (flag == LOCKED) { 99 return lock; 100 } else if (flag != 0) { 101 return on; 102 } else { 103 return 0; 104 } 105 } 106 107 /** 108 * Call this method after you handle a keypress so that the meta 109 * state will be reset to unshifted (if it is not still down) 110 * or primed to be reset to unshifted (once it is released). 111 */ 112 public static void adjustMetaAfterKeypress(Spannable content) { 113 adjust(content, CAP); 114 adjust(content, ALT); 115 adjust(content, SYM); 116 } 117 118 /** 119 * Returns true if this object is one that this class would use to 120 * keep track of meta state in the specified text. 121 */ 122 public static boolean isMetaTracker(CharSequence text, Object what) { 123 return what == CAP || what == ALT || what == SYM; 124 } 125 126 private static void adjust(Spannable content, Object what) { 127 int current = content.getSpanFlags(what); 128 129 if (current == PRESSED) 130 content.setSpan(what, 0, 0, USED); 131 else if (current == RELEASED) 132 content.removeSpan(what); 133 } 134 135 /** 136 * Call this if you are a method that ignores the locked meta state 137 * (arrow keys, for example) and you handle a key. 138 */ 139 protected static void resetLockedMeta(Spannable content) { 140 resetLock(content, CAP); 141 resetLock(content, ALT); 142 resetLock(content, SYM); 143 } 144 145 private static void resetLock(Spannable content, Object what) { 146 int current = content.getSpanFlags(what); 147 148 if (current == LOCKED) 149 content.removeSpan(what); 150 } 151 152 /** 153 * Handles presses of the meta keys. 154 */ 155 public boolean onKeyDown(View view, Editable content, 156 int keyCode, KeyEvent event) { 157 if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) { 158 press(content, CAP); 159 return true; 160 } 161 162 if (keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT 163 || keyCode == KeyEvent.KEYCODE_NUM) { 164 press(content, ALT); 165 return true; 166 } 167 168 if (keyCode == KeyEvent.KEYCODE_SYM) { 169 press(content, SYM); 170 return true; 171 } 172 173 return false; // no super to call through to 174 } 175 176 private void press(Editable content, Object what) { 177 int state = content.getSpanFlags(what); 178 179 if (state == PRESSED) 180 ; // repeat before use 181 else if (state == RELEASED) 182 content.setSpan(what, 0, 0, LOCKED); 183 else if (state == USED) 184 ; // repeat after use 185 else if (state == LOCKED) 186 content.removeSpan(what); 187 else 188 content.setSpan(what, 0, 0, PRESSED); 189 } 190 191 /** 192 * Handles release of the meta keys. 193 */ 194 public boolean onKeyUp(View view, Editable content, int keyCode, 195 KeyEvent event) { 196 if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) { 197 release(content, CAP); 198 return true; 199 } 200 201 if (keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT 202 || keyCode == KeyEvent.KEYCODE_NUM) { 203 release(content, ALT); 204 return true; 205 } 206 207 if (keyCode == KeyEvent.KEYCODE_SYM) { 208 release(content, SYM); 209 return true; 210 } 211 212 return false; // no super to call through to 213 } 214 215 private void release(Editable content, Object what) { 216 int current = content.getSpanFlags(what); 217 218 if (current == USED) 219 content.removeSpan(what); 220 else if (current == PRESSED) 221 content.setSpan(what, 0, 0, RELEASED); 222 } 223 224 /** 225 * The meta key has been pressed but has not yet been used. 226 */ 227 private static final int PRESSED = 228 Spannable.SPAN_MARK_MARK | (1 << Spannable.SPAN_USER_SHIFT); 229 230 /** 231 * The meta key has been pressed and released but has still 232 * not yet been used. 233 */ 234 private static final int RELEASED = 235 Spannable.SPAN_MARK_MARK | (2 << Spannable.SPAN_USER_SHIFT); 236 237 /** 238 * The meta key has been pressed and used but has not yet been released. 239 */ 240 private static final int USED = 241 Spannable.SPAN_MARK_MARK | (3 << Spannable.SPAN_USER_SHIFT); 242 243 /** 244 * The meta key has been pressed and released without use, and then 245 * pressed again; it may also have been released again. 246 */ 247 private static final int LOCKED = 248 Spannable.SPAN_MARK_MARK | (4 << Spannable.SPAN_USER_SHIFT); 249} 250 251