• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
SoftKeyboard(int skbXmlId, SkbTemplate skbTemplate, int skbWidth, int skbHeight)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 
setFlags(boolean cacheFlag, boolean stickyFlag, boolean isQwerty, boolean isQwertyUpperCase)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 
getCacheFlag()131     public boolean getCacheFlag() {
132         return mCacheFlag;
133     }
134 
setCacheId(int cacheId)135     public void setCacheId(int cacheId) {
136         mCacheId = cacheId;
137     }
138 
getStickyFlag()139     public boolean getStickyFlag() {
140         return mStickyFlag;
141     }
142 
setSkbBackground(Drawable skbBg)143     public void setSkbBackground(Drawable skbBg) {
144         mSkbBg = skbBg;
145     }
146 
setPopupBackground(Drawable popupBg)147     public void setPopupBackground(Drawable popupBg) {
148         mPopupBg = popupBg;
149     }
150 
setKeyBalloonBackground(Drawable balloonBg)151     public void setKeyBalloonBackground(Drawable balloonBg) {
152         mBalloonBg = balloonBg;
153     }
154 
setKeyMargins(float xMargin, float yMargin)155     public void setKeyMargins(float xMargin, float yMargin) {
156         mKeyXMargin = xMargin;
157         mKeyYMargin = yMargin;
158     }
159 
getCacheId()160     public int getCacheId() {
161         return mCacheId;
162     }
163 
reset()164     public void reset() {
165         if (null != mKeyRows) mKeyRows.clear();
166     }
167 
setNewlyLoadedFlag(boolean newlyLoadedFlag)168     public void setNewlyLoadedFlag(boolean newlyLoadedFlag) {
169         mNewlyLoadedFlag = newlyLoadedFlag;
170     }
171 
getNewlyLoadedFlag()172     public boolean getNewlyLoadedFlag() {
173         return mNewlyLoadedFlag;
174     }
175 
beginNewRow(int rowId, float yStartingPos)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 
addSoftKey(SoftKey softKey)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 
getSkbXmlId()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.
setSkbCoreSize(int skbCoreWidth, int skbCoreHeight)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 
getSkbCoreWidth()229     public int getSkbCoreWidth() {
230         return mSkbCoreWidth;
231     }
232 
getSkbCoreHeight()233     public int getSkbCoreHeight() {
234         return mSkbCoreHeight;
235     }
236 
getSkbTotalWidth()237     public int getSkbTotalWidth() {
238         Rect padding = getPadding();
239         return mSkbCoreWidth + padding.left + padding.right;
240     }
241 
getSkbTotalHeight()242     public int getSkbTotalHeight() {
243         Rect padding = getPadding();
244         return mSkbCoreHeight + padding.top + padding.bottom;
245     }
246 
getKeyXMargin()247     public int getKeyXMargin() {
248         Environment env = Environment.getInstance();
249         return (int) (mKeyXMargin * mSkbCoreWidth * env.getKeyXMarginFactor());
250     }
251 
getKeyYMargin()252     public int getKeyYMargin() {
253         Environment env = Environment.getInstance();
254         return (int) (mKeyYMargin * mSkbCoreHeight * env.getKeyYMarginFactor());
255     }
256 
getSkbBackground()257     public Drawable getSkbBackground() {
258         if (null != mSkbBg) return mSkbBg;
259         return mSkbTemplate.getSkbBackground();
260     }
261 
getBalloonBackground()262     public Drawable getBalloonBackground() {
263         if (null != mBalloonBg) return mBalloonBg;
264         return mSkbTemplate.getBalloonBackground();
265     }
266 
getPopupBackground()267     public Drawable getPopupBackground() {
268         if (null != mPopupBg) return mPopupBg;
269         return mSkbTemplate.getPopupBackground();
270     }
271 
getRowNum()272     public int getRowNum() {
273         if (null != mKeyRows) {
274             return mKeyRows.size();
275         }
276         return 0;
277     }
278 
getKeyRowForDisplay(int row)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 
getKey(int row, int location)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 
mapToKey(int x, int y)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 
switchQwertyMode(int toggle_state_id, boolean upperCase)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 
enableToggleState(int toggleStateId, boolean resetIfNotFound)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 
disableToggleState(int toggleStateId, boolean resetIfNotFound)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 
enableToggleStates(ToggleStates toggleStates)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 
getPadding()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      */
enableRow(int rowId)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
toString()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 
toShortString()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