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