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