SuggestionStripView.java revision 3e5a3c18bebbfb56012383411b24ee81ffde09cb
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;
648c3d5b6961a9b9d40c4bf21ad495f852971c24f4Tadashi G. Takaokaimport com.android.inputmethod.latin.LatinImeLogger;
658c3d5b6961a9b9d40c4bf21ad495f852971c24f4Tadashi G. Takaokaimport com.android.inputmethod.latin.R;
6692821614fd80112527e133f069e31ee48250314fTadashi G. Takaokaimport com.android.inputmethod.latin.ResourceUtils;
678c3d5b6961a9b9d40c4bf21ad495f852971c24f4Tadashi G. Takaokaimport com.android.inputmethod.latin.StaticInnerHandlerWrapper;
688c3d5b6961a9b9d40c4bf21ad495f852971c24f4Tadashi G. Takaokaimport com.android.inputmethod.latin.SuggestedWords;
69042557b45bf2554608cd22874633011157cd1752Jean Chalardimport com.android.inputmethod.latin.Utils;
70a9ca7867b5a7c0be115966211a05f5d460c8638cKurt Partridgeimport com.android.inputmethod.latin.define.ProductionFlag;
716b966160ac8570271547bf63217efa5e228d4accKurt Partridgeimport com.android.inputmethod.research.ResearchLogger;
72ddb61ea461b920d87be4ad78c8a36eec1013b965Tadashi G. Takaoka
73179ada958b0bb46c6b9c8eb8b220d84dd3db855aTadashi G. Takaokaimport java.util.ArrayList;
74179ada958b0bb46c6b9c8eb8b220d84dd3db855aTadashi G. Takaoka
75a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaokapublic final class SuggestionStripView extends RelativeLayout implements OnClickListener,
76c412309b7a32308b1b0a175dafc13f90254353c5Tadashi G. Takaoka        OnLongClickListener {
77c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaoka    public interface Listener {
783d8e7a62fc6182c12ca28c29ceeee52ed4b9eea7Jean Chalard        public boolean addWordToUserDictionary(String word);
796785b9072762e15bb49657ce7b7d228dab76e44aTadashi G. Takaoka        public void pickSuggestionManually(int index, CharSequence word);
80c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaoka    }
81c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaoka
8286e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka    // The maximum number of suggestions available. See {@link Suggest#mPrefMaxSuggestions}.
8329e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka    public static final int MAX_SUGGESTIONS = 18;
84923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
858c3d5b6961a9b9d40c4bf21ad495f852971c24f4Tadashi G. Takaoka    static final boolean DBG = LatinImeLogger.sDBG;
866f7218627eda110a8454053f8ecb7b80edfdc8cesatok
87913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka    private final ViewGroup mSuggestionsStrip;
883e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka    KeyboardView mKeyboardView;
8974b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka
9029e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka    private final View mMoreSuggestionsContainer;
9129e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka    private final MoreSuggestionsView mMoreSuggestionsView;
9229e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka    private final MoreSuggestions.Builder mMoreSuggestionsBuilder;
9329e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka    private final PopupWindow mMoreSuggestionsWindow;
9429e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka
955f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka    private final ArrayList<TextView> mWords = CollectionUtils.newArrayList();
965f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka    private final ArrayList<TextView> mInfos = CollectionUtils.newArrayList();
975f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka    private final ArrayList<View> mDividers = CollectionUtils.newArrayList();
9874b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka
9982411d47ba7e8133ed2390c6920945e139a738cesatok    private final PopupWindow mPreviewPopup;
10082411d47ba7e8133ed2390c6920945e139a738cesatok    private final TextView mPreviewText;
10166a787b953d703201c6b827abbee74e8cd9bb063Amith Yamasani
1023e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka    Listener mListener;
1033e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka    SuggestedWords mSuggestedWords = SuggestedWords.EMPTY;
1046a6075caba3865383eeeb52cccc63a28e4ae5900Amith Yamasani
1054702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka    private final SuggestionStripViewParams mParams;
106ae5c736e37973e26b201d45ff6c139862a6e05cfTadashi G. Takaoka    private static final float MIN_TEXT_XSCALE = 0.70f;
107b47319867ef3834a222865b8cb6abe62962e70f7Tadashi G. Takaoka
1084f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa    private final UiHandler mHandler = new UiHandler(this);
10955b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka
110a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaoka    private static final class UiHandler extends StaticInnerHandlerWrapper<SuggestionStripView> {
11155b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka        private static final int MSG_HIDE_PREVIEW = 0;
11255b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka
1133e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        public UiHandler(final SuggestionStripView outerInstance) {
1144f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa            super(outerInstance);
1154f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa        }
1164f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa
117e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka        @Override
1183e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        public void dispatchMessage(final Message msg) {
1194702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka            final SuggestionStripView suggestionStripView = getOuterInstance();
120e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka            switch (msg.what) {
121e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka            case MSG_HIDE_PREVIEW:
1224702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka                suggestionStripView.hidePreview();
123e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka                break;
124e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka            }
125e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka        }
12655b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka
12755b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka        public void cancelHidePreview() {
12855b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka            removeMessages(MSG_HIDE_PREVIEW);
12955b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka        }
13055b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka
13155b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka        public void cancelAllMessages() {
13255b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka            cancelHidePreview();
13355b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka        }
134e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka    }
135179ada958b0bb46c6b9c8eb8b220d84dd3db855aTadashi G. Takaoka
136a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaoka    private static final class SuggestionStripViewParams {
137d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka        private static final int DEFAULT_SUGGESTIONS_COUNT_IN_STRIP = 3;
138ef2bfad5903fb55adca61dbea51984fbc7e4375fTadashi G. Takaoka        private static final float DEFAULT_CENTER_SUGGESTION_PERCENTILE = 0.40f;
1399676038b6e9fb66760588d7f9c9c642cc493dd71Tadashi G. Takaoka        private static final int DEFAULT_MAX_MORE_SUGGESTIONS_ROW = 2;
140293db0873fd89b5c2bd077ffa5091321b3ab8149Ken Wakasa        private static final int PUNCTUATIONS_IN_STRIP = 5;
141d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka
14274b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka        public final int mPadding;
14374b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka        public final int mDividerWidth;
144913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        public final int mSuggestionsStripHeight;
145d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka        public final int mSuggestionsCountInStrip;
146c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        public final int mMoreSuggestionsRowHeight;
147c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        private int mMaxMoreSuggestionsRow;
1489676038b6e9fb66760588d7f9c9c642cc493dd71Tadashi G. Takaoka        public final float mMinMoreSuggestionsWidth;
1494269a7cebdfe5c76372d0f0dd187432f0594ffe5Tadashi G. Takaoka        public final int mMoreSuggestionsBottomGap;
15016713e5630b93fb5625df26745eb73271f189457Tadashi G. Takaoka
151660776e09b9a3b321074a94721d901a035ca1b9fKen Wakasa        private final ArrayList<TextView> mWords;
152660776e09b9a3b321074a94721d901a035ca1b9fKen Wakasa        private final ArrayList<View> mDividers;
153660776e09b9a3b321074a94721d901a035ca1b9fKen Wakasa        private final ArrayList<TextView> mInfos;
154d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka
155a44685ab326809185a0e4c44afabe8fd93441b4cTadashi G. Takaoka        private final int mColorValidTypedWord;
156d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka        private final int mColorTypedWord;
157d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka        private final int mColorAutoCorrect;
158913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        private final int mColorSuggested;
159697bf585f8dac6ffc4d008c02b63407cfdd83104Tadashi G. Takaoka        private final float mAlphaObsoleted;
160913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        private final float mCenterSuggestionWeight;
161913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        private final int mCenterSuggestionIndex;
162913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        private final Drawable mMoreSuggestionsHint;
1639bdce3a59e9bf00095bc92fae88b830b0a8455beFabrice Di Meglio        private static final String MORE_SUGGESTIONS_HINT = "\u2026";
1642eb8781dc2e42d55474fb6e5c461c56c066b8363Tadashi G. Takaoka        private static final String LEFTWARDS_ARROW = "\u2190";
165d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka
166d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka        private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD);
167d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka        private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan();
168d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka        private static final int AUTO_CORRECT_BOLD = 0x01;
169d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka        private static final int AUTO_CORRECT_UNDERLINE = 0x02;
17077a87e3cb5886b1319230906c77c0bc98b905248Tadashi G. Takaoka        private static final int VALID_TYPED_WORD_BOLD = 0x04;
171d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka
172d47a955610987c8abdab4d275c044aefc8a7f1dbTadashi G. Takaoka        private final int mSuggestionStripOption;
17374b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka
1745f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka        private final ArrayList<CharSequence> mTexts = CollectionUtils.newArrayList();
17574b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka
176ae5c736e37973e26b201d45ff6c139862a6e05cfTadashi G. Takaoka        public boolean mMoreSuggestionsAvailable;
177ae5c736e37973e26b201d45ff6c139862a6e05cfTadashi G. Takaoka
1789f763d14451685965039990891eb0bc8e8db0735Tadashi G. Takaoka        private final TextView mWordToSaveView;
1792eb8781dc2e42d55474fb6e5c461c56c066b8363Tadashi G. Takaoka        private final TextView mLeftwardsArrowView;
180c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka        private final TextView mHintToSaveView;
181c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka
1823e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        public SuggestionStripViewParams(final Context context, final AttributeSet attrs,
1833e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka                final int defStyle, final ArrayList<TextView> words, final ArrayList<View> dividers,
1843e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka                final ArrayList<TextView> infos) {
185d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka            mWords = words;
186d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka            mDividers = dividers;
187d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka            mInfos = infos;
188d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka
189d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka            final TextView word = words.get(0);
190d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka            final View divider = dividers.get(0);
191d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka            mPadding = word.getCompoundPaddingLeft() + word.getCompoundPaddingRight();
192d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka            divider.measure(
193d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
194d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka            mDividerWidth = divider.getMeasuredWidth();
195d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka
196d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka            final Resources res = word.getResources();
197d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka            mSuggestionsStripHeight = res.getDimensionPixelSize(R.dimen.suggestions_strip_height);
198d3e50a2acf04638f09786c487326f2c09e738f32Tadashi G. Takaoka
1994702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka            final TypedArray a = context.obtainStyledAttributes(attrs,
2004702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka                    R.styleable.SuggestionStripView, defStyle, R.style.SuggestionStripViewStyle);
2014702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka            mSuggestionStripOption = a.getInt(
2024702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka                    R.styleable.SuggestionStripView_suggestionStripOption, 0);
20392821614fd80112527e133f069e31ee48250314fTadashi G. Takaoka            final float alphaValidTypedWord = ResourceUtils.getFraction(a,
204ef2bfad5903fb55adca61dbea51984fbc7e4375fTadashi G. Takaoka                    R.styleable.SuggestionStripView_alphaValidTypedWord, 1.0f);
20592821614fd80112527e133f069e31ee48250314fTadashi G. Takaoka            final float alphaTypedWord = ResourceUtils.getFraction(a,
206ef2bfad5903fb55adca61dbea51984fbc7e4375fTadashi G. Takaoka                    R.styleable.SuggestionStripView_alphaTypedWord, 1.0f);
20792821614fd80112527e133f069e31ee48250314fTadashi G. Takaoka            final float alphaAutoCorrect = ResourceUtils.getFraction(a,
208ef2bfad5903fb55adca61dbea51984fbc7e4375fTadashi G. Takaoka                    R.styleable.SuggestionStripView_alphaAutoCorrect, 1.0f);
20992821614fd80112527e133f069e31ee48250314fTadashi G. Takaoka            final float alphaSuggested = ResourceUtils.getFraction(a,
210ef2bfad5903fb55adca61dbea51984fbc7e4375fTadashi G. Takaoka                    R.styleable.SuggestionStripView_alphaSuggested, 1.0f);
21192821614fd80112527e133f069e31ee48250314fTadashi G. Takaoka            mAlphaObsoleted = ResourceUtils.getFraction(a,
212ef2bfad5903fb55adca61dbea51984fbc7e4375fTadashi G. Takaoka                    R.styleable.SuggestionStripView_alphaSuggested, 1.0f);
2134702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka            mColorValidTypedWord = applyAlpha(a.getColor(
2144702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka                    R.styleable.SuggestionStripView_colorValidTypedWord, 0), alphaValidTypedWord);
2154702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka            mColorTypedWord = applyAlpha(a.getColor(
2164702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka                    R.styleable.SuggestionStripView_colorTypedWord, 0), alphaTypedWord);
2174702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka            mColorAutoCorrect = applyAlpha(a.getColor(
2184702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka                    R.styleable.SuggestionStripView_colorAutoCorrect, 0), alphaAutoCorrect);
2194702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka            mColorSuggested = applyAlpha(a.getColor(
2204702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka                    R.styleable.SuggestionStripView_colorSuggested, 0), alphaSuggested);
221913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            mSuggestionsCountInStrip = a.getInt(
2224702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka                    R.styleable.SuggestionStripView_suggestionsCountInStrip,
223913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                    DEFAULT_SUGGESTIONS_COUNT_IN_STRIP);
22492821614fd80112527e133f069e31ee48250314fTadashi G. Takaoka            mCenterSuggestionWeight = ResourceUtils.getFraction(a,
2254702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka                    R.styleable.SuggestionStripView_centerSuggestionPercentile,
226697bf585f8dac6ffc4d008c02b63407cfdd83104Tadashi G. Takaoka                    DEFAULT_CENTER_SUGGESTION_PERCENTILE);
2279676038b6e9fb66760588d7f9c9c642cc493dd71Tadashi G. Takaoka            mMaxMoreSuggestionsRow = a.getInt(
2284702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka                    R.styleable.SuggestionStripView_maxMoreSuggestionsRow,
2299676038b6e9fb66760588d7f9c9c642cc493dd71Tadashi G. Takaoka                    DEFAULT_MAX_MORE_SUGGESTIONS_ROW);
23092821614fd80112527e133f069e31ee48250314fTadashi G. Takaoka            mMinMoreSuggestionsWidth = ResourceUtils.getFraction(a,
231ef2bfad5903fb55adca61dbea51984fbc7e4375fTadashi G. Takaoka                    R.styleable.SuggestionStripView_minMoreSuggestionsWidth, 1.0f);
232d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka            a.recycle();
233d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka
2340967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka            mMoreSuggestionsHint = getMoreSuggestionsHint(res,
2350967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka                    res.getDimension(R.dimen.more_suggestions_hint_text_size), mColorAutoCorrect);
236913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            mCenterSuggestionIndex = mSuggestionsCountInStrip / 2;
2374269a7cebdfe5c76372d0f0dd187432f0594ffe5Tadashi G. Takaoka            mMoreSuggestionsBottomGap = res.getDimensionPixelOffset(
2384269a7cebdfe5c76372d0f0dd187432f0594ffe5Tadashi G. Takaoka                    R.dimen.more_suggestions_bottom_gap);
239c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka            mMoreSuggestionsRowHeight = res.getDimensionPixelSize(
240c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka                    R.dimen.more_suggestions_row_height);
24160a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka
242c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            final LayoutInflater inflater = LayoutInflater.from(context);
243913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            mWordToSaveView = (TextView)inflater.inflate(R.layout.suggestion_word, null);
2442eb8781dc2e42d55474fb6e5c461c56c066b8363Tadashi G. Takaoka            mLeftwardsArrowView = (TextView)inflater.inflate(R.layout.hint_add_to_dictionary, null);
2452eb8781dc2e42d55474fb6e5c461c56c066b8363Tadashi G. Takaoka            mHintToSaveView = (TextView)inflater.inflate(R.layout.hint_add_to_dictionary, null);
24674b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka        }
24774b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka
248c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        public int getMaxMoreSuggestionsRow() {
249c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka            return mMaxMoreSuggestionsRow;
250c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        }
251c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka
252c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        private int getMoreSuggestionsHeight() {
253c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka            return mMaxMoreSuggestionsRow * mMoreSuggestionsRowHeight + mMoreSuggestionsBottomGap;
254c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        }
255c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka
2563e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        public int setMoreSuggestionsHeight(final int remainingHeight) {
257c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka            final int currentHeight = getMoreSuggestionsHeight();
258c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka            if (currentHeight <= remainingHeight) {
259c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka                return currentHeight;
260c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka            }
261c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka
262c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka            mMaxMoreSuggestionsRow = (remainingHeight - mMoreSuggestionsBottomGap)
263c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka                    / mMoreSuggestionsRowHeight;
264c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka            final int newHeight = getMoreSuggestionsHeight();
265c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka            return newHeight;
266c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        }
267c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka
2683e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        private static Drawable getMoreSuggestionsHint(final Resources res, final float textSize,
2693e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka                final int color) {
2700967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka            final Paint paint = new Paint();
2710967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka            paint.setAntiAlias(true);
2720967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka            paint.setTextAlign(Align.CENTER);
2730967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka            paint.setTextSize(textSize);
2740967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka            paint.setColor(color);
2750967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka            final Rect bounds = new Rect();
276c9f562a9a4c2b634eb43d1ddaef69068c0d93f6fKen Wakasa            paint.getTextBounds(MORE_SUGGESTIONS_HINT, 0, MORE_SUGGESTIONS_HINT.length(), bounds);
2770967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka            final int width = Math.round(bounds.width() + 0.5f);
2780967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka            final int height = Math.round(bounds.height() + 0.5f);
2790967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka            final Bitmap buffer = Bitmap.createBitmap(
2800967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka                    width, (height * 3 / 2), Bitmap.Config.ARGB_8888);
2810967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka            final Canvas canvas = new Canvas(buffer);
2820967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka            canvas.drawText(MORE_SUGGESTIONS_HINT, width / 2, height, paint);
2830967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka            return new BitmapDrawable(res, buffer);
2840967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka        }
2850967ef40368c4bc7a0576ecc3dc222a8fe9b94c3Tadashi G. Takaoka
2863e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        private CharSequence getStyledSuggestionWord(final SuggestedWords suggestedWords,
2873e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka                final int pos) {
288e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka            final CharSequence word = suggestedWords.getWord(pos);
289e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka            final boolean isAutoCorrect = pos == 1 && suggestedWords.willAutoCorrect();
290e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka            final boolean isTypedWordValid = pos == 0 && suggestedWords.mTypedWordValid;
291d47a955610987c8abdab4d275c044aefc8a7f1dbTadashi G. Takaoka            if (!isAutoCorrect && !isTypedWordValid)
292d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka                return word;
293d47a955610987c8abdab4d275c044aefc8a7f1dbTadashi G. Takaoka
294d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka            final int len = word.length();
295d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka            final Spannable spannedWord = new SpannableString(word);
296d47a955610987c8abdab4d275c044aefc8a7f1dbTadashi G. Takaoka            final int option = mSuggestionStripOption;
297d47a955610987c8abdab4d275c044aefc8a7f1dbTadashi G. Takaoka            if ((isAutoCorrect && (option & AUTO_CORRECT_BOLD) != 0)
298d47a955610987c8abdab4d275c044aefc8a7f1dbTadashi G. Takaoka                    || (isTypedWordValid && (option & VALID_TYPED_WORD_BOLD) != 0)) {
299d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka                spannedWord.setSpan(BOLD_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
300d47a955610987c8abdab4d275c044aefc8a7f1dbTadashi G. Takaoka            }
301d47a955610987c8abdab4d275c044aefc8a7f1dbTadashi G. Takaoka            if (isAutoCorrect && (option & AUTO_CORRECT_UNDERLINE) != 0) {
302d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka                spannedWord.setSpan(UNDERLINE_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
303d47a955610987c8abdab4d275c044aefc8a7f1dbTadashi G. Takaoka            }
304d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka            return spannedWord;
305d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka        }
306d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka
3073e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        private int getWordPosition(final int index, final SuggestedWords suggestedWords) {
30860a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka            // TODO: This works for 3 suggestions. Revisit this algorithm when there are 5 or more
30960a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka            // suggestions.
310e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka            final int centerPos = suggestedWords.willAutoCorrect() ? 1 : 0;
311913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            if (index == mCenterSuggestionIndex) {
31260a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                return centerPos;
31360a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka            } else if (index == centerPos) {
314913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                return mCenterSuggestionIndex;
31560a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka            } else {
316d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka                return index;
317d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka            }
318d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka        }
319d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka
3203e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        private int getSuggestionTextColor(final int index, final SuggestedWords suggestedWords,
3213e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka                final int pos) {
322d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka            // TODO: Need to revisit this logic with bigram suggestions
323913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            final boolean isSuggested = (pos != 0);
324d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka
325d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka            final int color;
326e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka            if (index == mCenterSuggestionIndex && suggestedWords.willAutoCorrect()) {
327d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka                color = mColorAutoCorrect;
328e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka            } else if (index == mCenterSuggestionIndex && suggestedWords.mTypedWordValid) {
329a44685ab326809185a0e4c44afabe8fd93441b4cTadashi G. Takaoka                color = mColorValidTypedWord;
330913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            } else if (isSuggested) {
331913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                color = mColorSuggested;
332d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka            } else {
333d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka                color = mColorTypedWord;
334d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka            }
335c0c20b386fb06b688f56dd7f807e2bcbb70b3a59Jean Chalard            if (LatinImeLogger.sDBG && suggestedWords.size() > 1) {
336ebbb7e9665b2f9bce46ddc2daa82b9ef73f6c712Jean Chalard                // If we auto-correct, then the autocorrection is in slot 0 and the typed word
337ebbb7e9665b2f9bce46ddc2daa82b9ef73f6c712Jean Chalard                // is in slot 1.
338f72b8f5662afb42b08eb88e92731d52f99df13e3Jean Chalard                if (index == mCenterSuggestionIndex
339f72b8f5662afb42b08eb88e92731d52f99df13e3Jean Chalard                        && AutoCorrection.shouldBlockAutoCorrectionBySafetyNet(
340f72b8f5662afb42b08eb88e92731d52f99df13e3Jean Chalard                                suggestedWords.getWord(1).toString(), suggestedWords.getWord(0))) {
3412aa1dd45c44295e2f7e8ece1b520032d86b9f908satok                    return 0xFFFF0000;
3422aa1dd45c44295e2f7e8ece1b520032d86b9f908satok                }
3432aa1dd45c44295e2f7e8ece1b520032d86b9f908satok            }
34460a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka
3454f6cf258fe208329247ade7a6701ac9709820717Jean Chalard            if (suggestedWords.mIsObsoleteSuggestions && isSuggested) {
346697bf585f8dac6ffc4d008c02b63407cfdd83104Tadashi G. Takaoka                return applyAlpha(color, mAlphaObsoleted);
347d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka            } else {
348d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka                return color;
349d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka            }
350d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka        }
351d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka
352d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka        private static int applyAlpha(final int color, final float alpha) {
353d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka            final int newAlpha = (int)(Color.alpha(color) * alpha);
354d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka            return Color.argb(newAlpha, Color.red(color), Color.green(color), Color.blue(color));
355d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka        }
356d95a6365be213c1a1d2ab948fc5b80ee1ce0e3ceTadashi G. Takaoka
3572c47080e7b4b9b2a6c2dbf1444e041c1b7943316Tadashi G. Takaoka        private static void addDivider(final ViewGroup stripView, final View divider) {
3582c47080e7b4b9b2a6c2dbf1444e041c1b7943316Tadashi G. Takaoka            stripView.addView(divider);
3592c47080e7b4b9b2a6c2dbf1444e041c1b7943316Tadashi G. Takaoka            final LinearLayout.LayoutParams params =
3602c47080e7b4b9b2a6c2dbf1444e041c1b7943316Tadashi G. Takaoka                    (LinearLayout.LayoutParams)divider.getLayoutParams();
3612c47080e7b4b9b2a6c2dbf1444e041c1b7943316Tadashi G. Takaoka            params.gravity = Gravity.CENTER;
3622c47080e7b4b9b2a6c2dbf1444e041c1b7943316Tadashi G. Takaoka        }
3632c47080e7b4b9b2a6c2dbf1444e041c1b7943316Tadashi G. Takaoka
3643e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        public void layout(final SuggestedWords suggestedWords, final ViewGroup stripView,
3653e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka                final ViewGroup placer, final int stripWidth) {
366e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka            if (suggestedWords.mIsPunctuationSuggestions) {
367e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka                layoutPunctuationSuggestions(suggestedWords, stripView);
3683a566c48b0eb3625788e1cce3fb2f58d8f4b380dTadashi G. Takaoka                return;
36960a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka            }
37060a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka
371913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            final int countInStrip = mSuggestionsCountInStrip;
372e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka            setupTexts(suggestedWords, countInStrip);
373e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka            mMoreSuggestionsAvailable = (suggestedWords.size() > countInStrip);
37460a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka            int x = 0;
37560a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka            for (int index = 0; index < countInStrip; index++) {
376e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka                final int pos = getWordPosition(index, suggestedWords);
37760a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka
37860a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                if (index != 0) {
37960a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                    final View divider = mDividers.get(pos);
380913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                    // Add divider if this isn't the left most suggestion in suggestions strip.
3812c47080e7b4b9b2a6c2dbf1444e041c1b7943316Tadashi G. Takaoka                    addDivider(stripView, divider);
382013ba3e3e785980b74db83ac5ab1e11b438485daTadashi G. Takaoka                    x += divider.getMeasuredWidth();
38360a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                }
38460a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka
38516713e5630b93fb5625df26745eb73271f189457Tadashi G. Takaoka                final CharSequence styled = mTexts.get(pos);
38660a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                final TextView word = mWords.get(pos);
387913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                if (index == mCenterSuggestionIndex && mMoreSuggestionsAvailable) {
388ae5c736e37973e26b201d45ff6c139862a6e05cfTadashi G. Takaoka                    // TODO: This "more suggestions hint" should have nicely designed icon.
389ae5c736e37973e26b201d45ff6c139862a6e05cfTadashi G. Takaoka                    word.setCompoundDrawablesWithIntrinsicBounds(
390913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                            null, null, null, mMoreSuggestionsHint);
391580e1d7ac0cb90a464a3b3aed88047f85a4ab41fTadashi G. Takaoka                    // HACK: To align with other TextView that has no compound drawables.
392913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                    word.setCompoundDrawablePadding(-mMoreSuggestionsHint.getIntrinsicHeight());
393ae5c736e37973e26b201d45ff6c139862a6e05cfTadashi G. Takaoka                } else {
394580e1d7ac0cb90a464a3b3aed88047f85a4ab41fTadashi G. Takaoka                    word.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
395ae5c736e37973e26b201d45ff6c139862a6e05cfTadashi G. Takaoka                }
396ae5c736e37973e26b201d45ff6c139862a6e05cfTadashi G. Takaoka
397913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                // Disable this suggestion if the suggestion is null or empty.
39860a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                word.setEnabled(!TextUtils.isEmpty(styled));
399e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka                word.setTextColor(getSuggestionTextColor(index, suggestedWords, pos));
400913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                final int width = getSuggestionWidth(index, stripWidth);
40160a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                final CharSequence text = getEllipsizedText(styled, width, word.getPaint());
40260a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                final float scaleX = word.getTextScaleX();
40360a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                word.setText(text); // TextView.setText() resets text scale x to 1.0.
40460a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                word.setTextScaleX(scaleX);
40560a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                stripView.addView(word);
406b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaoka                setLayoutWeight(
407913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                        word, getSuggestionWeight(index), ViewGroup.LayoutParams.MATCH_PARENT);
408013ba3e3e785980b74db83ac5ab1e11b438485daTadashi G. Takaoka                x += word.getMeasuredWidth();
40916713e5630b93fb5625df26745eb73271f189457Tadashi G. Takaoka
410042557b45bf2554608cd22874633011157cd1752Jean Chalard                if (DBG && pos < suggestedWords.size()) {
411042557b45bf2554608cd22874633011157cd1752Jean Chalard                    final CharSequence debugInfo = Utils.getDebugInfo(suggestedWords, pos);
41216713e5630b93fb5625df26745eb73271f189457Tadashi G. Takaoka                    if (debugInfo != null) {
41360a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                        final TextView info = mInfos.get(pos);
41416713e5630b93fb5625df26745eb73271f189457Tadashi G. Takaoka                        info.setText(debugInfo);
415013ba3e3e785980b74db83ac5ab1e11b438485daTadashi G. Takaoka                        placer.addView(info);
416b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaoka                        info.measure(ViewGroup.LayoutParams.WRAP_CONTENT,
417b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaoka                                ViewGroup.LayoutParams.WRAP_CONTENT);
41860a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                        final int infoWidth = info.getMeasuredWidth();
41916713e5630b93fb5625df26745eb73271f189457Tadashi G. Takaoka                        final int y = info.getMeasuredHeight();
420c8461d1ae26d43a09c0f835d833f3d49fbe8d2f3Tadashi G. Takaoka                        ViewLayoutUtils.placeViewAt(
421013ba3e3e785980b74db83ac5ab1e11b438485daTadashi G. Takaoka                                info, x - infoWidth, y, infoWidth, info.getMeasuredHeight());
42216713e5630b93fb5625df26745eb73271f189457Tadashi G. Takaoka                    }
42360a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                }
42416713e5630b93fb5625df26745eb73271f189457Tadashi G. Takaoka            }
42516713e5630b93fb5625df26745eb73271f189457Tadashi G. Takaoka        }
42616713e5630b93fb5625df26745eb73271f189457Tadashi G. Takaoka
4273e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        private int getSuggestionWidth(final int index, final int maxWidth) {
428913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            final int paddings = mPadding * mSuggestionsCountInStrip;
429913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            final int dividers = mDividerWidth * (mSuggestionsCountInStrip - 1);
43060a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka            final int availableWidth = maxWidth - paddings - dividers;
431913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            return (int)(availableWidth * getSuggestionWeight(index));
43260a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka        }
43374b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka
4343e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        private float getSuggestionWeight(final int index) {
435913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            if (index == mCenterSuggestionIndex) {
436913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                return mCenterSuggestionWeight;
43760a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka            } else {
43860a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                // TODO: Revisit this for cases of 5 or more suggestions
439913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                return (1.0f - mCenterSuggestionWeight) / (mSuggestionsCountInStrip - 1);
44074b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka            }
44174b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka        }
44274b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka
4433e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        private void setupTexts(final SuggestedWords suggestedWords, final int countInStrip) {
44474b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka            mTexts.clear();
445e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka            final int count = Math.min(suggestedWords.size(), countInStrip);
44660a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka            for (int pos = 0; pos < count; pos++) {
447e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka                final CharSequence styled = getStyledSuggestionWord(suggestedWords, pos);
44874b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka                mTexts.add(styled);
44974b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka            }
45060a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka            for (int pos = count; pos < countInStrip; pos++) {
45160a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                // Make this inactive for touches in layout().
45260a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                mTexts.add(null);
45360a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka            }
45474b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka        }
45574b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka
4563e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        private void layoutPunctuationSuggestions(final SuggestedWords suggestedWords,
4573e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka                final ViewGroup stripView) {
458e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka            final int countInStrip = Math.min(suggestedWords.size(), PUNCTUATIONS_IN_STRIP);
45960a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka            for (int index = 0; index < countInStrip; index++) {
46060a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                if (index != 0) {
461913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                    // Add divider if this isn't the left most suggestion in suggestions strip.
4622c47080e7b4b9b2a6c2dbf1444e041c1b7943316Tadashi G. Takaoka                    addDivider(stripView, mDividers.get(index));
46360a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                }
46460a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka
46560a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                final TextView word = mWords.get(index);
46660a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                word.setEnabled(true);
467a44685ab326809185a0e4c44afabe8fd93441b4cTadashi G. Takaoka                word.setTextColor(mColorAutoCorrect);
468e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka                final CharSequence text = suggestedWords.getWord(index);
46960a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                word.setText(text);
47060a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                word.setTextScaleX(1.0f);
471ae5c736e37973e26b201d45ff6c139862a6e05cfTadashi G. Takaoka                word.setCompoundDrawables(null, null, null, null);
47260a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka                stripView.addView(word);
473913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                setLayoutWeight(word, 1.0f, mSuggestionsStripHeight);
47460a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka            }
475ae5c736e37973e26b201d45ff6c139862a6e05cfTadashi G. Takaoka            mMoreSuggestionsAvailable = false;
47674b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka        }
477c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka
4783e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        public void layoutAddToDictionaryHint(final CharSequence word, final ViewGroup stripView,
4793e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka                final int stripWidth, final CharSequence hintText, final OnClickListener listener) {
480c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            final int width = stripWidth - mDividerWidth - mPadding * 2;
481c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka
482c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            final TextView wordView = mWordToSaveView;
483c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            wordView.setTextColor(mColorTypedWord);
484913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            final int wordWidth = (int)(width * mCenterSuggestionWeight);
485c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            final CharSequence text = getEllipsizedText(word, wordWidth, wordView.getPaint());
486c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            final float wordScaleX = wordView.getTextScaleX();
487c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            wordView.setTag(word);
488c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            wordView.setText(text);
489c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            wordView.setTextScaleX(wordScaleX);
490c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            stripView.addView(wordView);
491913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            setLayoutWeight(wordView, mCenterSuggestionWeight, ViewGroup.LayoutParams.MATCH_PARENT);
492c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka
493c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            stripView.addView(mDividers.get(0));
494c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka
4952eb8781dc2e42d55474fb6e5c461c56c066b8363Tadashi G. Takaoka            final TextView leftArrowView = mLeftwardsArrowView;
4962eb8781dc2e42d55474fb6e5c461c56c066b8363Tadashi G. Takaoka            leftArrowView.setTextColor(mColorAutoCorrect);
4972eb8781dc2e42d55474fb6e5c461c56c066b8363Tadashi G. Takaoka            leftArrowView.setText(LEFTWARDS_ARROW);
4982eb8781dc2e42d55474fb6e5c461c56c066b8363Tadashi G. Takaoka            stripView.addView(leftArrowView);
4992eb8781dc2e42d55474fb6e5c461c56c066b8363Tadashi G. Takaoka
500c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            final TextView hintView = mHintToSaveView;
5012eb8781dc2e42d55474fb6e5c461c56c066b8363Tadashi G. Takaoka            hintView.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
502c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            hintView.setTextColor(mColorAutoCorrect);
5032eb8781dc2e42d55474fb6e5c461c56c066b8363Tadashi G. Takaoka            final int hintWidth = width - wordWidth - leftArrowView.getWidth();
504644d33d60ea5a87501274488013d65f55238895eKen Wakasa            final float hintScaleX = getTextScaleX(hintText, hintWidth, hintView.getPaint());
505644d33d60ea5a87501274488013d65f55238895eKen Wakasa            hintView.setText(hintText);
506c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            hintView.setTextScaleX(hintScaleX);
507c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            stripView.addView(hintView);
508b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaoka            setLayoutWeight(
509913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                    hintView, 1.0f - mCenterSuggestionWeight, ViewGroup.LayoutParams.MATCH_PARENT);
5109f763d14451685965039990891eb0bc8e8db0735Tadashi G. Takaoka
5119f763d14451685965039990891eb0bc8e8db0735Tadashi G. Takaoka            wordView.setOnClickListener(listener);
5129f763d14451685965039990891eb0bc8e8db0735Tadashi G. Takaoka            leftArrowView.setOnClickListener(listener);
5139f763d14451685965039990891eb0bc8e8db0735Tadashi G. Takaoka            hintView.setOnClickListener(listener);
5149f763d14451685965039990891eb0bc8e8db0735Tadashi G. Takaoka        }
5159f763d14451685965039990891eb0bc8e8db0735Tadashi G. Takaoka
5169f763d14451685965039990891eb0bc8e8db0735Tadashi G. Takaoka        public CharSequence getAddToDictionaryWord() {
5179f763d14451685965039990891eb0bc8e8db0735Tadashi G. Takaoka            return (CharSequence)mWordToSaveView.getTag();
5189f763d14451685965039990891eb0bc8e8db0735Tadashi G. Takaoka        }
5199f763d14451685965039990891eb0bc8e8db0735Tadashi G. Takaoka
5203e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        public boolean isAddToDictionaryShowing(final View v) {
5219f763d14451685965039990891eb0bc8e8db0735Tadashi G. Takaoka            return v == mWordToSaveView || v == mHintToSaveView || v == mLeftwardsArrowView;
522c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka        }
5232d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka
5243e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        private static void setLayoutWeight(final View v, final float weight, final int height) {
5252d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            final ViewGroup.LayoutParams lp = v.getLayoutParams();
5262d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            if (lp instanceof LinearLayout.LayoutParams) {
5272d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka                final LinearLayout.LayoutParams llp = (LinearLayout.LayoutParams)lp;
5282d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka                llp.weight = weight;
5292d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka                llp.width = 0;
5302d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka                llp.height = height;
5312d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            }
5322d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka        }
5332d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka
5343e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        private static float getTextScaleX(final CharSequence text, final int maxWidth,
5353e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka                final TextPaint paint) {
5362d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            paint.setTextScaleX(1.0f);
5372d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            final int width = getTextWidth(text, paint);
5382d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            if (width <= maxWidth) {
5392d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka                return 1.0f;
5402d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            }
5412d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            return maxWidth / (float)width;
5422d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka        }
5432d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka
5443e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        private static CharSequence getEllipsizedText(final CharSequence text, final int maxWidth,
5453e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka                final TextPaint paint) {
5462d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            if (text == null) return null;
5472d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            paint.setTextScaleX(1.0f);
5482d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            final int width = getTextWidth(text, paint);
5492d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            if (width <= maxWidth) {
5502d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka                return text;
5512d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            }
5522d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            final float scaleX = maxWidth / (float)width;
5532d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            if (scaleX >= MIN_TEXT_XSCALE) {
5542d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka                paint.setTextScaleX(scaleX);
5552d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka                return text;
5562d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            }
5572d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka
5582d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            // Note that TextUtils.ellipsize() use text-x-scale as 1.0 if ellipsize is needed. To
5592d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            // get squeezed and ellipsized text, passes enlarged width (maxWidth / MIN_TEXT_XSCALE).
5602d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            final CharSequence ellipsized = TextUtils.ellipsize(
5612d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka                    text, paint, maxWidth / MIN_TEXT_XSCALE, TextUtils.TruncateAt.MIDDLE);
5622d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            paint.setTextScaleX(MIN_TEXT_XSCALE);
5632d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            return ellipsized;
5642d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka        }
5652d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka
5663e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        private static int getTextWidth(final CharSequence text, final TextPaint paint) {
5672d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            if (TextUtils.isEmpty(text)) return 0;
5682d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            final Typeface savedTypeface = paint.getTypeface();
5692d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            paint.setTypeface(getTextTypeface(text));
5702d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            final int len = text.length();
5712d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            final float[] widths = new float[len];
5722d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            final int count = paint.getTextWidths(text, 0, len, widths);
5732d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            int width = 0;
5742d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            for (int i = 0; i < count; i++) {
5752d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka                width += Math.round(widths[i] + 0.5f);
5762d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            }
5772d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            paint.setTypeface(savedTypeface);
5782d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            return width;
5792d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka        }
5802d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka
5813e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        private static Typeface getTextTypeface(final CharSequence text) {
5822d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            if (!(text instanceof SpannableString))
5832d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka                return Typeface.DEFAULT;
5842d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka
5852d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            final SpannableString ss = (SpannableString)text;
5862d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            final StyleSpan[] styles = ss.getSpans(0, text.length(), StyleSpan.class);
5872d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            if (styles.length == 0)
5882d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka                return Typeface.DEFAULT;
5892d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka
5902d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            switch (styles[0].getStyle()) {
5912d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            case Typeface.BOLD: return Typeface.DEFAULT_BOLD;
5922d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            // TODO: BOLD_ITALIC, ITALIC case?
5932d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            default: return Typeface.DEFAULT;
5942d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka            }
5952d74806262431ce25b159e1ec5c6ac4a26007c3eTadashi G. Takaoka        }
59674b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka    }
59774b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka
598923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
5994702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka     * Construct a {@link SuggestionStripView} for showing suggestions to be picked by the user.
600923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @param context
601923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @param attrs
602923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
6033e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka    public SuggestionStripView(final Context context, final AttributeSet attrs) {
6044702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka        this(context, attrs, R.attr.suggestionStripViewStyle);
60508a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka    }
60608a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka
6073e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka    public SuggestionStripView(final Context context, final AttributeSet attrs,
6083e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka            final int defStyle) {
609c412309b7a32308b1b0a175dafc13f90254353c5Tadashi G. Takaoka        super(context, attrs, defStyle);
610923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
61116713e5630b93fb5625df26745eb73271f189457Tadashi G. Takaoka        final LayoutInflater inflater = LayoutInflater.from(context);
612913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        inflater.inflate(R.layout.suggestions_strip, this);
61386e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka
61486e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka        mPreviewPopup = new PopupWindow(context);
615913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        mPreviewText = (TextView) inflater.inflate(R.layout.suggestion_preview, null);
616b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaoka        mPreviewPopup.setWindowLayoutMode(
617b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaoka                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
618923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        mPreviewPopup.setContentView(mPreviewText);
619923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        mPreviewPopup.setBackgroundDrawable(null);
620923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
621913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        mSuggestionsStrip = (ViewGroup)findViewById(R.id.suggestions_strip);
62260a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka        for (int pos = 0; pos < MAX_SUGGESTIONS; pos++) {
623913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            final TextView word = (TextView)inflater.inflate(R.layout.suggestion_word, null);
62460a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka            word.setTag(pos);
6252442e779857e7eda253aadcb1c4dff5ccb3e53f4Tadashi G. Takaoka            word.setOnClickListener(this);
626ae5c736e37973e26b201d45ff6c139862a6e05cfTadashi G. Takaoka            word.setOnLongClickListener(this);
6272442e779857e7eda253aadcb1c4dff5ccb3e53f4Tadashi G. Takaoka            mWords.add(word);
628913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            final View divider = inflater.inflate(R.layout.suggestion_divider, null);
62960a004f78e73b5208c2a0a79454dabfbc0e1aa33Tadashi G. Takaoka            divider.setTag(pos);
630a1aab83a24e74cb0fad4b1c1e59b18d23ecbdad3Tadashi G. Takaoka            divider.setOnClickListener(this);
631a1aab83a24e74cb0fad4b1c1e59b18d23ecbdad3Tadashi G. Takaoka            mDividers.add(divider);
632913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            mInfos.add((TextView)inflater.inflate(R.layout.suggestion_info, null));
633c9716b28ce438e06b5cacc07fc002944bcbe24a0Tadashi G. Takaoka        }
634179ada958b0bb46c6b9c8eb8b220d84dd3db855aTadashi G. Takaoka
6354702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka        mParams = new SuggestionStripViewParams(
6364702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka                context, attrs, defStyle, mWords, mDividers, mInfos);
63729e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka
63829e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        mMoreSuggestionsContainer = inflater.inflate(R.layout.more_suggestions, null);
63929e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        mMoreSuggestionsView = (MoreSuggestionsView)mMoreSuggestionsContainer
64029e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka                .findViewById(R.id.more_suggestions_view);
64129e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        mMoreSuggestionsBuilder = new MoreSuggestions.Builder(mMoreSuggestionsView);
642bfc1d732ac92fa1bd5b19ad9e7c71ce9a1f9dc00Tadashi G. Takaoka
643bfc1d732ac92fa1bd5b19ad9e7c71ce9a1f9dc00Tadashi G. Takaoka        final PopupWindow moreWindow = new PopupWindow(context);
644bfc1d732ac92fa1bd5b19ad9e7c71ce9a1f9dc00Tadashi G. Takaoka        moreWindow.setWindowLayoutMode(
64529e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
646bfc1d732ac92fa1bd5b19ad9e7c71ce9a1f9dc00Tadashi G. Takaoka        moreWindow.setBackgroundDrawable(new ColorDrawable(android.R.color.transparent));
647bfc1d732ac92fa1bd5b19ad9e7c71ce9a1f9dc00Tadashi G. Takaoka        moreWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
648bfc1d732ac92fa1bd5b19ad9e7c71ce9a1f9dc00Tadashi G. Takaoka        moreWindow.setFocusable(true);
649bfc1d732ac92fa1bd5b19ad9e7c71ce9a1f9dc00Tadashi G. Takaoka        moreWindow.setOutsideTouchable(true);
650bfc1d732ac92fa1bd5b19ad9e7c71ce9a1f9dc00Tadashi G. Takaoka        moreWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
651bfc1d732ac92fa1bd5b19ad9e7c71ce9a1f9dc00Tadashi G. Takaoka            @Override
652bfc1d732ac92fa1bd5b19ad9e7c71ce9a1f9dc00Tadashi G. Takaoka            public void onDismiss() {
653bfc1d732ac92fa1bd5b19ad9e7c71ce9a1f9dc00Tadashi G. Takaoka                mKeyboardView.dimEntireKeyboard(false);
654bfc1d732ac92fa1bd5b19ad9e7c71ce9a1f9dc00Tadashi G. Takaoka            }
655bfc1d732ac92fa1bd5b19ad9e7c71ce9a1f9dc00Tadashi G. Takaoka        });
656bfc1d732ac92fa1bd5b19ad9e7c71ce9a1f9dc00Tadashi G. Takaoka        mMoreSuggestionsWindow = moreWindow;
657bfc1d732ac92fa1bd5b19ad9e7c71ce9a1f9dc00Tadashi G. Takaoka
65850e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka        final Resources res = context.getResources();
65950e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka        mMoreSuggestionsModalTolerance = res.getDimensionPixelOffset(
66050e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka                R.dimen.more_suggestions_modal_tolerance);
6613fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka        mMoreSuggestionsSlidingDetector = new GestureDetector(
6623fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka                context, mMoreSuggestionsSlidingListener);
663923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
664c9716b28ce438e06b5cacc07fc002944bcbe24a0Tadashi G. Takaoka
665923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
666c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaoka     * A connection back to the input method.
667923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     * @param listener
668923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
6693e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka    public void setListener(final Listener listener, final View inputView) {
670c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaoka        mListener = listener;
6711b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka        mKeyboardView = (KeyboardView)inputView.findViewById(R.id.keyboard_view);
672923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
673923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
6743e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka    public void setSuggestions(final SuggestedWords suggestedWords) {
675d5b765b3bbceb80b2e145ac7f4d66acbadd1ee4fJean Chalard        if (suggestedWords == null)
676a15e45ef45931ba46678b39c159518d8aa2d4d65Tadashi G. Takaoka            return;
67755b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka
678e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka        clear();
679e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka        mSuggestedWords = suggestedWords;
680e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka        mParams.layout(mSuggestedWords, mSuggestionsStrip, this, getWidth());
681a9ca7867b5a7c0be115966211a05f5d460c8638cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
6824702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka            ResearchLogger.suggestionStripView_setSuggestions(mSuggestedWords);
683a9ca7867b5a7c0be115966211a05f5d460c8638cKurt Partridge        }
68416713e5630b93fb5625df26745eb73271f189457Tadashi G. Takaoka    }
68574b6897a12ec603ef835aaa77a01f0c32f49aa1cTadashi G. Takaoka
6863e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka    public int setMoreSuggestionsHeight(final int remainingHeight) {
687c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        return mParams.setMoreSuggestionsHeight(remainingHeight);
688c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka    }
689b47319867ef3834a222865b8cb6abe62962e70f7Tadashi G. Takaoka
690b00a1d0c0adbdfc507676772201e979e539a2801Amith Yamasani    public boolean isShowingAddToDictionaryHint() {
691913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        return mSuggestionsStrip.getChildCount() > 0
6929f763d14451685965039990891eb0bc8e8db0735Tadashi G. Takaoka                && mParams.isAddToDictionaryShowing(mSuggestionsStrip.getChildAt(0));
693b00a1d0c0adbdfc507676772201e979e539a2801Amith Yamasani    }
694b00a1d0c0adbdfc507676772201e979e539a2801Amith Yamasani
6953e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka    public void showAddToDictionaryHint(final CharSequence word, final CharSequence hintText) {
696c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka        clear();
6979f763d14451685965039990891eb0bc8e8db0735Tadashi G. Takaoka        mParams.layoutAddToDictionaryHint(word, mSuggestionsStrip, getWidth(), hintText, this);
69866a787b953d703201c6b827abbee74e8cd9bb063Amith Yamasani    }
69966a787b953d703201c6b827abbee74e8cd9bb063Amith Yamasani
7006558253160e2039c87f424bd814f402ecd31de3bKen Wakasa    public boolean dismissAddToDictionaryHint() {
701c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka        if (isShowingAddToDictionaryHint()) {
702c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            clear();
703c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka            return true;
704c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka        }
705c04bbc1ae9be81d25a356407c27a8e7fa22028bfTadashi G. Takaoka        return false;
7066558253160e2039c87f424bd814f402ecd31de3bKen Wakasa    }
7076558253160e2039c87f424bd814f402ecd31de3bKen Wakasa
7087e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka    public SuggestedWords getSuggestions() {
709e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka        return mSuggestedWords;
710979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
711979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
712923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void clear() {
713913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        mSuggestionsStrip.removeAllViews();
714c412309b7a32308b1b0a175dafc13f90254353c5Tadashi G. Takaoka        removeAllViews();
715c412309b7a32308b1b0a175dafc13f90254353c5Tadashi G. Takaoka        addView(mSuggestionsStrip);
7161b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka        dismissMoreSuggestions();
717923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
718923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
7193e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka    void hidePreview() {
720179ada958b0bb46c6b9c8eb8b220d84dd3db855aTadashi G. Takaoka        mPreviewPopup.dismiss();
7216ec55a1599c74150e82ea7e4371ec815f0d2df27Amith Yamasani    }
722e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka
72329e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka    private final KeyboardActionListener mMoreSuggestionsListener =
72429e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            new KeyboardActionListener.Adapter() {
72529e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        @Override
7263e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka        public boolean onCustomRequest(final int requestCode) {
72729e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            final int index = requestCode;
728e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka            final CharSequence word = mSuggestedWords.getWord(index);
7296785b9072762e15bb49657ce7b7d228dab76e44aTadashi G. Takaoka            mListener.pickSuggestionManually(index, word);
7301b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka            dismissMoreSuggestions();
73129e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            return true;
73229e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        }
73329e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka
73429e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        @Override
73529e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        public void onCancelInput() {
7361b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka            dismissMoreSuggestions();
73729e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        }
73829e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka    };
73929e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka
74029e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka    private final MoreKeysPanel.Controller mMoreSuggestionsController =
74129e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            new MoreKeysPanel.Controller() {
74229e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        @Override
74329e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        public boolean dismissMoreKeysPanel() {
7441b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka            return dismissMoreSuggestions();
74529e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        }
74629e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka    };
74729e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka
7483e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka    boolean dismissMoreSuggestions() {
7491b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka        if (mMoreSuggestionsWindow.isShowing()) {
7501b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka            mMoreSuggestionsWindow.dismiss();
7511b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka            return true;
7521b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka        }
7531b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka        return false;
7541b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka    }
7551b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka
756e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka    @Override
7573e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka    public boolean onLongClick(final View view) {
7583fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka        return showMoreSuggestions();
7593fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka    }
7603fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka
7613e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka    boolean showMoreSuggestions() {
7627339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        final Keyboard parentKeyboard = KeyboardSwitcher.getInstance().getKeyboard();
7637339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        if (parentKeyboard == null) {
7647339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka            return false;
765ae5c736e37973e26b201d45ff6c139862a6e05cfTadashi G. Takaoka        }
7667339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        final SuggestionStripViewParams params = mParams;
7677339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        if (!params.mMoreSuggestionsAvailable) {
7687339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka            return false;
7697339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        }
7707339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        final int stripWidth = getWidth();
7717339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        final View container = mMoreSuggestionsContainer;
7727339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        final int maxWidth = stripWidth - container.getPaddingLeft() - container.getPaddingRight();
7737339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        final MoreSuggestions.Builder builder = mMoreSuggestionsBuilder;
7747339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        builder.layout(mSuggestedWords, params.mSuggestionsCountInStrip, maxWidth,
7757339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka                (int)(maxWidth * params.mMinMoreSuggestionsWidth),
7767339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka                params.getMaxMoreSuggestionsRow(), parentKeyboard);
7777339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        mMoreSuggestionsView.setKeyboard(builder.build());
7787339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        container.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
7797339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka
7807339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        final MoreKeysPanel moreKeysPanel = mMoreSuggestionsView;
7817339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        final int pointX = stripWidth / 2;
7827339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        final int pointY = -params.mMoreSuggestionsBottomGap;
7837339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        moreKeysPanel.showMoreKeysPanel(this, mMoreSuggestionsController, pointX, pointY,
7847339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka                mMoreSuggestionsWindow, mMoreSuggestionsListener);
7857339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        mMoreSuggestionsMode = MORE_SUGGESTIONS_CHECKING_MODAL_OR_SLIDING;
7867339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        mOriginX = mLastX;
7877339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        mOriginY = mLastY;
7887339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        mKeyboardView.dimEntireKeyboard(true);
7897339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        for (int i = 0; i < params.mSuggestionsCountInStrip; i++) {
7907339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka            mWords.get(i).setPressed(false);
7917339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        }
7927339a62a1368b632c0cedaf1d876a0e8590a47edTadashi G. Takaoka        return true;
793ae5c736e37973e26b201d45ff6c139862a6e05cfTadashi G. Takaoka    }
794ae5c736e37973e26b201d45ff6c139862a6e05cfTadashi G. Takaoka
79550e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka    // Working variables for onLongClick and dispatchTouchEvent.
796d7b00f34904c61f955e439160e2dbaa3a4b6983cTadashi G. Takaoka    private int mMoreSuggestionsMode = MORE_SUGGESTIONS_IN_MODAL_MODE;
797d7b00f34904c61f955e439160e2dbaa3a4b6983cTadashi G. Takaoka    private static final int MORE_SUGGESTIONS_IN_MODAL_MODE = 0;
798d7b00f34904c61f955e439160e2dbaa3a4b6983cTadashi G. Takaoka    private static final int MORE_SUGGESTIONS_CHECKING_MODAL_OR_SLIDING = 1;
799d7b00f34904c61f955e439160e2dbaa3a4b6983cTadashi G. Takaoka    private static final int MORE_SUGGESTIONS_IN_SLIDING_MODE = 2;
80050e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka    private int mLastX;
80150e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka    private int mLastY;
80250e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka    private int mOriginX;
80350e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka    private int mOriginY;
80450e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka    private final int mMoreSuggestionsModalTolerance;
8053fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka    private final GestureDetector mMoreSuggestionsSlidingDetector;
8063fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka    private final GestureDetector.OnGestureListener mMoreSuggestionsSlidingListener =
8073fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka            new GestureDetector.SimpleOnGestureListener() {
8083fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka        @Override
8093fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka        public boolean onScroll(MotionEvent down, MotionEvent me, float deltaX, float deltaY) {
8103fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka            final float dy = me.getY() - down.getY();
8113fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka            if (deltaY > 0 && dy < 0) {
8123fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka                return showMoreSuggestions();
8133fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka            }
8143fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka            return false;
8153fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka        }
8163fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka    };
81750e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka
818ae5c736e37973e26b201d45ff6c139862a6e05cfTadashi G. Takaoka    @Override
8193e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka    public boolean dispatchTouchEvent(final MotionEvent me) {
820d7b00f34904c61f955e439160e2dbaa3a4b6983cTadashi G. Takaoka        if (!mMoreSuggestionsWindow.isShowing()
821d7b00f34904c61f955e439160e2dbaa3a4b6983cTadashi G. Takaoka                || mMoreSuggestionsMode == MORE_SUGGESTIONS_IN_MODAL_MODE) {
82250e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka            mLastX = (int)me.getX();
82350e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka            mLastY = (int)me.getY();
8243fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka            if (mMoreSuggestionsSlidingDetector.onTouchEvent(me)) {
8253fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka                return true;
8263fc4ddec68b4f56f53ed6da80b5e44f38c085740Tadashi G. Takaoka            }
82729e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka            return super.dispatchTouchEvent(me);
82829e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        }
8298ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka
8308ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        final MoreKeysPanel moreKeysPanel = mMoreSuggestionsView;
83129e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        final int action = me.getAction();
83229e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        final long eventTime = me.getEventTime();
83329e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        final int index = me.getActionIndex();
83429e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        final int id = me.getPointerId(index);
8358ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        final PointerTracker tracker = PointerTracker.getPointerTracker(id, moreKeysPanel);
8368ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        final int x = (int)me.getX(index);
8378ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        final int y = (int)me.getY(index);
8388ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        final int translatedX = moreKeysPanel.translateX(x);
8398ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        final int translatedY = moreKeysPanel.translateY(y);
8408ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka
841d7b00f34904c61f955e439160e2dbaa3a4b6983cTadashi G. Takaoka        if (mMoreSuggestionsMode == MORE_SUGGESTIONS_CHECKING_MODAL_OR_SLIDING) {
84287104bace9cb9ed1a700f05a37743a21c2722fffTadashi G. Takaoka            if (Math.abs(x - mOriginX) >= mMoreSuggestionsModalTolerance
84387104bace9cb9ed1a700f05a37743a21c2722fffTadashi G. Takaoka                    || mOriginY - y >= mMoreSuggestionsModalTolerance) {
84487104bace9cb9ed1a700f05a37743a21c2722fffTadashi G. Takaoka                // Decided to be in the sliding input mode only when the touch point has been moved
84587104bace9cb9ed1a700f05a37743a21c2722fffTadashi G. Takaoka                // upward.
846d7b00f34904c61f955e439160e2dbaa3a4b6983cTadashi G. Takaoka                mMoreSuggestionsMode = MORE_SUGGESTIONS_IN_SLIDING_MODE;
847e51d164482c7896892d6eccb80f1e1e6fe6d50dbTadashi G. Takaoka                tracker.onShowMoreKeysPanel(translatedX, translatedY, moreKeysPanel);
84850e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka            } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP) {
84950e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka                // Decided to be in the modal input mode
850d7b00f34904c61f955e439160e2dbaa3a4b6983cTadashi G. Takaoka                mMoreSuggestionsMode = MORE_SUGGESTIONS_IN_MODAL_MODE;
85150e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka            }
85250e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka            return true;
85350e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka        }
85450e1073e11240fe51b3baf0e2ed80dac0d9f001dTadashi G. Takaoka
855d7b00f34904c61f955e439160e2dbaa3a4b6983cTadashi G. Takaoka        // MORE_SUGGESTIONS_IN_SLIDING_MODE
8568ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        tracker.processMotionEvent(action, translatedX, translatedY, eventTime, moreKeysPanel);
85729e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka        return true;
85829e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka    }
85929e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka
86029e7b7ed6ef88c3e10cc6469801fef87241c9cb5Tadashi G. Takaoka    @Override
8613e5a3c18bebbfb56012383411b24ee81ffde09cbTadashi G. Takaoka    public void onClick(final View view) {
8629f763d14451685965039990891eb0bc8e8db0735Tadashi G. Takaoka        if (mParams.isAddToDictionaryShowing(view)) {
8633d8e7a62fc6182c12ca28c29ceeee52ed4b9eea7Jean Chalard            mListener.addWordToUserDictionary(mParams.getAddToDictionaryWord().toString());
864717a8f50aec421f74e4d43432059c2fb41cb32c7Tadashi G. Takaoka            clear();
865717a8f50aec421f74e4d43432059c2fb41cb32c7Tadashi G. Takaoka            return;
866717a8f50aec421f74e4d43432059c2fb41cb32c7Tadashi G. Takaoka        }
867717a8f50aec421f74e4d43432059c2fb41cb32c7Tadashi G. Takaoka
86886e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka        final Object tag = view.getTag();
86986e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka        if (!(tag instanceof Integer))
87086e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka            return;
87186e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka        final int index = (Integer) tag;
872e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka        if (index >= mSuggestedWords.size())
87337deb112c727dfed5e94d054cf5f00f5d60c8120Tadashi G. Takaoka            return;
87486e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka
875e79b1a83126b41e09a8ec0a8dbb751ae0e02c7f6Tadashi G. Takaoka        final CharSequence word = mSuggestedWords.getWord(index);
8766785b9072762e15bb49657ce7b7d228dab76e44aTadashi G. Takaoka        mListener.pickSuggestionManually(index, word);
877923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
87882411d47ba7e8133ed2390c6920945e139a738cesatok
8796ec55a1599c74150e82ea7e4371ec815f0d2df27Amith Yamasani    @Override
88073cd4c1428ac370e1c15c5f61b1ce499f0d4f4c7Tadashi G. Takaoka    protected void onDetachedFromWindow() {
8816ec55a1599c74150e82ea7e4371ec815f0d2df27Amith Yamasani        super.onDetachedFromWindow();
88255b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka        mHandler.cancelAllMessages();
8836ec55a1599c74150e82ea7e4371ec815f0d2df27Amith Yamasani        hidePreview();
88432f4ceb2434b6f204cf8152b406d0f9a642feb73Tadashi G. Takaoka        dismissMoreSuggestions();
8856ec55a1599c74150e82ea7e4371ec815f0d2df27Amith Yamasani    }
886923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project}
887