Home | History | Annotate | Download | only in method
      1 /*
      2  * Copyright (C) 2007 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of 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,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.text.method;
     18 
     19 import android.content.ContentResolver;
     20 import android.content.Context;
     21 import android.database.ContentObserver;
     22 import android.os.Handler;
     23 import android.provider.Settings;
     24 import android.provider.Settings.System;
     25 import android.text.*;
     26 import android.view.KeyCharacterMap;
     27 import android.view.KeyEvent;
     28 import android.view.View;
     29 import android.text.InputType;
     30 
     31 import java.lang.ref.WeakReference;
     32 
     33 /**
     34  * This is the key listener for typing normal text.  It delegates to
     35  * other key listeners appropriate to the current keyboard and language.
     36  */
     37 public class TextKeyListener extends BaseKeyListener implements SpanWatcher {
     38     private static TextKeyListener[] sInstance =
     39         new TextKeyListener[Capitalize.values().length * 2];
     40 
     41     /* package */ static final Object ACTIVE = new NoCopySpan.Concrete();
     42     /* package */ static final Object CAPPED = new NoCopySpan.Concrete();
     43     /* package */ static final Object INHIBIT_REPLACEMENT = new NoCopySpan.Concrete();
     44     /* package */ static final Object LAST_TYPED = new NoCopySpan.Concrete();
     45 
     46     private Capitalize mAutoCap;
     47     private boolean mAutoText;
     48 
     49     private int mPrefs;
     50     private boolean mPrefsInited;
     51 
     52     /* package */ static final int AUTO_CAP = 1;
     53     /* package */ static final int AUTO_TEXT = 2;
     54     /* package */ static final int AUTO_PERIOD = 4;
     55     /* package */ static final int SHOW_PASSWORD = 8;
     56     private WeakReference<ContentResolver> mResolver;
     57     private TextKeyListener.SettingsObserver mObserver;
     58 
     59     /**
     60      * Creates a new TextKeyListener with the specified capitalization
     61      * and correction properties.
     62      *
     63      * @param cap when, if ever, to automatically capitalize.
     64      * @param autotext whether to automatically do spelling corrections.
     65      */
     66     public TextKeyListener(Capitalize cap, boolean autotext) {
     67         mAutoCap = cap;
     68         mAutoText = autotext;
     69     }
     70 
     71     /**
     72      * Returns a new or existing instance with the specified capitalization
     73      * and correction properties.
     74      *
     75      * @param cap when, if ever, to automatically capitalize.
     76      * @param autotext whether to automatically do spelling corrections.
     77      */
     78     public static TextKeyListener getInstance(boolean autotext,
     79                                               Capitalize cap) {
     80         int off = cap.ordinal() * 2 + (autotext ? 1 : 0);
     81 
     82         if (sInstance[off] == null) {
     83             sInstance[off] = new TextKeyListener(cap, autotext);
     84         }
     85 
     86         return sInstance[off];
     87     }
     88 
     89     /**
     90      * Returns a new or existing instance with no automatic capitalization
     91      * or correction.
     92      */
     93     public static TextKeyListener getInstance() {
     94         return getInstance(false, Capitalize.NONE);
     95     }
     96 
     97     /**
     98      * Returns whether it makes sense to automatically capitalize at the
     99      * specified position in the specified text, with the specified rules.
    100      *
    101      * @param cap the capitalization rules to consider.
    102      * @param cs the text in which an insertion is being made.
    103      * @param off the offset into that text where the insertion is being made.
    104      *
    105      * @return whether the character being inserted should be capitalized.
    106      */
    107     public static boolean shouldCap(Capitalize cap, CharSequence cs, int off) {
    108         int i;
    109         char c;
    110 
    111         if (cap == Capitalize.NONE) {
    112             return false;
    113         }
    114         if (cap == Capitalize.CHARACTERS) {
    115             return true;
    116         }
    117 
    118         return TextUtils.getCapsMode(cs, off, cap == Capitalize.WORDS
    119                 ? TextUtils.CAP_MODE_WORDS : TextUtils.CAP_MODE_SENTENCES)
    120                 != 0;
    121     }
    122 
    123     public int getInputType() {
    124         return makeTextContentType(mAutoCap, mAutoText);
    125     }
    126 
    127     @Override
    128     public boolean onKeyDown(View view, Editable content,
    129                              int keyCode, KeyEvent event) {
    130         KeyListener im = getKeyListener(event);
    131 
    132         return im.onKeyDown(view, content, keyCode, event);
    133     }
    134 
    135     @Override
    136     public boolean onKeyUp(View view, Editable content,
    137                            int keyCode, KeyEvent event) {
    138         KeyListener im = getKeyListener(event);
    139 
    140         return im.onKeyUp(view, content, keyCode, event);
    141     }
    142 
    143     @Override
    144     public boolean onKeyOther(View view, Editable content, KeyEvent event) {
    145         KeyListener im = getKeyListener(event);
    146 
    147         return im.onKeyOther(view, content, event);
    148     }
    149 
    150     /**
    151      * Clear all the input state (autotext, autocap, multitap, undo)
    152      * from the specified Editable, going beyond Editable.clear(), which
    153      * just clears the text but not the input state.
    154      *
    155      * @param e the buffer whose text and state are to be cleared.
    156      */
    157     public static void clear(Editable e) {
    158         e.clear();
    159         e.removeSpan(ACTIVE);
    160         e.removeSpan(CAPPED);
    161         e.removeSpan(INHIBIT_REPLACEMENT);
    162         e.removeSpan(LAST_TYPED);
    163 
    164         QwertyKeyListener.Replaced[] repl = e.getSpans(0, e.length(),
    165                                    QwertyKeyListener.Replaced.class);
    166         final int count = repl.length;
    167         for (int i = 0; i < count; i++) {
    168             e.removeSpan(repl[i]);
    169         }
    170     }
    171 
    172     public void onSpanAdded(Spannable s, Object what, int start, int end) { }
    173     public void onSpanRemoved(Spannable s, Object what, int start, int end) { }
    174 
    175     public void onSpanChanged(Spannable s, Object what, int start, int end,
    176                               int st, int en) {
    177         if (what == Selection.SELECTION_END) {
    178             s.removeSpan(ACTIVE);
    179         }
    180     }
    181 
    182     private KeyListener getKeyListener(KeyEvent event) {
    183         KeyCharacterMap kmap = KeyCharacterMap.load(event.getKeyboardDevice());
    184         int kind = kmap.getKeyboardType();
    185 
    186         if (kind == KeyCharacterMap.ALPHA) {
    187             return QwertyKeyListener.getInstance(mAutoText, mAutoCap);
    188         } else if (kind == KeyCharacterMap.NUMERIC) {
    189             return MultiTapKeyListener.getInstance(mAutoText, mAutoCap);
    190         }
    191 
    192         return NullKeyListener.getInstance();
    193     }
    194 
    195     public enum Capitalize {
    196         NONE, SENTENCES, WORDS, CHARACTERS,
    197     }
    198 
    199     private static class NullKeyListener implements KeyListener
    200     {
    201         public int getInputType() {
    202             return InputType.TYPE_NULL;
    203         }
    204 
    205         public boolean onKeyDown(View view, Editable content,
    206                                  int keyCode, KeyEvent event) {
    207             return false;
    208         }
    209 
    210         public boolean onKeyUp(View view, Editable content, int keyCode,
    211                                         KeyEvent event) {
    212             return false;
    213         }
    214 
    215         public boolean onKeyOther(View view, Editable content, KeyEvent event) {
    216             return false;
    217         }
    218 
    219         public void clearMetaKeyState(View view, Editable content, int states) {
    220         }
    221 
    222         public static NullKeyListener getInstance() {
    223             if (sInstance != null)
    224                 return sInstance;
    225 
    226             sInstance = new NullKeyListener();
    227             return sInstance;
    228         }
    229 
    230         private static NullKeyListener sInstance;
    231     }
    232 
    233     public void release() {
    234         if (mResolver != null) {
    235             final ContentResolver contentResolver = mResolver.get();
    236             if (contentResolver != null) {
    237                 contentResolver.unregisterContentObserver(mObserver);
    238                 mResolver.clear();
    239             }
    240             mObserver = null;
    241             mResolver = null;
    242             mPrefsInited = false;
    243         }
    244     }
    245 
    246     private void initPrefs(Context context) {
    247         final ContentResolver contentResolver = context.getContentResolver();
    248         mResolver = new WeakReference<ContentResolver>(contentResolver);
    249         if (mObserver == null) {
    250             mObserver = new SettingsObserver();
    251             contentResolver.registerContentObserver(Settings.System.CONTENT_URI, true, mObserver);
    252         }
    253 
    254         updatePrefs(contentResolver);
    255         mPrefsInited = true;
    256     }
    257 
    258     private class SettingsObserver extends ContentObserver {
    259         public SettingsObserver() {
    260             super(new Handler());
    261         }
    262 
    263         @Override
    264         public void onChange(boolean selfChange) {
    265             if (mResolver != null) {
    266                 final ContentResolver contentResolver = mResolver.get();
    267                 if (contentResolver == null) {
    268                     mPrefsInited = false;
    269                 } else {
    270                     updatePrefs(contentResolver);
    271                 }
    272             } else {
    273                 mPrefsInited = false;
    274             }
    275         }
    276     }
    277 
    278     private void updatePrefs(ContentResolver resolver) {
    279         boolean cap = System.getInt(resolver, System.TEXT_AUTO_CAPS, 1) > 0;
    280         boolean text = System.getInt(resolver, System.TEXT_AUTO_REPLACE, 1) > 0;
    281         boolean period = System.getInt(resolver, System.TEXT_AUTO_PUNCTUATE, 1) > 0;
    282         boolean pw = System.getInt(resolver, System.TEXT_SHOW_PASSWORD, 1) > 0;
    283 
    284         mPrefs = (cap ? AUTO_CAP : 0) |
    285                  (text ? AUTO_TEXT : 0) |
    286                  (period ? AUTO_PERIOD : 0) |
    287                  (pw ? SHOW_PASSWORD : 0);
    288     }
    289 
    290     /* package */ int getPrefs(Context context) {
    291         synchronized (this) {
    292             if (!mPrefsInited || mResolver.get() == null) {
    293                 initPrefs(context);
    294             }
    295         }
    296 
    297         return mPrefs;
    298     }
    299 }
    300