PasswordEntryKeyboardHelper.java revision 51ea88a86e5cf418e956b199de35658f0a5fd623
1/* 2 * Copyright (C) 2010 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17package com.android.internal.widget; 18 19import android.content.Context; 20import android.content.res.Resources; 21import android.inputmethodservice.Keyboard; 22import android.inputmethodservice.KeyboardView; 23import android.inputmethodservice.KeyboardView.OnKeyboardActionListener; 24import android.os.Handler; 25import android.os.SystemClock; 26import android.os.Vibrator; 27import android.provider.Settings; 28import android.util.Log; 29import android.view.KeyCharacterMap; 30import android.view.KeyEvent; 31import android.view.View; 32import android.view.ViewGroup; 33import android.view.ViewRootImpl; 34import com.android.internal.R; 35 36public class PasswordEntryKeyboardHelper implements OnKeyboardActionListener { 37 38 public static final int KEYBOARD_MODE_ALPHA = 0; 39 public static final int KEYBOARD_MODE_NUMERIC = 1; 40 private static final int KEYBOARD_STATE_NORMAL = 0; 41 private static final int KEYBOARD_STATE_SHIFTED = 1; 42 private static final int KEYBOARD_STATE_CAPSLOCK = 2; 43 private static final String TAG = "PasswordEntryKeyboardHelper"; 44 private int mKeyboardMode = KEYBOARD_MODE_ALPHA; 45 private int mKeyboardState = KEYBOARD_STATE_NORMAL; 46 private PasswordEntryKeyboard mQwertyKeyboard; 47 private PasswordEntryKeyboard mQwertyKeyboardShifted; 48 private PasswordEntryKeyboard mSymbolsKeyboard; 49 private PasswordEntryKeyboard mSymbolsKeyboardShifted; 50 private PasswordEntryKeyboard mNumericKeyboard; 51 private final Context mContext; 52 private final View mTargetView; 53 private final KeyboardView mKeyboardView; 54 private long[] mVibratePattern; 55 private final Vibrator mVibrator; 56 57 public PasswordEntryKeyboardHelper(Context context, KeyboardView keyboardView, View targetView) { 58 this(context, keyboardView, targetView, true); 59 } 60 61 public PasswordEntryKeyboardHelper(Context context, KeyboardView keyboardView, View targetView, 62 boolean useFullScreenWidth) { 63 mContext = context; 64 mTargetView = targetView; 65 mKeyboardView = keyboardView; 66 if (useFullScreenWidth 67 || mKeyboardView.getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT) { 68 createKeyboards(); 69 } else { 70 createKeyboardsWithSpecificSize(mKeyboardView.getLayoutParams().width, 71 mKeyboardView.getLayoutParams().height); 72 } 73 mKeyboardView.setOnKeyboardActionListener(this); 74 mVibrator = new Vibrator(); 75 } 76 77 public boolean isAlpha() { 78 return mKeyboardMode == KEYBOARD_MODE_ALPHA; 79 } 80 81 private void createKeyboardsWithSpecificSize(int viewWidth, int viewHeight) { 82 mNumericKeyboard = new PasswordEntryKeyboard(mContext, R.xml.password_kbd_numeric, 83 viewWidth, viewHeight); 84 mQwertyKeyboard = new PasswordEntryKeyboard(mContext, 85 R.xml.password_kbd_qwerty, R.id.mode_normal, viewWidth, viewHeight); 86 mQwertyKeyboard.enableShiftLock(); 87 88 mQwertyKeyboardShifted = new PasswordEntryKeyboard(mContext, 89 R.xml.password_kbd_qwerty_shifted, 90 R.id.mode_normal, viewWidth, viewHeight); 91 mQwertyKeyboardShifted.enableShiftLock(); 92 mQwertyKeyboardShifted.setShifted(true); // always shifted. 93 94 mSymbolsKeyboard = new PasswordEntryKeyboard(mContext, R.xml.password_kbd_symbols, 95 viewWidth, viewHeight); 96 mSymbolsKeyboard.enableShiftLock(); 97 98 mSymbolsKeyboardShifted = new PasswordEntryKeyboard(mContext, 99 R.xml.password_kbd_symbols_shift, viewWidth, viewHeight); 100 mSymbolsKeyboardShifted.enableShiftLock(); 101 mSymbolsKeyboardShifted.setShifted(true); // always shifted 102 } 103 104 private void createKeyboards() { 105 mNumericKeyboard = new PasswordEntryKeyboard(mContext, R.xml.password_kbd_numeric); 106 mQwertyKeyboard = new PasswordEntryKeyboard(mContext, 107 R.xml.password_kbd_qwerty, R.id.mode_normal); 108 mQwertyKeyboard.enableShiftLock(); 109 110 mQwertyKeyboardShifted = new PasswordEntryKeyboard(mContext, 111 R.xml.password_kbd_qwerty_shifted, 112 R.id.mode_normal); 113 mQwertyKeyboardShifted.enableShiftLock(); 114 mQwertyKeyboardShifted.setShifted(true); // always shifted. 115 116 mSymbolsKeyboard = new PasswordEntryKeyboard(mContext, R.xml.password_kbd_symbols); 117 mSymbolsKeyboard.enableShiftLock(); 118 119 mSymbolsKeyboardShifted = new PasswordEntryKeyboard(mContext, 120 R.xml.password_kbd_symbols_shift); 121 mSymbolsKeyboardShifted.enableShiftLock(); 122 mSymbolsKeyboardShifted.setShifted(true); // always shifted 123 } 124 125 public void setKeyboardMode(int mode) { 126 switch (mode) { 127 case KEYBOARD_MODE_ALPHA: 128 mKeyboardView.setKeyboard(mQwertyKeyboard); 129 mKeyboardState = KEYBOARD_STATE_NORMAL; 130 final boolean visiblePassword = Settings.System.getInt( 131 mContext.getContentResolver(), 132 Settings.System.TEXT_SHOW_PASSWORD, 1) != 0; 133 final boolean enablePreview = false; // TODO: grab from configuration 134 mKeyboardView.setPreviewEnabled(visiblePassword && enablePreview); 135 break; 136 case KEYBOARD_MODE_NUMERIC: 137 mKeyboardView.setKeyboard(mNumericKeyboard); 138 mKeyboardState = KEYBOARD_STATE_NORMAL; 139 mKeyboardView.setPreviewEnabled(false); // never show popup for numeric keypad 140 break; 141 } 142 mKeyboardMode = mode; 143 } 144 145 private void sendKeyEventsToTarget(int character) { 146 Handler handler = mTargetView.getHandler(); 147 KeyEvent[] events = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD).getEvents( 148 new char[] { (char) character }); 149 if (events != null) { 150 final int N = events.length; 151 for (int i=0; i<N; i++) { 152 KeyEvent event = events[i]; 153 event = KeyEvent.changeFlags(event, event.getFlags() 154 | KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE); 155 handler.sendMessage(handler.obtainMessage(ViewRootImpl.DISPATCH_KEY, event)); 156 } 157 } 158 } 159 160 public void sendDownUpKeyEvents(int keyEventCode) { 161 long eventTime = SystemClock.uptimeMillis(); 162 Handler handler = mTargetView.getHandler(); 163 handler.sendMessage(handler.obtainMessage(ViewRootImpl.DISPATCH_KEY_FROM_IME, 164 new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, 165 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 166 KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE))); 167 handler.sendMessage(handler.obtainMessage(ViewRootImpl.DISPATCH_KEY_FROM_IME, 168 new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_UP, keyEventCode, 0, 0, 169 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 170 KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE))); 171 } 172 173 public void onKey(int primaryCode, int[] keyCodes) { 174 if (primaryCode == Keyboard.KEYCODE_DELETE) { 175 handleBackspace(); 176 } else if (primaryCode == Keyboard.KEYCODE_SHIFT) { 177 handleShift(); 178 } else if (primaryCode == Keyboard.KEYCODE_CANCEL) { 179 handleClose(); 180 return; 181 } else if (primaryCode == Keyboard.KEYCODE_MODE_CHANGE && mKeyboardView != null) { 182 handleModeChange(); 183 } else { 184 handleCharacter(primaryCode, keyCodes); 185 // Switch back to old keyboard if we're not in capslock mode 186 if (mKeyboardState == KEYBOARD_STATE_SHIFTED) { 187 // skip to the unlocked state 188 mKeyboardState = KEYBOARD_STATE_CAPSLOCK; 189 handleShift(); 190 } 191 } 192 } 193 194 /** 195 * Sets and enables vibrate pattern. If id is 0 (or can't be loaded), vibrate is disabled. 196 * @param id resource id for array containing vibrate pattern. 197 */ 198 public void setVibratePattern(int id) { 199 int[] tmpArray = null; 200 try { 201 tmpArray = mContext.getResources().getIntArray(id); 202 } catch (Resources.NotFoundException e) { 203 if (id != 0) { 204 Log.e(TAG, "Vibrate pattern missing", e); 205 } 206 } 207 if (tmpArray == null) { 208 mVibratePattern = null; 209 return; 210 } 211 mVibratePattern = new long[tmpArray.length]; 212 for (int i = 0; i < tmpArray.length; i++) { 213 mVibratePattern[i] = tmpArray[i]; 214 } 215 } 216 217 private void handleModeChange() { 218 final Keyboard current = mKeyboardView.getKeyboard(); 219 Keyboard next = null; 220 if (current == mQwertyKeyboard || current == mQwertyKeyboardShifted) { 221 next = mSymbolsKeyboard; 222 } else if (current == mSymbolsKeyboard || current == mSymbolsKeyboardShifted) { 223 next = mQwertyKeyboard; 224 } 225 if (next != null) { 226 mKeyboardView.setKeyboard(next); 227 mKeyboardState = KEYBOARD_STATE_NORMAL; 228 } 229 } 230 231 public void handleBackspace() { 232 sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL); 233 } 234 235 private void handleShift() { 236 if (mKeyboardView == null) { 237 return; 238 } 239 Keyboard current = mKeyboardView.getKeyboard(); 240 PasswordEntryKeyboard next = null; 241 final boolean isAlphaMode = current == mQwertyKeyboard 242 || current == mQwertyKeyboardShifted; 243 if (mKeyboardState == KEYBOARD_STATE_NORMAL) { 244 mKeyboardState = isAlphaMode ? KEYBOARD_STATE_SHIFTED : KEYBOARD_STATE_CAPSLOCK; 245 next = isAlphaMode ? mQwertyKeyboardShifted : mSymbolsKeyboardShifted; 246 } else if (mKeyboardState == KEYBOARD_STATE_SHIFTED) { 247 mKeyboardState = KEYBOARD_STATE_CAPSLOCK; 248 next = isAlphaMode ? mQwertyKeyboardShifted : mSymbolsKeyboardShifted; 249 } else if (mKeyboardState == KEYBOARD_STATE_CAPSLOCK) { 250 mKeyboardState = KEYBOARD_STATE_NORMAL; 251 next = isAlphaMode ? mQwertyKeyboard : mSymbolsKeyboard; 252 } 253 if (next != null) { 254 if (next != current) { 255 mKeyboardView.setKeyboard(next); 256 } 257 next.setShiftLocked(mKeyboardState == KEYBOARD_STATE_CAPSLOCK); 258 mKeyboardView.setShifted(mKeyboardState != KEYBOARD_STATE_NORMAL); 259 } 260 } 261 262 private void handleCharacter(int primaryCode, int[] keyCodes) { 263 // Maybe turn off shift if not in capslock mode. 264 if (mKeyboardView.isShifted() && primaryCode != ' ' && primaryCode != '\n') { 265 primaryCode = Character.toUpperCase(primaryCode); 266 } 267 sendKeyEventsToTarget(primaryCode); 268 } 269 270 private void handleClose() { 271 272 } 273 274 public void onPress(int primaryCode) { 275 if (mVibratePattern != null) { 276 mVibrator.vibrate(mVibratePattern, -1); 277 } 278 } 279 280 public void onRelease(int primaryCode) { 281 282 } 283 284 public void onText(CharSequence text) { 285 286 } 287 288 public void swipeDown() { 289 290 } 291 292 public void swipeLeft() { 293 294 } 295 296 public void swipeRight() { 297 298 } 299 300 public void swipeUp() { 301 302 } 303}; 304