Home | History | Annotate | Download | only in pinyin
      1 /*
      2  * Copyright (C) 2009 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 com.android.inputmethod.pinyin;
     18 
     19 import com.android.inputmethod.pinyin.InputModeSwitcher.ToggleStates;
     20 
     21 import android.graphics.Rect;
     22 import android.graphics.drawable.Drawable;
     23 import android.view.KeyEvent;
     24 
     25 import java.util.ArrayList;
     26 import java.util.List;
     27 
     28 /**
     29  * Class used to represent a soft keyboard definition, including the height, the
     30  * background image, the image for high light, the keys, etc.
     31  */
     32 public class SoftKeyboard {
     33     /** The XML resource id for this soft keyboard. */
     34     private int mSkbXmlId;
     35 
     36     /** Do we need to cache this soft keyboard? */
     37     private boolean mCacheFlag;
     38 
     39     /**
     40      * After user switches to this soft keyboard, if this flag is true, this
     41      * soft keyboard will be kept unless explicit switching operation is
     42      * performed, otherwise IME will switch back to the previous keyboard layout
     43      * whenever user clicks on any none-function key.
     44      **/
     45     private boolean mStickyFlag;
     46 
     47     /**
     48      * The cache id for this soft keyboard. It is used to identify it in the
     49      * soft keyboard pool.
     50      */
     51     private int mCacheId;
     52 
     53     /**
     54      * Used to indicate whether this soft keyboard is newly loaded from an XML
     55      * file or is just gotten from the soft keyboard pool.
     56      */
     57     private boolean mNewlyLoadedFlag = true;
     58 
     59     /** The width of the soft keyboard. */
     60     private int mSkbCoreWidth;
     61 
     62     /** The height of the soft keyboard. */
     63     private int mSkbCoreHeight;
     64 
     65     /** The soft keyboard template for this soft keyboard. */
     66     private SkbTemplate mSkbTemplate;
     67 
     68     /** Used to indicate whether this soft keyboard is a QWERTY keyboard. */
     69     private boolean mIsQwerty;
     70 
     71     /**
     72      * When {@link #mIsQwerty} is true, this member is Used to indicate that the
     73      * soft keyboard should be displayed in uppercase.
     74      */
     75     private boolean mIsQwertyUpperCase;
     76 
     77     /**
     78      * The id of the rows which are enabled. Rows with id
     79      * {@link KeyRow#ALWAYS_SHOW_ROW_ID} are always enabled.
     80      */
     81     private int mEnabledRowId;
     82 
     83     /**
     84      * Rows in this soft keyboard. Each row has a id. Only matched rows will be
     85      * enabled.
     86      */
     87     private List<KeyRow> mKeyRows;
     88 
     89     /**
     90      * Background of the soft keyboard. If it is null, the one in the soft
     91      * keyboard template will be used.
     92      **/
     93     public Drawable mSkbBg;
     94 
     95     /**
     96      * Background for key balloon. If it is null, the one in the soft keyboard
     97      * template will be used.
     98      **/
     99     private Drawable mBalloonBg;
    100 
    101     /**
    102      * Background for popup mini soft keyboard. If it is null, the one in the
    103      * soft keyboard template will be used.
    104      **/
    105     private Drawable mPopupBg;
    106 
    107     /** The left and right margin of a key. */
    108     private float mKeyXMargin = 0;
    109 
    110     /** The top and bottom margin of a key. */
    111     private float mKeyYMargin = 0;
    112 
    113     private Rect mTmpRect = new Rect();
    114 
    115     public SoftKeyboard(int skbXmlId, SkbTemplate skbTemplate, int skbWidth,
    116             int skbHeight) {
    117         mSkbXmlId = skbXmlId;
    118         mSkbTemplate = skbTemplate;
    119         mSkbCoreWidth = skbWidth;
    120         mSkbCoreHeight = skbHeight;
    121     }
    122 
    123     public void setFlags(boolean cacheFlag, boolean stickyFlag,
    124             boolean isQwerty, boolean isQwertyUpperCase) {
    125         mCacheFlag = cacheFlag;
    126         mStickyFlag = stickyFlag;
    127         mIsQwerty = isQwerty;
    128         mIsQwertyUpperCase = isQwertyUpperCase;
    129     }
    130 
    131     public boolean getCacheFlag() {
    132         return mCacheFlag;
    133     }
    134 
    135     public void setCacheId(int cacheId) {
    136         mCacheId = cacheId;
    137     }
    138 
    139     public boolean getStickyFlag() {
    140         return mStickyFlag;
    141     }
    142 
    143     public void setSkbBackground(Drawable skbBg) {
    144         mSkbBg = skbBg;
    145     }
    146 
    147     public void setPopupBackground(Drawable popupBg) {
    148         mPopupBg = popupBg;
    149     }
    150 
    151     public void setKeyBalloonBackground(Drawable balloonBg) {
    152         mBalloonBg = balloonBg;
    153     }
    154 
    155     public void setKeyMargins(float xMargin, float yMargin) {
    156         mKeyXMargin = xMargin;
    157         mKeyYMargin = yMargin;
    158     }
    159 
    160     public int getCacheId() {
    161         return mCacheId;
    162     }
    163 
    164     public void reset() {
    165         if (null != mKeyRows) mKeyRows.clear();
    166     }
    167 
    168     public void setNewlyLoadedFlag(boolean newlyLoadedFlag) {
    169         mNewlyLoadedFlag = newlyLoadedFlag;
    170     }
    171 
    172     public boolean getNewlyLoadedFlag() {
    173         return mNewlyLoadedFlag;
    174     }
    175 
    176     public void beginNewRow(int rowId, float yStartingPos) {
    177         if (null == mKeyRows) mKeyRows = new ArrayList<KeyRow>();
    178         KeyRow keyRow = new KeyRow();
    179         keyRow.mRowId = rowId;
    180         keyRow.mTopF = yStartingPos;
    181         keyRow.mBottomF = yStartingPos;
    182         keyRow.mSoftKeys = new ArrayList<SoftKey>();
    183         mKeyRows.add(keyRow);
    184     }
    185 
    186     public boolean addSoftKey(SoftKey softKey) {
    187         if (mKeyRows.size() == 0) return false;
    188         KeyRow keyRow = mKeyRows.get(mKeyRows.size() - 1);
    189         if (null == keyRow) return false;
    190         List<SoftKey> softKeys = keyRow.mSoftKeys;
    191 
    192         softKey.setSkbCoreSize(mSkbCoreWidth, mSkbCoreHeight);
    193         softKeys.add(softKey);
    194         if (softKey.mTopF < keyRow.mTopF) {
    195             keyRow.mTopF = softKey.mTopF;
    196         }
    197         if (softKey.mBottomF > keyRow.mBottomF) {
    198             keyRow.mBottomF = softKey.mBottomF;
    199         }
    200         return true;
    201     }
    202 
    203     public int getSkbXmlId() {
    204         return mSkbXmlId;
    205     }
    206 
    207     // Set the size of the soft keyboard core. In other words, the background's
    208     // padding are not counted.
    209     public void setSkbCoreSize(int skbCoreWidth, int skbCoreHeight) {
    210         if (null == mKeyRows
    211                 || (skbCoreWidth == mSkbCoreWidth && skbCoreHeight == mSkbCoreHeight)) {
    212             return;
    213         }
    214         for (int row = 0; row < mKeyRows.size(); row++) {
    215             KeyRow keyRow = mKeyRows.get(row);
    216             keyRow.mBottom = (int) (skbCoreHeight * keyRow.mBottomF);
    217             keyRow.mTop = (int) (skbCoreHeight * keyRow.mTopF);
    218 
    219             List<SoftKey> softKeys = keyRow.mSoftKeys;
    220             for (int i = 0; i < softKeys.size(); i++) {
    221                 SoftKey softKey = softKeys.get(i);
    222                 softKey.setSkbCoreSize(skbCoreWidth, skbCoreHeight);
    223             }
    224         }
    225         mSkbCoreWidth = skbCoreWidth;
    226         mSkbCoreHeight = skbCoreHeight;
    227     }
    228 
    229     public int getSkbCoreWidth() {
    230         return mSkbCoreWidth;
    231     }
    232 
    233     public int getSkbCoreHeight() {
    234         return mSkbCoreHeight;
    235     }
    236 
    237     public int getSkbTotalWidth() {
    238         Rect padding = getPadding();
    239         return mSkbCoreWidth + padding.left + padding.right;
    240     }
    241 
    242     public int getSkbTotalHeight() {
    243         Rect padding = getPadding();
    244         return mSkbCoreHeight + padding.top + padding.bottom;
    245     }
    246 
    247     public int getKeyXMargin() {
    248         Environment env = Environment.getInstance();
    249         return (int) (mKeyXMargin * mSkbCoreWidth * env.getKeyXMarginFactor());
    250     }
    251 
    252     public int getKeyYMargin() {
    253         Environment env = Environment.getInstance();
    254         return (int) (mKeyYMargin * mSkbCoreHeight * env.getKeyYMarginFactor());
    255     }
    256 
    257     public Drawable getSkbBackground() {
    258         if (null != mSkbBg) return mSkbBg;
    259         return mSkbTemplate.getSkbBackground();
    260     }
    261 
    262     public Drawable getBalloonBackground() {
    263         if (null != mBalloonBg) return mBalloonBg;
    264         return mSkbTemplate.getBalloonBackground();
    265     }
    266 
    267     public Drawable getPopupBackground() {
    268         if (null != mPopupBg) return mPopupBg;
    269         return mSkbTemplate.getPopupBackground();
    270     }
    271 
    272     public int getRowNum() {
    273         if (null != mKeyRows) {
    274             return mKeyRows.size();
    275         }
    276         return 0;
    277     }
    278 
    279     public KeyRow getKeyRowForDisplay(int row) {
    280         if (null != mKeyRows && mKeyRows.size() > row) {
    281             KeyRow keyRow = mKeyRows.get(row);
    282             if (KeyRow.ALWAYS_SHOW_ROW_ID == keyRow.mRowId
    283                     || keyRow.mRowId == mEnabledRowId) {
    284                 return keyRow;
    285             }
    286         }
    287         return null;
    288     }
    289 
    290     public SoftKey getKey(int row, int location) {
    291         if (null != mKeyRows && mKeyRows.size() > row) {
    292             List<SoftKey> softKeys = mKeyRows.get(row).mSoftKeys;
    293             if (softKeys.size() > location) {
    294                 return softKeys.get(location);
    295             }
    296         }
    297         return null;
    298     }
    299 
    300     public SoftKey mapToKey(int x, int y) {
    301         if (null == mKeyRows) {
    302             return null;
    303         }
    304         // If the position is inside the rectangle of a certain key, return that
    305         // key.
    306         int rowNum = mKeyRows.size();
    307         for (int row = 0; row < rowNum; row++) {
    308             KeyRow keyRow = mKeyRows.get(row);
    309             if (KeyRow.ALWAYS_SHOW_ROW_ID != keyRow.mRowId
    310                     && keyRow.mRowId != mEnabledRowId) continue;
    311             if (keyRow.mTop > y && keyRow.mBottom <= y) continue;
    312 
    313             List<SoftKey> softKeys = keyRow.mSoftKeys;
    314             int keyNum = softKeys.size();
    315             for (int i = 0; i < keyNum; i++) {
    316                 SoftKey sKey = softKeys.get(i);
    317                 if (sKey.mLeft <= x && sKey.mTop <= y && sKey.mRight > x
    318                         && sKey.mBottom > y) {
    319                     return sKey;
    320                 }
    321             }
    322         }
    323 
    324         // If the position is outside the rectangles of all keys, find the
    325         // nearest one.
    326         SoftKey nearestKey = null;
    327         float nearestDis = Float.MAX_VALUE;
    328         for (int row = 0; row < rowNum; row++) {
    329             KeyRow keyRow = mKeyRows.get(row);
    330             if (KeyRow.ALWAYS_SHOW_ROW_ID != keyRow.mRowId
    331                     && keyRow.mRowId != mEnabledRowId) continue;
    332             if (keyRow.mTop > y && keyRow.mBottom <= y) continue;
    333 
    334             List<SoftKey> softKeys = keyRow.mSoftKeys;
    335             int keyNum = softKeys.size();
    336             for (int i = 0; i < keyNum; i++) {
    337                 SoftKey sKey = softKeys.get(i);
    338                 int disx = (sKey.mLeft + sKey.mRight) / 2 - x;
    339                 int disy = (sKey.mTop + sKey.mBottom) / 2 - y;
    340                 float dis = disx * disx + disy * disy;
    341                 if (dis < nearestDis) {
    342                     nearestDis = dis;
    343                     nearestKey = sKey;
    344                 }
    345             }
    346         }
    347         return nearestKey;
    348     }
    349 
    350     public void switchQwertyMode(int toggle_state_id, boolean upperCase) {
    351         if (!mIsQwerty) return;
    352 
    353         int rowNum = mKeyRows.size();
    354         for (int row = 0; row < rowNum; row++) {
    355             KeyRow keyRow = mKeyRows.get(row);
    356             List<SoftKey> softKeys = keyRow.mSoftKeys;
    357             int keyNum = softKeys.size();
    358             for (int i = 0; i < keyNum; i++) {
    359                 SoftKey sKey = softKeys.get(i);
    360                 if (sKey instanceof SoftKeyToggle) {
    361                     ((SoftKeyToggle) sKey).enableToggleState(toggle_state_id,
    362                             true);
    363                 }
    364                 if (sKey.mKeyCode >= KeyEvent.KEYCODE_A
    365                         && sKey.mKeyCode <= KeyEvent.KEYCODE_Z) {
    366                     sKey.changeCase(upperCase);
    367                 }
    368             }
    369         }
    370     }
    371 
    372     public void enableToggleState(int toggleStateId, boolean resetIfNotFound) {
    373         int rowNum = mKeyRows.size();
    374         for (int row = 0; row < rowNum; row++) {
    375             KeyRow keyRow = mKeyRows.get(row);
    376             List<SoftKey> softKeys = keyRow.mSoftKeys;
    377             int keyNum = softKeys.size();
    378             for (int i = 0; i < keyNum; i++) {
    379                 SoftKey sKey = softKeys.get(i);
    380                 if (sKey instanceof SoftKeyToggle) {
    381                     ((SoftKeyToggle) sKey).enableToggleState(toggleStateId,
    382                             resetIfNotFound);
    383                 }
    384             }
    385         }
    386     }
    387 
    388     public void disableToggleState(int toggleStateId, boolean resetIfNotFound) {
    389         int rowNum = mKeyRows.size();
    390         for (int row = 0; row < rowNum; row++) {
    391             KeyRow keyRow = mKeyRows.get(row);
    392             List<SoftKey> softKeys = keyRow.mSoftKeys;
    393             int keyNum = softKeys.size();
    394             for (int i = 0; i < keyNum; i++) {
    395                 SoftKey sKey = softKeys.get(i);
    396                 if (sKey instanceof SoftKeyToggle) {
    397                     ((SoftKeyToggle) sKey).disableToggleState(toggleStateId,
    398                             resetIfNotFound);
    399                 }
    400             }
    401         }
    402     }
    403 
    404     public void enableToggleStates(ToggleStates toggleStates) {
    405         if (null == toggleStates) return;
    406 
    407         enableRow(toggleStates.mRowIdToEnable);
    408 
    409         boolean isQwerty = toggleStates.mQwerty;
    410         boolean isQwertyUpperCase = toggleStates.mQwertyUpperCase;
    411         boolean needUpdateQwerty = (isQwerty && mIsQwerty && (mIsQwertyUpperCase != isQwertyUpperCase));
    412         int states[] = toggleStates.mKeyStates;
    413         int statesNum = toggleStates.mKeyStatesNum;
    414 
    415         int rowNum = mKeyRows.size();
    416         for (int row = 0; row < rowNum; row++) {
    417             KeyRow keyRow = mKeyRows.get(row);
    418             if (KeyRow.ALWAYS_SHOW_ROW_ID != keyRow.mRowId
    419                     && keyRow.mRowId != mEnabledRowId) {
    420                 continue;
    421             }
    422             List<SoftKey> softKeys = keyRow.mSoftKeys;
    423             int keyNum = softKeys.size();
    424             for (int keyPos = 0; keyPos < keyNum; keyPos++) {
    425                 SoftKey sKey = softKeys.get(keyPos);
    426                 if (sKey instanceof SoftKeyToggle) {
    427                     for (int statePos = 0; statePos < statesNum; statePos++) {
    428                         ((SoftKeyToggle) sKey).enableToggleState(
    429                                 states[statePos], statePos == 0);
    430                     }
    431                     if (0 == statesNum) {
    432                         ((SoftKeyToggle) sKey).disableAllToggleStates();
    433                     }
    434                 }
    435                 if (needUpdateQwerty) {
    436                     if (sKey.mKeyCode >= KeyEvent.KEYCODE_A
    437                             && sKey.mKeyCode <= KeyEvent.KEYCODE_Z) {
    438                         sKey.changeCase(isQwertyUpperCase);
    439                     }
    440                 }
    441             }
    442         }
    443         mIsQwertyUpperCase = isQwertyUpperCase;
    444     }
    445 
    446     private Rect getPadding() {
    447         mTmpRect.set(0, 0, 0, 0);
    448         Drawable skbBg = getSkbBackground();
    449         if (null == skbBg) return mTmpRect;
    450         skbBg.getPadding(mTmpRect);
    451         return mTmpRect;
    452     }
    453 
    454     /**
    455      * Enable a row with the give toggle Id. Rows with other toggle ids (except
    456      * the id {@link KeyRow#ALWAYS_SHOW_ROW_ID}) will be disabled.
    457      *
    458      * @param rowId The row id to enable.
    459      * @return True if the soft keyboard requires redrawing.
    460      */
    461     private boolean enableRow(int rowId) {
    462         if (KeyRow.ALWAYS_SHOW_ROW_ID == rowId) return false;
    463 
    464         boolean enabled = false;
    465         int rowNum = mKeyRows.size();
    466         for (int row = rowNum - 1; row >= 0; row--) {
    467             if (mKeyRows.get(row).mRowId == rowId) {
    468                 enabled = true;
    469                 break;
    470             }
    471         }
    472         if (enabled) {
    473             mEnabledRowId = rowId;
    474         }
    475         return enabled;
    476     }
    477 
    478     @Override
    479     public String toString() {
    480         String str = "------------------SkbInfo----------------------\n";
    481         String endStr = "-----------------------------------------------\n";
    482         str += "Width: " + String.valueOf(mSkbCoreWidth) + "\n";
    483         str += "Height: " + String.valueOf(mSkbCoreHeight) + "\n";
    484         str += "KeyRowNum: " + mKeyRows == null ? "0" : String.valueOf(mKeyRows
    485                 .size())
    486                 + "\n";
    487         if (null == mKeyRows) return str + endStr;
    488         int rowNum = mKeyRows.size();
    489         for (int row = 0; row < rowNum; row++) {
    490             KeyRow keyRow = mKeyRows.get(row);
    491             List<SoftKey> softKeys = keyRow.mSoftKeys;
    492             int keyNum = softKeys.size();
    493             for (int i = 0; i < softKeys.size(); i++) {
    494                 str += "-key " + String.valueOf(i) + ":"
    495                         + softKeys.get(i).toString();
    496             }
    497         }
    498         return str + endStr;
    499     }
    500 
    501     public String toShortString() {
    502         return super.toString();
    503     }
    504 
    505     class KeyRow {
    506         static final int ALWAYS_SHOW_ROW_ID = -1;
    507         static final int DEFAULT_ROW_ID = 0;
    508 
    509         List<SoftKey> mSoftKeys;
    510         /**
    511          * If the row id is {@link #ALWAYS_SHOW_ROW_ID}, this row will always be
    512          * enabled.
    513          */
    514         int mRowId;
    515         float mTopF;
    516         float mBottomF;
    517         int mTop;
    518         int mBottom;
    519     }
    520 }
    521