diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java new file mode 100644 index 000000000..03f38f366 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java @@ -0,0 +1,554 @@ +/* + * Copyright (C) 2013 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.suggestions; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.Typeface; +import android.graphics.Paint.Align; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.Spanned; +import android.text.TextPaint; +import android.text.TextUtils; +import android.text.style.CharacterStyle; +import android.text.style.StyleSpan; +import android.text.style.UnderlineSpan; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.View.OnClickListener; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.android.inputmethod.keyboard.ViewLayoutUtils; +import com.android.inputmethod.latin.AutoCorrection; +import com.android.inputmethod.latin.CollectionUtils; +import com.android.inputmethod.latin.LatinImeLogger; +import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.ResourceUtils; +import com.android.inputmethod.latin.SuggestedWords; +import com.android.inputmethod.latin.Utils; + +import java.util.ArrayList; + +final class SuggestionStripLayoutHelper { + private static final int DEFAULT_SUGGESTIONS_COUNT_IN_STRIP = 3; + private static final float DEFAULT_CENTER_SUGGESTION_PERCENTILE = 0.40f; + private static final int DEFAULT_MAX_MORE_SUGGESTIONS_ROW = 2; + private static final int PUNCTUATIONS_IN_STRIP = 5; + private static final float MIN_TEXT_XSCALE = 0.70f; + + public final int mPadding; + public final int mDividerWidth; + public final int mSuggestionsStripHeight; + public final int mSuggestionsCountInStrip; + public final int mMoreSuggestionsRowHeight; + private int mMaxMoreSuggestionsRow; + public final float mMinMoreSuggestionsWidth; + public final int mMoreSuggestionsBottomGap; + + private final ArrayList mWords; + private final ArrayList mDividers; + private final ArrayList mInfos; + + private final int mColorValidTypedWord; + private final int mColorTypedWord; + private final int mColorAutoCorrect; + private final int mColorSuggested; + private final float mAlphaObsoleted; + private final float mCenterSuggestionWeight; + private final int mCenterSuggestionIndex; + private final Drawable mMoreSuggestionsHint; + private static final String MORE_SUGGESTIONS_HINT = "\u2026"; + private static final String LEFTWARDS_ARROW = "\u2190"; + + private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD); + private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan(); + private static final int AUTO_CORRECT_BOLD = 0x01; + private static final int AUTO_CORRECT_UNDERLINE = 0x02; + private static final int VALID_TYPED_WORD_BOLD = 0x04; + + private final int mSuggestionStripOption; + + private final ArrayList mTexts = CollectionUtils.newArrayList(); + + public boolean mMoreSuggestionsAvailable; + + private final TextView mWordToSaveView; + private final TextView mLeftwardsArrowView; + private final TextView mHintToSaveView; + + public SuggestionStripLayoutHelper(final Context context, final AttributeSet attrs, + final int defStyle, final ArrayList words, final ArrayList dividers, + final ArrayList infos) { + mWords = words; + mDividers = dividers; + mInfos = infos; + + final TextView word = words.get(0); + final View divider = dividers.get(0); + mPadding = word.getCompoundPaddingLeft() + word.getCompoundPaddingRight(); + divider.measure(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + mDividerWidth = divider.getMeasuredWidth(); + + final Resources res = word.getResources(); + mSuggestionsStripHeight = res.getDimensionPixelSize(R.dimen.suggestions_strip_height); + + final TypedArray a = context.obtainStyledAttributes(attrs, + R.styleable.SuggestionStripView, defStyle, R.style.SuggestionStripViewStyle); + mSuggestionStripOption = a.getInt( + R.styleable.SuggestionStripView_suggestionStripOption, 0); + final float alphaValidTypedWord = ResourceUtils.getFraction(a, + R.styleable.SuggestionStripView_alphaValidTypedWord, 1.0f); + final float alphaTypedWord = ResourceUtils.getFraction(a, + R.styleable.SuggestionStripView_alphaTypedWord, 1.0f); + final float alphaAutoCorrect = ResourceUtils.getFraction(a, + R.styleable.SuggestionStripView_alphaAutoCorrect, 1.0f); + final float alphaSuggested = ResourceUtils.getFraction(a, + R.styleable.SuggestionStripView_alphaSuggested, 1.0f); + mAlphaObsoleted = ResourceUtils.getFraction(a, + R.styleable.SuggestionStripView_alphaSuggested, 1.0f); + mColorValidTypedWord = applyAlpha(a.getColor( + R.styleable.SuggestionStripView_colorValidTypedWord, 0), alphaValidTypedWord); + mColorTypedWord = applyAlpha(a.getColor( + R.styleable.SuggestionStripView_colorTypedWord, 0), alphaTypedWord); + mColorAutoCorrect = applyAlpha(a.getColor( + R.styleable.SuggestionStripView_colorAutoCorrect, 0), alphaAutoCorrect); + mColorSuggested = applyAlpha(a.getColor( + R.styleable.SuggestionStripView_colorSuggested, 0), alphaSuggested); + mSuggestionsCountInStrip = a.getInt( + R.styleable.SuggestionStripView_suggestionsCountInStrip, + DEFAULT_SUGGESTIONS_COUNT_IN_STRIP); + mCenterSuggestionWeight = ResourceUtils.getFraction(a, + R.styleable.SuggestionStripView_centerSuggestionPercentile, + DEFAULT_CENTER_SUGGESTION_PERCENTILE); + mMaxMoreSuggestionsRow = a.getInt( + R.styleable.SuggestionStripView_maxMoreSuggestionsRow, + DEFAULT_MAX_MORE_SUGGESTIONS_ROW); + mMinMoreSuggestionsWidth = ResourceUtils.getFraction(a, + R.styleable.SuggestionStripView_minMoreSuggestionsWidth, 1.0f); + a.recycle(); + + mMoreSuggestionsHint = getMoreSuggestionsHint(res, + res.getDimension(R.dimen.more_suggestions_hint_text_size), mColorAutoCorrect); + mCenterSuggestionIndex = mSuggestionsCountInStrip / 2; + mMoreSuggestionsBottomGap = res.getDimensionPixelOffset( + R.dimen.more_suggestions_bottom_gap); + mMoreSuggestionsRowHeight = res.getDimensionPixelSize(R.dimen.more_suggestions_row_height); + + final LayoutInflater inflater = LayoutInflater.from(context); + mWordToSaveView = (TextView)inflater.inflate(R.layout.suggestion_word, null); + mLeftwardsArrowView = (TextView)inflater.inflate(R.layout.hint_add_to_dictionary, null); + mHintToSaveView = (TextView)inflater.inflate(R.layout.hint_add_to_dictionary, null); + } + + public int getMaxMoreSuggestionsRow() { + return mMaxMoreSuggestionsRow; + } + + private int getMoreSuggestionsHeight() { + return mMaxMoreSuggestionsRow * mMoreSuggestionsRowHeight + mMoreSuggestionsBottomGap; + } + + public int setMoreSuggestionsHeight(final int remainingHeight) { + final int currentHeight = getMoreSuggestionsHeight(); + if (currentHeight <= remainingHeight) { + return currentHeight; + } + + mMaxMoreSuggestionsRow = (remainingHeight - mMoreSuggestionsBottomGap) + / mMoreSuggestionsRowHeight; + final int newHeight = getMoreSuggestionsHeight(); + return newHeight; + } + + private static Drawable getMoreSuggestionsHint(final Resources res, final float textSize, + final int color) { + final Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setTextAlign(Align.CENTER); + paint.setTextSize(textSize); + paint.setColor(color); + final Rect bounds = new Rect(); + paint.getTextBounds(MORE_SUGGESTIONS_HINT, 0, MORE_SUGGESTIONS_HINT.length(), bounds); + final int width = Math.round(bounds.width() + 0.5f); + final int height = Math.round(bounds.height() + 0.5f); + final Bitmap buffer = Bitmap.createBitmap(width, (height * 3 / 2), Bitmap.Config.ARGB_8888); + final Canvas canvas = new Canvas(buffer); + canvas.drawText(MORE_SUGGESTIONS_HINT, width / 2, height, paint); + return new BitmapDrawable(res, buffer); + } + + private CharSequence getStyledSuggestionWord(final SuggestedWords suggestedWords, + final int indexInSuggestedWords) { + final String word = suggestedWords.getWord(indexInSuggestedWords); + final boolean isAutoCorrect = indexInSuggestedWords == 1 + && suggestedWords.willAutoCorrect(); + final boolean isTypedWordValid = indexInSuggestedWords == 0 + && suggestedWords.mTypedWordValid; + if (!isAutoCorrect && !isTypedWordValid) + return word; + + final int len = word.length(); + final Spannable spannedWord = new SpannableString(word); + final int option = mSuggestionStripOption; + if ((isAutoCorrect && (option & AUTO_CORRECT_BOLD) != 0) + || (isTypedWordValid && (option & VALID_TYPED_WORD_BOLD) != 0)) { + spannedWord.setSpan(BOLD_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + } + if (isAutoCorrect && (option & AUTO_CORRECT_UNDERLINE) != 0) { + spannedWord.setSpan(UNDERLINE_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + } + return spannedWord; + } + + private int getIndexInSuggestedWords(final int indexInStrip, + final SuggestedWords suggestedWords) { + // TODO: This works for 3 suggestions. Revisit this algorithm when there are 5 or more + // suggestions. + final int mostImportantIndexInSuggestedWords = suggestedWords.willAutoCorrect() ? 1 : 0; + if (indexInStrip == mCenterSuggestionIndex) { + return mostImportantIndexInSuggestedWords; + } else if (indexInStrip == mostImportantIndexInSuggestedWords) { + return mCenterSuggestionIndex; + } else { + return indexInStrip; + } + } + + private int getSuggestionTextColor(final int indexInStrip, + final SuggestedWords suggestedWords) { + final int indexInSuggestedWords = getIndexInSuggestedWords( + indexInStrip, suggestedWords); + // TODO: Need to revisit this logic with bigram suggestions + final boolean isSuggested = (indexInSuggestedWords != 0); + + final int color; + if (indexInStrip == mCenterSuggestionIndex && suggestedWords.willAutoCorrect()) { + color = mColorAutoCorrect; + } else if (indexInStrip == mCenterSuggestionIndex && suggestedWords.mTypedWordValid) { + color = mColorValidTypedWord; + } else if (isSuggested) { + color = mColorSuggested; + } else { + color = mColorTypedWord; + } + if (LatinImeLogger.sDBG && suggestedWords.size() > 1) { + // If we auto-correct, then the autocorrection is in slot 0 and the typed word + // is in slot 1. + if (indexInStrip == mCenterSuggestionIndex + && AutoCorrection.shouldBlockAutoCorrectionBySafetyNet( + suggestedWords.getWord(1), suggestedWords.getWord(0))) { + return 0xFFFF0000; + } + } + + if (suggestedWords.mIsObsoleteSuggestions && isSuggested) { + return applyAlpha(color, mAlphaObsoleted); + } else { + return color; + } + } + + private static int applyAlpha(final int color, final float alpha) { + final int newAlpha = (int)(Color.alpha(color) * alpha); + return Color.argb(newAlpha, Color.red(color), Color.green(color), Color.blue(color)); + } + + private static void addDivider(final ViewGroup stripView, final View divider) { + stripView.addView(divider); + final LinearLayout.LayoutParams params = + (LinearLayout.LayoutParams)divider.getLayoutParams(); + params.gravity = Gravity.CENTER; + } + + public void layout(final SuggestedWords suggestedWords, final ViewGroup stripView, + final ViewGroup placer, final int stripWidth) { + if (suggestedWords.mIsPunctuationSuggestions) { + layoutPunctuationSuggestions(suggestedWords, stripView); + return; + } + + final int countInStrip = mSuggestionsCountInStrip; + setupTexts(suggestedWords, countInStrip); + mMoreSuggestionsAvailable = (suggestedWords.size() > countInStrip); + int x = 0; + for (int indexInStrip = 0; indexInStrip < countInStrip; indexInStrip++) { + if (indexInStrip != 0) { + final View divider = mDividers.get(indexInStrip); + // Add divider if this isn't the left most suggestion in suggestions strip. + addDivider(stripView, divider); + x += divider.getMeasuredWidth(); + } + + final int width = getSuggestionWidth(indexInStrip, stripWidth); + final TextView word = layoutWord(suggestedWords, indexInStrip, width); + stripView.addView(word); + setLayoutWeight(word, getSuggestionWeight(indexInStrip), + ViewGroup.LayoutParams.MATCH_PARENT); + x += word.getMeasuredWidth(); + + if (SuggestionStripView.DBG) { + layoutDebugInfo(suggestedWords, indexInStrip, placer, x); + } + } + } + + /** + * Format appropriately the suggested word indirectly specified by + * indexInStrip as text in a corresponding {@link TextView}. When the + * suggested word doesn't exist, the corresponding {@link TextView} will be disabled + * and never respond to user interaction. The suggested word may be shrunk or ellipsized to + * fit in the specified width. + * + * The indexInStrip argument is the index in the suggestion strip. The indices + * increase towards the right for LTR scripts and the left for RTL scripts, starting with 0. + * The index of the most important suggestion is in {@link #mCenterSuggestionIndex}. This + * usually doesn't match the index in suggedtedWords -- see + * {@link #getIndexInSuggestedWords(int,SuggestedWords)}. + * + * @param suggestedWords the list of suggestions. + * @param indexInStrip the in the suggestion strip. + * @param width the maximum width for layout in pixels. + * @return the {@link TextView} containing the suggested word appropriately formatted. + */ + private TextView layoutWord(final SuggestedWords suggestedWords, final int indexInStrip, + final int width) { + final int indexInSuggestedWords = getIndexInSuggestedWords(indexInStrip, suggestedWords); + final CharSequence styled = mTexts.get(indexInSuggestedWords); + final TextView word = mWords.get(indexInSuggestedWords); + if (indexInStrip == mCenterSuggestionIndex && mMoreSuggestionsAvailable) { + // TODO: This "more suggestions hint" should have a nicely designed icon. + word.setCompoundDrawablesWithIntrinsicBounds( + null, null, null, mMoreSuggestionsHint); + // HACK: Align with other TextViews that have no compound drawables. + word.setCompoundDrawablePadding(-mMoreSuggestionsHint.getIntrinsicHeight()); + } else { + word.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null); + } + + // Disable this suggestion if the suggestion is null or empty. + word.setEnabled(!TextUtils.isEmpty(styled)); + word.setTextColor(getSuggestionTextColor(indexInStrip, suggestedWords)); + final CharSequence text = getEllipsizedText(styled, width, word.getPaint()); + final float scaleX = word.getTextScaleX(); + word.setText(text); // TextView.setText() resets text scale x to 1.0. + word.setTextScaleX(scaleX); + return word; + } + + private void layoutDebugInfo(final SuggestedWords suggestedWords, final int indexInStrip, + final ViewGroup placer, final int x) { + final int indexInSuggestedWords = getIndexInSuggestedWords(indexInStrip, suggestedWords); + if (indexInSuggestedWords >= suggestedWords.size()) { + return; + } + final String debugInfo = Utils.getDebugInfo(suggestedWords, indexInSuggestedWords); + if (debugInfo == null) { + return; + } + final TextView info = mInfos.get(indexInSuggestedWords); + info.setText(debugInfo); + placer.addView(info); + info.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + final int infoWidth = info.getMeasuredWidth(); + final int y = info.getMeasuredHeight(); + ViewLayoutUtils.placeViewAt( + info, x - infoWidth, y, infoWidth, info.getMeasuredHeight()); + } + + private int getSuggestionWidth(final int indexInStrip, final int maxWidth) { + final int paddings = mPadding * mSuggestionsCountInStrip; + final int dividers = mDividerWidth * (mSuggestionsCountInStrip - 1); + final int availableWidth = maxWidth - paddings - dividers; + return (int)(availableWidth * getSuggestionWeight(indexInStrip)); + } + + private float getSuggestionWeight(final int indexInStrip) { + if (indexInStrip == mCenterSuggestionIndex) { + return mCenterSuggestionWeight; + } else { + // TODO: Revisit this for cases of 5 or more suggestions + return (1.0f - mCenterSuggestionWeight) / (mSuggestionsCountInStrip - 1); + } + } + + private void setupTexts(final SuggestedWords suggestedWords, final int countInStrip) { + mTexts.clear(); + final int count = Math.min(suggestedWords.size(), countInStrip); + for (int pos = 0; pos < count; pos++) { + final CharSequence styled = getStyledSuggestionWord(suggestedWords, pos); + mTexts.add(styled); + } + for (int pos = count; pos < countInStrip; pos++) { + // Make this inactive for touches in layout(). + mTexts.add(null); + } + } + + private void layoutPunctuationSuggestions(final SuggestedWords suggestedWords, + final ViewGroup stripView) { + final int countInStrip = Math.min(suggestedWords.size(), PUNCTUATIONS_IN_STRIP); + for (int indexInStrip = 0; indexInStrip < countInStrip; indexInStrip++) { + if (indexInStrip != 0) { + // Add divider if this isn't the left most suggestion in suggestions strip. + addDivider(stripView, mDividers.get(indexInStrip)); + } + + final TextView word = mWords.get(indexInStrip); + word.setEnabled(true); + word.setTextColor(mColorAutoCorrect); + final String text = suggestedWords.getWord(indexInStrip); + word.setText(text); + word.setTextScaleX(1.0f); + word.setCompoundDrawables(null, null, null, null); + stripView.addView(word); + setLayoutWeight(word, 1.0f, mSuggestionsStripHeight); + } + mMoreSuggestionsAvailable = false; + } + + public void layoutAddToDictionaryHint(final String word, final ViewGroup stripView, + final int stripWidth, final CharSequence hintText, final OnClickListener listener) { + final int width = stripWidth - mDividerWidth - mPadding * 2; + + final TextView wordView = mWordToSaveView; + wordView.setTextColor(mColorTypedWord); + final int wordWidth = (int)(width * mCenterSuggestionWeight); + final CharSequence text = getEllipsizedText(word, wordWidth, wordView.getPaint()); + final float wordScaleX = wordView.getTextScaleX(); + wordView.setTag(word); + wordView.setText(text); + wordView.setTextScaleX(wordScaleX); + stripView.addView(wordView); + setLayoutWeight(wordView, mCenterSuggestionWeight, ViewGroup.LayoutParams.MATCH_PARENT); + + stripView.addView(mDividers.get(0)); + + final TextView leftArrowView = mLeftwardsArrowView; + leftArrowView.setTextColor(mColorAutoCorrect); + leftArrowView.setText(LEFTWARDS_ARROW); + stripView.addView(leftArrowView); + + final TextView hintView = mHintToSaveView; + hintView.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL); + hintView.setTextColor(mColorAutoCorrect); + final int hintWidth = width - wordWidth - leftArrowView.getWidth(); + final float hintScaleX = getTextScaleX(hintText, hintWidth, hintView.getPaint()); + hintView.setText(hintText); + hintView.setTextScaleX(hintScaleX); + stripView.addView(hintView); + setLayoutWeight( + hintView, 1.0f - mCenterSuggestionWeight, ViewGroup.LayoutParams.MATCH_PARENT); + + wordView.setOnClickListener(listener); + leftArrowView.setOnClickListener(listener); + hintView.setOnClickListener(listener); + } + + public CharSequence getAddToDictionaryWord() { + return (CharSequence)mWordToSaveView.getTag(); + } + + public boolean isAddToDictionaryShowing(final View v) { + return v == mWordToSaveView || v == mHintToSaveView || v == mLeftwardsArrowView; + } + + private static void setLayoutWeight(final View v, final float weight, final int height) { + final ViewGroup.LayoutParams lp = v.getLayoutParams(); + if (lp instanceof LinearLayout.LayoutParams) { + final LinearLayout.LayoutParams llp = (LinearLayout.LayoutParams)lp; + llp.weight = weight; + llp.width = 0; + llp.height = height; + } + } + + private static float getTextScaleX(final CharSequence text, final int maxWidth, + final TextPaint paint) { + paint.setTextScaleX(1.0f); + final int width = getTextWidth(text, paint); + if (width <= maxWidth) { + return 1.0f; + } + return maxWidth / (float)width; + } + + private static CharSequence getEllipsizedText(final CharSequence text, final int maxWidth, + final TextPaint paint) { + if (text == null) return null; + paint.setTextScaleX(1.0f); + final int width = getTextWidth(text, paint); + if (width <= maxWidth) { + return text; + } + final float scaleX = maxWidth / (float)width; + if (scaleX >= MIN_TEXT_XSCALE) { + paint.setTextScaleX(scaleX); + return text; + } + + // Note that TextUtils.ellipsize() use text-x-scale as 1.0 if ellipsize is needed. To + // get squeezed and ellipsized text, passes enlarged width (maxWidth / MIN_TEXT_XSCALE). + final CharSequence ellipsized = TextUtils.ellipsize( + text, paint, maxWidth / MIN_TEXT_XSCALE, TextUtils.TruncateAt.MIDDLE); + paint.setTextScaleX(MIN_TEXT_XSCALE); + return ellipsized; + } + + private static int getTextWidth(final CharSequence text, final TextPaint paint) { + if (TextUtils.isEmpty(text)) return 0; + final Typeface savedTypeface = paint.getTypeface(); + paint.setTypeface(getTextTypeface(text)); + final int len = text.length(); + final float[] widths = new float[len]; + final int count = paint.getTextWidths(text, 0, len, widths); + int width = 0; + for (int i = 0; i < count; i++) { + width += Math.round(widths[i] + 0.5f); + } + paint.setTypeface(savedTypeface); + return width; + } + + private static Typeface getTextTypeface(final CharSequence text) { + if (!(text instanceof SpannableString)) + return Typeface.DEFAULT; + + final SpannableString ss = (SpannableString)text; + final StyleSpan[] styles = ss.getSpans(0, text.length(), StyleSpan.class); + if (styles.length == 0) + return Typeface.DEFAULT; + + switch (styles[0].getStyle()) { + case Typeface.BOLD: return Typeface.DEFAULT_BOLD; + // TODO: BOLD_ITALIC, ITALIC case? + default: return Typeface.DEFAULT; + } + } +} diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java index 9764610b3..59a05de47 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java @@ -18,34 +18,14 @@ package com.android.inputmethod.latin.suggestions; import android.content.Context; import android.content.res.Resources; -import android.content.res.TypedArray; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Paint.Align; -import android.graphics.Rect; -import android.graphics.Typeface; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.text.Spannable; -import android.text.SpannableString; -import android.text.Spanned; -import android.text.TextPaint; -import android.text.TextUtils; -import android.text.style.CharacterStyle; -import android.text.style.StyleSpan; -import android.text.style.UnderlineSpan; import android.util.AttributeSet; import android.view.GestureDetector; -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; import android.view.ViewGroup; -import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; @@ -53,17 +33,13 @@ import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.MainKeyboardView; import com.android.inputmethod.keyboard.MoreKeysPanel; -import com.android.inputmethod.keyboard.ViewLayoutUtils; import com.android.inputmethod.latin.AudioAndHapticFeedbackManager; -import com.android.inputmethod.latin.AutoCorrection; import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.ResourceUtils; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; -import com.android.inputmethod.latin.Utils; import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.suggestions.MoreSuggestions.MoreSuggestionsListener; import com.android.inputmethod.research.ResearchLogger; @@ -96,509 +72,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick Listener mListener; private SuggestedWords mSuggestedWords = SuggestedWords.EMPTY; - private final SuggestionStripViewParams mParams; - private static final float MIN_TEXT_XSCALE = 0.70f; - - private static final class SuggestionStripViewParams { - private static final int DEFAULT_SUGGESTIONS_COUNT_IN_STRIP = 3; - private static final float DEFAULT_CENTER_SUGGESTION_PERCENTILE = 0.40f; - private static final int DEFAULT_MAX_MORE_SUGGESTIONS_ROW = 2; - private static final int PUNCTUATIONS_IN_STRIP = 5; - - public final int mPadding; - public final int mDividerWidth; - public final int mSuggestionsStripHeight; - public final int mSuggestionsCountInStrip; - public final int mMoreSuggestionsRowHeight; - private int mMaxMoreSuggestionsRow; - public final float mMinMoreSuggestionsWidth; - public final int mMoreSuggestionsBottomGap; - - private final ArrayList mWords; - private final ArrayList mDividers; - private final ArrayList mInfos; - - private final int mColorValidTypedWord; - private final int mColorTypedWord; - private final int mColorAutoCorrect; - private final int mColorSuggested; - private final float mAlphaObsoleted; - private final float mCenterSuggestionWeight; - private final int mCenterSuggestionIndex; - private final Drawable mMoreSuggestionsHint; - private static final String MORE_SUGGESTIONS_HINT = "\u2026"; - private static final String LEFTWARDS_ARROW = "\u2190"; - - private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD); - private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan(); - private static final int AUTO_CORRECT_BOLD = 0x01; - private static final int AUTO_CORRECT_UNDERLINE = 0x02; - private static final int VALID_TYPED_WORD_BOLD = 0x04; - - private final int mSuggestionStripOption; - - private final ArrayList mTexts = CollectionUtils.newArrayList(); - - public boolean mMoreSuggestionsAvailable; - - private final TextView mWordToSaveView; - private final TextView mLeftwardsArrowView; - private final TextView mHintToSaveView; - - public SuggestionStripViewParams(final Context context, final AttributeSet attrs, - final int defStyle, final ArrayList words, final ArrayList dividers, - final ArrayList infos) { - mWords = words; - mDividers = dividers; - mInfos = infos; - - final TextView word = words.get(0); - final View divider = dividers.get(0); - mPadding = word.getCompoundPaddingLeft() + word.getCompoundPaddingRight(); - divider.measure( - ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); - mDividerWidth = divider.getMeasuredWidth(); - - final Resources res = word.getResources(); - mSuggestionsStripHeight = res.getDimensionPixelSize(R.dimen.suggestions_strip_height); - - final TypedArray a = context.obtainStyledAttributes(attrs, - R.styleable.SuggestionStripView, defStyle, R.style.SuggestionStripViewStyle); - mSuggestionStripOption = a.getInt( - R.styleable.SuggestionStripView_suggestionStripOption, 0); - final float alphaValidTypedWord = ResourceUtils.getFraction(a, - R.styleable.SuggestionStripView_alphaValidTypedWord, 1.0f); - final float alphaTypedWord = ResourceUtils.getFraction(a, - R.styleable.SuggestionStripView_alphaTypedWord, 1.0f); - final float alphaAutoCorrect = ResourceUtils.getFraction(a, - R.styleable.SuggestionStripView_alphaAutoCorrect, 1.0f); - final float alphaSuggested = ResourceUtils.getFraction(a, - R.styleable.SuggestionStripView_alphaSuggested, 1.0f); - mAlphaObsoleted = ResourceUtils.getFraction(a, - R.styleable.SuggestionStripView_alphaSuggested, 1.0f); - mColorValidTypedWord = applyAlpha(a.getColor( - R.styleable.SuggestionStripView_colorValidTypedWord, 0), alphaValidTypedWord); - mColorTypedWord = applyAlpha(a.getColor( - R.styleable.SuggestionStripView_colorTypedWord, 0), alphaTypedWord); - mColorAutoCorrect = applyAlpha(a.getColor( - R.styleable.SuggestionStripView_colorAutoCorrect, 0), alphaAutoCorrect); - mColorSuggested = applyAlpha(a.getColor( - R.styleable.SuggestionStripView_colorSuggested, 0), alphaSuggested); - mSuggestionsCountInStrip = a.getInt( - R.styleable.SuggestionStripView_suggestionsCountInStrip, - DEFAULT_SUGGESTIONS_COUNT_IN_STRIP); - mCenterSuggestionWeight = ResourceUtils.getFraction(a, - R.styleable.SuggestionStripView_centerSuggestionPercentile, - DEFAULT_CENTER_SUGGESTION_PERCENTILE); - mMaxMoreSuggestionsRow = a.getInt( - R.styleable.SuggestionStripView_maxMoreSuggestionsRow, - DEFAULT_MAX_MORE_SUGGESTIONS_ROW); - mMinMoreSuggestionsWidth = ResourceUtils.getFraction(a, - R.styleable.SuggestionStripView_minMoreSuggestionsWidth, 1.0f); - a.recycle(); - - mMoreSuggestionsHint = getMoreSuggestionsHint(res, - res.getDimension(R.dimen.more_suggestions_hint_text_size), mColorAutoCorrect); - mCenterSuggestionIndex = mSuggestionsCountInStrip / 2; - mMoreSuggestionsBottomGap = res.getDimensionPixelOffset( - R.dimen.more_suggestions_bottom_gap); - mMoreSuggestionsRowHeight = res.getDimensionPixelSize( - R.dimen.more_suggestions_row_height); - - final LayoutInflater inflater = LayoutInflater.from(context); - mWordToSaveView = (TextView)inflater.inflate(R.layout.suggestion_word, null); - mLeftwardsArrowView = (TextView)inflater.inflate(R.layout.hint_add_to_dictionary, null); - mHintToSaveView = (TextView)inflater.inflate(R.layout.hint_add_to_dictionary, null); - } - - public int getMaxMoreSuggestionsRow() { - return mMaxMoreSuggestionsRow; - } - - private int getMoreSuggestionsHeight() { - return mMaxMoreSuggestionsRow * mMoreSuggestionsRowHeight + mMoreSuggestionsBottomGap; - } - - public int setMoreSuggestionsHeight(final int remainingHeight) { - final int currentHeight = getMoreSuggestionsHeight(); - if (currentHeight <= remainingHeight) { - return currentHeight; - } - - mMaxMoreSuggestionsRow = (remainingHeight - mMoreSuggestionsBottomGap) - / mMoreSuggestionsRowHeight; - final int newHeight = getMoreSuggestionsHeight(); - return newHeight; - } - - private static Drawable getMoreSuggestionsHint(final Resources res, final float textSize, - final int color) { - final Paint paint = new Paint(); - paint.setAntiAlias(true); - paint.setTextAlign(Align.CENTER); - paint.setTextSize(textSize); - paint.setColor(color); - final Rect bounds = new Rect(); - paint.getTextBounds(MORE_SUGGESTIONS_HINT, 0, MORE_SUGGESTIONS_HINT.length(), bounds); - final int width = Math.round(bounds.width() + 0.5f); - final int height = Math.round(bounds.height() + 0.5f); - final Bitmap buffer = Bitmap.createBitmap( - width, (height * 3 / 2), Bitmap.Config.ARGB_8888); - final Canvas canvas = new Canvas(buffer); - canvas.drawText(MORE_SUGGESTIONS_HINT, width / 2, height, paint); - return new BitmapDrawable(res, buffer); - } - - private CharSequence getStyledSuggestionWord(final SuggestedWords suggestedWords, - final int indexInSuggestedWords) { - final String word = suggestedWords.getWord(indexInSuggestedWords); - final boolean isAutoCorrect = indexInSuggestedWords == 1 - && suggestedWords.willAutoCorrect(); - final boolean isTypedWordValid = indexInSuggestedWords == 0 - && suggestedWords.mTypedWordValid; - if (!isAutoCorrect && !isTypedWordValid) - return word; - - final int len = word.length(); - final Spannable spannedWord = new SpannableString(word); - final int option = mSuggestionStripOption; - if ((isAutoCorrect && (option & AUTO_CORRECT_BOLD) != 0) - || (isTypedWordValid && (option & VALID_TYPED_WORD_BOLD) != 0)) { - spannedWord.setSpan(BOLD_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - } - if (isAutoCorrect && (option & AUTO_CORRECT_UNDERLINE) != 0) { - spannedWord.setSpan(UNDERLINE_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - } - return spannedWord; - } - - private int getIndexInSuggestedWords(final int indexInStrip, - final SuggestedWords suggestedWords) { - // TODO: This works for 3 suggestions. Revisit this algorithm when there are 5 or more - // suggestions. - final int mostImportantIndexInSuggestedWords = suggestedWords.willAutoCorrect() ? 1 : 0; - if (indexInStrip == mCenterSuggestionIndex) { - return mostImportantIndexInSuggestedWords; - } else if (indexInStrip == mostImportantIndexInSuggestedWords) { - return mCenterSuggestionIndex; - } else { - return indexInStrip; - } - } - - private int getSuggestionTextColor(final int indexInStrip, - final SuggestedWords suggestedWords) { - final int indexInSuggestedWords = getIndexInSuggestedWords( - indexInStrip, suggestedWords); - // TODO: Need to revisit this logic with bigram suggestions - final boolean isSuggested = (indexInSuggestedWords != 0); - - final int color; - if (indexInStrip == mCenterSuggestionIndex && suggestedWords.willAutoCorrect()) { - color = mColorAutoCorrect; - } else if (indexInStrip == mCenterSuggestionIndex && suggestedWords.mTypedWordValid) { - color = mColorValidTypedWord; - } else if (isSuggested) { - color = mColorSuggested; - } else { - color = mColorTypedWord; - } - if (LatinImeLogger.sDBG && suggestedWords.size() > 1) { - // If we auto-correct, then the autocorrection is in slot 0 and the typed word - // is in slot 1. - if (indexInStrip == mCenterSuggestionIndex - && AutoCorrection.shouldBlockAutoCorrectionBySafetyNet( - suggestedWords.getWord(1), suggestedWords.getWord(0))) { - return 0xFFFF0000; - } - } - - if (suggestedWords.mIsObsoleteSuggestions && isSuggested) { - return applyAlpha(color, mAlphaObsoleted); - } else { - return color; - } - } - - private static int applyAlpha(final int color, final float alpha) { - final int newAlpha = (int)(Color.alpha(color) * alpha); - return Color.argb(newAlpha, Color.red(color), Color.green(color), Color.blue(color)); - } - - private static void addDivider(final ViewGroup stripView, final View divider) { - stripView.addView(divider); - final LinearLayout.LayoutParams params = - (LinearLayout.LayoutParams)divider.getLayoutParams(); - params.gravity = Gravity.CENTER; - } - - public void layout(final SuggestedWords suggestedWords, final ViewGroup stripView, - final ViewGroup placer, final int stripWidth) { - if (suggestedWords.mIsPunctuationSuggestions) { - layoutPunctuationSuggestions(suggestedWords, stripView); - return; - } - - final int countInStrip = mSuggestionsCountInStrip; - setupTexts(suggestedWords, countInStrip); - mMoreSuggestionsAvailable = (suggestedWords.size() > countInStrip); - int x = 0; - for (int indexInStrip = 0; indexInStrip < countInStrip; indexInStrip++) { - if (indexInStrip != 0) { - final View divider = mDividers.get(indexInStrip); - // Add divider if this isn't the left most suggestion in suggestions strip. - addDivider(stripView, divider); - x += divider.getMeasuredWidth(); - } - - final int width = getSuggestionWidth(indexInStrip, stripWidth); - final TextView word = layoutWord(suggestedWords, indexInStrip, width); - stripView.addView(word); - setLayoutWeight(word, getSuggestionWeight(indexInStrip), - ViewGroup.LayoutParams.MATCH_PARENT); - x += word.getMeasuredWidth(); - - if (DBG) { - layoutDebugInfo(suggestedWords, indexInStrip, placer, x); - } - } - } - - /** - * Format appropriately the suggested word indirectly specified by - * indexInStrip as text in a corresponding {@link TextView}. When the - * suggested word doesn't exist, the corresponding {@link TextView} will be disabled - * and never respond to user interaction. The suggested word may be shrunk or ellipsized to - * fit in the specified width. - * - * The indexInStrip argument is the index in the suggestion strip. The indices - * increase towards the right for LTR scripts and the left for RTL scripts, starting with 0. - * The index of the most important suggestion is in {@link #mCenterSuggestionIndex}. This - * usually doesn't match the index in suggedtedWords -- see - * {@link #getIndexInSuggestedWords(int,SuggestedWords)}. - * - * @param suggestedWords the list of suggestions. - * @param indexInStrip the in the suggestion strip. - * @param width the maximum width for layout in pixels. - * @return the {@link TextView} containing the suggested word appropriately formatted. - */ - private TextView layoutWord(final SuggestedWords suggestedWords, final int indexInStrip, - final int width) { - final int indexInSuggestedWords = getIndexInSuggestedWords( - indexInStrip, suggestedWords); - final CharSequence styled = mTexts.get(indexInSuggestedWords); - final TextView word = mWords.get(indexInSuggestedWords); - if (indexInStrip == mCenterSuggestionIndex && mMoreSuggestionsAvailable) { - // TODO: This "more suggestions hint" should have a nicely designed icon. - word.setCompoundDrawablesWithIntrinsicBounds( - null, null, null, mMoreSuggestionsHint); - // HACK: Align with other TextViews that have no compound drawables. - word.setCompoundDrawablePadding(-mMoreSuggestionsHint.getIntrinsicHeight()); - } else { - word.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null); - } - - // Disable this suggestion if the suggestion is null or empty. - word.setEnabled(!TextUtils.isEmpty(styled)); - word.setTextColor(getSuggestionTextColor(indexInStrip, suggestedWords)); - final CharSequence text = getEllipsizedText(styled, width, word.getPaint()); - final float scaleX = word.getTextScaleX(); - word.setText(text); // TextView.setText() resets text scale x to 1.0. - word.setTextScaleX(scaleX); - return word; - } - - private void layoutDebugInfo(final SuggestedWords suggestedWords, final int indexInStrip, - final ViewGroup placer, final int x) { - final int indexInSuggestedWords = getIndexInSuggestedWords( - indexInStrip, suggestedWords); - if (indexInSuggestedWords >= suggestedWords.size()) { - return; - } - final String debugInfo = Utils.getDebugInfo(suggestedWords, indexInSuggestedWords); - if (debugInfo == null) { - return; - } - final TextView info = mInfos.get(indexInSuggestedWords); - info.setText(debugInfo); - placer.addView(info); - info.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - final int infoWidth = info.getMeasuredWidth(); - final int y = info.getMeasuredHeight(); - ViewLayoutUtils.placeViewAt( - info, x - infoWidth, y, infoWidth, info.getMeasuredHeight()); - } - - private int getSuggestionWidth(final int indexInStrip, final int maxWidth) { - final int paddings = mPadding * mSuggestionsCountInStrip; - final int dividers = mDividerWidth * (mSuggestionsCountInStrip - 1); - final int availableWidth = maxWidth - paddings - dividers; - return (int)(availableWidth * getSuggestionWeight(indexInStrip)); - } - - private float getSuggestionWeight(final int indexInStrip) { - if (indexInStrip == mCenterSuggestionIndex) { - return mCenterSuggestionWeight; - } else { - // TODO: Revisit this for cases of 5 or more suggestions - return (1.0f - mCenterSuggestionWeight) / (mSuggestionsCountInStrip - 1); - } - } - - private void setupTexts(final SuggestedWords suggestedWords, final int countInStrip) { - mTexts.clear(); - final int count = Math.min(suggestedWords.size(), countInStrip); - for (int pos = 0; pos < count; pos++) { - final CharSequence styled = getStyledSuggestionWord(suggestedWords, pos); - mTexts.add(styled); - } - for (int pos = count; pos < countInStrip; pos++) { - // Make this inactive for touches in layout(). - mTexts.add(null); - } - } - - private void layoutPunctuationSuggestions(final SuggestedWords suggestedWords, - final ViewGroup stripView) { - final int countInStrip = Math.min(suggestedWords.size(), PUNCTUATIONS_IN_STRIP); - for (int indexInStrip = 0; indexInStrip < countInStrip; indexInStrip++) { - if (indexInStrip != 0) { - // Add divider if this isn't the left most suggestion in suggestions strip. - addDivider(stripView, mDividers.get(indexInStrip)); - } - - final TextView word = mWords.get(indexInStrip); - word.setEnabled(true); - word.setTextColor(mColorAutoCorrect); - final String text = suggestedWords.getWord(indexInStrip); - word.setText(text); - word.setTextScaleX(1.0f); - word.setCompoundDrawables(null, null, null, null); - stripView.addView(word); - setLayoutWeight(word, 1.0f, mSuggestionsStripHeight); - } - mMoreSuggestionsAvailable = false; - } - - public void layoutAddToDictionaryHint(final String word, final ViewGroup stripView, - final int stripWidth, final CharSequence hintText, final OnClickListener listener) { - final int width = stripWidth - mDividerWidth - mPadding * 2; - - final TextView wordView = mWordToSaveView; - wordView.setTextColor(mColorTypedWord); - final int wordWidth = (int)(width * mCenterSuggestionWeight); - final CharSequence text = getEllipsizedText(word, wordWidth, wordView.getPaint()); - final float wordScaleX = wordView.getTextScaleX(); - wordView.setTag(word); - wordView.setText(text); - wordView.setTextScaleX(wordScaleX); - stripView.addView(wordView); - setLayoutWeight(wordView, mCenterSuggestionWeight, ViewGroup.LayoutParams.MATCH_PARENT); - - stripView.addView(mDividers.get(0)); - - final TextView leftArrowView = mLeftwardsArrowView; - leftArrowView.setTextColor(mColorAutoCorrect); - leftArrowView.setText(LEFTWARDS_ARROW); - stripView.addView(leftArrowView); - - final TextView hintView = mHintToSaveView; - hintView.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL); - hintView.setTextColor(mColorAutoCorrect); - final int hintWidth = width - wordWidth - leftArrowView.getWidth(); - final float hintScaleX = getTextScaleX(hintText, hintWidth, hintView.getPaint()); - hintView.setText(hintText); - hintView.setTextScaleX(hintScaleX); - stripView.addView(hintView); - setLayoutWeight( - hintView, 1.0f - mCenterSuggestionWeight, ViewGroup.LayoutParams.MATCH_PARENT); - - wordView.setOnClickListener(listener); - leftArrowView.setOnClickListener(listener); - hintView.setOnClickListener(listener); - } - - public CharSequence getAddToDictionaryWord() { - return (CharSequence)mWordToSaveView.getTag(); - } - - public boolean isAddToDictionaryShowing(final View v) { - return v == mWordToSaveView || v == mHintToSaveView || v == mLeftwardsArrowView; - } - - private static void setLayoutWeight(final View v, final float weight, final int height) { - final ViewGroup.LayoutParams lp = v.getLayoutParams(); - if (lp instanceof LinearLayout.LayoutParams) { - final LinearLayout.LayoutParams llp = (LinearLayout.LayoutParams)lp; - llp.weight = weight; - llp.width = 0; - llp.height = height; - } - } - - private static float getTextScaleX(final CharSequence text, final int maxWidth, - final TextPaint paint) { - paint.setTextScaleX(1.0f); - final int width = getTextWidth(text, paint); - if (width <= maxWidth) { - return 1.0f; - } - return maxWidth / (float)width; - } - - private static CharSequence getEllipsizedText(final CharSequence text, final int maxWidth, - final TextPaint paint) { - if (text == null) return null; - paint.setTextScaleX(1.0f); - final int width = getTextWidth(text, paint); - if (width <= maxWidth) { - return text; - } - final float scaleX = maxWidth / (float)width; - if (scaleX >= MIN_TEXT_XSCALE) { - paint.setTextScaleX(scaleX); - return text; - } - - // Note that TextUtils.ellipsize() use text-x-scale as 1.0 if ellipsize is needed. To - // get squeezed and ellipsized text, passes enlarged width (maxWidth / MIN_TEXT_XSCALE). - final CharSequence ellipsized = TextUtils.ellipsize( - text, paint, maxWidth / MIN_TEXT_XSCALE, TextUtils.TruncateAt.MIDDLE); - paint.setTextScaleX(MIN_TEXT_XSCALE); - return ellipsized; - } - - private static int getTextWidth(final CharSequence text, final TextPaint paint) { - if (TextUtils.isEmpty(text)) return 0; - final Typeface savedTypeface = paint.getTypeface(); - paint.setTypeface(getTextTypeface(text)); - final int len = text.length(); - final float[] widths = new float[len]; - final int count = paint.getTextWidths(text, 0, len, widths); - int width = 0; - for (int i = 0; i < count; i++) { - width += Math.round(widths[i] + 0.5f); - } - paint.setTypeface(savedTypeface); - return width; - } - - private static Typeface getTextTypeface(final CharSequence text) { - if (!(text instanceof SpannableString)) - return Typeface.DEFAULT; - - final SpannableString ss = (SpannableString)text; - final StyleSpan[] styles = ss.getSpans(0, text.length(), StyleSpan.class); - if (styles.length == 0) - return Typeface.DEFAULT; - - switch (styles[0].getStyle()) { - case Typeface.BOLD: return Typeface.DEFAULT_BOLD; - // TODO: BOLD_ITALIC, ITALIC case? - default: return Typeface.DEFAULT; - } - } - } + private final SuggestionStripLayoutHelper mLayoutHelper; /** * Construct a {@link SuggestionStripView} for showing suggestions to be picked by the user. @@ -630,7 +104,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick mInfos.add((TextView)inflater.inflate(R.layout.suggestion_info, null)); } - mParams = new SuggestionStripViewParams( + mLayoutHelper = new SuggestionStripLayoutHelper( context, attrs, defStyle, mWords, mDividers, mInfos); mMoreSuggestionsContainer = inflater.inflate(R.layout.more_suggestions, null); @@ -657,24 +131,25 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick public void setSuggestions(final SuggestedWords suggestedWords) { clear(); mSuggestedWords = suggestedWords; - mParams.layout(mSuggestedWords, mSuggestionsStrip, this, getWidth()); + mLayoutHelper.layout(mSuggestedWords, mSuggestionsStrip, this, getWidth()); if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { ResearchLogger.suggestionStripView_setSuggestions(mSuggestedWords); } } public int setMoreSuggestionsHeight(final int remainingHeight) { - return mParams.setMoreSuggestionsHeight(remainingHeight); + return mLayoutHelper.setMoreSuggestionsHeight(remainingHeight); } public boolean isShowingAddToDictionaryHint() { return mSuggestionsStrip.getChildCount() > 0 - && mParams.isAddToDictionaryShowing(mSuggestionsStrip.getChildAt(0)); + && mLayoutHelper.isAddToDictionaryShowing(mSuggestionsStrip.getChildAt(0)); } public void showAddToDictionaryHint(final String word, final CharSequence hintText) { clear(); - mParams.layoutAddToDictionaryHint(word, mSuggestionsStrip, getWidth(), hintText, this); + mLayoutHelper.layoutAddToDictionaryHint( + word, mSuggestionsStrip, getWidth(), hintText, this); } public boolean dismissAddToDictionaryHint() { @@ -739,29 +214,29 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick if (parentKeyboard == null) { return false; } - final SuggestionStripViewParams params = mParams; - if (!params.mMoreSuggestionsAvailable) { + final SuggestionStripLayoutHelper layoutHelper = mLayoutHelper; + if (!layoutHelper.mMoreSuggestionsAvailable) { return false; } final int stripWidth = getWidth(); final View container = mMoreSuggestionsContainer; final int maxWidth = stripWidth - container.getPaddingLeft() - container.getPaddingRight(); final MoreSuggestions.Builder builder = mMoreSuggestionsBuilder; - builder.layout(mSuggestedWords, params.mSuggestionsCountInStrip, maxWidth, - (int)(maxWidth * params.mMinMoreSuggestionsWidth), - params.getMaxMoreSuggestionsRow(), parentKeyboard); + builder.layout(mSuggestedWords, layoutHelper.mSuggestionsCountInStrip, maxWidth, + (int)(maxWidth * layoutHelper.mMinMoreSuggestionsWidth), + layoutHelper.getMaxMoreSuggestionsRow(), parentKeyboard); 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 = -params.mMoreSuggestionsBottomGap; + final int pointY = -layoutHelper.mMoreSuggestionsBottomGap; moreKeysPanel.showMoreKeysPanel(this, mMoreSuggestionsController, pointX, pointY, mMoreSuggestionsListener); mMoreSuggestionsMode = MORE_SUGGESTIONS_CHECKING_MODAL_OR_SLIDING; mOriginX = mLastX; mOriginY = mLastY; - for (int i = 0; i < params.mSuggestionsCountInStrip; i++) { + for (int i = 0; i < layoutHelper.mSuggestionsCountInStrip; i++) { mWords.get(i).setPressed(false); } return true; @@ -832,8 +307,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick @Override public void onClick(final View view) { - if (mParams.isAddToDictionaryShowing(view)) { - mListener.addWordToUserDictionary(mParams.getAddToDictionaryWord().toString()); + if (mLayoutHelper.isAddToDictionaryShowing(view)) { + mListener.addWordToUserDictionary(mLayoutHelper.getAddToDictionaryWord().toString()); clear(); return; }