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