| /* |
| * Copyright (C) 2010 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| |
| package com.android.internal.widget; |
| |
| import android.content.Context; |
| import android.content.res.Resources; |
| import android.inputmethodservice.Keyboard; |
| import android.inputmethodservice.KeyboardView; |
| import android.inputmethodservice.KeyboardView.OnKeyboardActionListener; |
| import android.os.Handler; |
| import android.os.SystemClock; |
| import android.os.Vibrator; |
| import android.provider.Settings; |
| import android.util.Log; |
| import android.view.HapticFeedbackConstants; |
| import android.view.KeyCharacterMap; |
| import android.view.KeyEvent; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.view.ViewGroup.LayoutParams; |
| import android.view.ViewRootImpl; |
| import com.android.internal.R; |
| |
| public class PasswordEntryKeyboardHelper implements OnKeyboardActionListener { |
| |
| public static final int KEYBOARD_MODE_ALPHA = 0; |
| public static final int KEYBOARD_MODE_NUMERIC = 1; |
| private static final int KEYBOARD_STATE_NORMAL = 0; |
| private static final int KEYBOARD_STATE_SHIFTED = 1; |
| private static final int KEYBOARD_STATE_CAPSLOCK = 2; |
| private static final String TAG = "PasswordEntryKeyboardHelper"; |
| private int mKeyboardMode = KEYBOARD_MODE_ALPHA; |
| private int mKeyboardState = KEYBOARD_STATE_NORMAL; |
| private PasswordEntryKeyboard mQwertyKeyboard; |
| private PasswordEntryKeyboard mQwertyKeyboardShifted; |
| private PasswordEntryKeyboard mSymbolsKeyboard; |
| private PasswordEntryKeyboard mSymbolsKeyboardShifted; |
| private PasswordEntryKeyboard mNumericKeyboard; |
| private final Context mContext; |
| private final View mTargetView; |
| private final KeyboardView mKeyboardView; |
| private long[] mVibratePattern; |
| private boolean mEnableHaptics = false; |
| |
| private static final int NUMERIC = 0; |
| private static final int QWERTY = 1; |
| private static final int QWERTY_SHIFTED = 2; |
| private static final int SYMBOLS = 3; |
| private static final int SYMBOLS_SHIFTED = 4; |
| |
| int mLayouts[] = new int[] { |
| R.xml.password_kbd_numeric, |
| R.xml.password_kbd_qwerty, |
| R.xml.password_kbd_qwerty_shifted, |
| R.xml.password_kbd_symbols, |
| R.xml.password_kbd_symbols_shift |
| }; |
| |
| private boolean mUsingScreenWidth; |
| |
| public PasswordEntryKeyboardHelper(Context context, KeyboardView keyboardView, View targetView) { |
| this(context, keyboardView, targetView, true, null); |
| } |
| |
| public PasswordEntryKeyboardHelper(Context context, KeyboardView keyboardView, View targetView, |
| boolean useFullScreenWidth) { |
| this(context, keyboardView, targetView, useFullScreenWidth, null); |
| } |
| |
| public PasswordEntryKeyboardHelper(Context context, KeyboardView keyboardView, View targetView, |
| boolean useFullScreenWidth, int layouts[]) { |
| mContext = context; |
| mTargetView = targetView; |
| mKeyboardView = keyboardView; |
| mKeyboardView.setOnKeyboardActionListener(this); |
| mUsingScreenWidth = useFullScreenWidth; |
| if (layouts != null) { |
| if (layouts.length != mLayouts.length) { |
| throw new RuntimeException("Wrong number of layouts"); |
| } |
| for (int i = 0; i < mLayouts.length; i++) { |
| mLayouts[i] = layouts[i]; |
| } |
| } |
| createKeyboards(); |
| } |
| |
| public void createKeyboards() { |
| LayoutParams lp = mKeyboardView.getLayoutParams(); |
| if (mUsingScreenWidth || lp.width == ViewGroup.LayoutParams.MATCH_PARENT) { |
| createKeyboardsWithDefaultWidth(); |
| } else { |
| createKeyboardsWithSpecificSize(lp.width, lp.height); |
| } |
| } |
| |
| public void setEnableHaptics(boolean enabled) { |
| mEnableHaptics = enabled; |
| } |
| |
| public boolean isAlpha() { |
| return mKeyboardMode == KEYBOARD_MODE_ALPHA; |
| } |
| |
| private void createKeyboardsWithSpecificSize(int width, int height) { |
| mNumericKeyboard = new PasswordEntryKeyboard(mContext, mLayouts[NUMERIC], width, height); |
| mQwertyKeyboard = new PasswordEntryKeyboard(mContext, mLayouts[QWERTY], R.id.mode_normal, |
| width, height); |
| mQwertyKeyboard.enableShiftLock(); |
| |
| mQwertyKeyboardShifted = new PasswordEntryKeyboard(mContext, mLayouts[QWERTY_SHIFTED], |
| R.id.mode_normal, width, height); |
| mQwertyKeyboardShifted.enableShiftLock(); |
| mQwertyKeyboardShifted.setShifted(true); // always shifted. |
| |
| mSymbolsKeyboard = new PasswordEntryKeyboard(mContext, mLayouts[SYMBOLS], width, height); |
| mSymbolsKeyboard.enableShiftLock(); |
| |
| mSymbolsKeyboardShifted = new PasswordEntryKeyboard(mContext, mLayouts[SYMBOLS_SHIFTED], |
| width, height); |
| mSymbolsKeyboardShifted.enableShiftLock(); |
| mSymbolsKeyboardShifted.setShifted(true); // always shifted |
| } |
| |
| private void createKeyboardsWithDefaultWidth() { |
| mNumericKeyboard = new PasswordEntryKeyboard(mContext, mLayouts[NUMERIC]); |
| mQwertyKeyboard = new PasswordEntryKeyboard(mContext, mLayouts[QWERTY], R.id.mode_normal); |
| mQwertyKeyboard.enableShiftLock(); |
| |
| mQwertyKeyboardShifted = new PasswordEntryKeyboard(mContext, mLayouts[QWERTY_SHIFTED], |
| R.id.mode_normal); |
| mQwertyKeyboardShifted.enableShiftLock(); |
| mQwertyKeyboardShifted.setShifted(true); // always shifted. |
| |
| mSymbolsKeyboard = new PasswordEntryKeyboard(mContext, mLayouts[SYMBOLS]); |
| mSymbolsKeyboard.enableShiftLock(); |
| |
| mSymbolsKeyboardShifted = new PasswordEntryKeyboard(mContext, mLayouts[SYMBOLS_SHIFTED]); |
| mSymbolsKeyboardShifted.enableShiftLock(); |
| mSymbolsKeyboardShifted.setShifted(true); // always shifted |
| } |
| |
| public void setKeyboardMode(int mode) { |
| switch (mode) { |
| case KEYBOARD_MODE_ALPHA: |
| mKeyboardView.setKeyboard(mQwertyKeyboard); |
| mKeyboardState = KEYBOARD_STATE_NORMAL; |
| final boolean visiblePassword = Settings.System.getInt( |
| mContext.getContentResolver(), |
| Settings.System.TEXT_SHOW_PASSWORD, 1) != 0; |
| final boolean enablePreview = false; // TODO: grab from configuration |
| mKeyboardView.setPreviewEnabled(visiblePassword && enablePreview); |
| break; |
| case KEYBOARD_MODE_NUMERIC: |
| mKeyboardView.setKeyboard(mNumericKeyboard); |
| mKeyboardState = KEYBOARD_STATE_NORMAL; |
| mKeyboardView.setPreviewEnabled(false); // never show popup for numeric keypad |
| break; |
| } |
| mKeyboardMode = mode; |
| } |
| |
| private void sendKeyEventsToTarget(int character) { |
| ViewRootImpl viewRootImpl = mTargetView.getViewRootImpl(); |
| KeyEvent[] events = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD).getEvents( |
| new char[] { (char) character }); |
| if (events != null) { |
| final int N = events.length; |
| for (int i=0; i<N; i++) { |
| KeyEvent event = events[i]; |
| event = KeyEvent.changeFlags(event, event.getFlags() |
| | KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE); |
| viewRootImpl.dispatchKey(event); |
| } |
| } |
| } |
| |
| public void sendDownUpKeyEvents(int keyEventCode) { |
| long eventTime = SystemClock.uptimeMillis(); |
| ViewRootImpl viewRootImpl = mTargetView.getViewRootImpl(); |
| viewRootImpl.dispatchKeyFromIme( |
| new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, |
| KeyCharacterMap.VIRTUAL_KEYBOARD, 0, |
| KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE)); |
| viewRootImpl.dispatchKeyFromIme( |
| new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_UP, keyEventCode, 0, 0, |
| KeyCharacterMap.VIRTUAL_KEYBOARD, 0, |
| KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE)); |
| } |
| |
| public void onKey(int primaryCode, int[] keyCodes) { |
| if (primaryCode == Keyboard.KEYCODE_DELETE) { |
| handleBackspace(); |
| } else if (primaryCode == Keyboard.KEYCODE_SHIFT) { |
| handleShift(); |
| } else if (primaryCode == Keyboard.KEYCODE_CANCEL) { |
| handleClose(); |
| return; |
| } else if (primaryCode == Keyboard.KEYCODE_MODE_CHANGE && mKeyboardView != null) { |
| handleModeChange(); |
| } else { |
| handleCharacter(primaryCode, keyCodes); |
| // Switch back to old keyboard if we're not in capslock mode |
| if (mKeyboardState == KEYBOARD_STATE_SHIFTED) { |
| // skip to the unlocked state |
| mKeyboardState = KEYBOARD_STATE_CAPSLOCK; |
| handleShift(); |
| } |
| } |
| } |
| |
| /** |
| * Sets and enables vibrate pattern. If id is 0 (or can't be loaded), vibrate is disabled. |
| * @param id resource id for array containing vibrate pattern. |
| */ |
| public void setVibratePattern(int id) { |
| int[] tmpArray = null; |
| try { |
| tmpArray = mContext.getResources().getIntArray(id); |
| } catch (Resources.NotFoundException e) { |
| if (id != 0) { |
| Log.e(TAG, "Vibrate pattern missing", e); |
| } |
| } |
| if (tmpArray == null) { |
| mVibratePattern = null; |
| return; |
| } |
| mVibratePattern = new long[tmpArray.length]; |
| for (int i = 0; i < tmpArray.length; i++) { |
| mVibratePattern[i] = tmpArray[i]; |
| } |
| } |
| |
| private void handleModeChange() { |
| final Keyboard current = mKeyboardView.getKeyboard(); |
| Keyboard next = null; |
| if (current == mQwertyKeyboard || current == mQwertyKeyboardShifted) { |
| next = mSymbolsKeyboard; |
| } else if (current == mSymbolsKeyboard || current == mSymbolsKeyboardShifted) { |
| next = mQwertyKeyboard; |
| } |
| if (next != null) { |
| mKeyboardView.setKeyboard(next); |
| mKeyboardState = KEYBOARD_STATE_NORMAL; |
| } |
| } |
| |
| public void handleBackspace() { |
| sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL); |
| performHapticFeedback(); |
| } |
| |
| private void handleShift() { |
| if (mKeyboardView == null) { |
| return; |
| } |
| Keyboard current = mKeyboardView.getKeyboard(); |
| PasswordEntryKeyboard next = null; |
| final boolean isAlphaMode = current == mQwertyKeyboard |
| || current == mQwertyKeyboardShifted; |
| if (mKeyboardState == KEYBOARD_STATE_NORMAL) { |
| mKeyboardState = isAlphaMode ? KEYBOARD_STATE_SHIFTED : KEYBOARD_STATE_CAPSLOCK; |
| next = isAlphaMode ? mQwertyKeyboardShifted : mSymbolsKeyboardShifted; |
| } else if (mKeyboardState == KEYBOARD_STATE_SHIFTED) { |
| mKeyboardState = KEYBOARD_STATE_CAPSLOCK; |
| next = isAlphaMode ? mQwertyKeyboardShifted : mSymbolsKeyboardShifted; |
| } else if (mKeyboardState == KEYBOARD_STATE_CAPSLOCK) { |
| mKeyboardState = KEYBOARD_STATE_NORMAL; |
| next = isAlphaMode ? mQwertyKeyboard : mSymbolsKeyboard; |
| } |
| if (next != null) { |
| if (next != current) { |
| mKeyboardView.setKeyboard(next); |
| } |
| next.setShiftLocked(mKeyboardState == KEYBOARD_STATE_CAPSLOCK); |
| mKeyboardView.setShifted(mKeyboardState != KEYBOARD_STATE_NORMAL); |
| } |
| } |
| |
| private void handleCharacter(int primaryCode, int[] keyCodes) { |
| // Maybe turn off shift if not in capslock mode. |
| if (mKeyboardView.isShifted() && primaryCode != ' ' && primaryCode != '\n') { |
| primaryCode = Character.toUpperCase(primaryCode); |
| } |
| sendKeyEventsToTarget(primaryCode); |
| } |
| |
| private void handleClose() { |
| |
| } |
| |
| public void onPress(int primaryCode) { |
| performHapticFeedback(); |
| } |
| |
| private void performHapticFeedback() { |
| if (mEnableHaptics) { |
| mKeyboardView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, |
| HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING |
| | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); |
| } |
| } |
| |
| public void onRelease(int primaryCode) { |
| |
| } |
| |
| public void onText(CharSequence text) { |
| |
| } |
| |
| public void swipeDown() { |
| |
| } |
| |
| public void swipeLeft() { |
| |
| } |
| |
| public void swipeRight() { |
| |
| } |
| |
| public void swipeUp() { |
| |
| } |
| }; |