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