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