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.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 
     73     public PasswordEntryKeyboardHelper(Context context, KeyboardView keyboardView, View targetView) {
     74         this(context, keyboardView, targetView, true, null);
     75     }
     76 
     77     public PasswordEntryKeyboardHelper(Context context, KeyboardView keyboardView, View targetView,
     78             boolean useFullScreenWidth) {
     79         this(context, keyboardView, targetView, useFullScreenWidth, null);
     80     }
     81 
     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 
    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 
    109     public void setEnableHaptics(boolean enabled) {
    110         mEnableHaptics = enabled;
    111     }
    112 
    113     public boolean isAlpha() {
    114         return mKeyboardMode == KEYBOARD_MODE_ALPHA;
    115     }
    116 
    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 
    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 
    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 
    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 
    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 
    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      */
    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 
    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 
    261     public void handleBackspace() {
    262         sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL);
    263         performHapticFeedback();
    264     }
    265 
    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 
    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 
    301     private void handleClose() {
    302 
    303     }
    304 
    305     public void onPress(int primaryCode) {
    306         performHapticFeedback();
    307     }
    308 
    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 
    317     public void onRelease(int primaryCode) {
    318 
    319     }
    320 
    321     public void onText(CharSequence text) {
    322 
    323     }
    324 
    325     public void swipeDown() {
    326 
    327     }
    328 
    329     public void swipeLeft() {
    330 
    331     }
    332 
    333     public void swipeRight() {
    334 
    335     }
    336 
    337     public void swipeUp() {
    338 
    339     }
    340 };
    341