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.content.res.XmlResourceParser; 22import android.graphics.drawable.Drawable; 23import android.inputmethodservice.Keyboard; 24import android.inputmethodservice.KeyboardView; 25import com.android.internal.R; 26 27/** 28 * A basic, embed-able keyboard designed for password entry. Allows entry of all Latin-1 characters. 29 * 30 * It has two modes: alpha and numeric. In alpha mode, it allows all Latin-1 characters and enables 31 * an additional keyboard with symbols. In numeric mode, it shows a 12-key DTMF dialer-like 32 * keypad with alpha characters hints. 33 */ 34public class PasswordEntryKeyboard extends Keyboard { 35 private static final int SHIFT_OFF = 0; 36 private static final int SHIFT_ON = 1; 37 private static final int SHIFT_LOCKED = 2; 38 public static final int KEYCODE_SPACE = ' '; 39 40 private Drawable mShiftIcon; 41 private Drawable mShiftLockIcon; 42 43 // These two arrays must be the same length 44 private Drawable[] mOldShiftIcons = { null, null }; 45 private Key[] mShiftKeys = { null, null }; 46 47 private Key mEnterKey; 48 private Key mF1Key; 49 private Key mSpaceKey; 50 private int mShiftState = SHIFT_OFF; 51 52 static int sSpacebarVerticalCorrection; 53 54 public PasswordEntryKeyboard(Context context, int xmlLayoutResId) { 55 this(context, xmlLayoutResId, 0); 56 } 57 58 public PasswordEntryKeyboard(Context context, int xmlLayoutResId, int width, int height) { 59 this(context, xmlLayoutResId, 0, width, height); 60 } 61 62 public PasswordEntryKeyboard(Context context, int xmlLayoutResId, int mode) { 63 super(context, xmlLayoutResId, mode); 64 init(context); 65 } 66 67 public PasswordEntryKeyboard(Context context, int xmlLayoutResId, int mode, 68 int width, int height) { 69 super(context, xmlLayoutResId, mode, width, height); 70 init(context); 71 } 72 73 private void init(Context context) { 74 final Resources res = context.getResources(); 75 mShiftIcon = context.getDrawable(R.drawable.sym_keyboard_shift); 76 mShiftLockIcon = context.getDrawable(R.drawable.sym_keyboard_shift_locked); 77 sSpacebarVerticalCorrection = res.getDimensionPixelOffset( 78 R.dimen.password_keyboard_spacebar_vertical_correction); 79 } 80 81 public PasswordEntryKeyboard(Context context, int layoutTemplateResId, 82 CharSequence characters, int columns, int horizontalPadding) { 83 super(context, layoutTemplateResId, characters, columns, horizontalPadding); 84 } 85 86 @Override 87 protected Key createKeyFromXml(Resources res, Row parent, int x, int y, 88 XmlResourceParser parser) { 89 LatinKey key = new LatinKey(res, parent, x, y, parser); 90 final int code = key.codes[0]; 91 if (code >=0 && code != '\n' && (code < 32 || code > 127)) { 92 // Log.w(TAG, "Key code for " + key.label + " is not latin-1"); 93 key.label = " "; 94 key.setEnabled(false); 95 } 96 switch (key.codes[0]) { 97 case 10: 98 mEnterKey = key; 99 break; 100 case PasswordEntryKeyboardView.KEYCODE_F1: 101 mF1Key = key; 102 break; 103 case 32: 104 mSpaceKey = key; 105 break; 106 } 107 return key; 108 } 109 110 /** 111 * Allows enter key resources to be overridden 112 * @param res resources to grab given items from 113 * @param previewId preview drawable shown on enter key 114 * @param iconId normal drawable shown on enter key 115 * @param labelId string shown on enter key 116 */ 117 void setEnterKeyResources(Resources res, int previewId, int iconId, int labelId) { 118 if (mEnterKey != null) { 119 // Reset some of the rarely used attributes. 120 mEnterKey.popupCharacters = null; 121 mEnterKey.popupResId = 0; 122 mEnterKey.text = null; 123 124 mEnterKey.iconPreview = res.getDrawable(previewId); 125 mEnterKey.icon = res.getDrawable(iconId); 126 mEnterKey.label = res.getText(labelId); 127 128 // Set the initial size of the preview icon 129 if (mEnterKey.iconPreview != null) { 130 mEnterKey.iconPreview.setBounds(0, 0, 131 mEnterKey.iconPreview.getIntrinsicWidth(), 132 mEnterKey.iconPreview.getIntrinsicHeight()); 133 } 134 } 135 } 136 137 /** 138 * Allows shiftlock to be turned on. See {@link #setShiftLocked(boolean)} 139 * 140 */ 141 void enableShiftLock() { 142 int i = 0; 143 for (int index : getShiftKeyIndices()) { 144 if (index >= 0 && i < mShiftKeys.length) { 145 mShiftKeys[i] = getKeys().get(index); 146 if (mShiftKeys[i] instanceof LatinKey) { 147 ((LatinKey)mShiftKeys[i]).enableShiftLock(); 148 } 149 mOldShiftIcons[i] = mShiftKeys[i].icon; 150 i++; 151 } 152 } 153 } 154 155 /** 156 * Turn on shift lock. This turns on the LED for this key, if it has one. 157 * It should be followed by a call to {@link KeyboardView#invalidateKey(int)} 158 * or {@link KeyboardView#invalidateAllKeys()} 159 * 160 * @param shiftLocked 161 */ 162 void setShiftLocked(boolean shiftLocked) { 163 for (Key shiftKey : mShiftKeys) { 164 if (shiftKey != null) { 165 shiftKey.on = shiftLocked; 166 shiftKey.icon = mShiftLockIcon; 167 } 168 } 169 mShiftState = shiftLocked ? SHIFT_LOCKED : SHIFT_ON; 170 } 171 172 /** 173 * Turn on shift mode. Sets shift mode and turns on icon for shift key. 174 * It should be followed by a call to {@link KeyboardView#invalidateKey(int)} 175 * or {@link KeyboardView#invalidateAllKeys()} 176 * 177 * @param shiftLocked 178 */ 179 @Override 180 public boolean setShifted(boolean shiftState) { 181 boolean shiftChanged = false; 182 if (shiftState == false) { 183 shiftChanged = mShiftState != SHIFT_OFF; 184 mShiftState = SHIFT_OFF; 185 } else if (mShiftState == SHIFT_OFF) { 186 shiftChanged = mShiftState == SHIFT_OFF; 187 mShiftState = SHIFT_ON; 188 } 189 for (int i = 0; i < mShiftKeys.length; i++) { 190 if (mShiftKeys[i] != null) { 191 if (shiftState == false) { 192 mShiftKeys[i].on = false; 193 mShiftKeys[i].icon = mOldShiftIcons[i]; 194 } else if (mShiftState == SHIFT_OFF) { 195 mShiftKeys[i].on = false; 196 mShiftKeys[i].icon = mShiftIcon; 197 } 198 } else { 199 // return super.setShifted(shiftState); 200 } 201 } 202 return shiftChanged; 203 } 204 205 /** 206 * Whether or not keyboard is shifted. 207 * @return true if keyboard state is shifted. 208 */ 209 @Override 210 public boolean isShifted() { 211 if (mShiftKeys[0] != null) { 212 return mShiftState != SHIFT_OFF; 213 } else { 214 return super.isShifted(); 215 } 216 } 217 218 static class LatinKey extends Keyboard.Key { 219 private boolean mShiftLockEnabled; 220 private boolean mEnabled = true; 221 222 public LatinKey(Resources res, Keyboard.Row parent, int x, int y, 223 XmlResourceParser parser) { 224 super(res, parent, x, y, parser); 225 if (popupCharacters != null && popupCharacters.length() == 0) { 226 // If there is a keyboard with no keys specified in popupCharacters 227 popupResId = 0; 228 } 229 } 230 231 void setEnabled(boolean enabled) { 232 mEnabled = enabled; 233 } 234 235 void enableShiftLock() { 236 mShiftLockEnabled = true; 237 } 238 239 @Override 240 public void onReleased(boolean inside) { 241 if (!mShiftLockEnabled) { 242 super.onReleased(inside); 243 } else { 244 pressed = !pressed; 245 } 246 } 247 248 /** 249 * Overriding this method so that we can reduce the target area for certain keys. 250 */ 251 @Override 252 public boolean isInside(int x, int y) { 253 if (!mEnabled) { 254 return false; 255 } 256 final int code = codes[0]; 257 if (code == KEYCODE_SHIFT || code == KEYCODE_DELETE) { 258 y -= height / 10; 259 if (code == KEYCODE_SHIFT) x += width / 6; 260 if (code == KEYCODE_DELETE) x -= width / 6; 261 } else if (code == KEYCODE_SPACE) { 262 y += PasswordEntryKeyboard.sSpacebarVerticalCorrection; 263 } 264 return super.isInside(x, y); 265 } 266 } 267} 268