SuggestionStripView.java revision 5faf41951929301af80026bc3191812ef874fd5a
1923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/*
2913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka * Copyright (C) 2011 The Android Open Source Project
3e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka *
4923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * use this file except in compliance with the License. You may obtain a copy of
6923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * the License at
7e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka *
8923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0
9e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka *
10923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
11923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * License for the specific language governing permissions and limitations under
14923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * the License.
15923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */
16923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
178c3d5b6961a9b9d40c4bf21ad495f852971c24f4Tadashi G. Takaokapackage com.android.inputmethod.latin.suggestions;
18923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
19923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.Context;
2066a787b953d703201c6b827abbee74e8cd9bb063Amith Yamasaniimport android.content.res.Resources;
2108a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaokaimport android.content.res.TypedArray;
220967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaokaimport android.graphics.Bitmap;
230967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaokaimport android.graphics.Canvas;
246f7218627eda110a8454053f8ecb7b80edfdc8cesatokimport android.graphics.Color;
250967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaokaimport android.graphics.Paint;
260967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaokaimport android.graphics.Paint.Align;
270967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaokaimport android.graphics.Rect;
28923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.graphics.Typeface;
290967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaokaimport android.graphics.drawable.BitmapDrawable;
30bfc1d732ac92fa1bd5b19ad9e7c71ce9a1f9dc00Tadashi G. Takaokaimport android.graphics.drawable.ColorDrawable;
31e583371b13ebd9aa336d0883e8c174ae4918c758Tadashi G. Takaokaimport android.graphics.drawable.Drawable;
32e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaokaimport android.os.Message;
33522d5c16d20657e7d26009fe9c067404e16a6c2aTadashi G. Takaokaimport android.text.Spannable;
34522d5c16d20657e7d26009fe9c067404e16a6c2aTadashi G. Takaokaimport android.text.SpannableString;
35e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaokaimport android.text.Spanned;
36b47319867ef3834a222865b8cb6abe62962e70f7Tadashi G. Takaokaimport android.text.TextPaint;
37e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaokaimport android.text.TextUtils;
38522d5c16d20657e7d26009fe9c067404e16a6c2aTadashi G. Takaokaimport android.text.style.CharacterStyle;
3974b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaokaimport android.text.style.StyleSpan;
40522d5c16d20657e7d26009fe9c067404e16a6c2aTadashi G. Takaokaimport android.text.style.UnderlineSpan;
41923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.util.AttributeSet;
423fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaokaimport android.view.GestureDetector;
43923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.view.Gravity;
44923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.view.LayoutInflater;
4529e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaokaimport android.view.MotionEvent;
46923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.view.View;
47e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaokaimport android.view.View.OnClickListener;
48ae5c736e37973e26b201d45ff6c139862a6e05cfTadashi G. Takaokaimport android.view.View.OnLongClickListener;
49e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaokaimport android.view.ViewGroup;
50e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaokaimport android.widget.LinearLayout;
51923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.widget.PopupWindow;
52c412309b7a32308b1b0a175dafc13f90254353c5Tadashi G. Takaokaimport android.widget.RelativeLayout;
53923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.widget.TextView;
54923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
557339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaokaimport com.android.inputmethod.keyboard.Keyboard;
5629e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyboardActionListener;
577339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyboardSwitcher;
581b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyboardView;
5929e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaokaimport com.android.inputmethod.keyboard.MoreKeysPanel;
6029e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaokaimport com.android.inputmethod.keyboard.PointerTracker;
61c8461d1ae26d43a09c0f835d833f3d49fbe8d2f3Tadashi G. Takaokaimport com.android.inputmethod.keyboard.ViewLayoutUtils;
62f72b8f5662afb42b08eb88e92731d52f99df13e3Jean Chalardimport com.android.inputmethod.latin.AutoCorrection;
635f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaokaimport com.android.inputmethod.latin.CollectionUtils;
645faf41951929301af80026bc3191812ef874fd5aTadashi G. Takaokaimport com.android.inputmethod.latin.Constants;
658c3d5b6961a9b9d40c4bf21ad495f852971c24f4Tadashi G. Takaokaimport com.android.inputmethod.latin.LatinImeLogger;
668c3d5b6961a9b9d40c4bf21ad495f852971c24f4Tadashi G. Takaokaimport com.android.inputmethod.latin.R;
6792821614fd80112527e133f069e31ee48250314fTadashi G. Takaokaimport com.android.inputmethod.latin.ResourceUtils;
688c3d5b6961a9b9d40c4bf21ad495f852971c24f4Tadashi G. Takaokaimport com.android.inputmethod.latin.StaticInnerHandlerWrapper;
698c3d5b6961a9b9d40c4bf21ad495f852971c24f4Tadashi G. Takaokaimport com.android.inputmethod.latin.SuggestedWords;
70042557b45bf2554608cd22874633011157cd1752Jean Chalardimport com.android.inputmethod.latin.Utils;
71a9ca7867b5a7c0be115966211a05f5d460c8638cKurt Partridgeimport com.android.inputmethod.latin.define.ProductionFlag;
726b966160ac8570271547bf63217efa5e228d4accKurt Partridgeimport com.android.inputmethod.research.ResearchLogger;
73ddb61ea461b920d87be4ad78c8a36eec1013b965Tadashi G. Takaoka
74179ada958b0bb46c6b9c8eb8b220d84dd3db855aTadashi G. Takaokaimport java.util.ArrayList;
75179ada958b0bb46c6b9c8eb8b220d84dd3db855aTadashi G. Takaoka
76a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaokapublic final class SuggestionStripView extends RelativeLayout implements OnClickListener,
77c412309b7a32308b1b0a175dafc13f90254353c5Tadashi G. Takaoka        OnLongClickListener {
78c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaoka    public interface Listener {
793d8e7a62fc6182c12ca28c29ceeee52ed4b9eea7Jean Chalard        public boolean addWordToUserDictionary(String word);
806785b9072762e15bb49657ce7b7d228dab76e44aTadashi G. Takaoka        public void pickSuggestionManually(int index, CharSequence word);
81c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaoka    }
82c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaoka
8386e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka    // The maximum number of suggestions available. See {@link Suggest#mPrefMaxSuggestions}.
8429e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka    public static final int MAX_SUGGESTIONS = 18;
85923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
868c3d5b6961a9b9d40c4bf21ad495f852971c24f4Tadashi G. Takaoka    static final boolean DBG = LatinImeLogger.sDBG;
876f7218627eda110a8454053f8ecb7b80edfdc8cesatok
88913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka    private final ViewGroup mSuggestionsStrip;
893e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka    KeyboardView mKeyboardView;
9074b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka
9129e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka    private final View mMoreSuggestionsContainer;
9229e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka    private final MoreSuggestionsView mMoreSuggestionsView;
9329e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka    private final MoreSuggestions.Builder mMoreSuggestionsBuilder;
9429e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka    private final PopupWindow mMoreSuggestionsWindow;
9529e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka
965f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka    private final ArrayList<TextView> mWords = CollectionUtils.newArrayList();
975f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka    private final ArrayList<TextView> mInfos = CollectionUtils.newArrayList();
985f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka    private final ArrayList<View> mDividers = CollectionUtils.newArrayList();
9974b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka
10082411d47ba7e8133ed2390c6920945e139a738cesatok    private final PopupWindow mPreviewPopup;
10182411d47ba7e8133ed2390c6920945e139a738cesatok    private final TextView mPreviewText;
10266a787b953d703201c6b827abbee74e8cd9bb063Amith Yamasani
1033e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka    Listener mListener;
1043e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka    SuggestedWords mSuggestedWords = SuggestedWords.EMPTY;
1056a6075caba3865383eeeb52cccc63a28e4ae5900Amith Yamasani
1064702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka    private final SuggestionStripViewParams mParams;
107ae5c736e37973e26b201d45ff6c139862a6e05cfTadashi G. Takaoka    private static final float MIN_TEXT_XSCALE = 0.70f;
108b47319867ef3834a222865b8cb6abe62962e70f7Tadashi G. Takaoka
1094f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa    private final UiHandler mHandler = new UiHandler(this);
11055b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka
111a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaoka    private static final class UiHandler extends StaticInnerHandlerWrapper<SuggestionStripView> {
11255b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka        private static final int MSG_HIDE_PREVIEW = 0;
11355b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka
1143e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        public UiHandler(final SuggestionStripView outerInstance) {
1154f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa            super(outerInstance);
1164f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa        }
1174f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa
118e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka        @Override
1193e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        public void dispatchMessage(final Message msg) {
1204702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka            final SuggestionStripView suggestionStripView = getOuterInstance();
121e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka            switch (msg.what) {
122e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka            case MSG_HIDE_PREVIEW:
1234702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka                suggestionStripView.hidePreview();
124e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka                break;
125e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka            }
126e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka        }
12755b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka
12855b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka        public void cancelHidePreview() {
12955b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka            removeMessages(MSG_HIDE_PREVIEW);
13055b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka        }
13155b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka
13255b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka        public void cancelAllMessages() {
13355b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka            cancelHidePreview();
13455b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka        }
135e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka    }
136179ada958b0bb46c6b9c8eb8b220d84dd3db855aTadashi G. Takaoka
137a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaoka    private static final class SuggestionStripViewParams {
138d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka        private static final int DEFAULT_SUGGESTIONS_COUNT_IN_STRIP = 3;
139ef2bfad5903fb55adca61dbea51984fbc7e4375fTadashi G. Takaoka        private static final float DEFAULT_CENTER_SUGGESTION_PERCENTILE = 0.40f;
1409676038b6e9fb66760588d7f9c9c642cc493dd71Tadashi G. Takaoka        private static final int DEFAULT_MAX_MORE_SUGGESTIONS_ROW = 2;
141293db0873fd89b5c2bd077ffa5091321b3ab8149Ken Wakasa        private static final int PUNCTUATIONS_IN_STRIP = 5;
142d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka
14374b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka        public final int mPadding;
14474b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka        public final int mDividerWidth;
145913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        public final int mSuggestionsStripHeight;
146d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka        public final int mSuggestionsCountInStrip;
147c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        public final int mMoreSuggestionsRowHeight;
148c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        private int mMaxMoreSuggestionsRow;
1499676038b6e9fb66760588d7f9c9c642cc493dd71Tadashi G. Takaoka        public final float mMinMoreSuggestionsWidth;
1504269a7cebdfe5c76372d0f0dd187432f0594ffe5Tadashi G. Takaoka        public final int mMoreSuggestionsBottomGap;
15116713e5630b93fb5625df26745eb73271f189457Tadashi G. Takaoka
152660776e09b9a3b321074a94721d901a035ca1b9fKen Wakasa        private final ArrayList<TextView> mWords;
153660776e09b9a3b321074a94721d901a035ca1b9fKen Wakasa        private final ArrayList<View> mDividers;
154660776e09b9a3b321074a94721d901a035ca1b9fKen Wakasa        private final ArrayList<TextView> mInfos;
155d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka
156a44685ab326809185a0e4c44afabe8fd93441b4cTadashi G. Takaoka        private final int mColorValidTypedWord;
157d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka        private final int mColorTypedWord;
158d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka        private final int mColorAutoCorrect;
159913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        private final int mColorSuggested;
160697bf585f8dac6ffc4d008c02b63407cfdd83104Tadashi G. Takaoka        private final float mAlphaObsoleted;
161913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        private final float mCenterSuggestionWeight;
162913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        private final int mCenterSuggestionIndex;
163913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        private final Drawable mMoreSuggestionsHint;
1649bdce3a59e9bf00095bc92fae88b830b0a8455beFabrice Di Meglio        private static final String MORE_SUGGESTIONS_HINT = "\u2026";
1652eb8781dc2e42d55474fb6e5c461c56c066b8363Tadashi G. Takaoka        private static final String LEFTWARDS_ARROW = "\u2190";
166d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka
167d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka        private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD);
168d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka        private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan();
169d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka        private static final int AUTO_CORRECT_BOLD = 0x01;
170d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka        private static final int AUTO_CORRECT_UNDERLINE = 0x02;
17177a87e3cb5886b1319230906c77c0bc98b905248Tadashi G. Takaoka        private static final int VALID_TYPED_WORD_BOLD = 0x04;
172d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka
173d47a955610987c8abdab4d275c044aefc8a7f1dbTadashi G. Takaoka        private final int mSuggestionStripOption;
17474b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka
1755f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka        private final ArrayList<CharSequence> mTexts = CollectionUtils.newArrayList();
17674b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka
177ae5c736e37973e26b201d45ff6c139862a6e05cfTadashi G. Takaoka        public boolean mMoreSuggestionsAvailable;
178ae5c736e37973e26b201d45ff6c139862a6e05cfTadashi G. Takaoka
1799f763d14451685965039990891eb0bc8e8db0735Tadashi G. Takaoka        private final TextView mWordToSaveView;
1802eb8781dc2e42d55474fb6e5c461c56c066b8363Tadashi G. Takaoka        private final TextView mLeftwardsArrowView;
181c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka        private final TextView mHintToSaveView;
182c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka
1833e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        public SuggestionStripViewParams(final Context context, final AttributeSet attrs,
1843e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka                final int defStyle, final ArrayList<TextView> words, final ArrayList<View> dividers,
1853e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka                final ArrayList<TextView> infos) {
186d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka            mWords = words;
187d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka            mDividers = dividers;
188d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka            mInfos = infos;
189d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka
190d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka            final TextView word = words.get(0);
191d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka            final View divider = dividers.get(0);
192d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka            mPadding = word.getCompoundPaddingLeft() + word.getCompoundPaddingRight();
193d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka            divider.measure(
194d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
195d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka            mDividerWidth = divider.getMeasuredWidth();
196d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka
197d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka            final Resources res = word.getResources();
198d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka            mSuggestionsStripHeight = res.getDimensionPixelSize(R.dimen.suggestions_strip_height);
199d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka
2004702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka            final TypedArray a = context.obtainStyledAttributes(attrs,
2014702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka                    R.styleable.SuggestionStripView, defStyle, R.style.SuggestionStripViewStyle);
2024702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka            mSuggestionStripOption = a.getInt(
2034702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka                    R.styleable.SuggestionStripView_suggestionStripOption, 0);
20492821614fd80112527e133f069e31ee48250314fTadashi G. Takaoka            final float alphaValidTypedWord = ResourceUtils.getFraction(a,
205ef2bfad5903fb55adca61dbea51984fbc7e4375fTadashi G. Takaoka                    R.styleable.SuggestionStripView_alphaValidTypedWord, 1.0f);
20692821614fd80112527e133f069e31ee48250314fTadashi G. Takaoka            final float alphaTypedWord = ResourceUtils.getFraction(a,
207ef2bfad5903fb55adca61dbea51984fbc7e4375fTadashi G. Takaoka                    R.styleable.SuggestionStripView_alphaTypedWord, 1.0f);
20892821614fd80112527e133f069e31ee48250314fTadashi G. Takaoka            final float alphaAutoCorrect = ResourceUtils.getFraction(a,
209ef2bfad5903fb55adca61dbea51984fbc7e4375fTadashi G. Takaoka                    R.styleable.SuggestionStripView_alphaAutoCorrect, 1.0f);
21092821614fd80112527e133f069e31ee48250314fTadashi G. Takaoka            final float alphaSuggested = ResourceUtils.getFraction(a,
211ef2bfad5903fb55adca61dbea51984fbc7e4375fTadashi G. Takaoka                    R.styleable.SuggestionStripView_alphaSuggested, 1.0f);
21292821614fd80112527e133f069e31ee48250314fTadashi G. Takaoka            mAlphaObsoleted = ResourceUtils.getFraction(a,
213ef2bfad5903fb55adca61dbea51984fbc7e4375fTadashi G. Takaoka                    R.styleable.SuggestionStripView_alphaSuggested, 1.0f);
2144702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka            mColorValidTypedWord = applyAlpha(a.getColor(
2154702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka                    R.styleable.SuggestionStripView_colorValidTypedWord, 0), alphaValidTypedWord);
2164702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka            mColorTypedWord = applyAlpha(a.getColor(
2174702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka                    R.styleable.SuggestionStripView_colorTypedWord, 0), alphaTypedWord);
2184702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka            mColorAutoCorrect = applyAlpha(a.getColor(
2194702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka                    R.styleable.SuggestionStripView_colorAutoCorrect, 0), alphaAutoCorrect);
2204702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka            mColorSuggested = applyAlpha(a.getColor(
2214702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka                    R.styleable.SuggestionStripView_colorSuggested, 0), alphaSuggested);
222913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            mSuggestionsCountInStrip = a.getInt(
2234702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka                    R.styleable.SuggestionStripView_suggestionsCountInStrip,
224913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                    DEFAULT_SUGGESTIONS_COUNT_IN_STRIP);
22592821614fd80112527e133f069e31ee48250314fTadashi G. Takaoka            mCenterSuggestionWeight = ResourceUtils.getFraction(a,
2264702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka                    R.styleable.SuggestionStripView_centerSuggestionPercentile,
227697bf585f8dac6ffc4d008c02b63407cfdd83104Tadashi G. Takaoka                    DEFAULT_CENTER_SUGGESTION_PERCENTILE);
2289676038b6e9fb66760588d7f9c9c642cc493dd71Tadashi G. Takaoka            mMaxMoreSuggestionsRow = a.getInt(
2294702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka                    R.styleable.SuggestionStripView_maxMoreSuggestionsRow,
2309676038b6e9fb66760588d7f9c9c642cc493dd71Tadashi G. Takaoka                    DEFAULT_MAX_MORE_SUGGESTIONS_ROW);
23192821614fd80112527e133f069e31ee48250314fTadashi G. Takaoka            mMinMoreSuggestionsWidth = ResourceUtils.getFraction(a,
232ef2bfad5903fb55adca61dbea51984fbc7e4375fTadashi G. Takaoka                    R.styleable.SuggestionStripView_minMoreSuggestionsWidth, 1.0f);
233d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka            a.recycle();
234d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka
2350967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka            mMoreSuggestionsHint = getMoreSuggestionsHint(res,
2360967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka                    res.getDimension(R.dimen.more_suggestions_hint_text_size), mColorAutoCorrect);
237913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            mCenterSuggestionIndex = mSuggestionsCountInStrip / 2;
2384269a7cebdfe5c76372d0f0dd187432f0594ffe5Tadashi G. Takaoka            mMoreSuggestionsBottomGap = res.getDimensionPixelOffset(
2394269a7cebdfe5c76372d0f0dd187432f0594ffe5Tadashi G. Takaoka                    R.dimen.more_suggestions_bottom_gap);
240c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka            mMoreSuggestionsRowHeight = res.getDimensionPixelSize(
241c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka                    R.dimen.more_suggestions_row_height);
24260a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka
243c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            final LayoutInflater inflater = LayoutInflater.from(context);
244913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            mWordToSaveView = (TextView)inflater.inflate(R.layout.suggestion_word, null);
2452eb8781dc2e42d55474fb6e5c461c56c066b8363Tadashi G. Takaoka            mLeftwardsArrowView = (TextView)inflater.inflate(R.layout.hint_add_to_dictionary, null);
2462eb8781dc2e42d55474fb6e5c461c56c066b8363Tadashi G. Takaoka            mHintToSaveView = (TextView)inflater.inflate(R.layout.hint_add_to_dictionary, null);
24774b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka        }
24874b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka
249c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        public int getMaxMoreSuggestionsRow() {
250c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka            return mMaxMoreSuggestionsRow;
251c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        }
252c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka
253c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        private int getMoreSuggestionsHeight() {
254c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka            return mMaxMoreSuggestionsRow * mMoreSuggestionsRowHeight + mMoreSuggestionsBottomGap;
255c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        }
256c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka
2573e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        public int setMoreSuggestionsHeight(final int remainingHeight) {
258c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka            final int currentHeight = getMoreSuggestionsHeight();
259c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka            if (currentHeight <= remainingHeight) {
260c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka                return currentHeight;
261c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka            }
262c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka
263c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka            mMaxMoreSuggestionsRow = (remainingHeight - mMoreSuggestionsBottomGap)
264c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka                    / mMoreSuggestionsRowHeight;
265c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka            final int newHeight = getMoreSuggestionsHeight();
266c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka            return newHeight;
267c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        }
268c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka
2693e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        private static Drawable getMoreSuggestionsHint(final Resources res, final float textSize,
2703e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka                final int color) {
2710967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka            final Paint paint = new Paint();
2720967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka            paint.setAntiAlias(true);
2730967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka            paint.setTextAlign(Align.CENTER);
2740967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka            paint.setTextSize(textSize);
2750967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka            paint.setColor(color);
2760967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka            final Rect bounds = new Rect();
277c9f562a9a4c2b634eb43d1ddaef69068c0d93f6fKen Wakasa            paint.getTextBounds(MORE_SUGGESTIONS_HINT, 0, MORE_SUGGESTIONS_HINT.length(), bounds);
2780967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka            final int width = Math.round(bounds.width() + 0.5f);
2790967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka            final int height = Math.round(bounds.height() + 0.5f);
2800967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka            final Bitmap buffer = Bitmap.createBitmap(
2810967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka                    width, (height * 3 / 2), Bitmap.Config.ARGB_8888);
2820967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka            final Canvas canvas = new Canvas(buffer);
2830967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka            canvas.drawText(MORE_SUGGESTIONS_HINT, width / 2, height, paint);
2840967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka            return new BitmapDrawable(res, buffer);
2850967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka        }
2860967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka
2873e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        private CharSequence getStyledSuggestionWord(final SuggestedWords suggestedWords,
2883e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka                final int pos) {
289e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka            final CharSequence word = suggestedWords.getWord(pos);
290e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka            final boolean isAutoCorrect = pos == 1 && suggestedWords.willAutoCorrect();
291e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka            final boolean isTypedWordValid = pos == 0 && suggestedWords.mTypedWordValid;
292d47a955610987c8abdab4d275c044aefc8a7f1dbTadashi G. Takaoka            if (!isAutoCorrect && !isTypedWordValid)
293d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka                return word;
294d47a955610987c8abdab4d275c044aefc8a7f1dbTadashi G. Takaoka
295d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka            final int len = word.length();
296d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka            final Spannable spannedWord = new SpannableString(word);
297d47a955610987c8abdab4d275c044aefc8a7f1dbTadashi G. Takaoka            final int option = mSuggestionStripOption;
298d47a955610987c8abdab4d275c044aefc8a7f1dbTadashi G. Takaoka            if ((isAutoCorrect && (option & AUTO_CORRECT_BOLD) != 0)
299d47a955610987c8abdab4d275c044aefc8a7f1dbTadashi G. Takaoka                    || (isTypedWordValid && (option & VALID_TYPED_WORD_BOLD) != 0)) {
300d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka                spannedWord.setSpan(BOLD_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
301d47a955610987c8abdab4d275c044aefc8a7f1dbTadashi G. Takaoka            }
302d47a955610987c8abdab4d275c044aefc8a7f1dbTadashi G. Takaoka            if (isAutoCorrect && (option & AUTO_CORRECT_UNDERLINE) != 0) {
303d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka                spannedWord.setSpan(UNDERLINE_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
304d47a955610987c8abdab4d275c044aefc8a7f1dbTadashi G. Takaoka            }
305d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka            return spannedWord;
306d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka        }
307d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka
3083e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        private int getWordPosition(final int index, final SuggestedWords suggestedWords) {
30960a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka            // TODO: This works for 3 suggestions. Revisit this algorithm when there are 5 or more
31060a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka            // suggestions.
311e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka            final int centerPos = suggestedWords.willAutoCorrect() ? 1 : 0;
312913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            if (index == mCenterSuggestionIndex) {
31360a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                return centerPos;
31460a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka            } else if (index == centerPos) {
315913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                return mCenterSuggestionIndex;
31660a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka            } else {
317d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka                return index;
318d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka            }
319d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka        }
320d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka
3213e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        private int getSuggestionTextColor(final int index, final SuggestedWords suggestedWords,
3223e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka                final int pos) {
323d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka            // TODO: Need to revisit this logic with bigram suggestions
324913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            final boolean isSuggested = (pos != 0);
325d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka
326d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka            final int color;
327e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka            if (index == mCenterSuggestionIndex && suggestedWords.willAutoCorrect()) {
328d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka                color = mColorAutoCorrect;
329e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka            } else if (index == mCenterSuggestionIndex && suggestedWords.mTypedWordValid) {
330a44685ab326809185a0e4c44afabe8fd93441b4cTadashi G. Takaoka                color = mColorValidTypedWord;
331913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            } else if (isSuggested) {
332913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                color = mColorSuggested;
333d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka            } else {
334d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka                color = mColorTypedWord;
335d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka            }
336c0c20b386fb06b688f56dd7f807e2bcbb70b3a59Jean Chalard            if (LatinImeLogger.sDBG && suggestedWords.size() > 1) {
337ebbb7e9665b2f9bce46ddc2daa82b9ef73f6c712Jean Chalard                // If we auto-correct, then the autocorrection is in slot 0 and the typed word
338ebbb7e9665b2f9bce46ddc2daa82b9ef73f6c712Jean Chalard                // is in slot 1.
339f72b8f5662afb42b08eb88e92731d52f99df13e3Jean Chalard                if (index == mCenterSuggestionIndex
340f72b8f5662afb42b08eb88e92731d52f99df13e3Jean Chalard                        && AutoCorrection.shouldBlockAutoCorrectionBySafetyNet(
341f72b8f5662afb42b08eb88e92731d52f99df13e3Jean Chalard                                suggestedWords.getWord(1).toString(), suggestedWords.getWord(0))) {
3422aa1dd45c44295e2f7e8ece1b520032d86b9f908satok                    return 0xFFFF0000;
3432aa1dd45c44295e2f7e8ece1b520032d86b9f908satok                }
3442aa1dd45c44295e2f7e8ece1b520032d86b9f908satok            }
34560a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka
3464f6cf258fe208329247ade7a6701ac9709820717Jean Chalard            if (suggestedWords.mIsObsoleteSuggestions && isSuggested) {
347697bf585f8dac6ffc4d008c02b63407cfdd83104Tadashi G. Takaoka                return applyAlpha(color, mAlphaObsoleted);
348d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka            } else {
349d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka                return color;
350d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka            }
351d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka        }
352d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka
353d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka        private static int applyAlpha(final int color, final float alpha) {
354d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka            final int newAlpha = (int)(Color.alpha(color) * alpha);
355d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka            return Color.argb(newAlpha, Color.red(color), Color.green(color), Color.blue(color));
356d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka        }
357d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka
3582c47080e7b4b9b2a6c2dbf1444e041c1b7943316Tadashi G. Takaoka        private static void addDivider(final ViewGroup stripView, final View divider) {
3592c47080e7b4b9b2a6c2dbf1444e041c1b7943316Tadashi G. Takaoka            stripView.addView(divider);
3602c47080e7b4b9b2a6c2dbf1444e041c1b7943316Tadashi G. Takaoka            final LinearLayout.LayoutParams params =
3612c47080e7b4b9b2a6c2dbf1444e041c1b7943316Tadashi G. Takaoka                    (LinearLayout.LayoutParams)divider.getLayoutParams();
3622c47080e7b4b9b2a6c2dbf1444e041c1b7943316Tadashi G. Takaoka            params.gravity = Gravity.CENTER;
3632c47080e7b4b9b2a6c2dbf1444e041c1b7943316Tadashi G. Takaoka        }
3642c47080e7b4b9b2a6c2dbf1444e041c1b7943316Tadashi G. Takaoka
3653e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        public void layout(final SuggestedWords suggestedWords, final ViewGroup stripView,
3663e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka                final ViewGroup placer, final int stripWidth) {
367e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka            if (suggestedWords.mIsPunctuationSuggestions) {
368e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka                layoutPunctuationSuggestions(suggestedWords, stripView);
3693a566c48b0eb3625788e1cce3fb2f58d8f4b380dTadashi G. Takaoka                return;
37060a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka            }
37160a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka
372913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            final int countInStrip = mSuggestionsCountInStrip;
373e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka            setupTexts(suggestedWords, countInStrip);
374e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka            mMoreSuggestionsAvailable = (suggestedWords.size() > countInStrip);
37560a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka            int x = 0;
37660a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka            for (int index = 0; index < countInStrip; index++) {
377e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka                final int pos = getWordPosition(index, suggestedWords);
37860a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka
37960a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                if (index != 0) {
38060a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                    final View divider = mDividers.get(pos);
381913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                    // Add divider if this isn't the left most suggestion in suggestions strip.
3822c47080e7b4b9b2a6c2dbf1444e041c1b7943316Tadashi G. Takaoka                    addDivider(stripView, divider);
383013ba3e3e785980b74db83ac5ab1e11b438485daTadashi G. Takaoka                    x += divider.getMeasuredWidth();
38460a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                }
38560a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka
38616713e5630b93fb5625df26745eb73271f189457Tadashi G. Takaoka                final CharSequence styled = mTexts.get(pos);
38760a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                final TextView word = mWords.get(pos);
388913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                if (index == mCenterSuggestionIndex && mMoreSuggestionsAvailable) {
389ae5c736e37973e26b201d45ff6c139862a6e05cfTadashi G. Takaoka                    // TODO: This "more suggestions hint" should have nicely designed icon.
390ae5c736e37973e26b201d45ff6c139862a6e05cfTadashi G. Takaoka                    word.setCompoundDrawablesWithIntrinsicBounds(
391913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                            null, null, null, mMoreSuggestionsHint);
392580e1d7ac0cb90a464a3b3aed88047f85a4ab41fTadashi G. Takaoka                    // HACK: To align with other TextView that has no compound drawables.
393913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                    word.setCompoundDrawablePadding(-mMoreSuggestionsHint.getIntrinsicHeight());
394ae5c736e37973e26b201d45ff6c139862a6e05cfTadashi G. Takaoka                } else {
395580e1d7ac0cb90a464a3b3aed88047f85a4ab41fTadashi G. Takaoka                    word.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
396ae5c736e37973e26b201d45ff6c139862a6e05cfTadashi G. Takaoka                }
397ae5c736e37973e26b201d45ff6c139862a6e05cfTadashi G. Takaoka
398913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                // Disable this suggestion if the suggestion is null or empty.
39960a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                word.setEnabled(!TextUtils.isEmpty(styled));
400e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka                word.setTextColor(getSuggestionTextColor(index, suggestedWords, pos));
401913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                final int width = getSuggestionWidth(index, stripWidth);
40260a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                final CharSequence text = getEllipsizedText(styled, width, word.getPaint());
40360a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                final float scaleX = word.getTextScaleX();
40460a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                word.setText(text); // TextView.setText() resets text scale x to 1.0.
40560a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                word.setTextScaleX(scaleX);
40660a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                stripView.addView(word);
407b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaoka                setLayoutWeight(
408913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                        word, getSuggestionWeight(index), ViewGroup.LayoutParams.MATCH_PARENT);
409013ba3e3e785980b74db83ac5ab1e11b438485daTadashi G. Takaoka                x += word.getMeasuredWidth();
41016713e5630b93fb5625df26745eb73271f189457Tadashi G. Takaoka
411042557b45bf2554608cd22874633011157cd1752Jean Chalard                if (DBG && pos < suggestedWords.size()) {
412042557b45bf2554608cd22874633011157cd1752Jean Chalard                    final CharSequence debugInfo = Utils.getDebugInfo(suggestedWords, pos);
41316713e5630b93fb5625df26745eb73271f189457Tadashi G. Takaoka                    if (debugInfo != null) {
41460a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                        final TextView info = mInfos.get(pos);
41516713e5630b93fb5625df26745eb73271f189457Tadashi G. Takaoka                        info.setText(debugInfo);
416013ba3e3e785980b74db83ac5ab1e11b438485daTadashi G. Takaoka                        placer.addView(info);
417b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaoka                        info.measure(ViewGroup.LayoutParams.WRAP_CONTENT,
418b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaoka                                ViewGroup.LayoutParams.WRAP_CONTENT);
41960a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                        final int infoWidth = info.getMeasuredWidth();
42016713e5630b93fb5625df26745eb73271f189457Tadashi G. Takaoka                        final int y = info.getMeasuredHeight();
421c8461d1ae26d43a09c0f835d833f3d49fbe8d2f3Tadashi G. Takaoka                        ViewLayoutUtils.placeViewAt(
422013ba3e3e785980b74db83ac5ab1e11b438485daTadashi G. Takaoka                                info, x - infoWidth, y, infoWidth, info.getMeasuredHeight());
42316713e5630b93fb5625df26745eb73271f189457Tadashi G. Takaoka                    }
42460a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                }
42516713e5630b93fb5625df26745eb73271f189457Tadashi G. Takaoka            }
42616713e5630b93fb5625df26745eb73271f189457Tadashi G. Takaoka        }
42716713e5630b93fb5625df26745eb73271f189457Tadashi G. Takaoka
4283e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        private int getSuggestionWidth(final int index, final int maxWidth) {
429913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            final int paddings = mPadding * mSuggestionsCountInStrip;
430913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            final int dividers = mDividerWidth * (mSuggestionsCountInStrip - 1);
43160a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka            final int availableWidth = maxWidth - paddings - dividers;
432913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            return (int)(availableWidth * getSuggestionWeight(index));
43360a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka        }
43474b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka
4353e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        private float getSuggestionWeight(final int index) {
436913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            if (index == mCenterSuggestionIndex) {
437913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                return mCenterSuggestionWeight;
43860a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka            } else {
43960a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                // TODO: Revisit this for cases of 5 or more suggestions
440913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                return (1.0f - mCenterSuggestionWeight) / (mSuggestionsCountInStrip - 1);
44174b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka            }
44274b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka        }
44374b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka
4443e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        private void setupTexts(final SuggestedWords suggestedWords, final int countInStrip) {
44574b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka            mTexts.clear();
446e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka            final int count = Math.min(suggestedWords.size(), countInStrip);
44760a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka            for (int pos = 0; pos < count; pos++) {
448e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka                final CharSequence styled = getStyledSuggestionWord(suggestedWords, pos);
44974b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka                mTexts.add(styled);
45074b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka            }
45160a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka            for (int pos = count; pos < countInStrip; pos++) {
45260a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                // Make this inactive for touches in layout().
45360a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                mTexts.add(null);
45460a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka            }
45574b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka        }
45674b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka
4573e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        private void layoutPunctuationSuggestions(final SuggestedWords suggestedWords,
4583e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka                final ViewGroup stripView) {
459e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka            final int countInStrip = Math.min(suggestedWords.size(), PUNCTUATIONS_IN_STRIP);
46060a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka            for (int index = 0; index < countInStrip; index++) {
46160a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                if (index != 0) {
462913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                    // Add divider if this isn't the left most suggestion in suggestions strip.
4632c47080e7b4b9b2a6c2dbf1444e041c1b7943316Tadashi G. Takaoka                    addDivider(stripView, mDividers.get(index));
46460a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                }
46560a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka
46660a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                final TextView word = mWords.get(index);
46760a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                word.setEnabled(true);
468a44685ab326809185a0e4c44afabe8fd93441b4cTadashi G. Takaoka                word.setTextColor(mColorAutoCorrect);
469e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka                final CharSequence text = suggestedWords.getWord(index);
47060a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                word.setText(text);
47160a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                word.setTextScaleX(1.0f);
472ae5c736e37973e26b201d45ff6c139862a6e05cfTadashi G. Takaoka                word.setCompoundDrawables(null, null, null, null);
47360a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                stripView.addView(word);
474913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                setLayoutWeight(word, 1.0f, mSuggestionsStripHeight);
47560a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka            }
476ae5c736e37973e26b201d45ff6c139862a6e05cfTadashi G. Takaoka            mMoreSuggestionsAvailable = false;
47774b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka        }
478c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka
4793e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        public void layoutAddToDictionaryHint(final CharSequence word, final ViewGroup stripView,
4803e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka                final int stripWidth, final CharSequence hintText, final OnClickListener listener) {
481c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            final int width = stripWidth - mDividerWidth - mPadding * 2;
482c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka
483c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            final TextView wordView = mWordToSaveView;
484c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            wordView.setTextColor(mColorTypedWord);
485913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            final int wordWidth = (int)(width * mCenterSuggestionWeight);
486c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            final CharSequence text = getEllipsizedText(word, wordWidth, wordView.getPaint());
487c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            final float wordScaleX = wordView.getTextScaleX();
488c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            wordView.setTag(word);
489c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            wordView.setText(text);
490c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            wordView.setTextScaleX(wordScaleX);
491c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            stripView.addView(wordView);
492913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            setLayoutWeight(wordView, mCenterSuggestionWeight, ViewGroup.LayoutParams.MATCH_PARENT);
493c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka
494c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            stripView.addView(mDividers.get(0));
495c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka
4962eb8781dc2e42d55474fb6e5c461c56c066b8363Tadashi G. Takaoka            final TextView leftArrowView = mLeftwardsArrowView;
4972eb8781dc2e42d55474fb6e5c461c56c066b8363Tadashi G. Takaoka            leftArrowView.setTextColor(mColorAutoCorrect);
4982eb8781dc2e42d55474fb6e5c461c56c066b8363Tadashi G. Takaoka            leftArrowView.setText(LEFTWARDS_ARROW);
4992eb8781dc2e42d55474fb6e5c461c56c066b8363Tadashi G. Takaoka            stripView.addView(leftArrowView);
5002eb8781dc2e42d55474fb6e5c461c56c066b8363Tadashi G. Takaoka
501c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            final TextView hintView = mHintToSaveView;
5022eb8781dc2e42d55474fb6e5c461c56c066b8363Tadashi G. Takaoka            hintView.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
503c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            hintView.setTextColor(mColorAutoCorrect);
5042eb8781dc2e42d55474fb6e5c461c56c066b8363Tadashi G. Takaoka            final int hintWidth = width - wordWidth - leftArrowView.getWidth();
505644d33d60ea5a87501274488013d65f55238895eKen Wakasa            final float hintScaleX = getTextScaleX(hintText, hintWidth, hintView.getPaint());
506644d33d60ea5a87501274488013d65f55238895eKen Wakasa            hintView.setText(hintText);
507c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            hintView.setTextScaleX(hintScaleX);
508c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            stripView.addView(hintView);
509b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaoka            setLayoutWeight(
510913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                    hintView, 1.0f - mCenterSuggestionWeight, ViewGroup.LayoutParams.MATCH_PARENT);
5119f763d14451685965039990891eb0bc8e8db0735Tadashi G. Takaoka
5129f763d14451685965039990891eb0bc8e8db0735Tadashi G. Takaoka            wordView.setOnClickListener(listener);
5139f763d14451685965039990891eb0bc8e8db0735Tadashi G. Takaoka            leftArrowView.setOnClickListener(listener);
5149f763d14451685965039990891eb0bc8e8db0735Tadashi G. Takaoka            hintView.setOnClickListener(listener);
5159f763d14451685965039990891eb0bc8e8db0735Tadashi G. Takaoka        }
5169f763d14451685965039990891eb0bc8e8db0735Tadashi G. Takaoka
5179f763d14451685965039990891eb0bc8e8db0735Tadashi G. Takaoka        public CharSequence getAddToDictionaryWord() {
5189f763d14451685965039990891eb0bc8e8db0735Tadashi G. Takaoka            return (CharSequence)mWordToSaveView.getTag();
5199f763d14451685965039990891eb0bc8e8db0735Tadashi G. Takaoka        }
5209f763d14451685965039990891eb0bc8e8db0735Tadashi G. Takaoka
5213e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        public boolean isAddToDictionaryShowing(final View v) {
5229f763d14451685965039990891eb0bc8e8db0735Tadashi G. Takaoka            return v == mWordToSaveView || v == mHintToSaveView || v == mLeftwardsArrowView;
523c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka        }
5242d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka
5253e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        private static void setLayoutWeight(final View v, final float weight, final int height) {
5262d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            final ViewGroup.LayoutParams lp = v.getLayoutParams();
5272d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            if (lp instanceof LinearLayout.LayoutParams) {
5282d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka                final LinearLayout.LayoutParams llp = (LinearLayout.LayoutParams)lp;
5292d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka                llp.weight = weight;
5302d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka                llp.width = 0;
5312d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka                llp.height = height;
5322d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            }
5332d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka        }
5342d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka
5353e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        private static float getTextScaleX(final CharSequence text, final int maxWidth,
5363e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka                final TextPaint paint) {
5372d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            paint.setTextScaleX(1.0f);
5382d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            final int width = getTextWidth(text, paint);
5392d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            if (width <= maxWidth) {
5402d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka                return 1.0f;
5412d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            }
5422d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            return maxWidth / (float)width;
5432d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka        }
5442d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka
5453e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        private static CharSequence getEllipsizedText(final CharSequence text, final int maxWidth,
5463e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka                final TextPaint paint) {
5472d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            if (text == null) return null;
5482d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            paint.setTextScaleX(1.0f);
5492d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            final int width = getTextWidth(text, paint);
5502d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            if (width <= maxWidth) {
5512d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka                return text;
5522d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            }
5532d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            final float scaleX = maxWidth / (float)width;
5542d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            if (scaleX >= MIN_TEXT_XSCALE) {
5552d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka                paint.setTextScaleX(scaleX);
5562d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka                return text;
5572d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            }
5582d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka
5592d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            // Note that TextUtils.ellipsize() use text-x-scale as 1.0 if ellipsize is needed. To
5602d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            // get squeezed and ellipsized text, passes enlarged width (maxWidth / MIN_TEXT_XSCALE).
5612d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            final CharSequence ellipsized = TextUtils.ellipsize(
5622d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka                    text, paint, maxWidth / MIN_TEXT_XSCALE, TextUtils.TruncateAt.MIDDLE);
5632d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            paint.setTextScaleX(MIN_TEXT_XSCALE);
5642d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            return ellipsized;
5652d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka        }
5662d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka
5673e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        private static int getTextWidth(final CharSequence text, final TextPaint paint) {
5682d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            if (TextUtils.isEmpty(text)) return 0;
5692d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            final Typeface savedTypeface = paint.getTypeface();
5702d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            paint.setTypeface(getTextTypeface(text));
5712d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            final int len = text.length();
5722d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            final float[] widths = new float[len];
5732d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            final int count = paint.getTextWidths(text, 0, len, widths);
5742d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            int width = 0;
5752d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            for (int i = 0; i < count; i++) {
5762d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka                width += Math.round(widths[i] + 0.5f);
5772d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            }
5782d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            paint.setTypeface(savedTypeface);
5792d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            return width;
5802d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka        }
5812d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka
5823e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        private static Typeface getTextTypeface(final CharSequence text) {
5832d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            if (!(text instanceof SpannableString))
5842d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka                return Typeface.DEFAULT;
5852d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka
5862d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            final SpannableString ss = (SpannableString)text;
5872d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            final StyleSpan[] styles = ss.getSpans(0, text.length(), StyleSpan.class);
5882d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            if (styles.length == 0)
5892d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka                return Typeface.DEFAULT;
5902d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka
5912d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            switch (styles[0].getStyle()) {
5922d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            case Typeface.BOLD: return Typeface.DEFAULT_BOLD;
5932d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            // TODO: BOLD_ITALIC, ITALIC case?
5942d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            default: return Typeface.DEFAULT;
5952d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            }
5962d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka        }
59774b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka    }
59874b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka
599923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
6004702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka     * Construct a {@link SuggestionStripView} for showing suggestions to be picked by the user.
601923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @param context
602923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @param attrs
603923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
6043e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka    public SuggestionStripView(final Context context, final AttributeSet attrs) {
6054702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka        this(context, attrs, R.attr.suggestionStripViewStyle);
60608a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka    }
60708a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka
6083e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka    public SuggestionStripView(final Context context, final AttributeSet attrs,
6093e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka            final int defStyle) {
610c412309b7a32308b1b0a175dafc13f90254353c5Tadashi G. Takaoka        super(context, attrs, defStyle);
611923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
61216713e5630b93fb5625df26745eb73271f189457Tadashi G. Takaoka        final LayoutInflater inflater = LayoutInflater.from(context);
613913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        inflater.inflate(R.layout.suggestions_strip, this);
61486e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka
61586e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka        mPreviewPopup = new PopupWindow(context);
616913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        mPreviewText = (TextView) inflater.inflate(R.layout.suggestion_preview, null);
617b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaoka        mPreviewPopup.setWindowLayoutMode(
618b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaoka                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
619923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        mPreviewPopup.setContentView(mPreviewText);
620923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        mPreviewPopup.setBackgroundDrawable(null);
621923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
622913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        mSuggestionsStrip = (ViewGroup)findViewById(R.id.suggestions_strip);
62360a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka        for (int pos = 0; pos < MAX_SUGGESTIONS; pos++) {
624913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            final TextView word = (TextView)inflater.inflate(R.layout.suggestion_word, null);
62560a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka            word.setTag(pos);
6262442e779857e7eda253aadcb1c4dff5ccb3e53f4Tadashi G. Takaoka            word.setOnClickListener(this);
627ae5c736e37973e26b201d45ff6c139862a6e05cfTadashi G. Takaoka            word.setOnLongClickListener(this);
6282442e779857e7eda253aadcb1c4dff5ccb3e53f4Tadashi G. Takaoka            mWords.add(word);
629913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            final View divider = inflater.inflate(R.layout.suggestion_divider, null);
63060a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka            divider.setTag(pos);
631a1aab83a24e74cb0fad4b1c1e59b18d23ecbdad3Tadashi G. Takaoka            divider.setOnClickListener(this);
632a1aab83a24e74cb0fad4b1c1e59b18d23ecbdad3Tadashi G. Takaoka            mDividers.add(divider);
633913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            mInfos.add((TextView)inflater.inflate(R.layout.suggestion_info, null));
634c9716b28ce438e06b5cacc07fc002944bcbe24a0Tadashi G. Takaoka        }
635179ada958b0bb46c6b9c8eb8b220d84dd3db855aTadashi G. Takaoka
6364702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka        mParams = new SuggestionStripViewParams(
6374702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka                context, attrs, defStyle, mWords, mDividers, mInfos);
63829e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka
63929e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        mMoreSuggestionsContainer = inflater.inflate(R.layout.more_suggestions, null);
64029e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        mMoreSuggestionsView = (MoreSuggestionsView)mMoreSuggestionsContainer
64129e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka                .findViewById(R.id.more_suggestions_view);
64229e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        mMoreSuggestionsBuilder = new MoreSuggestions.Builder(mMoreSuggestionsView);
643bfc1d732ac92fa1bd5b19ad9e7c71ce9a1f9dc00Tadashi G. Takaoka
644bfc1d732ac92fa1bd5b19ad9e7c71ce9a1f9dc00Tadashi G. Takaoka        final PopupWindow moreWindow = new PopupWindow(context);
645bfc1d732ac92fa1bd5b19ad9e7c71ce9a1f9dc00Tadashi G. Takaoka        moreWindow.setWindowLayoutMode(
64629e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
647bfc1d732ac92fa1bd5b19ad9e7c71ce9a1f9dc00Tadashi G. Takaoka        moreWindow.setBackgroundDrawable(new ColorDrawable(android.R.color.transparent));
648bfc1d732ac92fa1bd5b19ad9e7c71ce9a1f9dc00Tadashi G. Takaoka        moreWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
649bfc1d732ac92fa1bd5b19ad9e7c71ce9a1f9dc00Tadashi G. Takaoka        moreWindow.setFocusable(true);
650bfc1d732ac92fa1bd5b19ad9e7c71ce9a1f9dc00Tadashi G. Takaoka        moreWindow.setOutsideTouchable(true);
651bfc1d732ac92fa1bd5b19ad9e7c71ce9a1f9dc00Tadashi G. Takaoka        moreWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
652bfc1d732ac92fa1bd5b19ad9e7c71ce9a1f9dc00Tadashi G. Takaoka            @Override
653bfc1d732ac92fa1bd5b19ad9e7c71ce9a1f9dc00Tadashi G. Takaoka            public void onDismiss() {
654bfc1d732ac92fa1bd5b19ad9e7c71ce9a1f9dc00Tadashi G. Takaoka                mKeyboardView.dimEntireKeyboard(false);
655bfc1d732ac92fa1bd5b19ad9e7c71ce9a1f9dc00Tadashi G. Takaoka            }
656bfc1d732ac92fa1bd5b19ad9e7c71ce9a1f9dc00Tadashi G. Takaoka        });
657bfc1d732ac92fa1bd5b19ad9e7c71ce9a1f9dc00Tadashi G. Takaoka        mMoreSuggestionsWindow = moreWindow;
658bfc1d732ac92fa1bd5b19ad9e7c71ce9a1f9dc00Tadashi G. Takaoka
65950e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka        final Resources res = context.getResources();
66050e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka        mMoreSuggestionsModalTolerance = res.getDimensionPixelOffset(
66150e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka                R.dimen.more_suggestions_modal_tolerance);
6623fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka        mMoreSuggestionsSlidingDetector = new GestureDetector(
6633fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka                context, mMoreSuggestionsSlidingListener);
664923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
665c9716b28ce438e06b5cacc07fc002944bcbe24a0Tadashi G. Takaoka
666923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
667c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaoka     * A connection back to the input method.
668923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @param listener
669923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
6703e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka    public void setListener(final Listener listener, final View inputView) {
671c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaoka        mListener = listener;
6721b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka        mKeyboardView = (KeyboardView)inputView.findViewById(R.id.keyboard_view);
673923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
674923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
6753e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka    public void setSuggestions(final SuggestedWords suggestedWords) {
676e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka        clear();
677e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka        mSuggestedWords = suggestedWords;
678e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka        mParams.layout(mSuggestedWords, mSuggestionsStrip, this, getWidth());
679a9ca7867b5a7c0be115966211a05f5d460c8638cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
6804702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka            ResearchLogger.suggestionStripView_setSuggestions(mSuggestedWords);
681a9ca7867b5a7c0be115966211a05f5d460c8638cKurt Partridge        }
68216713e5630b93fb5625df26745eb73271f189457Tadashi G. Takaoka    }
68374b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka
6843e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka    public int setMoreSuggestionsHeight(final int remainingHeight) {
685c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        return mParams.setMoreSuggestionsHeight(remainingHeight);
686c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka    }
687b47319867ef3834a222865b8cb6abe62962e70f7Tadashi G. Takaoka
688b00a1d0c0adbdfc507676772201e979e539a2801Amith Yamasani    public boolean isShowingAddToDictionaryHint() {
689913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        return mSuggestionsStrip.getChildCount() > 0
6909f763d14451685965039990891eb0bc8e8db0735Tadashi G. Takaoka                && mParams.isAddToDictionaryShowing(mSuggestionsStrip.getChildAt(0));
691b00a1d0c0adbdfc507676772201e979e539a2801Amith Yamasani    }
692b00a1d0c0adbdfc507676772201e979e539a2801Amith Yamasani
6933e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka    public void showAddToDictionaryHint(final CharSequence word, final CharSequence hintText) {
694c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka        clear();
6959f763d14451685965039990891eb0bc8e8db0735Tadashi G. Takaoka        mParams.layoutAddToDictionaryHint(word, mSuggestionsStrip, getWidth(), hintText, this);
69666a787b953d703201c6b827abbee74e8cd9bb063Amith Yamasani    }
69766a787b953d703201c6b827abbee74e8cd9bb063Amith Yamasani
6986558253160e2039c87f424bd814f402ecd31de3bKen Wakasa    public boolean dismissAddToDictionaryHint() {
699c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka        if (isShowingAddToDictionaryHint()) {
700c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            clear();
701c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            return true;
702c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka        }
703c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka        return false;
7046558253160e2039c87f424bd814f402ecd31de3bKen Wakasa    }
7056558253160e2039c87f424bd814f402ecd31de3bKen Wakasa
7067e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka    public SuggestedWords getSuggestions() {
707e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka        return mSuggestedWords;
708979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
709979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
710923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void clear() {
711913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        mSuggestionsStrip.removeAllViews();
712c412309b7a32308b1b0a175dafc13f90254353c5Tadashi G. Takaoka        removeAllViews();
713c412309b7a32308b1b0a175dafc13f90254353c5Tadashi G. Takaoka        addView(mSuggestionsStrip);
7141b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka        dismissMoreSuggestions();
715923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
716923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
7173e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka    void hidePreview() {
718179ada958b0bb46c6b9c8eb8b220d84dd3db855aTadashi G. Takaoka        mPreviewPopup.dismiss();
7196ec55a1599c74150e82ea7e4371ec815f0d2df27Amith Yamasani    }
720e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka
72129e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka    private final KeyboardActionListener mMoreSuggestionsListener =
72229e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            new KeyboardActionListener.Adapter() {
72329e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        @Override
7243e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        public boolean onCustomRequest(final int requestCode) {
72529e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            final int index = requestCode;
726e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka            final CharSequence word = mSuggestedWords.getWord(index);
7276785b9072762e15bb49657ce7b7d228dab76e44aTadashi G. Takaoka            mListener.pickSuggestionManually(index, word);
7281b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka            dismissMoreSuggestions();
72929e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            return true;
73029e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        }
73129e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka
73229e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        @Override
73329e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        public void onCancelInput() {
7341b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka            dismissMoreSuggestions();
73529e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        }
73629e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka    };
73729e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka
73829e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka    private final MoreKeysPanel.Controller mMoreSuggestionsController =
73929e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            new MoreKeysPanel.Controller() {
74029e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        @Override
74129e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        public boolean dismissMoreKeysPanel() {
7421b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka            return dismissMoreSuggestions();
74329e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        }
74429e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka    };
74529e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka
7463e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka    boolean dismissMoreSuggestions() {
7471b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka        if (mMoreSuggestionsWindow.isShowing()) {
7481b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka            mMoreSuggestionsWindow.dismiss();
7491b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka            return true;
7501b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka        }
7511b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka        return false;
7521b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka    }
7531b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka
754e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka    @Override
7553e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka    public boolean onLongClick(final View view) {
7565faf41951929301af80026bc3191812ef874fd5aTadashi G. Takaoka        KeyboardSwitcher.getInstance().hapticAndAudioFeedback(Constants.NOT_A_CODE);
7573fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka        return showMoreSuggestions();
7583fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka    }
7593fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka
7603e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka    boolean showMoreSuggestions() {
7617339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        final Keyboard parentKeyboard = KeyboardSwitcher.getInstance().getKeyboard();
7627339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        if (parentKeyboard == null) {
7637339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka            return false;
764ae5c736e37973e26b201d45ff6c139862a6e05cfTadashi G. Takaoka        }
7657339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        final SuggestionStripViewParams params = mParams;
7667339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        if (!params.mMoreSuggestionsAvailable) {
7677339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka            return false;
7687339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        }
7697339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        final int stripWidth = getWidth();
7707339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        final View container = mMoreSuggestionsContainer;
7717339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        final int maxWidth = stripWidth - container.getPaddingLeft() - container.getPaddingRight();
7727339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        final MoreSuggestions.Builder builder = mMoreSuggestionsBuilder;
7737339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        builder.layout(mSuggestedWords, params.mSuggestionsCountInStrip, maxWidth,
7747339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka                (int)(maxWidth * params.mMinMoreSuggestionsWidth),
7757339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka                params.getMaxMoreSuggestionsRow(), parentKeyboard);
7767339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        mMoreSuggestionsView.setKeyboard(builder.build());
7777339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        container.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
7787339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka
7797339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        final MoreKeysPanel moreKeysPanel = mMoreSuggestionsView;
7807339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        final int pointX = stripWidth / 2;
7817339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        final int pointY = -params.mMoreSuggestionsBottomGap;
7827339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        moreKeysPanel.showMoreKeysPanel(this, mMoreSuggestionsController, pointX, pointY,
7837339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka                mMoreSuggestionsWindow, mMoreSuggestionsListener);
7847339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        mMoreSuggestionsMode = MORE_SUGGESTIONS_CHECKING_MODAL_OR_SLIDING;
7857339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        mOriginX = mLastX;
7867339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        mOriginY = mLastY;
7877339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        mKeyboardView.dimEntireKeyboard(true);
7887339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        for (int i = 0; i < params.mSuggestionsCountInStrip; i++) {
7897339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka            mWords.get(i).setPressed(false);
7907339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        }
7917339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        return true;
792ae5c736e37973e26b201d45ff6c139862a6e05cfTadashi G. Takaoka    }
793ae5c736e37973e26b201d45ff6c139862a6e05cfTadashi G. Takaoka
79450e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka    // Working variables for onLongClick and dispatchTouchEvent.
795d7b00f34904c61f955e439160e2dbaa3a4b6983cTadashi G. Takaoka    private int mMoreSuggestionsMode = MORE_SUGGESTIONS_IN_MODAL_MODE;
796d7b00f34904c61f955e439160e2dbaa3a4b6983cTadashi G. Takaoka    private static final int MORE_SUGGESTIONS_IN_MODAL_MODE = 0;
797d7b00f34904c61f955e439160e2dbaa3a4b6983cTadashi G. Takaoka    private static final int MORE_SUGGESTIONS_CHECKING_MODAL_OR_SLIDING = 1;
798d7b00f34904c61f955e439160e2dbaa3a4b6983cTadashi G. Takaoka    private static final int MORE_SUGGESTIONS_IN_SLIDING_MODE = 2;
79950e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka    private int mLastX;
80050e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka    private int mLastY;
80150e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka    private int mOriginX;
80250e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka    private int mOriginY;
80350e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka    private final int mMoreSuggestionsModalTolerance;
8043fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka    private final GestureDetector mMoreSuggestionsSlidingDetector;
8053fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka    private final GestureDetector.OnGestureListener mMoreSuggestionsSlidingListener =
8063fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka            new GestureDetector.SimpleOnGestureListener() {
8073fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka        @Override
8083fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka        public boolean onScroll(MotionEvent down, MotionEvent me, float deltaX, float deltaY) {
8093fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka            final float dy = me.getY() - down.getY();
8103fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka            if (deltaY > 0 && dy < 0) {
8113fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka                return showMoreSuggestions();
8123fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka            }
8133fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka            return false;
8143fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka        }
8153fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka    };
81650e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka
817ae5c736e37973e26b201d45ff6c139862a6e05cfTadashi G. Takaoka    @Override
8183e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka    public boolean dispatchTouchEvent(final MotionEvent me) {
819d7b00f34904c61f955e439160e2dbaa3a4b6983cTadashi G. Takaoka        if (!mMoreSuggestionsWindow.isShowing()
820d7b00f34904c61f955e439160e2dbaa3a4b6983cTadashi G. Takaoka                || mMoreSuggestionsMode == MORE_SUGGESTIONS_IN_MODAL_MODE) {
82150e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka            mLastX = (int)me.getX();
82250e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka            mLastY = (int)me.getY();
8233fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka            if (mMoreSuggestionsSlidingDetector.onTouchEvent(me)) {
8243fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka                return true;
8253fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka            }
82629e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            return super.dispatchTouchEvent(me);
82729e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        }
8288ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka
8298ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        final MoreKeysPanel moreKeysPanel = mMoreSuggestionsView;
83029e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        final int action = me.getAction();
83129e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        final long eventTime = me.getEventTime();
83229e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        final int index = me.getActionIndex();
83329e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        final int id = me.getPointerId(index);
8348ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        final PointerTracker tracker = PointerTracker.getPointerTracker(id, moreKeysPanel);
8358ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        final int x = (int)me.getX(index);
8368ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        final int y = (int)me.getY(index);
8378ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        final int translatedX = moreKeysPanel.translateX(x);
8388ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        final int translatedY = moreKeysPanel.translateY(y);
8398ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka
840d7b00f34904c61f955e439160e2dbaa3a4b6983cTadashi G. Takaoka        if (mMoreSuggestionsMode == MORE_SUGGESTIONS_CHECKING_MODAL_OR_SLIDING) {
84187104bace9cb9ed1a700f05a37743a21c2722fffTadashi G. Takaoka            if (Math.abs(x - mOriginX) >= mMoreSuggestionsModalTolerance
84287104bace9cb9ed1a700f05a37743a21c2722fffTadashi G. Takaoka                    || mOriginY - y >= mMoreSuggestionsModalTolerance) {
84387104bace9cb9ed1a700f05a37743a21c2722fffTadashi G. Takaoka                // Decided to be in the sliding input mode only when the touch point has been moved
84487104bace9cb9ed1a700f05a37743a21c2722fffTadashi G. Takaoka                // upward.
845d7b00f34904c61f955e439160e2dbaa3a4b6983cTadashi G. Takaoka                mMoreSuggestionsMode = MORE_SUGGESTIONS_IN_SLIDING_MODE;
846e51d164482c7896892d6eccb80f1e1e6fe6d50dbTadashi G. Takaoka                tracker.onShowMoreKeysPanel(translatedX, translatedY, moreKeysPanel);
84750e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka            } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP) {
84850e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka                // Decided to be in the modal input mode
849d7b00f34904c61f955e439160e2dbaa3a4b6983cTadashi G. Takaoka                mMoreSuggestionsMode = MORE_SUGGESTIONS_IN_MODAL_MODE;
85050e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka            }
85150e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka            return true;
85250e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka        }
85350e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka
854d7b00f34904c61f955e439160e2dbaa3a4b6983cTadashi G. Takaoka        // MORE_SUGGESTIONS_IN_SLIDING_MODE
8558ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        tracker.processMotionEvent(action, translatedX, translatedY, eventTime, moreKeysPanel);
85629e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        return true;
85729e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka    }
85829e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka
85929e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka    @Override
8603e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka    public void onClick(final View view) {
8619f763d14451685965039990891eb0bc8e8db0735Tadashi G. Takaoka        if (mParams.isAddToDictionaryShowing(view)) {
8623d8e7a62fc6182c12ca28c29ceeee52ed4b9eea7Jean Chalard            mListener.addWordToUserDictionary(mParams.getAddToDictionaryWord().toString());
863717a8f50aec421f74e4d43432059c2fb41cb32c7Tadashi G. Takaoka            clear();
864717a8f50aec421f74e4d43432059c2fb41cb32c7Tadashi G. Takaoka            return;
865717a8f50aec421f74e4d43432059c2fb41cb32c7Tadashi G. Takaoka        }
866717a8f50aec421f74e4d43432059c2fb41cb32c7Tadashi G. Takaoka
86786e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka        final Object tag = view.getTag();
86886e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka        if (!(tag instanceof Integer))
86986e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka            return;
87086e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka        final int index = (Integer) tag;
871e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka        if (index >= mSuggestedWords.size())
87237deb112c727dfed5e94d054cf5f00f5d60c8120Tadashi G. Takaoka            return;
87386e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka
874e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka        final CharSequence word = mSuggestedWords.getWord(index);
8756785b9072762e15bb49657ce7b7d228dab76e44aTadashi G. Takaoka        mListener.pickSuggestionManually(index, word);
876923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
87782411d47ba7e8133ed2390c6920945e139a738cesatok
8786ec55a1599c74150e82ea7e4371ec815f0d2df27Amith Yamasani    @Override
87973cd4c1428ac370e1c15c5f61b1ce499f0d4f4c7Tadashi G. Takaoka    protected void onDetachedFromWindow() {
8806ec55a1599c74150e82ea7e4371ec815f0d2df27Amith Yamasani        super.onDetachedFromWindow();
88155b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka        mHandler.cancelAllMessages();
8826ec55a1599c74150e82ea7e4371ec815f0d2df27Amith Yamasani        hidePreview();
88332f4ceb2434b6f204cf8152b406d0f9a642feb73Tadashi G. Takaoka        dismissMoreSuggestions();
8846ec55a1599c74150e82ea7e4371ec815f0d2df27Amith Yamasani    }
885923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project}
886