diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin/CandidateView.java')
-rw-r--r-- | java/src/com/android/inputmethod/latin/CandidateView.java | 232 |
1 files changed, 191 insertions, 41 deletions
diff --git a/java/src/com/android/inputmethod/latin/CandidateView.java b/java/src/com/android/inputmethod/latin/CandidateView.java index b4f6b2c91..2a29e1f8f 100644 --- a/java/src/com/android/inputmethod/latin/CandidateView.java +++ b/java/src/com/android/inputmethod/latin/CandidateView.java @@ -18,6 +18,7 @@ package com.android.inputmethod.latin; import android.content.Context; import android.content.res.Resources; +import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.Typeface; import android.os.Handler; @@ -38,6 +39,7 @@ import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.view.ViewGroup; +import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.PopupWindow; import android.widget.TextView; @@ -56,16 +58,29 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD); private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan(); - private static final int MAX_SUGGESTIONS = 16; + // The maximum number of suggestions available. See {@link Suggest#mPrefMaxSuggestions}. + private static final int MAX_SUGGESTIONS = 18; + private static final int UNSPECIFIED_MEASURESPEC = MeasureSpec.makeMeasureSpec( + 0, MeasureSpec.UNSPECIFIED); private static final boolean DBG = LatinImeLogger.sDBG; + private static final int NUM_CANDIDATES_IN_STRIP = 3; + private final ImageView mExpandCandidatesPane; + private final ImageView mCloseCandidatesPane; + private ViewGroup mCandidatesPane; + private ViewGroup mCandidatesPaneContainer; + private View mKeyboardView; private final ArrayList<TextView> mWords = new ArrayList<TextView>(); private final ArrayList<View> mDividers = new ArrayList<View>(); private final int mCandidatePadding; - private final boolean mConfigCandidateHighlightFontColorEnabled; + 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; @@ -129,27 +144,44 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo * @param attrs */ public CandidateView(Context context, AttributeSet attrs) { - super(context, attrs); + this(context, attrs, R.attr.candidateViewStyle); + } + + public CandidateView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); Resources res = context.getResources(); - mPreviewPopup = new PopupWindow(context); LayoutInflater inflater = LayoutInflater.from(context); + inflater.inflate(R.layout.candidates_strip, this); + + mPreviewPopup = new PopupWindow(context); mPreviewText = (TextView) inflater.inflate(R.layout.candidate_preview, null); mPreviewPopup.setWindowLayoutMode(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); mPreviewPopup.setContentView(mPreviewText); mPreviewPopup.setBackgroundDrawable(null); - mConfigCandidateHighlightFontColorEnabled = - res.getBoolean(R.bool.config_candidate_highlight_font_color_enabled); - mColorTypedWord = res.getColor(R.color.candidate_typed_word); - mColorAutoCorrect = res.getColor(R.color.candidate_auto_correct); - mColorSuggestedCandidate = res.getColor(R.color.candidate_suggested); - mInvertedForegroundColorSpan = new ForegroundColorSpan(mColorTypedWord ^ 0x00ffffff); - mInvertedBackgroundColorSpan = new BackgroundColorSpan(mColorTypedWord); mCandidatePadding = res.getDimensionPixelOffset(R.dimen.candidate_padding); + mCandidateStripHeight = res.getDimensionPixelOffset(R.dimen.candidate_strip_height); for (int i = 0; i < MAX_SUGGESTIONS; i++) { - final TextView tv = (TextView)inflater.inflate(R.layout.candidate, null); + final TextView tv; + switch (i) { + case 0: + tv = (TextView)findViewById(R.id.candidate_left); + tv.setPadding(mCandidatePadding, 0, 0, 0); + break; + case 1: + tv = (TextView)findViewById(R.id.candidate_center); + break; + case 2: + tv = (TextView)findViewById(R.id.candidate_right); + break; + default: + tv = (TextView)inflater.inflate(R.layout.candidate, null); + break; + } + if (i < NUM_CANDIDATES_IN_STRIP) + setLayoutWeight(tv, 1.0f); tv.setTag(i); tv.setOnClickListener(this); if (i == 0) @@ -157,19 +189,53 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo mWords.add(tv); if (i > 0) { final View divider = inflater.inflate(R.layout.candidate_divider, null); + divider.measure(UNSPECIFIED_MEASURESPEC, UNSPECIFIED_MEASURESPEC); mDividers.add(divider); } } - scrollTo(0, getScrollY()); + 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); + mInvertedForegroundColorSpan = new ForegroundColorSpan(mColorTypedWord ^ 0x00ffffff); + mInvertedBackgroundColorSpan = new BackgroundColorSpan(mColorTypedWord); + + mExpandCandidatesPane = (ImageView)findViewById(R.id.expand_candidates_pane); + mExpandCandidatesPane.setImageDrawable( + a.getDrawable(R.styleable.CandidateView_iconExpandPane)); + mExpandCandidatesPane.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + expandCandidatesPane(); + } + }); + mCloseCandidatesPane = (ImageView)findViewById(R.id.close_candidates_pane); + mCloseCandidatesPane.setImageDrawable( + a.getDrawable(R.styleable.CandidateView_iconClosePane)); + mCloseCandidatesPane.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + closeCandidatesPane(); + } + }); + + a.recycle(); } /** * A connection back to the input method. * @param listener */ - public void setListener(Listener listener) { + public void setListener(Listener listener, View inputView) { mListener = listener; + mKeyboardView = inputView.findViewById(R.id.keyboard_view); + mCandidatesPane = (ViewGroup)inputView.findViewById(R.id.candidates_pane); + mCandidatesPane.setOnClickListener(this); + mCandidatesPaneContainer = (ViewGroup)inputView.findViewById( + R.id.candidates_pane_container); } public void setSuggestions(SuggestedWords suggestions) { @@ -183,22 +249,32 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo } } + private static void setLayoutWeight(View v, float weight) { + ViewGroup.LayoutParams lp = v.getLayoutParams(); + if (lp instanceof LinearLayout.LayoutParams) { + LinearLayout.LayoutParams llp = (LinearLayout.LayoutParams)lp; + llp.width = 0; + llp.weight = weight; + } + } + private CharSequence getStyledCandidateWord(CharSequence word, boolean isAutoCorrect) { if (!isAutoCorrect) return word; - final CharacterStyle style = mConfigCandidateHighlightFontColorEnabled ? BOLD_SPAN - : UNDERLINE_SPAN; final Spannable spannedWord = new SpannableString(word); - spannedWord.setSpan(style, 0, word.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + if ((mAutoCorrectHighlight & AUTO_CORRECT_BOLD) != 0) + spannedWord.setSpan(BOLD_SPAN, 0, word.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + if ((mAutoCorrectHighlight & 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 && mConfigCandidateHighlightFontColorEnabled) { + if (isAutoCorrect) { color = mColorAutoCorrect; - } else if (isSuggestedCandidate && mConfigCandidateHighlightFontColorEnabled) { + } else if (isSuggestedCandidate) { color = mColorSuggestedCandidate; } else { color = mColorTypedWord; @@ -216,7 +292,14 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo final List<SuggestedWordInfo> suggestedWordInfoList = suggestions.mSuggestedWordInfoList; clear(); + final int paneWidth = getWidth(); + final int dividerWidth = mDividers.get(0).getMeasuredWidth(); + int x = 0; + int y = 0; + int fromIndex = NUM_CANDIDATES_IN_STRIP; final int count = Math.min(mWords.size(), suggestions.size()); + closeCandidatesPane(); + mExpandCandidatesPane.setEnabled(count >= NUM_CANDIDATES_IN_STRIP); for (int i = 0; i < count; i++) { final CharSequence word = suggestions.getWord(i); if (word == null) continue; @@ -233,39 +316,97 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo final boolean isPunctuationSuggestions = (word.length() == 1 && count > 1); final TextView tv = mWords.get(i); + // 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). tv.setTextColor(getCandidateTextColor(isAutoCorrect, isSuggestedCandidate || isPunctuationSuggestions, info)); tv.setText(getStyledCandidateWord(word, isAutoCorrect)); - if (i == 0) { - tv.setPadding(mCandidatePadding, 0, 0, 0); - } else if (i == count - 1) { - tv.setPadding(0, 0, mCandidatePadding, 0); - } else { - tv.setPadding(0, 0, 0, 0); + // TODO: call TextView.setTextScaleX() to fit the candidate in single line. + if (i >= NUM_CANDIDATES_IN_STRIP) { + tv.measure(UNSPECIFIED_MEASURESPEC, UNSPECIFIED_MEASURESPEC); + final int width = tv.getMeasuredWidth(); + // TODO: Handle overflow case. + if (dividerWidth + x + width >= paneWidth) { + centeringCandidates(fromIndex, i - 1, x, paneWidth); + x = 0; + y += mCandidateStripHeight; + fromIndex = i; + } + if (x != 0) { + final View divider = mDividers.get(i - NUM_CANDIDATES_IN_STRIP); + mCandidatesPane.addView(divider); + placeCandidateAt(divider, x, y); + x += dividerWidth; + } + mCandidatesPane.addView(tv); + placeCandidateAt(tv, x, y); + x += width; } - if (i > 0) - addView(mDividers.get(i - 1)); - addView(tv); if (DBG && info != null) { final TextView dv = new TextView(getContext(), null); dv.setTextSize(10.0f); dv.setTextColor(0xff808080); dv.setText(info.getDebugString()); - addView(dv); + // TODO: debug view for candidate strip needed. + mCandidatesPane.addView(dv); LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)dv.getLayoutParams(); lp.gravity = Gravity.BOTTOM; } } + if (x != 0) { + // Centering last candidates row. + centeringCandidates(fromIndex, count - 1, x, paneWidth); + } + } + + private void placeCandidateAt(View v, int x, int y) { + ViewGroup.LayoutParams lp = v.getLayoutParams(); + if (lp instanceof ViewGroup.MarginLayoutParams) { + ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams)lp; + mlp.width = v.getMeasuredWidth(); + mlp.height = v.getMeasuredHeight(); + mlp.setMargins(x, y + (mCandidateStripHeight - mlp.height) / 2, 0, 0); + } + } - scrollTo(0, getScrollY()); - requestLayout(); + private void centeringCandidates(int from, int to, int width, int paneWidth) { + final ViewGroup pane = mCandidatesPane; + final int fromIndex = pane.indexOfChild(mWords.get(from)); + final int toIndex = pane.indexOfChild(mWords.get(to)); + final int offset = (paneWidth - width) / 2; + for (int index = fromIndex; index <= toIndex; index++) { + offsetMargin(pane.getChildAt(index), offset, 0); + } + } + + private static void offsetMargin(View v, int dx, int dy) { + if (v == null) + return; + ViewGroup.LayoutParams lp = v.getLayoutParams(); + if (lp instanceof ViewGroup.MarginLayoutParams) { + ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams)lp; + mlp.setMargins(mlp.leftMargin + dx, mlp.topMargin + dy, 0, 0); + } + } + + private void expandCandidatesPane() { + mExpandCandidatesPane.setVisibility(View.GONE); + mCloseCandidatesPane.setVisibility(View.VISIBLE); + mCandidatesPaneContainer.setMinimumHeight(mKeyboardView.getMeasuredHeight()); + mCandidatesPaneContainer.setVisibility(View.VISIBLE); + mKeyboardView.setVisibility(View.GONE); + } + + private void closeCandidatesPane() { + mExpandCandidatesPane.setVisibility(View.VISIBLE); + mCloseCandidatesPane.setVisibility(View.GONE); + mCandidatesPaneContainer.setVisibility(View.GONE); + mKeyboardView.setVisibility(View.VISIBLE); } public void onAutoCorrectionInverted(CharSequence autoCorrectedWord) { - // Displaying auto corrected word as inverted is enabled only when highlighting candidate - // with color is disabled. - if (mConfigCandidateHighlightFontColorEnabled) + if ((mAutoCorrectHighlight & AUTO_CORRECT_INVERT) == 0) return; final TextView tv = mWords.get(1); final Spannable word = new SpannableString(autoCorrectedWord); @@ -278,10 +419,6 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo mShowingAutoCorrectionInverted = true; } - public boolean isConfigCandidateHighlightFontColorEnabled() { - return mConfigCandidateHighlightFontColorEnabled; - } - public boolean isShowingAddToDictionaryHint() { return mShowingAddToDictionary; } @@ -310,7 +447,9 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo public void clear() { mShowingAddToDictionary = false; mShowingAutoCorrectionInverted = false; - removeAllViews(); + for (int i = 0; i < NUM_CANDIDATES_IN_STRIP; i++) + mWords.get(i).setText(null); + mCandidatesPane.removeAllViews(); } private void hidePreview() { @@ -349,9 +488,13 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo @Override public boolean onLongClick(View view) { - final int index = (Integer) view.getTag(); + final Object tag = view.getTag(); + if (!(tag instanceof Integer)) + return true; + final int index = (Integer) tag; if (index >= mSuggestions.size()) return true; + final CharSequence word = mSuggestions.getWord(index); if (word.length() < 2) return false; @@ -361,15 +504,22 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo @Override public void onClick(View view) { - final int index = (Integer) view.getTag(); + final Object tag = view.getTag(); + if (!(tag instanceof Integer)) + return; + final int index = (Integer) tag; if (index >= mSuggestions.size()) return; + final CharSequence word = mSuggestions.getWord(index); if (mShowingAddToDictionary && index == 0) { addToDictionary(word); } else { 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 |