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