diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin')
6 files changed, 578 insertions, 173 deletions
diff --git a/java/src/com/android/inputmethod/latin/CandidateView.java b/java/src/com/android/inputmethod/latin/CandidateView.java index b9ded31cb..fe48f0bc1 100644 --- a/java/src/com/android/inputmethod/latin/CandidateView.java +++ b/java/src/com/android/inputmethod/latin/CandidateView.java @@ -23,6 +23,7 @@ import android.graphics.Color; import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.os.Message; +import android.os.SystemClock; import android.text.Spannable; import android.text.SpannableString; import android.text.Spanned; @@ -34,8 +35,10 @@ import android.text.style.ForegroundColorSpan; import android.text.style.StyleSpan; import android.text.style.UnderlineSpan; import android.util.AttributeSet; +import android.util.DisplayMetrics; import android.view.Gravity; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; @@ -46,6 +49,9 @@ import android.widget.TextView; import com.android.inputmethod.compat.FrameLayoutCompatUtils; import com.android.inputmethod.compat.LinearLayoutCompatUtils; +import com.android.inputmethod.keyboard.KeyboardActionListener; +import com.android.inputmethod.keyboard.MoreKeysPanel; +import com.android.inputmethod.keyboard.PointerTracker; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import java.util.ArrayList; @@ -58,16 +64,19 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo } // The maximum number of suggestions available. See {@link Suggest#mPrefMaxSuggestions}. - private static final int MAX_SUGGESTIONS = 18; + public static final int MAX_SUGGESTIONS = 18; private static final boolean DBG = LatinImeLogger.sDBG; private final ViewGroup mCandidatesPlacer; private final ViewGroup mCandidatesStrip; - private ViewGroup mCandidatesPane; - private ViewGroup mCandidatesPaneContainer; private View mKeyboardView; + private final View mMoreSuggestionsContainer; + private final MoreSuggestionsView mMoreSuggestionsView; + private final MoreSuggestions.Builder mMoreSuggestionsBuilder; + private final PopupWindow mMoreSuggestionsWindow; + private final ArrayList<TextView> mWords = new ArrayList<TextView>(); private final ArrayList<TextView> mInfos = new ArrayList<TextView>(); private final ArrayList<View> mDividers = new ArrayList<View>(); @@ -80,7 +89,6 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo private boolean mShowingAutoCorrectionInverted; private final SuggestionsStripParams mStripParams; - private final SuggestionsPaneParams mPaneParams; private static final float MIN_TEXT_XSCALE = 0.70f; private final UiHandler mHandler = new UiHandler(this); @@ -137,7 +145,6 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo private static class CandidateViewParams { public final int mPadding; public final int mDividerWidth; - public final int mDividerHeight; public final int mCandidateStripHeight; protected final List<TextView> mWords; @@ -156,96 +163,9 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo divider.measure( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); mDividerWidth = divider.getMeasuredWidth(); - mDividerHeight = divider.getMeasuredHeight(); final Resources res = word.getResources(); - mCandidateStripHeight = res.getDimensionPixelOffset(R.dimen.candidate_strip_height); - } - } - - private static class SuggestionsPaneParams extends CandidateViewParams { - public SuggestionsPaneParams(List<TextView> words, List<View> dividers, - List<TextView> infos) { - super(words, dividers, infos); - } - - public int layout(SuggestedWords suggestions, ViewGroup paneView, int from, int textColor, - int paneWidth) { - final int count = Math.min(mWords.size(), suggestions.size()); - View centeringFrom = null, lastView = null; - int x = 0, y = 0; - for (int index = from; index < count; index++) { - final int pos = index; - final TextView word = mWords.get(pos); - final View divider = mDividers.get(pos); - final TextPaint paint = word.getPaint(); - word.setTextColor(textColor); - final CharSequence styled = suggestions.getWord(pos); - - final TextView info; - if (DBG) { - final CharSequence debugInfo = getDebugInfo(suggestions, index); - if (debugInfo != null) { - info = mInfos.get(index); - info.setText(debugInfo); - } else { - info = null; - } - } else { - info = null; - } - - final CharSequence text; - final float scaleX; - paint.setTextScaleX(1.0f); - final int textWidth = getTextWidth(styled, paint); - int available = paneWidth - x - mPadding; - if (textWidth >= available) { - // Needs new row, centering previous row. - centeringCandidates(paneView, centeringFrom, lastView, x, paneWidth); - x = 0; - y += mCandidateStripHeight; - } - if (x != 0) { - // Add divider if this isn't the left most suggestion in current row. - paneView.addView(divider); - FrameLayoutCompatUtils.placeViewAt(divider, x, y - + (mCandidateStripHeight - mDividerHeight) / 2, mDividerWidth, - mDividerHeight); - x += mDividerWidth; - } - available = paneWidth - x - mPadding; - text = getEllipsizedText(styled, available, paint); - scaleX = paint.getTextScaleX(); - word.setText(text); - word.setTextScaleX(scaleX); - paneView.addView(word); - lastView = word; - if (x == 0) - centeringFrom = word; - word.measure(ViewGroup.LayoutParams.WRAP_CONTENT, - MeasureSpec.makeMeasureSpec(mCandidateStripHeight, MeasureSpec.EXACTLY)); - final int width = word.getMeasuredWidth(); - final int height = word.getMeasuredHeight(); - FrameLayoutCompatUtils.placeViewAt(word, x, y + (mCandidateStripHeight - height) - / 2, width, height); - x += width; - if (info != null) { - paneView.addView(info); - lastView = info; - info.measure(ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT); - final int infoWidth = info.getMeasuredWidth(); - FrameLayoutCompatUtils.placeViewAt( - info, x - infoWidth, y, infoWidth, info.getMeasuredHeight()); - } - } - if (x != 0) { - // Centering last candidates row. - centeringCandidates(paneView, centeringFrom, lastView, x, paneWidth); - } - - return count - from; + mCandidateStripHeight = res.getDimensionPixelSize(R.dimen.candidate_strip_height); } } @@ -257,7 +177,7 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo private final int mColorTypedWord; private final int mColorAutoCorrect; private final int mColorSuggestedCandidate; - private final int mCandidateCountInStrip; + public final int mCandidateCountInStrip; private final float mCenterCandidateWeight; private final int mCenterCandidateIndex; private final Drawable mMoreCandidateHint; @@ -311,10 +231,6 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo mHintToSaveText = context.getText(R.string.hint_add_to_dictionary); } - public int getTextColor() { - return mColorTypedWord; - } - private CharSequence getStyledCandidateWord(SuggestedWords suggestions, int pos) { final CharSequence word = suggestions.getWord(pos); final boolean isAutoCorrect = pos == 1 && willAutoCorrect(suggestions); @@ -389,10 +305,11 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo return word; } - public int layout(SuggestedWords suggestions, ViewGroup stripView, ViewGroup placer, + public void layout(SuggestedWords suggestions, ViewGroup stripView, ViewGroup placer, int stripWidth) { if (suggestions.isPunctuationSuggestions()) { - return layoutPunctuationSuggestions(suggestions, stripView); + layoutPunctuationSuggestions(suggestions, stripView); + return; } final int countInStrip = mCandidateCountInStrip; @@ -449,8 +366,6 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo } } } - - return countInStrip; } private int getCandidateWidth(int index, int maxWidth) { @@ -482,7 +397,7 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo } } - private int layoutPunctuationSuggestions(SuggestedWords suggestions, ViewGroup stripView) { + private void layoutPunctuationSuggestions(SuggestedWords suggestions, ViewGroup stripView) { final int countInStrip = Math.min(suggestions.size(), PUNCTUATIONS_IN_STRIP); for (int index = 0; index < countInStrip; index++) { if (index != 0) { @@ -501,7 +416,6 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo setLayoutWeight(word, 1.0f, mCandidateStripHeight); } mMoreSuggestionsAvailable = false; - return countInStrip; } public void layoutAddToDictionaryHint(CharSequence word, ViewGroup stripView, @@ -583,8 +497,16 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo mStripParams = new SuggestionsStripParams(context, attrs, defStyle, mWords, mDividers, mInfos); - mPaneParams = new SuggestionsPaneParams(mWords, mDividers, mInfos); mStripParams.mWordToSaveView.setOnClickListener(this); + + mMoreSuggestionsContainer = inflater.inflate(R.layout.more_suggestions, null); + mMoreSuggestionsView = (MoreSuggestionsView)mMoreSuggestionsContainer + .findViewById(R.id.more_suggestions_view); + mMoreSuggestionsBuilder = new MoreSuggestions.Builder(mMoreSuggestionsView); + mMoreSuggestionsWindow = new PopupWindow(context); + mMoreSuggestionsWindow.setWindowLayoutMode( + ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + mMoreSuggestionsWindow.setBackgroundDrawable(null); } /** @@ -594,11 +516,6 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo public void setListener(Listener listener, View inputView) { mListener = listener; mKeyboardView = inputView.findViewById(R.id.keyboard_view); - mCandidatesPane = FrameLayoutCompatUtils.getPlacer( - (ViewGroup)inputView.findViewById(R.id.candidates_pane)); - mCandidatesPane.setOnClickListener(this); - mCandidatesPaneContainer = (ViewGroup)inputView.findViewById( - R.id.candidates_pane_container); } public void setSuggestions(SuggestedWords suggestions) { @@ -614,15 +531,10 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo private void updateSuggestions() { clear(); - closeCandidatesPane(); if (mSuggestions.size() == 0) return; - final int width = getWidth(); - final int countInStrip = mStripParams.layout( - mSuggestions, mCandidatesStrip, mCandidatesPlacer, width); - mPaneParams.layout( - mSuggestions, mCandidatesPane, countInStrip, mStripParams.getTextColor(), width); + mStripParams.layout(mSuggestions, mCandidatesStrip, mCandidatesPlacer, getWidth()); } private static CharSequence getDebugInfo(SuggestedWords suggestions, int pos) { @@ -648,26 +560,6 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo } } - private static void centeringCandidates(ViewGroup parent, View from, View to, int width, - int parentWidth) { - final int fromIndex = parent.indexOfChild(from); - final int toIndex = parent.indexOfChild(to); - final int offset = (parentWidth - width) / 2; - for (int index = fromIndex; index <= toIndex; index++) { - offsetMargin(parent.getChildAt(index), offset, 0); - } - } - - private static void offsetMargin(View v, int dx, int dy) { - if (v == null) - return; - final ViewGroup.LayoutParams lp = v.getLayoutParams(); - if (lp instanceof ViewGroup.MarginLayoutParams) { - final ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams)lp; - mlp.setMargins(mlp.leftMargin + dx, mlp.topMargin + dy, 0, 0); - } - } - private static float getTextScaleX(CharSequence text, int maxWidth, TextPaint paint) { paint.setTextScaleX(1.0f); final int width = getTextWidth(text, paint); @@ -730,25 +622,6 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo } } - private void expandCandidatesPane() { - mCandidatesPaneContainer.setMinimumHeight(mKeyboardView.getMeasuredHeight()); - mCandidatesPaneContainer.setVisibility(VISIBLE); - mKeyboardView.setVisibility(GONE); - } - - private void closeCandidatesPane() { - mCandidatesPaneContainer.setVisibility(GONE); - mKeyboardView.setVisibility(VISIBLE); - } - - private void toggleCandidatesPane() { - if (mCandidatesPaneContainer.getVisibility() == VISIBLE) { - closeCandidatesPane(); - } else { - expandCandidatesPane(); - } - } - public void onAutoCorrectionInverted(CharSequence autoCorrectedWord) { final CharSequence inverted = mStripParams.getInvertedText(autoCorrectedWord); if (inverted == null) @@ -785,8 +658,7 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo mCandidatesPlacer.removeAllViews(); mCandidatesPlacer.addView(mCandidatesStrip); mCandidatesStrip.removeAllViews(); - mCandidatesPane.removeAllViews(); - closeCandidatesPane(); + mMoreSuggestionsWindow.dismiss(); } private void hidePreview() { @@ -823,16 +695,103 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo } } + private final KeyboardActionListener mMoreSuggestionsListener = + new KeyboardActionListener.Adapter() { + @Override + public boolean onCustomRequest(int requestCode) { + final int index = requestCode; + final CharSequence word = mSuggestions.getWord(index); + mListener.pickSuggestionManually(index, word); + mMoreSuggestionsView.dismissMoreKeysPanel(); + return true; + } + + @Override + public void onCancelInput() { + mMoreSuggestionsView.dismissMoreKeysPanel(); + } + }; + + private final MoreKeysPanel.Controller mMoreSuggestionsController = + new MoreKeysPanel.Controller() { + @Override + public boolean dismissMoreKeysPanel() { + if (mMoreSuggestionsWindow.isShowing()) { + mMoreSuggestionsWindow.dismiss(); + return true; + } + return false; + } + }; + @Override public boolean onLongClick(View view) { - if (mStripParams.mMoreSuggestionsAvailable) { - toggleCandidatesPane(); + final SuggestionsStripParams params = mStripParams; + if (params.mMoreSuggestionsAvailable) { + final int stripWidth = getWidth(); + final View container = mMoreSuggestionsContainer; + final int maxWidth = stripWidth - container.getPaddingLeft() + - container.getPaddingRight(); + final DisplayMetrics dm = getContext().getResources().getDisplayMetrics(); + // TODO: Revise how we determine the height + final int maxHeight = dm.heightPixels - mKeyboardView.getHeight() - getHeight() * 3; + final MoreSuggestions.Builder builder = mMoreSuggestionsBuilder; + builder.layout(mSuggestions, params.mCandidateCountInStrip, maxWidth, maxHeight); + mMoreSuggestionsView.setKeyboard(builder.build()); + container.measure( + ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + + final MoreKeysPanel moreKeysPanel = mMoreSuggestionsView; + final int pointX = stripWidth / 2; + final int pointY = 0; + moreKeysPanel.showMoreKeysPanel( + this, mMoreSuggestionsController, pointX, pointY, + mMoreSuggestionsWindow, mMoreSuggestionsListener); + // TODO: Should figure out how to select the pointer tracker correctly. + final PointerTracker tracker = PointerTracker.getPointerTracker(0, moreKeysPanel); + final int translatedX = moreKeysPanel.translateX(tracker.getLastX()); + final int translatedY = moreKeysPanel.translateY(tracker.getLastY()); + tracker.onShowMoreKeysPanel( + translatedX, translatedY, SystemClock.uptimeMillis(), moreKeysPanel); + view.setPressed(false); + // TODO: Should gray out the keyboard here as well? return true; } return false; } @Override + public boolean dispatchTouchEvent(MotionEvent me) { + if (!mMoreSuggestionsWindow.isShowing()) { + return super.dispatchTouchEvent(me); + } + final int action = me.getAction(); + final long eventTime = me.getEventTime(); + final int index = me.getActionIndex(); + final int id = me.getPointerId(index); + final PointerTracker tracker = PointerTracker.getPointerTracker(id, mMoreSuggestionsView); + final int x = mMoreSuggestionsView.translateX((int)me.getX(index)); + final int y = mMoreSuggestionsView.translateY((int)me.getY(index)); + switch (action) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: + tracker.onDownEvent(x, y, eventTime, mMoreSuggestionsView); + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_POINTER_UP: + tracker.onUpEvent(x, y, eventTime); + break; + case MotionEvent.ACTION_MOVE: + tracker.onMoveEvent(x, y, eventTime); + break; + case MotionEvent.ACTION_CANCEL: + tracker.onCancelEvent(x, y, eventTime); + break; + } + return true; + } + + @Override public void onClick(View view) { if (view == mStripParams.mWordToSaveView) { addToDictionary((CharSequence)view.getTag()); @@ -840,11 +799,6 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo return; } - if (view == mCandidatesPane) { - closeCandidatesPane(); - return; - } - final Object tag = view.getTag(); if (!(tag instanceof Integer)) return; @@ -854,9 +808,6 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo final CharSequence word = mSuggestions.getWord(index); mListener.pickSuggestionManually(index, word); - // Because some punctuation letters are not treated as word separator depending on locale, - // {@link #setSuggestions} might not be called and candidates pane left opened. - closeCandidatesPane(); } @Override diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 7559108f3..da6c22107 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -118,6 +118,12 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar */ public static final String SUBTYPE_EXTRA_VALUE_ASCII_CAPABLE = "AsciiCapable"; + /** + * The subtype extra value used to indicate that the subtype keyboard layout should be loaded + * from the specified locale. + */ + public static final String SUBTYPE_EXTRA_VALUE_KEYBOARD_LOCALE = "KeyboardLocale"; + private static final int EXTENDED_TOUCHABLE_REGION_HEIGHT = 100; // How many continuous deletes at which to start deleting at a higher speed. diff --git a/java/src/com/android/inputmethod/latin/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/MoreSuggestions.java new file mode 100644 index 000000000..0446fb2a8 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/MoreSuggestions.java @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2011 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.latin; + +import android.graphics.Paint; +import android.text.TextUtils; + +import com.android.inputmethod.keyboard.Key; +import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.keyboard.KeyboardSwitcher; +import com.android.inputmethod.keyboard.KeyboardView; +import com.android.inputmethod.keyboard.internal.KeyboardBuilder; +import com.android.inputmethod.keyboard.internal.KeyboardParams; +import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; + +public class MoreSuggestions extends Keyboard { + private static final boolean DBG = LatinImeLogger.sDBG; + + public static final int SUGGESTION_CODE_BASE = 1024; + + private MoreSuggestions(Builder.MoreSuggestionsParam params) { + super(params); + } + + public static class Builder extends KeyboardBuilder<Builder.MoreSuggestionsParam> { + private final MoreSuggestionsView mPaneView; + private SuggestedWords mSuggestions; + private int mFromPos; + private int mToPos; + + public static class MoreSuggestionsParam extends KeyboardParams { + private final int[] mWidths = new int[CandidateView.MAX_SUGGESTIONS]; + private final int[] mRowNumbers = new int[CandidateView.MAX_SUGGESTIONS]; + private final int[] mColumnOrders = new int[CandidateView.MAX_SUGGESTIONS]; + private final int[] mNumColumnsInRow = new int[CandidateView.MAX_SUGGESTIONS]; + private static final int MAX_COLUMNS_IN_ROW = 3; + private int mNumRows; + + public int layout(SuggestedWords suggestions, int fromPos, int maxWidth, int maxHeight, + KeyboardView view) { + clearKeys(); + final Paint paint = new Paint(); + paint.setAntiAlias(true); + final int padding = (int) view.getContext().getResources() + .getDimension(R.dimen.more_suggestions_key_horizontal_padding); + + int row = 0; + int pos = fromPos, rowStartPos = fromPos; + final int size = Math.min(suggestions.size(), CandidateView.MAX_SUGGESTIONS); + while (pos < size) { + final CharSequence word = suggestions.getWord(pos); + // TODO: Should take care of text x-scaling. + mWidths[pos] = (int)view.getDefaultLabelWidth(word, paint) + padding; + final int numColumn = pos - rowStartPos + 1; + if (numColumn > MAX_COLUMNS_IN_ROW + || !fitInWidth(rowStartPos, pos + 1, maxWidth / numColumn)) { + if ((row + 1) * mDefaultRowHeight > maxHeight) { + break; + } + mNumColumnsInRow[row] = pos - rowStartPos; + rowStartPos = pos; + row++; + } + mColumnOrders[pos] = pos - rowStartPos; + mRowNumbers[pos] = row; + pos++; + } + mNumColumnsInRow[row] = pos - rowStartPos; + mNumRows = row + 1; + mWidth = mOccupiedWidth = calcurateMaxRowWidth(fromPos, pos); + mHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight + mVerticalGap; + return pos - fromPos; + } + + private boolean fitInWidth(int startPos, int endPos, int width) { + for (int pos = startPos; pos < endPos; pos++) { + if (mWidths[pos] > width) + return false; + } + return true; + } + + private int calcurateMaxRowWidth(int startPos, int endPos) { + int maxRowWidth = 0; + int pos = startPos; + for (int row = 0; row < mNumRows; row++) { + final int numColumn = mNumColumnsInRow[row]; + int maxKeyWidth = 0; + while (pos < endPos && mRowNumbers[pos] == row) { + maxKeyWidth = Math.max(maxKeyWidth, mWidths[pos]); + pos++; + } + maxRowWidth = Math.max(maxRowWidth, maxKeyWidth * numColumn); + } + return maxRowWidth; + } + + private static final int[][] COLUMN_ORDER_TO_NUMBER = { + { 0, }, + { 1, 0, }, + { 2, 0, 1}, + }; + + private int getColumnNumber(int pos) { + final int columnOrder = mColumnOrders[pos]; + final int numColumn = mNumColumnsInRow[mRowNumbers[pos]]; + return COLUMN_ORDER_TO_NUMBER[numColumn - 1][columnOrder]; + } + + public int getX(int pos) { + final int columnNumber = getColumnNumber(pos); + return columnNumber * getWidth(pos); + } + + public int getY(int pos) { + final int row = mRowNumbers[pos]; + return (mNumRows -1 - row) * mDefaultRowHeight + mTopPadding; + } + + public int getWidth(int pos) { + final int row = mRowNumbers[pos]; + final int numColumn = mNumColumnsInRow[row]; + return mWidth / numColumn; + } + + public int getFlags(int pos) { + int rowFlags = 0; + + final int row = mRowNumbers[pos]; + if (row == 0) + rowFlags |= Keyboard.EDGE_BOTTOM; + if (row == mNumRows - 1) + rowFlags |= Keyboard.EDGE_TOP; + + final int numColumn = mNumColumnsInRow[row]; + final int column = getColumnNumber(pos); + if (column == 0) + rowFlags |= Keyboard.EDGE_LEFT; + if (column == numColumn - 1) + rowFlags |= Keyboard.EDGE_RIGHT; + + return rowFlags; + } + } + + public Builder(MoreSuggestionsView paneView) { + super(paneView.getContext(), new MoreSuggestionsParam()); + mPaneView = paneView; + } + + public Builder layout(SuggestedWords suggestions, int fromPos, int maxWidth, + int maxHeight) { + final Keyboard keyboard = KeyboardSwitcher.getInstance().getLatinKeyboard(); + final int xmlId = R.xml.kbd_suggestions_pane_template; + load(keyboard.mId.cloneWithNewXml(mResources.getResourceEntryName(xmlId), xmlId)); + mParams.mVerticalGap = mParams.mTopPadding = keyboard.mVerticalGap / 2; + + final int count = mParams.layout(suggestions, fromPos, maxWidth, maxHeight, mPaneView); + mFromPos = fromPos; + mToPos = fromPos + count; + mSuggestions = suggestions; + return this; + } + + private static String getDebugInfo(SuggestedWords suggestions, int pos) { + if (!DBG) return null; + final SuggestedWordInfo wordInfo = suggestions.getInfo(pos); + if (wordInfo == null) return null; + final String info = wordInfo.getDebugString(); + if (TextUtils.isEmpty(info)) return null; + return info; + } + + @Override + public MoreSuggestions build() { + final MoreSuggestionsParam params = mParams; + for (int pos = mFromPos; pos < mToPos; pos++) { + final String word = mSuggestions.getWord(pos).toString(); + final String info = getDebugInfo(mSuggestions, pos); + final int index = pos + SUGGESTION_CODE_BASE; + final Key key = new Key( + params, word, info, null, index, null, params.getX(pos), params.getY(pos), + params.getWidth(pos), params.mDefaultRowHeight, params.getFlags(pos)); + params.onAddKey(key); + } + return new MoreSuggestions(params); + } + } +} diff --git a/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java b/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java new file mode 100644 index 000000000..828490112 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2011 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.latin; + +import android.content.Context; +import android.content.res.Resources; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.View; +import android.widget.PopupWindow; + +import com.android.inputmethod.keyboard.Key; +import com.android.inputmethod.keyboard.KeyDetector; +import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.keyboard.KeyboardActionListener; +import com.android.inputmethod.keyboard.KeyboardView; +import com.android.inputmethod.keyboard.MoreKeysPanel; +import com.android.inputmethod.keyboard.PointerTracker; +import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy; +import com.android.inputmethod.keyboard.PointerTracker.TimerProxy; + +import java.util.List; + +/** + * A view that renders a virtual {@link MoreSuggestions}. It handles rendering of keys and detecting + * key presses and touch movements. + */ +public class MoreSuggestionsView extends KeyboardView implements MoreKeysPanel { + private final int[] mCoordinates = new int[2]; + + private final KeyDetector mKeyDetector; + + private Controller mController; + private KeyboardActionListener mListener; + private int mOriginX; + private int mOriginY; + + private static class SuggestionsPaneKeyDetector extends KeyDetector { + private final int mSlideAllowanceSquare; + private final int mSlideAllowanceSquareTop; + + public SuggestionsPaneKeyDetector(float slideAllowance) { + super(/* keyHysteresisDistance */0); + mSlideAllowanceSquare = (int)(slideAllowance * slideAllowance); + // Top slide allowance is slightly longer (sqrt(2) times) than other edges. + mSlideAllowanceSquareTop = mSlideAllowanceSquare * 2; + } + + @Override + public boolean alwaysAllowsSlidingInput() { + return true; + } + + @Override + protected int getMaxNearbyKeys() { + // No nearby key will be returned. + return 1; + } + + @Override + public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes) { + final List<Key> keys = getKeyboard().mKeys; + final int touchX = getTouchX(x); + final int touchY = getTouchY(y); + + int nearestIndex = NOT_A_KEY; + int nearestDist = (y < 0) ? mSlideAllowanceSquareTop : mSlideAllowanceSquare; + final int keyCount = keys.size(); + for (int index = 0; index < keyCount; index++) { + final int dist = keys.get(index).squaredDistanceToEdge(touchX, touchY); + if (dist < nearestDist) { + nearestIndex = index; + nearestDist = dist; + } + } + + if (allCodes != null && nearestIndex != NOT_A_KEY) + allCodes[0] = keys.get(nearestIndex).mCode; + return nearestIndex; + } + } + + private static final TimerProxy EMPTY_TIMER_PROXY = new TimerProxy.Adapter(); + + private final KeyboardActionListener mSuggestionsPaneListener = + new KeyboardActionListener.Adapter() { + @Override + public void onPress(int primaryCode, boolean withSliding) { + mListener.onPress(primaryCode, withSliding); + } + + @Override + public void onRelease(int primaryCode, boolean withSliding) { + mListener.onRelease(primaryCode, withSliding); + } + + @Override + public void onCodeInput(int primaryCode, int[] keyCodes, int x, int y) { + mListener.onCustomRequest(primaryCode - MoreSuggestions.SUGGESTION_CODE_BASE); + } + + @Override + public void onCancelInput() { + mListener.onCancelInput(); + } + }; + + public MoreSuggestionsView(Context context, AttributeSet attrs) { + this(context, attrs, R.attr.suggestionsPaneViewStyle); + } + + public MoreSuggestionsView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + final Resources res = context.getResources(); + // Override default ProximityKeyDetector. + mKeyDetector = new SuggestionsPaneKeyDetector(res.getDimension( + R.dimen.more_suggestions_slide_allowance)); + // Remove gesture detector on suggestions pane + setKeyPreviewPopupEnabled(false, 0); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final Keyboard keyboard = getKeyboard(); + if (keyboard != null) { + final int width = keyboard.mOccupiedWidth + getPaddingLeft() + getPaddingRight(); + final int height = keyboard.mOccupiedHeight + getPaddingTop() + getPaddingBottom(); + setMeasuredDimension(width, height); + } else { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + } + + @Override + public void setKeyboard(Keyboard keyboard) { + super.setKeyboard(keyboard); + mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(), + -getPaddingTop() + mVerticalCorrection); + } + + @Override + public KeyDetector getKeyDetector() { + return mKeyDetector; + } + + @Override + public KeyboardActionListener getKeyboardActionListener() { + return mSuggestionsPaneListener; + } + + @Override + public DrawingProxy getDrawingProxy() { + return this; + } + + @Override + public TimerProxy getTimerProxy() { + return EMPTY_TIMER_PROXY; + } + + @Override + public void setKeyPreviewPopupEnabled(boolean previewEnabled, int delay) { + // Suggestions pane needs no pop-up key preview displayed, so we pass always false with a + // delay of 0. The delay does not matter actually since the popup is not shown anyway. + super.setKeyPreviewPopupEnabled(false, 0); + } + + @Override + public void setShifted(boolean shifted) { + // Nothing to do with. + } + + @Override + public void showMoreKeysPanel(View parentView, Controller controller, int pointX, int pointY, + PopupWindow window, KeyboardActionListener listener) { + mController = controller; + mListener = listener; + final View container = (View)getParent(); + final MoreSuggestions pane = (MoreSuggestions)getKeyboard(); + + parentView.getLocationInWindow(mCoordinates); + final int paneLeft = pointX - (pane.mOccupiedWidth / 2) + parentView.getPaddingLeft(); + final int x = wrapUp(Math.max(0, Math.min(paneLeft, + parentView.getWidth() - pane.mOccupiedWidth)) + - container.getPaddingLeft() + mCoordinates[0], + container.getMeasuredWidth(), 0, parentView.getWidth()); + final int y = pointY + - (container.getMeasuredHeight() - container.getPaddingBottom()) + + parentView.getPaddingTop() + mCoordinates[1]; + + window.setContentView(container); + window.setWidth(container.getMeasuredWidth()); + window.setHeight(container.getMeasuredHeight()); + window.showAtLocation(parentView, Gravity.NO_GRAVITY, x, y); + + mOriginX = x + container.getPaddingLeft() - mCoordinates[0]; + mOriginY = y + container.getPaddingTop() - mCoordinates[1]; + } + + private static int wrapUp(int x, int width, int left, int right) { + if (x < left) + return left; + if (x + width > right) + return right - width; + return x; + } + + @Override + public boolean dismissMoreKeysPanel() { + return mController.dismissMoreKeysPanel(); + } + + @Override + public int translateX(int x) { + return x - mOriginX; + } + + @Override + public int translateY(int y) { + return y - mOriginY; + } +} diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java index b79c441d7..1e2ef4824 100644 --- a/java/src/com/android/inputmethod/latin/Settings.java +++ b/java/src/com/android/inputmethod/latin/Settings.java @@ -336,7 +336,9 @@ public class Settings extends InputMethodSettingsActivity R.string.auto_correction_threshold_mode_index_off); final String currentSetting = mAutoCorrectionThresholdPreference.getValue(); mBigramSuggestion.setEnabled(!currentSetting.equals(autoCorrectionOff)); - mBigramPrediction.setEnabled(!currentSetting.equals(autoCorrectionOff)); + if (null != mBigramPrediction) { + mBigramPrediction.setEnabled(!currentSetting.equals(autoCorrectionOff)); + } } public Activity getActivityInternal() { diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java index 87d854940..a60f444fb 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java +++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java @@ -138,7 +138,7 @@ public class SubtypeSwitcher { mEnabledLanguagesOfCurrentInputMethod.clear(); mEnabledKeyboardSubtypesOfCurrentInputMethod.clear(); for (InputMethodSubtypeCompatWrapper ims : mAllEnabledSubtypesOfCurrentInputMethod) { - final String locale = ims.getLocale(); + final String locale = getSubtypeLocale(ims); final String mode = ims.getMode(); mLocaleSplitter.setString(locale); if (mLocaleSplitter.hasNext()) { @@ -167,7 +167,7 @@ public class SubtypeSwitcher { Log.d(TAG, "Update shortcut IME from : " + (mShortcutInputMethodInfo == null ? "<null>" : mShortcutInputMethodInfo.getId()) + ", " - + (mShortcutSubtype == null ? "<null>" : (mShortcutSubtype.getLocale() + + (mShortcutSubtype == null ? "<null>" : (getSubtypeLocale(mShortcutSubtype) + ", " + mShortcutSubtype.getMode()))); } // TODO: Update an icon for shortcut IME @@ -189,11 +189,17 @@ public class SubtypeSwitcher { Log.d(TAG, "Update shortcut IME to : " + (mShortcutInputMethodInfo == null ? "<null>" : mShortcutInputMethodInfo.getId()) + ", " - + (mShortcutSubtype == null ? "<null>" : (mShortcutSubtype.getLocale() + + (mShortcutSubtype == null ? "<null>" : (getSubtypeLocale(mShortcutSubtype) + ", " + mShortcutSubtype.getMode()))); } } + private static String getSubtypeLocale(InputMethodSubtypeCompatWrapper subtype) { + final String keyboardLocale = subtype.getExtraValueOf( + LatinIME.SUBTYPE_EXTRA_VALUE_KEYBOARD_LOCALE); + return keyboardLocale != null ? keyboardLocale : subtype.getLocale(); + } + // Update the current subtype. LatinIME.onCurrentInputMethodSubtypeChanged calls this function. public void updateSubtype(InputMethodSubtypeCompatWrapper newSubtype) { final String newLocale; @@ -206,7 +212,7 @@ public class SubtypeSwitcher { newLocale = "en_US"; newMode = KEYBOARD_MODE; } else { - newLocale = newSubtype.getLocale(); + newLocale = getSubtypeLocale(newSubtype); newMode = newSubtype.getMode(); } if (DBG) { @@ -332,7 +338,7 @@ public class SubtypeSwitcher { final String imiPackageName = imi.getPackageName(); if (DBG) { Log.d(TAG, "Update icons of IME: " + imiPackageName + "," - + subtype.getLocale() + "," + subtype.getMode()); + + getSubtypeLocale(subtype) + "," + subtype.getMode()); } if (subtype != null) { return pm.getDrawable(imiPackageName, subtype.getIconResId(), |