| /* |
| * Copyright (C) 2009 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.inputmethod.pinyin; |
| |
| import com.android.inputmethod.pinyin.InputModeSwitcher.ToggleStates; |
| |
| import android.graphics.Rect; |
| import android.graphics.drawable.Drawable; |
| import android.view.KeyEvent; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** |
| * Class used to represent a soft keyboard definition, including the height, the |
| * background image, the image for high light, the keys, etc. |
| */ |
| public class SoftKeyboard { |
| /** The XML resource id for this soft keyboard. */ |
| private int mSkbXmlId; |
| |
| /** Do we need to cache this soft keyboard? */ |
| private boolean mCacheFlag; |
| |
| /** |
| * After user switches to this soft keyboard, if this flag is true, this |
| * soft keyboard will be kept unless explicit switching operation is |
| * performed, otherwise IME will switch back to the previous keyboard layout |
| * whenever user clicks on any none-function key. |
| **/ |
| private boolean mStickyFlag; |
| |
| /** |
| * The cache id for this soft keyboard. It is used to identify it in the |
| * soft keyboard pool. |
| */ |
| private int mCacheId; |
| |
| /** |
| * Used to indicate whether this soft keyboard is newly loaded from an XML |
| * file or is just gotten from the soft keyboard pool. |
| */ |
| private boolean mNewlyLoadedFlag = true; |
| |
| /** The width of the soft keyboard. */ |
| private int mSkbCoreWidth; |
| |
| /** The height of the soft keyboard. */ |
| private int mSkbCoreHeight; |
| |
| /** The soft keyboard template for this soft keyboard. */ |
| private SkbTemplate mSkbTemplate; |
| |
| /** Used to indicate whether this soft keyboard is a QWERTY keyboard. */ |
| private boolean mIsQwerty; |
| |
| /** |
| * When {@link #mIsQwerty} is true, this member is Used to indicate that the |
| * soft keyboard should be displayed in uppercase. |
| */ |
| private boolean mIsQwertyUpperCase; |
| |
| /** |
| * The id of the rows which are enabled. Rows with id |
| * {@link KeyRow#ALWAYS_SHOW_ROW_ID} are always enabled. |
| */ |
| private int mEnabledRowId; |
| |
| /** |
| * Rows in this soft keyboard. Each row has a id. Only matched rows will be |
| * enabled. |
| */ |
| private List<KeyRow> mKeyRows; |
| |
| /** |
| * Background of the soft keyboard. If it is null, the one in the soft |
| * keyboard template will be used. |
| **/ |
| public Drawable mSkbBg; |
| |
| /** |
| * Background for key balloon. If it is null, the one in the soft keyboard |
| * template will be used. |
| **/ |
| private Drawable mBalloonBg; |
| |
| /** |
| * Background for popup mini soft keyboard. If it is null, the one in the |
| * soft keyboard template will be used. |
| **/ |
| private Drawable mPopupBg; |
| |
| /** The left and right margin of a key. */ |
| private float mKeyXMargin = 0; |
| |
| /** The top and bottom margin of a key. */ |
| private float mKeyYMargin = 0; |
| |
| private Rect mTmpRect = new Rect(); |
| |
| public SoftKeyboard(int skbXmlId, SkbTemplate skbTemplate, int skbWidth, |
| int skbHeight) { |
| mSkbXmlId = skbXmlId; |
| mSkbTemplate = skbTemplate; |
| mSkbCoreWidth = skbWidth; |
| mSkbCoreHeight = skbHeight; |
| } |
| |
| public void setFlags(boolean cacheFlag, boolean stickyFlag, |
| boolean isQwerty, boolean isQwertyUpperCase) { |
| mCacheFlag = cacheFlag; |
| mStickyFlag = stickyFlag; |
| mIsQwerty = isQwerty; |
| mIsQwertyUpperCase = isQwertyUpperCase; |
| } |
| |
| public boolean getCacheFlag() { |
| return mCacheFlag; |
| } |
| |
| public void setCacheId(int cacheId) { |
| mCacheId = cacheId; |
| } |
| |
| public boolean getStickyFlag() { |
| return mStickyFlag; |
| } |
| |
| public void setSkbBackground(Drawable skbBg) { |
| mSkbBg = skbBg; |
| } |
| |
| public void setPopupBackground(Drawable popupBg) { |
| mPopupBg = popupBg; |
| } |
| |
| public void setKeyBalloonBackground(Drawable balloonBg) { |
| mBalloonBg = balloonBg; |
| } |
| |
| public void setKeyMargins(float xMargin, float yMargin) { |
| mKeyXMargin = xMargin; |
| mKeyYMargin = yMargin; |
| } |
| |
| public int getCacheId() { |
| return mCacheId; |
| } |
| |
| public void reset() { |
| if (null != mKeyRows) mKeyRows.clear(); |
| } |
| |
| public void setNewlyLoadedFlag(boolean newlyLoadedFlag) { |
| mNewlyLoadedFlag = newlyLoadedFlag; |
| } |
| |
| public boolean getNewlyLoadedFlag() { |
| return mNewlyLoadedFlag; |
| } |
| |
| public void beginNewRow(int rowId, float yStartingPos) { |
| if (null == mKeyRows) mKeyRows = new ArrayList<KeyRow>(); |
| KeyRow keyRow = new KeyRow(); |
| keyRow.mRowId = rowId; |
| keyRow.mTopF = yStartingPos; |
| keyRow.mBottomF = yStartingPos; |
| keyRow.mSoftKeys = new ArrayList<SoftKey>(); |
| mKeyRows.add(keyRow); |
| } |
| |
| public boolean addSoftKey(SoftKey softKey) { |
| if (mKeyRows.size() == 0) return false; |
| KeyRow keyRow = mKeyRows.get(mKeyRows.size() - 1); |
| if (null == keyRow) return false; |
| List<SoftKey> softKeys = keyRow.mSoftKeys; |
| |
| softKey.setSkbCoreSize(mSkbCoreWidth, mSkbCoreHeight); |
| softKeys.add(softKey); |
| if (softKey.mTopF < keyRow.mTopF) { |
| keyRow.mTopF = softKey.mTopF; |
| } |
| if (softKey.mBottomF > keyRow.mBottomF) { |
| keyRow.mBottomF = softKey.mBottomF; |
| } |
| return true; |
| } |
| |
| public int getSkbXmlId() { |
| return mSkbXmlId; |
| } |
| |
| // Set the size of the soft keyboard core. In other words, the background's |
| // padding are not counted. |
| public void setSkbCoreSize(int skbCoreWidth, int skbCoreHeight) { |
| if (null == mKeyRows |
| || (skbCoreWidth == mSkbCoreWidth && skbCoreHeight == mSkbCoreHeight)) { |
| return; |
| } |
| for (int row = 0; row < mKeyRows.size(); row++) { |
| KeyRow keyRow = mKeyRows.get(row); |
| keyRow.mBottom = (int) (skbCoreHeight * keyRow.mBottomF); |
| keyRow.mTop = (int) (skbCoreHeight * keyRow.mTopF); |
| |
| List<SoftKey> softKeys = keyRow.mSoftKeys; |
| for (int i = 0; i < softKeys.size(); i++) { |
| SoftKey softKey = softKeys.get(i); |
| softKey.setSkbCoreSize(skbCoreWidth, skbCoreHeight); |
| } |
| } |
| mSkbCoreWidth = skbCoreWidth; |
| mSkbCoreHeight = skbCoreHeight; |
| } |
| |
| public int getSkbCoreWidth() { |
| return mSkbCoreWidth; |
| } |
| |
| public int getSkbCoreHeight() { |
| return mSkbCoreHeight; |
| } |
| |
| public int getSkbTotalWidth() { |
| Rect padding = getPadding(); |
| return mSkbCoreWidth + padding.left + padding.right; |
| } |
| |
| public int getSkbTotalHeight() { |
| Rect padding = getPadding(); |
| return mSkbCoreHeight + padding.top + padding.bottom; |
| } |
| |
| public int getKeyXMargin() { |
| Environment env = Environment.getInstance(); |
| return (int) (mKeyXMargin * mSkbCoreWidth * env.getKeyXMarginFactor()); |
| } |
| |
| public int getKeyYMargin() { |
| Environment env = Environment.getInstance(); |
| return (int) (mKeyYMargin * mSkbCoreHeight * env.getKeyYMarginFactor()); |
| } |
| |
| public Drawable getSkbBackground() { |
| if (null != mSkbBg) return mSkbBg; |
| return mSkbTemplate.getSkbBackground(); |
| } |
| |
| public Drawable getBalloonBackground() { |
| if (null != mBalloonBg) return mBalloonBg; |
| return mSkbTemplate.getBalloonBackground(); |
| } |
| |
| public Drawable getPopupBackground() { |
| if (null != mPopupBg) return mPopupBg; |
| return mSkbTemplate.getPopupBackground(); |
| } |
| |
| public int getRowNum() { |
| if (null != mKeyRows) { |
| return mKeyRows.size(); |
| } |
| return 0; |
| } |
| |
| public KeyRow getKeyRowForDisplay(int row) { |
| if (null != mKeyRows && mKeyRows.size() > row) { |
| KeyRow keyRow = mKeyRows.get(row); |
| if (KeyRow.ALWAYS_SHOW_ROW_ID == keyRow.mRowId |
| || keyRow.mRowId == mEnabledRowId) { |
| return keyRow; |
| } |
| } |
| return null; |
| } |
| |
| public SoftKey getKey(int row, int location) { |
| if (null != mKeyRows && mKeyRows.size() > row) { |
| List<SoftKey> softKeys = mKeyRows.get(row).mSoftKeys; |
| if (softKeys.size() > location) { |
| return softKeys.get(location); |
| } |
| } |
| return null; |
| } |
| |
| public SoftKey mapToKey(int x, int y) { |
| if (null == mKeyRows) { |
| return null; |
| } |
| // If the position is inside the rectangle of a certain key, return that |
| // key. |
| int rowNum = mKeyRows.size(); |
| for (int row = 0; row < rowNum; row++) { |
| KeyRow keyRow = mKeyRows.get(row); |
| if (KeyRow.ALWAYS_SHOW_ROW_ID != keyRow.mRowId |
| && keyRow.mRowId != mEnabledRowId) continue; |
| if (keyRow.mTop > y && keyRow.mBottom <= y) continue; |
| |
| List<SoftKey> softKeys = keyRow.mSoftKeys; |
| int keyNum = softKeys.size(); |
| for (int i = 0; i < keyNum; i++) { |
| SoftKey sKey = softKeys.get(i); |
| if (sKey.mLeft <= x && sKey.mTop <= y && sKey.mRight > x |
| && sKey.mBottom > y) { |
| return sKey; |
| } |
| } |
| } |
| |
| // If the position is outside the rectangles of all keys, find the |
| // nearest one. |
| SoftKey nearestKey = null; |
| float nearestDis = Float.MAX_VALUE; |
| for (int row = 0; row < rowNum; row++) { |
| KeyRow keyRow = mKeyRows.get(row); |
| if (KeyRow.ALWAYS_SHOW_ROW_ID != keyRow.mRowId |
| && keyRow.mRowId != mEnabledRowId) continue; |
| if (keyRow.mTop > y && keyRow.mBottom <= y) continue; |
| |
| List<SoftKey> softKeys = keyRow.mSoftKeys; |
| int keyNum = softKeys.size(); |
| for (int i = 0; i < keyNum; i++) { |
| SoftKey sKey = softKeys.get(i); |
| int disx = (sKey.mLeft + sKey.mRight) / 2 - x; |
| int disy = (sKey.mTop + sKey.mBottom) / 2 - y; |
| float dis = disx * disx + disy * disy; |
| if (dis < nearestDis) { |
| nearestDis = dis; |
| nearestKey = sKey; |
| } |
| } |
| } |
| return nearestKey; |
| } |
| |
| public void switchQwertyMode(int toggle_state_id, boolean upperCase) { |
| if (!mIsQwerty) return; |
| |
| int rowNum = mKeyRows.size(); |
| for (int row = 0; row < rowNum; row++) { |
| KeyRow keyRow = mKeyRows.get(row); |
| List<SoftKey> softKeys = keyRow.mSoftKeys; |
| int keyNum = softKeys.size(); |
| for (int i = 0; i < keyNum; i++) { |
| SoftKey sKey = softKeys.get(i); |
| if (sKey instanceof SoftKeyToggle) { |
| ((SoftKeyToggle) sKey).enableToggleState(toggle_state_id, |
| true); |
| } |
| if (sKey.mKeyCode >= KeyEvent.KEYCODE_A |
| && sKey.mKeyCode <= KeyEvent.KEYCODE_Z) { |
| sKey.changeCase(upperCase); |
| } |
| } |
| } |
| } |
| |
| public void enableToggleState(int toggleStateId, boolean resetIfNotFound) { |
| int rowNum = mKeyRows.size(); |
| for (int row = 0; row < rowNum; row++) { |
| KeyRow keyRow = mKeyRows.get(row); |
| List<SoftKey> softKeys = keyRow.mSoftKeys; |
| int keyNum = softKeys.size(); |
| for (int i = 0; i < keyNum; i++) { |
| SoftKey sKey = softKeys.get(i); |
| if (sKey instanceof SoftKeyToggle) { |
| ((SoftKeyToggle) sKey).enableToggleState(toggleStateId, |
| resetIfNotFound); |
| } |
| } |
| } |
| } |
| |
| public void disableToggleState(int toggleStateId, boolean resetIfNotFound) { |
| int rowNum = mKeyRows.size(); |
| for (int row = 0; row < rowNum; row++) { |
| KeyRow keyRow = mKeyRows.get(row); |
| List<SoftKey> softKeys = keyRow.mSoftKeys; |
| int keyNum = softKeys.size(); |
| for (int i = 0; i < keyNum; i++) { |
| SoftKey sKey = softKeys.get(i); |
| if (sKey instanceof SoftKeyToggle) { |
| ((SoftKeyToggle) sKey).disableToggleState(toggleStateId, |
| resetIfNotFound); |
| } |
| } |
| } |
| } |
| |
| public void enableToggleStates(ToggleStates toggleStates) { |
| if (null == toggleStates) return; |
| |
| enableRow(toggleStates.mRowIdToEnable); |
| |
| boolean isQwerty = toggleStates.mQwerty; |
| boolean isQwertyUpperCase = toggleStates.mQwertyUpperCase; |
| boolean needUpdateQwerty = (isQwerty && mIsQwerty && (mIsQwertyUpperCase != isQwertyUpperCase)); |
| int states[] = toggleStates.mKeyStates; |
| int statesNum = toggleStates.mKeyStatesNum; |
| |
| int rowNum = mKeyRows.size(); |
| for (int row = 0; row < rowNum; row++) { |
| KeyRow keyRow = mKeyRows.get(row); |
| if (KeyRow.ALWAYS_SHOW_ROW_ID != keyRow.mRowId |
| && keyRow.mRowId != mEnabledRowId) { |
| continue; |
| } |
| List<SoftKey> softKeys = keyRow.mSoftKeys; |
| int keyNum = softKeys.size(); |
| for (int keyPos = 0; keyPos < keyNum; keyPos++) { |
| SoftKey sKey = softKeys.get(keyPos); |
| if (sKey instanceof SoftKeyToggle) { |
| for (int statePos = 0; statePos < statesNum; statePos++) { |
| ((SoftKeyToggle) sKey).enableToggleState( |
| states[statePos], statePos == 0); |
| } |
| if (0 == statesNum) { |
| ((SoftKeyToggle) sKey).disableAllToggleStates(); |
| } |
| } |
| if (needUpdateQwerty) { |
| if (sKey.mKeyCode >= KeyEvent.KEYCODE_A |
| && sKey.mKeyCode <= KeyEvent.KEYCODE_Z) { |
| sKey.changeCase(isQwertyUpperCase); |
| } |
| } |
| } |
| } |
| mIsQwertyUpperCase = isQwertyUpperCase; |
| } |
| |
| private Rect getPadding() { |
| mTmpRect.set(0, 0, 0, 0); |
| Drawable skbBg = getSkbBackground(); |
| if (null == skbBg) return mTmpRect; |
| skbBg.getPadding(mTmpRect); |
| return mTmpRect; |
| } |
| |
| /** |
| * Enable a row with the give toggle Id. Rows with other toggle ids (except |
| * the id {@link KeyRow#ALWAYS_SHOW_ROW_ID}) will be disabled. |
| * |
| * @param rowId The row id to enable. |
| * @return True if the soft keyboard requires redrawing. |
| */ |
| private boolean enableRow(int rowId) { |
| if (KeyRow.ALWAYS_SHOW_ROW_ID == rowId) return false; |
| |
| boolean enabled = false; |
| int rowNum = mKeyRows.size(); |
| for (int row = rowNum - 1; row >= 0; row--) { |
| if (mKeyRows.get(row).mRowId == rowId) { |
| enabled = true; |
| break; |
| } |
| } |
| if (enabled) { |
| mEnabledRowId = rowId; |
| } |
| return enabled; |
| } |
| |
| @Override |
| public String toString() { |
| String str = "------------------SkbInfo----------------------\n"; |
| String endStr = "-----------------------------------------------\n"; |
| str += "Width: " + String.valueOf(mSkbCoreWidth) + "\n"; |
| str += "Height: " + String.valueOf(mSkbCoreHeight) + "\n"; |
| str += "KeyRowNum: " + mKeyRows == null ? "0" : String.valueOf(mKeyRows |
| .size()) |
| + "\n"; |
| if (null == mKeyRows) return str + endStr; |
| int rowNum = mKeyRows.size(); |
| for (int row = 0; row < rowNum; row++) { |
| KeyRow keyRow = mKeyRows.get(row); |
| List<SoftKey> softKeys = keyRow.mSoftKeys; |
| int keyNum = softKeys.size(); |
| for (int i = 0; i < softKeys.size(); i++) { |
| str += "-key " + String.valueOf(i) + ":" |
| + softKeys.get(i).toString(); |
| } |
| } |
| return str + endStr; |
| } |
| |
| public String toShortString() { |
| return super.toString(); |
| } |
| |
| class KeyRow { |
| static final int ALWAYS_SHOW_ROW_ID = -1; |
| static final int DEFAULT_ROW_ID = 0; |
| |
| List<SoftKey> mSoftKeys; |
| /** |
| * If the row id is {@link #ALWAYS_SHOW_ROW_ID}, this row will always be |
| * enabled. |
| */ |
| int mRowId; |
| float mTopF; |
| float mBottomF; |
| int mTop; |
| int mBottom; |
| } |
| } |