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