diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin/CandidateView.java')
-rw-r--r-- | java/src/com/android/inputmethod/latin/CandidateView.java | 742 |
1 files changed, 395 insertions, 347 deletions
diff --git a/java/src/com/android/inputmethod/latin/CandidateView.java b/java/src/com/android/inputmethod/latin/CandidateView.java index 4baf52e52..b5b59ac12 100644 --- a/java/src/com/android/inputmethod/latin/CandidateView.java +++ b/java/src/com/android/inputmethod/latin/CandidateView.java @@ -38,6 +38,7 @@ import android.view.Gravity; import android.view.LayoutInflater; 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.PopupWindow; @@ -50,15 +51,12 @@ import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import java.util.ArrayList; import java.util.List; -public class CandidateView extends LinearLayout implements OnClickListener { - +public class CandidateView extends LinearLayout implements OnClickListener, OnLongClickListener { public interface Listener { public boolean addWordToDictionary(String word); public void pickSuggestionManually(int index, CharSequence word); } - private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD); - private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan(); // The maximum number of suggestions available. See {@link Suggest#mPrefMaxSuggestions}. private static final int MAX_SUGGESTIONS = 18; private static final int WRAP_CONTENT = ViewGroup.LayoutParams.WRAP_CONTENT; @@ -67,11 +65,6 @@ public class CandidateView extends LinearLayout implements OnClickListener { private static final boolean DBG = LatinImeLogger.sDBG; private final ViewGroup mCandidatesStrip; - private final int mCandidateCountInStrip; - private static final int DEFAULT_CANDIDATE_COUNT_IN_STRIP = 3; - private final ViewGroup mCandidatesPaneControl; - private final TextView mExpandCandidatesPane; - private final TextView mCloseCandidatesPane; private ViewGroup mCandidatesPane; private ViewGroup mCandidatesPaneContainer; private View mKeyboardView; @@ -80,17 +73,6 @@ public class CandidateView extends LinearLayout implements OnClickListener { private final ArrayList<TextView> mInfos = new ArrayList<TextView>(); private final ArrayList<View> mDividers = new ArrayList<View>(); - private final int mCandidateStripHeight; - private final CharacterStyle mInvertedForegroundColorSpan; - private final CharacterStyle mInvertedBackgroundColorSpan; - private final int mAutoCorrectHighlight; - private static final int AUTO_CORRECT_BOLD = 0x01; - private static final int AUTO_CORRECT_UNDERLINE = 0x02; - private static final int AUTO_CORRECT_INVERT = 0x04; - private final int mColorTypedWord; - private final int mColorAutoCorrect; - private final int mColorSuggestedCandidate; - private final PopupWindow mPreviewPopup; private final TextView mPreviewText; @@ -102,9 +84,9 @@ public class CandidateView extends LinearLayout implements OnClickListener { private boolean mShowingAutoCorrectionInverted; private boolean mShowingAddToDictionary; - private final CandidateViewLayoutParams mParams; - private static final int PUNCTUATIONS_IN_STRIP = 6; - private static final float MIN_TEXT_XSCALE = 0.75f; + private final SuggestionsStripParams mStripParams; + private final SuggestionsPaneParams mPaneParams; + private static final float MIN_TEXT_XSCALE = 0.70f; private final UiHandler mHandler = new UiHandler(this); @@ -157,118 +139,358 @@ public class CandidateView extends LinearLayout implements OnClickListener { } } - private static class CandidateViewLayoutParams { - public final TextPaint mPaint; + private static class CandidateViewParams { public final int mPadding; public final int mDividerWidth; public final int mDividerHeight; - public final int mControlWidth; - private final int mAutoCorrectHighlight; + public final int mCandidateStripHeight; - public final ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(); + protected final List<TextView> mWords; + protected final List<View> mDividers; + protected final List<TextView> mInfos; - public int mCountInStrip; - // True if the mCountInStrip suggestions can fit in suggestion strip in equally divided - // width without squeezing the text. - public boolean mCanUseFixedWidthColumns; - public int mMaxWidth; - public int mAvailableWidthForWords; - public int mConstantWidthForPaddings; - public int mVariableWidthForWords; - public float mScaleX; + protected CandidateViewParams(List<TextView> words, List<View> dividers, + List<TextView> infos) { + mWords = words; + mDividers = dividers; + mInfos = infos; - public CandidateViewLayoutParams(Resources res, TextView word, View divider, View control, - int autoCorrectHighlight) { - mPaint = new TextPaint(); - final float textSize = res.getDimension(R.dimen.candidate_text_size); - mPaint.setTextSize(textSize); + final TextView word = words.get(0); + final View divider = dividers.get(0); mPadding = word.getCompoundPaddingLeft() + word.getCompoundPaddingRight(); divider.measure(WRAP_CONTENT, MATCH_PARENT); mDividerWidth = divider.getMeasuredWidth(); mDividerHeight = divider.getMeasuredHeight(); - mControlWidth = control.getMeasuredWidth(); - mAutoCorrectHighlight = autoCorrectHighlight; + + final Resources res = word.getResources(); + mCandidateStripHeight = res.getDimensionPixelOffset(R.dimen.candidate_strip_height); } + } - public void layoutStrip(SuggestedWords suggestions, int maxWidth, int maxCount) { - final int size = suggestions.size(); - if (size == 0) return; - setupTexts(suggestions, size, mAutoCorrectHighlight); - mCountInStrip = Math.min(maxCount, size); - mScaleX = 1.0f; + 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; + } - do { - mMaxWidth = maxWidth; - if (size > mCountInStrip) { - mMaxWidth -= mControlWidth; + 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(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(WRAP_CONTENT, 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; + } + } + + private static class SuggestionsStripParams extends CandidateViewParams { + private static final int DEFAULT_CANDIDATE_COUNT_IN_STRIP = 3; + private static final int DEFAULT_CENTER_CANDIDATE_PERCENTILE = 40; + private static final int PUNCTUATIONS_IN_STRIP = 6; + + private final int mColorTypedWord; + private final int mColorAutoCorrect; + private final int mColorSuggestedCandidate; + private final int mCandidateCountInStrip; + private final float mCenterCandidateWeight; + private final int mCenterCandidateIndex; + private final Drawable mMoreCandidateHint; + + private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD); + private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan(); + private final CharacterStyle mInvertedForegroundColorSpan; + private final CharacterStyle mInvertedBackgroundColorSpan; + private static final int AUTO_CORRECT_BOLD = 0x01; + private static final int AUTO_CORRECT_UNDERLINE = 0x02; + private static final int AUTO_CORRECT_INVERT = 0x04; + + private final TextPaint mPaint; + private final int mAutoCorrectHighlight; - tryLayout(); + private final ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(); + + public final boolean mAutoCorrectionVisualFlashEnabled; + public boolean mMoreSuggestionsAvailable; + + public SuggestionsStripParams(Context context, AttributeSet attrs, int defStyle, + List<TextView> words, List<View> dividers, List<TextView> infos) { + super(words, dividers, infos); + final TypedArray a = context.obtainStyledAttributes( + attrs, R.styleable.CandidateView, defStyle, R.style.CandidateViewStyle); + mAutoCorrectionVisualFlashEnabled = a.getBoolean( + R.styleable.CandidateView_autoCorrectionVisualFlashEnabled, false); + mAutoCorrectHighlight = a.getInt(R.styleable.CandidateView_autoCorrectHighlight, 0); + mColorTypedWord = a.getColor(R.styleable.CandidateView_colorTypedWord, 0); + mColorAutoCorrect = a.getColor(R.styleable.CandidateView_colorAutoCorrect, 0); + mColorSuggestedCandidate = a.getColor(R.styleable.CandidateView_colorSuggested, 0); + mCandidateCountInStrip = a.getInt( + R.styleable.CandidateView_candidateCountInStrip, + DEFAULT_CANDIDATE_COUNT_IN_STRIP); + mCenterCandidateWeight = a.getInt( + R.styleable.CandidateView_centerCandidatePercentile, + DEFAULT_CENTER_CANDIDATE_PERCENTILE) / 100.0f; + a.recycle(); + + mCenterCandidateIndex = mCandidateCountInStrip / 2; + final Resources res = context.getResources(); + mMoreCandidateHint = res.getDrawable(R.drawable.more_suggestions_hint); + + mInvertedForegroundColorSpan = new ForegroundColorSpan(mColorTypedWord ^ 0x00ffffff); + mInvertedBackgroundColorSpan = new BackgroundColorSpan(mColorTypedWord); - if (mCanUseFixedWidthColumns) { - return; + mPaint = new TextPaint(); + final float textSize = res.getDimension(R.dimen.candidate_text_size); + mPaint.setTextSize(textSize); + } + + public int getTextColor() { + return mColorTypedWord; + } + + private CharSequence getStyledCandidateWord(CharSequence word, boolean isAutoCorrect) { + if (!isAutoCorrect) + return word; + final int len = word.length(); + final Spannable spannedWord = new SpannableString(word); + if ((mAutoCorrectHighlight & AUTO_CORRECT_BOLD) != 0) + spannedWord.setSpan(BOLD_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + if ((mAutoCorrectHighlight & AUTO_CORRECT_UNDERLINE) != 0) + spannedWord.setSpan(UNDERLINE_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + return spannedWord; + } + + private static boolean willAutoCorrect(SuggestedWords suggestions) { + return !suggestions.mTypedWordValid && suggestions.mHasMinimalSuggestion; + } + + private int getWordPosition(int index, SuggestedWords suggestions) { + // TODO: This works for 3 suggestions. Revisit this algorithm when there are 5 or more + // suggestions. + final int centerPos = willAutoCorrect(suggestions) ? 1 : 0; + if (index == mCenterCandidateIndex) { + return centerPos; + } else if (index == centerPos) { + return mCenterCandidateIndex; + } else { + return index; + } + } + + private int getCandidateTextColor(int index, SuggestedWords suggestions, int pos) { + // TODO: Need to revisit this logic with bigram suggestions + final boolean isSuggestedCandidate = (pos != 0); + + final int color; + if (index == mCenterCandidateIndex && willAutoCorrect(suggestions)) { + color = mColorAutoCorrect; + } else if (isSuggestedCandidate) { + color = mColorSuggestedCandidate; + } else { + color = mColorTypedWord; + } + + final SuggestedWordInfo info = (pos < suggestions.size()) + ? suggestions.getInfo(pos) : null; + if (info != null && info.isPreviousSuggestedWord()) { + return applyAlpha(color, 0.5f); + } 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)); + } + + public CharSequence getInvertedText(CharSequence text) { + if ((mAutoCorrectHighlight & AUTO_CORRECT_INVERT) == 0) + return null; + final int len = text.length(); + final Spannable word = new SpannableString(text); + word.setSpan(mInvertedBackgroundColorSpan, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + word.setSpan(mInvertedForegroundColorSpan, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + return word; + } + + public int layout(SuggestedWords suggestions, ViewGroup stripView, ViewGroup paneView, + int stripWidth) { + if (suggestions.isPunctuationSuggestions()) { + return layoutPunctuationSuggestions(suggestions, stripView); + } + + final int countInStrip = mCandidateCountInStrip; + setupTexts(suggestions, countInStrip); + mMoreSuggestionsAvailable = (suggestions.size() > countInStrip); + int x = 0; + for (int index = 0; index < countInStrip; index++) { + final int pos = getWordPosition(index, suggestions); + + if (index != 0) { + final View divider = mDividers.get(pos); + // Add divider if this isn't the left most suggestion in candidate strip. + stripView.addView(divider); } - if (mVariableWidthForWords <= mAvailableWidthForWords) { - return; + + final CharSequence styled = mTexts.get(pos); + final TextView word = mWords.get(pos); + if (index == mCenterCandidateIndex && mMoreSuggestionsAvailable) { + // TODO: This "more suggestions hint" should have nicely designed icon. + word.setCompoundDrawablesWithIntrinsicBounds( + null, null, null, mMoreCandidateHint); + } else { + word.setCompoundDrawables(null, null, null, null); } - final float scaleX = mAvailableWidthForWords / (float)mVariableWidthForWords; - if (scaleX >= MIN_TEXT_XSCALE) { - mScaleX = scaleX; - return; + // Disable this candidate if the suggestion is null or empty. + word.setEnabled(!TextUtils.isEmpty(styled)); + word.setTextColor(getCandidateTextColor(index, suggestions, pos)); + final int width = getCandidateWidth(index, stripWidth); + 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); + stripView.addView(word); + setLayoutWeight(word, getCandidateWeight(index), mCandidateStripHeight); + + if (DBG) { + final CharSequence debugInfo = getDebugInfo(suggestions, pos); + if (debugInfo != null) { + final TextView info = mInfos.get(pos); + info.setText(debugInfo); + paneView.addView(info); + info.measure(WRAP_CONTENT, WRAP_CONTENT); + final int infoWidth = info.getMeasuredWidth(); + final int y = info.getMeasuredHeight(); + FrameLayoutCompatUtils.placeViewAt(info, x, 0, infoWidth, y); + x += infoWidth * 2; + } } + } + + return countInStrip; + } + + private int getCandidateWidth(int index, int maxWidth) { + final int paddings = mPadding * mCandidateCountInStrip; + final int dividers = mDividerWidth * (mCandidateCountInStrip - 1); + final int availableWidth = maxWidth - paddings - dividers; + return (int)(availableWidth * getCandidateWeight(index)); + } - mCountInStrip--; - } while (mCountInStrip > 1); - } - - public void tryLayout() { - final int maxCount = mCountInStrip; - final int dividers = mDividerWidth * (maxCount - 1); - mConstantWidthForPaddings = dividers + mPadding * maxCount; - mAvailableWidthForWords = mMaxWidth - mConstantWidthForPaddings; - - mPaint.setTextScaleX(mScaleX); - final int maxFixedWidthForWord = (mMaxWidth - dividers) / maxCount - mPadding; - mCanUseFixedWidthColumns = true; - mVariableWidthForWords = 0; - for (int i = 0; i < maxCount; i++) { - final int width = getTextWidth(mTexts.get(i), mPaint); - if (width > maxFixedWidthForWord) - mCanUseFixedWidthColumns = false; - mVariableWidthForWords += width; + private float getCandidateWeight(int index) { + if (index == mCenterCandidateIndex) { + return mCenterCandidateWeight; + } else { + // TODO: Revisit this for cases of 5 or more suggestions + return (1.0f - mCenterCandidateWeight) / (mCandidateCountInStrip - 1); } } - private void setupTexts(SuggestedWords suggestions, int count, int autoCorrectHighlight) { + private void setupTexts(SuggestedWords suggestions, int countInStrip) { mTexts.clear(); - for (int i = 0; i < count; i++) { - final CharSequence suggestion = suggestions.getWord(i); - if (suggestion == null) { - // Skip an empty suggestion, but we need to add a place-holder for it in order - // to avoid an exception in the loop in updateSuggestions(). - mTexts.add(""); - continue; - } - - final boolean isAutoCorrect = suggestions.mHasMinimalSuggestion - && ((i == 1 && !suggestions.mTypedWordValid) - || (i == 0 && suggestions.mTypedWordValid)); - // HACK: even if i == 0, we use mColorOther when this suggestion's length is 1 - // and there are multiple suggestions, such as the default punctuation list. - // TODO: Need to revisit this logic with bigram suggestions - final CharSequence styled = getStyledCandidateWord(suggestion, isAutoCorrect, - autoCorrectHighlight); + final int count = Math.min(suggestions.size(), countInStrip); + for (int pos = 0; pos < count; pos++) { + final CharSequence word = suggestions.getWord(pos); + final boolean isAutoCorrect = pos == 1 && willAutoCorrect(suggestions); + final CharSequence styled = getStyledCandidateWord(word, isAutoCorrect); mTexts.add(styled); } + for (int pos = count; pos < countInStrip; pos++) { + // Make this inactive for touches in layout(). + mTexts.add(null); + } } - @Override - public String toString() { - return String.format( - "count=%d width=%d avail=%d fixcol=%s scaleX=%4.2f const=%d var=%d", - mCountInStrip, mMaxWidth, mAvailableWidthForWords, mCanUseFixedWidthColumns, - mScaleX, mConstantWidthForPaddings, mVariableWidthForWords); + private int 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) { + // Add divider if this isn't the left most suggestion in candidate strip. + stripView.addView(mDividers.get(index)); + } + + final TextView word = mWords.get(index); + word.setEnabled(true); + word.setTextColor(mColorTypedWord); + final CharSequence text = suggestions.getWord(index); + word.setText(text); + word.setTextScaleX(1.0f); + word.setCompoundDrawables(null, null, null, null); + stripView.addView(word); + setLayoutWeight(word, 1.0f, mCandidateStripHeight); + } + mMoreSuggestionsAvailable = false; + return countInStrip; } } @@ -295,18 +517,7 @@ public class CandidateView extends LinearLayout implements OnClickListener { setBackgroundDrawable(LinearLayoutCompatUtils.getBackgroundDrawable( context, attrs, defStyle, R.style.CandidateViewStyle)); - final TypedArray a = context.obtainStyledAttributes( - attrs, R.styleable.CandidateView, defStyle, R.style.CandidateViewStyle); - mAutoCorrectHighlight = a.getInt(R.styleable.CandidateView_autoCorrectHighlight, 0); - mColorTypedWord = a.getColor(R.styleable.CandidateView_colorTypedWord, 0); - mColorAutoCorrect = a.getColor(R.styleable.CandidateView_colorAutoCorrect, 0); - mColorSuggestedCandidate = a.getColor(R.styleable.CandidateView_colorSuggested, 0); - mCandidateCountInStrip = a.getInt( - R.styleable.CandidateView_candidateCountInStrip, DEFAULT_CANDIDATE_COUNT_IN_STRIP); - a.recycle(); - - Resources res = context.getResources(); - LayoutInflater inflater = LayoutInflater.from(context); + final LayoutInflater inflater = LayoutInflater.from(context); inflater.inflate(R.layout.candidates_strip, this); mPreviewPopup = new PopupWindow(context); @@ -317,56 +528,26 @@ public class CandidateView extends LinearLayout implements OnClickListener { mPreviewPopup.setBackgroundDrawable(null); mCandidatesStrip = (ViewGroup)findViewById(R.id.candidates_strip); - mCandidateStripHeight = res.getDimensionPixelOffset(R.dimen.candidate_strip_height); - for (int i = 0; i < MAX_SUGGESTIONS; i++) { + for (int pos = 0; pos < MAX_SUGGESTIONS; pos++) { final TextView word = (TextView)inflater.inflate(R.layout.candidate_word, null); - word.setTag(i); + word.setTag(pos); word.setOnClickListener(this); + word.setOnLongClickListener(this); mWords.add(word); + final View divider = inflater.inflate(R.layout.candidate_divider, null); + divider.setTag(pos); + divider.setOnClickListener(this); + mDividers.add(divider); mInfos.add((TextView)inflater.inflate(R.layout.candidate_info, null)); - mDividers.add(inflater.inflate(R.layout.candidate_divider, null)); } mTouchToSave = findViewById(R.id.touch_to_save); mWordToSave = (TextView)findViewById(R.id.word_to_save); mWordToSave.setOnClickListener(this); - mInvertedForegroundColorSpan = new ForegroundColorSpan(mColorTypedWord ^ 0x00ffffff); - mInvertedBackgroundColorSpan = new BackgroundColorSpan(mColorTypedWord); - - final TypedArray keyboardViewAttr = context.obtainStyledAttributes( - attrs, R.styleable.KeyboardView, R.attr.keyboardViewStyle, R.style.KeyboardView); - final Drawable expandBackground = keyboardViewAttr.getDrawable( - R.styleable.KeyboardView_keyBackground); - final Drawable closeBackground = keyboardViewAttr.getDrawable( - R.styleable.KeyboardView_keyBackground); - final int keyTextColor = keyboardViewAttr.getColor( - R.styleable.KeyboardView_keyTextColor, 0xFF000000); - keyboardViewAttr.recycle(); - - mCandidatesPaneControl = (ViewGroup)findViewById(R.id.candidates_pane_control); - mExpandCandidatesPane = (TextView)findViewById(R.id.expand_candidates_pane); - mExpandCandidatesPane.setBackgroundDrawable(expandBackground); - mExpandCandidatesPane.setTextColor(keyTextColor); - mExpandCandidatesPane.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View view) { - expandCandidatesPane(); - } - }); - mCloseCandidatesPane = (TextView)findViewById(R.id.close_candidates_pane); - mCloseCandidatesPane.setBackgroundDrawable(closeBackground); - mCloseCandidatesPane.setTextColor(keyTextColor); - mCloseCandidatesPane.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View view) { - closeCandidatesPane(); - } - }); - mCandidatesPaneControl.measure(WRAP_CONTENT, WRAP_CONTENT); - - mParams = new CandidateViewLayoutParams(res, - mWords.get(0), mDividers.get(0), mCandidatesPaneControl, mAutoCorrectHighlight); + mStripParams = new SuggestionsStripParams(context, attrs, defStyle, mWords, mDividers, + mInfos); + mPaneParams = new SuggestionsPaneParams(mWords, mDividers, mInfos); } /** @@ -387,7 +568,6 @@ public class CandidateView extends LinearLayout implements OnClickListener { if (suggestions == null) return; mSuggestions = suggestions; - mExpandCandidatesPane.setEnabled(false); if (mShowingAutoCorrectionInverted) { mHandler.postUpdateSuggestions(); } else { @@ -395,181 +575,30 @@ public class CandidateView extends LinearLayout implements OnClickListener { } } - private static CharSequence getStyledCandidateWord(CharSequence word, boolean isAutoCorrect, - int autoCorrectHighlight) { - if (!isAutoCorrect) - return word; - final Spannable spannedWord = new SpannableString(word); - if ((autoCorrectHighlight & AUTO_CORRECT_BOLD) != 0) - spannedWord.setSpan(BOLD_SPAN, 0, word.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - if ((autoCorrectHighlight & AUTO_CORRECT_UNDERLINE) != 0) - spannedWord.setSpan(UNDERLINE_SPAN, 0, word.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - return spannedWord; - } - - private int getCandidateTextColor(boolean isAutoCorrect, boolean isSuggestedCandidate, - SuggestedWordInfo info) { - final int color; - if (isAutoCorrect) { - color = mColorAutoCorrect; - } else if (isSuggestedCandidate) { - color = mColorSuggestedCandidate; - } else { - color = mColorTypedWord; - } - if (info != null && info.isPreviousSuggestedWord()) { - final int newAlpha = (int)(Color.alpha(color) * 0.5f); - return Color.argb(newAlpha, Color.red(color), Color.green(color), Color.blue(color)); - } else { - return color; - } - } - private void updateSuggestions() { - final SuggestedWords suggestions = mSuggestions; - final List<SuggestedWordInfo> suggestedWordInfoList = suggestions.mSuggestedWordInfoList; - final int paneWidth = getWidth(); - final CandidateViewLayoutParams params = mParams; - clear(); closeCandidatesPane(); - if (suggestions.size() == 0) + if (mSuggestions.size() == 0) return; - params.layoutStrip(suggestions, paneWidth, suggestions.isPunctuationSuggestions() - ? PUNCTUATIONS_IN_STRIP : mCandidateCountInStrip); - - final int count = Math.min(mWords.size(), suggestions.size()); - if (count <= params.mCountInStrip && !DBG) { - mCandidatesPaneControl.setVisibility(GONE); - } else { - mCandidatesPaneControl.setVisibility(VISIBLE); - mExpandCandidatesPane.setVisibility(VISIBLE); - mExpandCandidatesPane.setEnabled(true); - } - - final int countInStrip = params.mCountInStrip; - View centeringFrom = null, lastView = null; - int x = 0, y = 0, infoX = 0; - for (int i = 0; i < count; i++) { - final int pos; - if (i <= 1) { - final boolean willAutoCorrect = !suggestions.mTypedWordValid - && suggestions.mHasMinimalSuggestion; - pos = willAutoCorrect ? 1 - i : i; - } else { - pos = i; - } - final CharSequence suggestion = suggestions.getWord(pos); - if (suggestion == null) continue; - - final SuggestedWordInfo suggestionInfo = (suggestedWordInfoList != null) - ? suggestedWordInfoList.get(pos) : null; - final boolean isAutoCorrect = suggestions.mHasMinimalSuggestion - && ((pos == 1 && !suggestions.mTypedWordValid) - || (pos == 0 && suggestions.mTypedWordValid)); - // HACK: even if i == 0, we use mColorOther when this suggestion's length is 1 - // and there are multiple suggestions, such as the default punctuation list. - // TODO: Need to revisit this logic with bigram suggestions - final boolean isSuggestedCandidate = (pos != 0); - final boolean isPunctuationSuggestions = (suggestion.length() == 1 && count > 1); - - final TextView word = mWords.get(pos); - final TextPaint paint = word.getPaint(); - // TODO: Reorder candidates in strip as appropriate. The center candidate should hold - // the word when space is typed (valid typed word or auto corrected word). - word.setTextColor(getCandidateTextColor(isAutoCorrect, - isSuggestedCandidate || isPunctuationSuggestions, suggestionInfo)); - final CharSequence styled = params.mTexts.get(pos); - - final TextView info; - if (DBG && suggestionInfo != null - && !TextUtils.isEmpty(suggestionInfo.getDebugString())) { - info = mInfos.get(i); - info.setText(suggestionInfo.getDebugString()); - } else { - info = null; - } + final int width = getWidth(); + final int countInStrip = mStripParams.layout( + mSuggestions, mCandidatesStrip, mCandidatesPane, width); + final int countInPane = mPaneParams.layout( + mSuggestions, mCandidatesPane, countInStrip, mStripParams.getTextColor(), width); + } - final CharSequence text; - final float scaleX; - if (i < countInStrip) { - if (i == 0 && params.mCountInStrip == 1) { - text = getEllipsizedText(styled, params.mMaxWidth, paint); - scaleX = paint.getTextScaleX(); - } else { - text = styled; - scaleX = params.mScaleX; - } - word.setText(text); - word.setTextScaleX(scaleX); - if (i != 0) { - // Add divider if this isn't the left most suggestion in candidate strip. - mCandidatesStrip.addView(mDividers.get(i)); - } - mCandidatesStrip.addView(word); - if (params.mCanUseFixedWidthColumns) { - setLayoutWeight(word, 1.0f, mCandidateStripHeight); - } else { - final int width = getTextWidth(text, paint) + params.mPadding; - setLayoutWeight(word, width, mCandidateStripHeight); - } - if (info != null) { - mCandidatesPane.addView(info); - info.measure(WRAP_CONTENT, WRAP_CONTENT); - final int width = info.getMeasuredWidth(); - y = info.getMeasuredHeight(); - FrameLayoutCompatUtils.placeViewAt(info, infoX, 0, width, y); - infoX += width * 2; - } - } else { - paint.setTextScaleX(1.0f); - final int textWidth = getTextWidth(styled, paint); - int available = paneWidth - x - params.mPadding; - if (textWidth >= available) { - // Needs new row, centering previous row. - centeringCandidates(centeringFrom, lastView, x, paneWidth); - x = 0; - y += mCandidateStripHeight; - } - if (x != 0) { - // Add divider if this isn't the left most suggestion in current row. - final View divider = mDividers.get(i); - mCandidatesPane.addView(divider); - FrameLayoutCompatUtils.placeViewAt( - divider, x, y + (mCandidateStripHeight - params.mDividerHeight) / 2, - params.mDividerWidth, params.mDividerHeight); - x += params.mDividerWidth; - } - available = paneWidth - x - params.mPadding; - text = getEllipsizedText(styled, available, paint); - scaleX = paint.getTextScaleX(); - word.setText(text); - word.setTextScaleX(scaleX); - mCandidatesPane.addView(word); - lastView = word; - if (x == 0) centeringFrom = word; - word.measure(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) { - mCandidatesPane.addView(info); - lastView = info; - info.measure(WRAP_CONTENT, WRAP_CONTENT); - final int infoWidth = info.getMeasuredWidth(); - FrameLayoutCompatUtils.placeViewAt( - info, x - infoWidth, y, infoWidth, info.getMeasuredHeight()); + private static CharSequence getDebugInfo(SuggestedWords suggestions, int pos) { + if (DBG && pos < suggestions.size()) { + final SuggestedWordInfo wordInfo = suggestions.getInfo(pos); + if (wordInfo != null) { + final CharSequence debugInfo = wordInfo.getDebugString(); + if (!TextUtils.isEmpty(debugInfo)) { + return debugInfo; } } } - if (x != 0) { - // Centering last candidates row. - centeringCandidates(centeringFrom, lastView, x, paneWidth); - } + return null; } private static void setLayoutWeight(View v, float weight, int height) { @@ -582,13 +611,13 @@ public class CandidateView extends LinearLayout implements OnClickListener { } } - private void centeringCandidates(View from, View to, int width, int paneWidth) { - final ViewGroup pane = mCandidatesPane; - final int fromIndex = pane.indexOfChild(from); - final int toIndex = pane.indexOfChild(to); - final int offset = (paneWidth - width) / 2; + 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(pane.getChildAt(index), offset, 0); + offsetMargin(parent.getChildAt(index), offset, 0); } } @@ -604,16 +633,20 @@ public class CandidateView extends LinearLayout implements OnClickListener { private static CharSequence getEllipsizedText(CharSequence text, int maxWidth, TextPaint paint) { + if (text == null) return null; paint.setTextScaleX(1.0f); final int width = getTextWidth(text, paint); - final float scaleX = Math.min(maxWidth / (float)width, 1.0f); + 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 ellipsezed text, passes enlarged width (maxWidth / MIN_TEXT_XSCALE). + // 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); @@ -652,31 +685,33 @@ public class CandidateView extends LinearLayout implements OnClickListener { } private void expandCandidatesPane() { - mExpandCandidatesPane.setVisibility(GONE); - mCloseCandidatesPane.setVisibility(VISIBLE); mCandidatesPaneContainer.setMinimumHeight(mKeyboardView.getMeasuredHeight()); mCandidatesPaneContainer.setVisibility(VISIBLE); mKeyboardView.setVisibility(GONE); } private void closeCandidatesPane() { - mExpandCandidatesPane.setVisibility(VISIBLE); - mCloseCandidatesPane.setVisibility(GONE); mCandidatesPaneContainer.setVisibility(GONE); mKeyboardView.setVisibility(VISIBLE); } + private void toggleCandidatesPane() { + if (mCandidatesPaneContainer.getVisibility() == VISIBLE) { + closeCandidatesPane(); + } else { + expandCandidatesPane(); + } + } + public void onAutoCorrectionInverted(CharSequence autoCorrectedWord) { - if ((mAutoCorrectHighlight & AUTO_CORRECT_INVERT) == 0) + if (!mStripParams.mAutoCorrectionVisualFlashEnabled) { + return; + } + final CharSequence inverted = mStripParams.getInvertedText(autoCorrectedWord); + if (inverted == null) return; final TextView tv = mWords.get(1); - final Spannable word = new SpannableString(autoCorrectedWord); - final int wordLength = word.length(); - word.setSpan(mInvertedBackgroundColorSpan, 0, wordLength, - Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - word.setSpan(mInvertedForegroundColorSpan, 0, wordLength, - Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - tv.setText(word); + tv.setText(inverted); mShowingAutoCorrectionInverted = true; } @@ -688,7 +723,6 @@ public class CandidateView extends LinearLayout implements OnClickListener { mWordToSave.setText(word); mShowingAddToDictionary = true; mCandidatesStrip.setVisibility(GONE); - mCandidatesPaneControl.setVisibility(GONE); mTouchToSave.setVisibility(VISIBLE); } @@ -721,7 +755,7 @@ public class CandidateView extends LinearLayout implements OnClickListener { return; final TextView previewText = mPreviewText; - previewText.setTextColor(mColorTypedWord); + previewText.setTextColor(mStripParams.mColorTypedWord); previewText.setText(word); previewText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); @@ -747,6 +781,15 @@ public class CandidateView extends LinearLayout implements OnClickListener { } @Override + public boolean onLongClick(View view) { + if (mStripParams.mMoreSuggestionsAvailable) { + toggleCandidatesPane(); + return true; + } + return false; + } + + @Override public void onClick(View view) { if (view == mWordToSave) { addToDictionary(((TextView)view).getText()); @@ -754,6 +797,11 @@ public class CandidateView extends LinearLayout implements OnClickListener { return; } + if (view == mCandidatesPane) { + closeCandidatesPane(); + return; + } + final Object tag = view.getTag(); if (!(tag instanceof Integer)) return; |