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