/* * 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 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 = new KeyRow(); keyRow.mRowId = rowId; keyRow.mTopF = yStartingPos; keyRow.mBottomF = yStartingPos; keyRow.mSoftKeys = new ArrayList(); 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 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 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 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 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 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 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 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 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 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 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 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; } }