PasswordEntryKeyboard.java revision 0b31970cac04259a6e20dfc6d6e42cd9532528e3
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 java.util.Locale; 20import android.content.Context; 21import android.content.res.Resources; 22import android.content.res.XmlResourceParser; 23import android.graphics.Bitmap; 24import android.graphics.Canvas; 25import android.graphics.Paint; 26import android.graphics.PorterDuff; 27import android.graphics.Paint.Align; 28import android.graphics.drawable.BitmapDrawable; 29import android.graphics.drawable.Drawable; 30import android.inputmethodservice.Keyboard; 31import android.inputmethodservice.KeyboardView; 32import android.util.Log; 33import com.android.internal.R; 34 35/** 36 * A basic, embed-able keyboard designed for password entry. Allows entry of all Latin-1 characters. 37 * 38 * It has two modes: alpha and numeric. In alpha mode, it allows all Latin-1 characters and enables 39 * an additional keyboard with symbols. In numeric mode, it shows a 12-key DTMF dialer-like 40 * keypad with alpha characters hints. 41 */ 42public class PasswordEntryKeyboard extends Keyboard { 43 private static final String TAG = "PasswordEntryKeyboard"; 44 private static final int SHIFT_OFF = 0; 45 private static final int SHIFT_ON = 1; 46 private static final int SHIFT_LOCKED = 2; 47 public static final int KEYCODE_SPACE = ' '; 48 49 private Drawable mShiftIcon; 50 private Drawable mShiftLockIcon; 51 private Drawable mShiftLockPreviewIcon; 52 private Drawable mOldShiftIcon; 53 private Drawable mOldShiftPreviewIcon; 54 private Drawable mSpaceIcon; 55 private Key mShiftKey; 56 private Key mEnterKey; 57 private Key mF1Key; 58 private Key mSpaceKey; 59 private Locale mLocale; 60 private Resources mRes; 61 private int mExtensionResId; 62 private int mShiftState = SHIFT_OFF; 63 64 static int sSpacebarVerticalCorrection; 65 66 public PasswordEntryKeyboard(Context context, int xmlLayoutResId) { 67 this(context, xmlLayoutResId, 0); 68 } 69 70 public PasswordEntryKeyboard(Context context, int xmlLayoutResId, int mode) { 71 super(context, xmlLayoutResId, mode); 72 final Resources res = context.getResources(); 73 mRes = res; 74 mShiftIcon = res.getDrawable(R.drawable.sym_keyboard_shift); 75 mShiftLockIcon = res.getDrawable(R.drawable.sym_keyboard_shift_locked); 76 mShiftLockPreviewIcon = res.getDrawable(R.drawable.sym_keyboard_feedback_shift_locked); 77 mShiftLockPreviewIcon.setBounds(0, 0, 78 mShiftLockPreviewIcon.getIntrinsicWidth(), 79 mShiftLockPreviewIcon.getIntrinsicHeight()); 80 mSpaceIcon = res.getDrawable(R.drawable.sym_keyboard_space); 81 sSpacebarVerticalCorrection = res.getDimensionPixelOffset( 82 R.dimen.password_keyboard_spacebar_vertical_correction); 83 } 84 85 public PasswordEntryKeyboard(Context context, int layoutTemplateResId, 86 CharSequence characters, int columns, int horizontalPadding) { 87 super(context, layoutTemplateResId, characters, columns, horizontalPadding); 88 } 89 90 @Override 91 protected Key createKeyFromXml(Resources res, Row parent, int x, int y, 92 XmlResourceParser parser) { 93 LatinKey key = new LatinKey(res, parent, x, y, parser); 94 final int code = key.codes[0]; 95 if (code >=0 && code != '\n' && (code < 32 || code > 127)) { 96 Log.w(TAG, "Key code for " + key.label + " is not latin-1"); 97 key.label = " "; 98 key.setEnabled(false); 99 } 100 switch (key.codes[0]) { 101 case 10: 102 mEnterKey = key; 103 break; 104 case PasswordEntryKeyboardView.KEYCODE_F1: 105 mF1Key = key; 106 break; 107 case 32: 108 mSpaceKey = key; 109 break; 110 } 111 return key; 112 } 113 114 /** 115 * Allows enter key resources to be overridden 116 * @param res resources to grab given items from 117 * @param previewId preview drawable shown on enter key 118 * @param iconId normal drawable shown on enter key 119 * @param labelId string shown on enter key 120 */ 121 void setEnterKeyResources(Resources res, int previewId, int iconId, int labelId) { 122 if (mEnterKey != null) { 123 // Reset some of the rarely used attributes. 124 mEnterKey.popupCharacters = null; 125 mEnterKey.popupResId = 0; 126 mEnterKey.text = null; 127 128 mEnterKey.iconPreview = res.getDrawable(previewId); 129 mEnterKey.icon = res.getDrawable(iconId); 130 mEnterKey.label = res.getText(labelId); 131 132 // Set the initial size of the preview icon 133 if (mEnterKey.iconPreview != null) { 134 mEnterKey.iconPreview.setBounds(0, 0, 135 mEnterKey.iconPreview.getIntrinsicWidth(), 136 mEnterKey.iconPreview.getIntrinsicHeight()); 137 } 138 } 139 } 140 141 /** 142 * Allows shiftlock to be turned on. See {@link #setShiftLocked(boolean)} 143 * 144 */ 145 void enableShiftLock() { 146 int index = getShiftKeyIndex(); 147 if (index >= 0) { 148 mShiftKey = getKeys().get(index); 149 if (mShiftKey instanceof LatinKey) { 150 ((LatinKey)mShiftKey).enableShiftLock(); 151 } 152 mOldShiftIcon = mShiftKey.icon; 153 mOldShiftPreviewIcon = mShiftKey.iconPreview; 154 } 155 } 156 157 /** 158 * Turn on shift lock. This turns on the LED for this key, if it has one. 159 * It should be followed by a call to {@link KeyboardView#invalidateKey(int)} 160 * or {@link KeyboardView#invalidateAllKeys()} 161 * 162 * @param shiftLocked 163 */ 164 void setShiftLocked(boolean shiftLocked) { 165 if (mShiftKey != null) { 166 if (shiftLocked) { 167 mShiftKey.on = true; 168 mShiftKey.icon = mShiftLockIcon; 169 mShiftState = SHIFT_LOCKED; 170 } else { 171 mShiftKey.on = false; 172 mShiftKey.icon = mShiftLockIcon; 173 mShiftState = SHIFT_ON; 174 } 175 } 176 } 177 178 /** 179 * Turn on shift mode. Sets shift mode and turns on icon for shift key. 180 * It should be followed by a call to {@link KeyboardView#invalidateKey(int)} 181 * or {@link KeyboardView#invalidateAllKeys()} 182 * 183 * @param shiftLocked 184 */ 185 @Override 186 public boolean setShifted(boolean shiftState) { 187 boolean shiftChanged = false; 188 if (mShiftKey != null) { 189 if (shiftState == false) { 190 shiftChanged = mShiftState != SHIFT_OFF; 191 mShiftState = SHIFT_OFF; 192 mShiftKey.on = false; 193 mShiftKey.icon = mOldShiftIcon; 194 } else if (mShiftState == SHIFT_OFF) { 195 shiftChanged = mShiftState == SHIFT_OFF; 196 mShiftState = SHIFT_ON; 197 mShiftKey.on = false; 198 mShiftKey.icon = mShiftIcon; 199 } 200 } else { 201 return super.setShifted(shiftState); 202 } 203 return shiftChanged; 204 } 205 206 /** 207 * Whether or not keyboard is shifted. 208 * @return true if keyboard state is shifted. 209 */ 210 @Override 211 public boolean isShifted() { 212 if (mShiftKey != null) { 213 return mShiftState != SHIFT_OFF; 214 } else { 215 return super.isShifted(); 216 } 217 } 218 219 /** 220 * Sets keyboard extension. Keyboard extension is shown when input is detected above keyboard 221 * while keyboard has focus. 222 * 223 * @param resId 224 */ 225 public void setExtension(int resId) { 226 mExtensionResId = resId; 227 } 228 229 /** 230 * Get current extesion resource id. 231 * 232 * @return resource id, 0 if not set. 233 */ 234 public int getExtension() { 235 return mExtensionResId; 236 } 237 238 private void updateSpaceBarForLocale() { 239 if (mLocale != null) { 240 // Create the graphic for spacebar 241 Bitmap buffer = Bitmap.createBitmap(mSpaceKey.width, mSpaceIcon.getIntrinsicHeight(), 242 Bitmap.Config.ARGB_8888); 243 Canvas canvas = new Canvas(buffer); 244 canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR); 245 Paint paint = new Paint(); 246 paint.setAntiAlias(true); 247 // TODO: Make the text size a customizable attribute 248 paint.setTextSize(22); 249 paint.setTextAlign(Align.CENTER); 250 // Draw a drop shadow for the text 251 paint.setShadowLayer(1f, 0, 0, 0xFF000000); 252 paint.setColor(0x80C0C0C0); 253 canvas.drawText(mLocale.getDisplayLanguage(mLocale), 254 buffer.getWidth() / 2, - paint.ascent() + 2, paint); 255 int x = (buffer.getWidth() - mSpaceIcon.getIntrinsicWidth()) / 2; 256 int y = buffer.getHeight() - mSpaceIcon.getIntrinsicHeight(); 257 mSpaceIcon.setBounds(x, y, 258 x + mSpaceIcon.getIntrinsicWidth(), y + mSpaceIcon.getIntrinsicHeight()); 259 mSpaceIcon.draw(canvas); 260 mSpaceKey.icon = new BitmapDrawable(mRes, buffer); 261 mSpaceKey.repeatable = false; 262 } else { 263 mSpaceKey.icon = mRes.getDrawable(R.drawable.sym_keyboard_space); 264 mSpaceKey.repeatable = true; 265 } 266 } 267 268 public void setLanguage(Locale locale) { 269 if (mLocale != null && mLocale.equals(locale)) return; 270 mLocale = locale; 271 updateSpaceBarForLocale(); 272 } 273 274 static class LatinKey extends Keyboard.Key { 275 private boolean mShiftLockEnabled; 276 private boolean mEnabled = true; 277 278 public LatinKey(Resources res, Keyboard.Row parent, int x, int y, 279 XmlResourceParser parser) { 280 super(res, parent, x, y, parser); 281 if (popupCharacters != null && popupCharacters.length() == 0) { 282 // If there is a keyboard with no keys specified in popupCharacters 283 popupResId = 0; 284 } 285 } 286 287 void setEnabled(boolean enabled) { 288 mEnabled = enabled; 289 } 290 291 void enableShiftLock() { 292 mShiftLockEnabled = true; 293 } 294 295 @Override 296 public void onReleased(boolean inside) { 297 if (!mShiftLockEnabled) { 298 super.onReleased(inside); 299 } else { 300 pressed = !pressed; 301 } 302 } 303 304 /** 305 * Overriding this method so that we can reduce the target area for certain keys. 306 */ 307 @Override 308 public boolean isInside(int x, int y) { 309 if (!mEnabled) { 310 return false; 311 } 312 final int code = codes[0]; 313 if (code == KEYCODE_SHIFT || code == KEYCODE_DELETE) { 314 y -= height / 10; 315 if (code == KEYCODE_SHIFT) x += width / 6; 316 if (code == KEYCODE_DELETE) x -= width / 6; 317 } else if (code == KEYCODE_SPACE) { 318 y += PasswordEntryKeyboard.sSpacebarVerticalCorrection; 319 } 320 return super.isInside(x, y); 321 } 322 } 323} 324