Skip to content
Snippets Groups Projects
Commit 60a004f7 authored by Tadashi G. Takaoka's avatar Tadashi G. Takaoka
Browse files

Fixed count suggestions strip

Bug: 5023981
Change-Id: I434d23bdfb653989866d3822c978cd929a2b553c
parent 5bcf8ee6
No related branches found
No related tags found
No related merge requests found
...@@ -51,5 +51,4 @@ ...@@ -51,5 +51,4 @@
<fraction name="key_uppercase_letter_ratio">29%</fraction> <fraction name="key_uppercase_letter_ratio">29%</fraction>
<dimen name="candidate_strip_padding">40.0mm</dimen> <dimen name="candidate_strip_padding">40.0mm</dimen>
<integer name="candidate_count_in_strip">5</integer>
</resources> </resources>
...@@ -59,5 +59,4 @@ ...@@ -59,5 +59,4 @@
<dimen name="key_preview_offset_ics">0.05in</dimen> <dimen name="key_preview_offset_ics">0.05in</dimen>
<dimen name="candidate_strip_padding">40.0mm</dimen> <dimen name="candidate_strip_padding">40.0mm</dimen>
<integer name="candidate_count_in_strip">5</integer>
</resources> </resources>
...@@ -116,6 +116,7 @@ ...@@ -116,6 +116,7 @@
<attr name="colorAutoCorrect" format="color" /> <attr name="colorAutoCorrect" format="color" />
<attr name="colorSuggested" format="color" /> <attr name="colorSuggested" format="color" />
<attr name="candidateCountInStrip" format="integer" /> <attr name="candidateCountInStrip" format="integer" />
<attr name="centerCandidatePercentile" format="integer" />
</declare-styleable> </declare-styleable>
<declare-styleable name="Keyboard"> <declare-styleable name="Keyboard">
......
...@@ -86,6 +86,7 @@ ...@@ -86,6 +86,7 @@
<dimen name="candidate_padding">6dip</dimen> <dimen name="candidate_padding">6dip</dimen>
<dimen name="candidate_text_size">18dip</dimen> <dimen name="candidate_text_size">18dip</dimen>
<integer name="candidate_count_in_strip">3</integer> <integer name="candidate_count_in_strip">3</integer>
<integer name="center_candidate_percentile">40</integer>
<!-- If the screen height in landscape is larger than the below value, then the keyboard <!-- If the screen height in landscape is larger than the below value, then the keyboard
will not go into extract (fullscreen) mode. --> will not go into extract (fullscreen) mode. -->
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
--> -->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Symbols that are suggested between words --> <!-- Symbols that are suggested between words -->
<string name="suggested_punctuations">!?,\u0022\u0027:();-/@_</string> <string name="suggested_punctuations">!?,\u0022:;()\u0027-/@_</string>
<!-- Symbols that should be swapped with a magic space --> <!-- Symbols that should be swapped with a magic space -->
<string name="magic_space_swapping_symbols">.,;:!?)]}\u0022</string> <string name="magic_space_swapping_symbols">.,;:!?)]}\u0022</string>
<!-- Symbols that should strip a magic space --> <!-- Symbols that should strip a magic space -->
......
...@@ -90,6 +90,7 @@ ...@@ -90,6 +90,7 @@
<item name="colorAutoCorrect">#FFFCAE00</item> <item name="colorAutoCorrect">#FFFCAE00</item>
<item name="colorSuggested">#FFFCAE00</item> <item name="colorSuggested">#FFFCAE00</item>
<item name="candidateCountInStrip">@integer/candidate_count_in_strip</item> <item name="candidateCountInStrip">@integer/candidate_count_in_strip</item>
<item name="centerCandidatePercentile">@integer/center_candidate_percentile</item>
</style> </style>
<!-- Theme "Basic high contrast" --> <!-- Theme "Basic high contrast" -->
<style name="KeyboardView.HighContrast" parent="KeyboardView"> <style name="KeyboardView.HighContrast" parent="KeyboardView">
...@@ -192,6 +193,7 @@ ...@@ -192,6 +193,7 @@
<item name="colorAutoCorrect">#FF3DC8FF</item> <item name="colorAutoCorrect">#FF3DC8FF</item>
<item name="colorSuggested">#FFFFFFFF</item> <item name="colorSuggested">#FFFFFFFF</item>
<item name="candidateCountInStrip">@integer/candidate_count_in_strip</item> <item name="candidateCountInStrip">@integer/candidate_count_in_strip</item>
<item name="centerCandidatePercentile">@integer/center_candidate_percentile</item>
</style> </style>
<style name="PopupMiniKeyboardAnimation"> <style name="PopupMiniKeyboardAnimation">
<item name="android:windowEnterAnimation">@anim/mini_keyboard_fadein</item> <item name="android:windowEnterAnimation">@anim/mini_keyboard_fadein</item>
......
...@@ -259,12 +259,15 @@ public class CandidateView extends LinearLayout implements OnClickListener { ...@@ -259,12 +259,15 @@ public class CandidateView extends LinearLayout implements OnClickListener {
private static class SuggestionsStripParams extends CandidateViewParams { private static class SuggestionsStripParams extends CandidateViewParams {
private static final int DEFAULT_CANDIDATE_COUNT_IN_STRIP = 3; 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 static final int PUNCTUATIONS_IN_STRIP = 6;
private final int mColorTypedWord; private final int mColorTypedWord;
private final int mColorAutoCorrect; private final int mColorAutoCorrect;
private final int mColorSuggestedCandidate; private final int mColorSuggestedCandidate;
private final int mCandidateCountInStrip; private final int mCandidateCountInStrip;
private final float mCenterCandidateWeight;
private final int mCenterCandidateIndex;
private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD); private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD);
private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan(); private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan();
...@@ -278,17 +281,6 @@ public class CandidateView extends LinearLayout implements OnClickListener { ...@@ -278,17 +281,6 @@ public class CandidateView extends LinearLayout implements OnClickListener {
private final int mAutoCorrectHighlight; private final int mAutoCorrectHighlight;
private final ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(); private final ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>();
private SuggestedWords mSuggestedWords;
private int mCountInStrip;
// True if the mCountInStrip suggestions can fit in suggestion strip in equally divided
// width without squeezing the text.
private boolean mCanUseFixedWidthColumns;
private int mMaxWidth;
private int mAvailableWidthForWords;
private int mConstantWidthForPaddings;
private int mVariableWidthForWords;
private float mScaleX;
public SuggestionsStripParams(Context context, AttributeSet attrs, int defStyle, public SuggestionsStripParams(Context context, AttributeSet attrs, int defStyle,
List<TextView> words, List<View> dividers, List<TextView> infos, View control) { List<TextView> words, List<View> dividers, List<TextView> infos, View control) {
...@@ -302,8 +294,13 @@ public class CandidateView extends LinearLayout implements OnClickListener { ...@@ -302,8 +294,13 @@ public class CandidateView extends LinearLayout implements OnClickListener {
mCandidateCountInStrip = a.getInt( mCandidateCountInStrip = a.getInt(
R.styleable.CandidateView_candidateCountInStrip, R.styleable.CandidateView_candidateCountInStrip,
DEFAULT_CANDIDATE_COUNT_IN_STRIP); DEFAULT_CANDIDATE_COUNT_IN_STRIP);
mCenterCandidateWeight = a.getInt(
R.styleable.CandidateView_centerCandidatePercentile,
DEFAULT_CENTER_CANDIDATE_PERCENTILE) / 100.0f;
a.recycle(); a.recycle();
mCenterCandidateIndex = mCandidateCountInStrip / 2;
mInvertedForegroundColorSpan = new ForegroundColorSpan(mColorTypedWord ^ 0x00ffffff); mInvertedForegroundColorSpan = new ForegroundColorSpan(mColorTypedWord ^ 0x00ffffff);
mInvertedBackgroundColorSpan = new BackgroundColorSpan(mColorTypedWord); mInvertedBackgroundColorSpan = new BackgroundColorSpan(mColorTypedWord);
...@@ -328,35 +325,38 @@ public class CandidateView extends LinearLayout implements OnClickListener { ...@@ -328,35 +325,38 @@ public class CandidateView extends LinearLayout implements OnClickListener {
return spannedWord; return spannedWord;
} }
private int getWordPosition(int index) { private static boolean willAutoCorrect(SuggestedWords suggestions) {
if (index >= 2) { 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; return index;
} }
final boolean willAutoCorrect = !mSuggestedWords.mTypedWordValid
&& mSuggestedWords.mHasMinimalSuggestion;
return willAutoCorrect ? 1 - index : index;
} }
private int getCandidateTextColor(int pos) { private int getCandidateTextColor(int index, SuggestedWords suggestions, int pos) {
final SuggestedWords suggestions = mSuggestedWords;
final boolean isAutoCorrect = suggestions.mHasMinimalSuggestion
&& ((pos == 1 && !suggestions.mTypedWordValid)
|| (pos == 0 && suggestions.mTypedWordValid));
// TODO: Need to revisit this logic with bigram suggestions // TODO: Need to revisit this logic with bigram suggestions
final boolean isSuggestedCandidate = (pos != 0); final boolean isSuggestedCandidate = (pos != 0);
final boolean isPunctuationSuggestions = suggestions.isPunctuationSuggestions();
final int color; final int color;
if (isPunctuationSuggestions) { if (index == mCenterCandidateIndex && willAutoCorrect(suggestions)) {
color = mColorTypedWord;
} else if (isAutoCorrect) {
color = mColorAutoCorrect; color = mColorAutoCorrect;
} else if (isSuggestedCandidate) { } else if (isSuggestedCandidate) {
color = mColorSuggestedCandidate; color = mColorSuggestedCandidate;
} else { } else {
color = mColorTypedWord; color = mColorTypedWord;
} }
final SuggestedWordInfo info = suggestions.getInfo(pos);
final SuggestedWordInfo info = (pos < suggestions.size())
? suggestions.getInfo(pos) : null;
if (info != null && info.isPreviousSuggestedWord()) { if (info != null && info.isPreviousSuggestedWord()) {
return applyAlpha(color, 0.5f); return applyAlpha(color, 0.5f);
} else { } else {
...@@ -381,136 +381,105 @@ public class CandidateView extends LinearLayout implements OnClickListener { ...@@ -381,136 +381,105 @@ public class CandidateView extends LinearLayout implements OnClickListener {
public int layout(SuggestedWords suggestions, ViewGroup stripView, ViewGroup paneView, public int layout(SuggestedWords suggestions, ViewGroup stripView, ViewGroup paneView,
int stripWidth) { int stripWidth) {
mSuggestedWords = suggestions; if (suggestions.isPunctuationSuggestions()) {
final int maxCount = suggestions.isPunctuationSuggestions() return layoutPunctuationSuggestions(suggestions, stripView);
? PUNCTUATIONS_IN_STRIP : mCandidateCountInStrip; }
final int size = suggestions.size();
setupTexts(suggestions, size); final int countInStrip = mCandidateCountInStrip;
mCountInStrip = Math.min(maxCount, size); setupTexts(suggestions, countInStrip);
mScaleX = 1.0f; final int maxWidth = (suggestions.size() <= countInStrip)
calculateParameters(size, stripWidth); ? stripWidth : stripWidth - mControlWidth;
int infoX = 0; int x = 0;
for (int index = 0; index < mCountInStrip; index++) { for (int index = 0; index < countInStrip; index++) {
final int pos = getWordPosition(index); final int pos = getWordPosition(index, suggestions);
final TextView word = mWords.get(pos);
final View divider = mDividers.get(pos); if (index != 0) {
final TextPaint paint = word.getPaint(); final View divider = mDividers.get(pos);
// TODO: Reorder candidates in strip as appropriate. The center candidate should // Add divider if this isn't the left most suggestion in candidate strip.
// hold the word when space is typed (valid typed word or auto corrected word). stripView.addView(divider);
word.setTextColor(getCandidateTextColor(pos)); }
final CharSequence styled = mTexts.get(pos); final CharSequence styled = mTexts.get(pos);
final TextView word = mWords.get(pos);
// 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, maxWidth);
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);
final TextView info;
if (DBG) { if (DBG) {
final CharSequence debugInfo = getDebugInfo(mSuggestedWords, index); final CharSequence debugInfo = getDebugInfo(suggestions, pos);
if (debugInfo != null) { if (debugInfo != null) {
info = mInfos.get(index); final TextView info = mInfos.get(pos);
info.setText(debugInfo); info.setText(debugInfo);
} else {
info = null;
}
} else {
info = null;
}
final CharSequence text;
final float scaleX;
if (index == 0 && mCountInStrip == 1) {
text = getEllipsizedText(styled, mMaxWidth, paint);
scaleX = paint.getTextScaleX();
} else {
text = styled;
scaleX = mScaleX;
}
word.setText(text);
word.setTextScaleX(scaleX);
if (index != 0) {
// Add divider if this isn't the left most suggestion in candidate strip.
stripView.addView(divider);
}
stripView.addView(word);
if (mCanUseFixedWidthColumns) {
setLayoutWeight(word, 1.0f, mCandidateStripHeight);
} else {
final int width = getTextWidth(text, paint) + mPadding;
setLayoutWeight(word, width, mCandidateStripHeight);
}
if (info != null) {
paneView.addView(info); paneView.addView(info);
info.measure(WRAP_CONTENT, WRAP_CONTENT); info.measure(WRAP_CONTENT, WRAP_CONTENT);
final int width = info.getMeasuredWidth(); final int infoWidth = info.getMeasuredWidth();
final int y = info.getMeasuredHeight(); final int y = info.getMeasuredHeight();
FrameLayoutCompatUtils.placeViewAt(info, infoX, 0, width, y); FrameLayoutCompatUtils.placeViewAt(info, x, 0, infoWidth, y);
infoX += width * 2; x += infoWidth * 2;
} }
}
} }
return mCountInStrip; return countInStrip;
} }
private void calculateParameters(int size, int maxWidth) { private int getCandidateWidth(int index, int maxWidth) {
do { final int paddings = mPadding * mCandidateCountInStrip;
mMaxWidth = maxWidth; final int dividers = mDividerWidth * (mCandidateCountInStrip - 1);
if (size > mCountInStrip) { final int availableWidth = maxWidth - paddings - dividers;
mMaxWidth -= mControlWidth; return (int)(availableWidth * getCandidateWeight(index));
} }
tryLayout();
if (mCanUseFixedWidthColumns) {
return;
}
if (mVariableWidthForWords <= mAvailableWidthForWords) {
return;
}
final float scaleX = mAvailableWidthForWords / (float)mVariableWidthForWords;
if (scaleX >= MIN_TEXT_XSCALE) {
mScaleX = scaleX;
return;
}
mCountInStrip--; private float getCandidateWeight(int index) {
} while (mCountInStrip > 1); if (index == mCenterCandidateIndex) {
} return mCenterCandidateWeight;
} else {
private void tryLayout() { // TODO: Revisit this for cases of 5 or more suggestions
final int maxCount = mCountInStrip; return (1.0f - mCenterCandidateWeight) / (mCandidateCountInStrip - 1);
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 void setupTexts(SuggestedWords suggestions, int count) { private void setupTexts(SuggestedWords suggestions, int countInStrip) {
mTexts.clear(); mTexts.clear();
for (int i = 0; i < count; i++) { final int count = Math.min(suggestions.size(), countInStrip);
final CharSequence word = suggestions.getWord(i); for (int pos = 0; pos < count; pos++) {
final boolean isAutoCorrect = suggestions.mHasMinimalSuggestion final CharSequence word = suggestions.getWord(pos);
&& ((i == 1 && !suggestions.mTypedWordValid) final boolean isAutoCorrect = pos == 1 && willAutoCorrect(suggestions);
|| (i == 0 && suggestions.mTypedWordValid));
final CharSequence styled = getStyledCandidateWord(word, isAutoCorrect); final CharSequence styled = getStyledCandidateWord(word, isAutoCorrect);
mTexts.add(styled); mTexts.add(styled);
} }
for (int pos = count; pos < countInStrip; pos++) {
// Make this inactive for touches in layout().
mTexts.add(null);
}
} }
@Override private int layoutPunctuationSuggestions(SuggestedWords suggestions, ViewGroup stripView) {
public String toString() { final int countInStrip = Math.min(suggestions.size(), PUNCTUATIONS_IN_STRIP);
return String.format( for (int index = 0; index < countInStrip; index++) {
"count=%d width=%d avail=%d fixcol=%s scaleX=%4.2f const=%d var=%d", if (index != 0) {
mCountInStrip, mMaxWidth, mAvailableWidthForWords, mCanUseFixedWidthColumns, // Add divider if this isn't the left most suggestion in candidate strip.
mScaleX, mConstantWidthForPaddings, mVariableWidthForWords); 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);
stripView.addView(word);
setLayoutWeight(word, 1.0f, mCandidateStripHeight);
}
return countInStrip;
} }
} }
...@@ -548,13 +517,13 @@ public class CandidateView extends LinearLayout implements OnClickListener { ...@@ -548,13 +517,13 @@ public class CandidateView extends LinearLayout implements OnClickListener {
mPreviewPopup.setBackgroundDrawable(null); mPreviewPopup.setBackgroundDrawable(null);
mCandidatesStrip = (ViewGroup)findViewById(R.id.candidates_strip); mCandidatesStrip = (ViewGroup)findViewById(R.id.candidates_strip);
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); final TextView word = (TextView)inflater.inflate(R.layout.candidate_word, null);
word.setTag(i); word.setTag(pos);
word.setOnClickListener(this); word.setOnClickListener(this);
mWords.add(word); mWords.add(word);
final View divider = inflater.inflate(R.layout.candidate_divider, null); final View divider = inflater.inflate(R.layout.candidate_divider, null);
divider.setTag(i); divider.setTag(pos);
divider.setOnClickListener(this); divider.setOnClickListener(this);
mDividers.add(divider); mDividers.add(divider);
mInfos.add((TextView)inflater.inflate(R.layout.candidate_info, null)); mInfos.add((TextView)inflater.inflate(R.layout.candidate_info, null));
...@@ -649,7 +618,7 @@ public class CandidateView extends LinearLayout implements OnClickListener { ...@@ -649,7 +618,7 @@ public class CandidateView extends LinearLayout implements OnClickListener {
} }
private static CharSequence getDebugInfo(SuggestedWords suggestions, int pos) { private static CharSequence getDebugInfo(SuggestedWords suggestions, int pos) {
if (DBG) { if (DBG && pos < suggestions.size()) {
final SuggestedWordInfo wordInfo = suggestions.getInfo(pos); final SuggestedWordInfo wordInfo = suggestions.getInfo(pos);
if (wordInfo != null) { if (wordInfo != null) {
final CharSequence debugInfo = wordInfo.getDebugString(); final CharSequence debugInfo = wordInfo.getDebugString();
...@@ -695,14 +664,17 @@ public class CandidateView extends LinearLayout implements OnClickListener { ...@@ -695,14 +664,17 @@ public class CandidateView extends LinearLayout implements OnClickListener {
TextPaint paint) { TextPaint paint) {
paint.setTextScaleX(1.0f); paint.setTextScaleX(1.0f);
final int width = getTextWidth(text, paint); 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) { if (scaleX >= MIN_TEXT_XSCALE) {
paint.setTextScaleX(scaleX); paint.setTextScaleX(scaleX);
return text; return text;
} }
// Note that TextUtils.ellipsize() use text-x-scale as 1.0 if ellipsize is needed. To get // 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( final CharSequence ellipsized = TextUtils.ellipsize(
text, paint, maxWidth / MIN_TEXT_XSCALE, TextUtils.TruncateAt.MIDDLE); text, paint, maxWidth / MIN_TEXT_XSCALE, TextUtils.TruncateAt.MIDDLE);
paint.setTextScaleX(MIN_TEXT_XSCALE); paint.setTextScaleX(MIN_TEXT_XSCALE);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment