SuggestionStripView.java revision 08a6f2aea71d998206c47c16dcda4eaa90f8c9ea
1923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/* 2e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka * Copyright (C) 2010 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 17923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectpackage com.android.inputmethod.latin; 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; 226f7218627eda110a8454053f8ecb7b80edfdc8cesatokimport android.graphics.Color; 23923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.graphics.Typeface; 24e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaokaimport android.os.Handler; 25e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaokaimport android.os.Message; 26522d5c16d20657e7d26009fe9c067404e16a6c2aTadashi G. Takaokaimport android.text.Spannable; 27522d5c16d20657e7d26009fe9c067404e16a6c2aTadashi G. Takaokaimport android.text.SpannableString; 28e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaokaimport android.text.Spanned; 29e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaokaimport android.text.TextUtils; 3055b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaokaimport android.text.style.BackgroundColorSpan; 31522d5c16d20657e7d26009fe9c067404e16a6c2aTadashi G. Takaokaimport android.text.style.CharacterStyle; 3255b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaokaimport android.text.style.ForegroundColorSpan; 33522d5c16d20657e7d26009fe9c067404e16a6c2aTadashi G. Takaokaimport android.text.style.StyleSpan; 34522d5c16d20657e7d26009fe9c067404e16a6c2aTadashi G. Takaokaimport android.text.style.UnderlineSpan; 35923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.util.AttributeSet; 36923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.view.Gravity; 37923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.view.LayoutInflater; 38923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.view.View; 39e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaokaimport android.view.View.OnClickListener; 40e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaokaimport android.view.View.OnLongClickListener; 41e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaokaimport android.view.ViewGroup; 4208a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaokaimport android.widget.ImageView; 43e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaokaimport android.widget.LinearLayout; 44923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.widget.PopupWindow; 45923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.widget.TextView; 46923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 47ddb61ea461b920d87be4ad78c8a36eec1013b965Tadashi G. Takaokaimport com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; 48ddb61ea461b920d87be4ad78c8a36eec1013b965Tadashi G. Takaoka 49179ada958b0bb46c6b9c8eb8b220d84dd3db855aTadashi G. Takaokaimport java.util.ArrayList; 506f7218627eda110a8454053f8ecb7b80edfdc8cesatokimport java.util.List; 51179ada958b0bb46c6b9c8eb8b220d84dd3db855aTadashi G. Takaoka 52e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaokapublic class CandidateView extends LinearLayout implements OnClickListener, OnLongClickListener { 53923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 54c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaoka public interface Listener { 55c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaoka public boolean addWordToDictionary(String word); 56c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaoka public void pickSuggestionManually(int index, CharSequence word); 57c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaoka } 58c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaoka 5982411d47ba7e8133ed2390c6920945e139a738cesatok private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD); 6082411d47ba7e8133ed2390c6920945e139a738cesatok private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan(); 6186e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka // The maximum number of suggestions available. See {@link Suggest#mPrefMaxSuggestions}. 6286e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka private static final int MAX_SUGGESTIONS = 18; 6386e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka private static final int UNSPECIFIED_MEASURESPEC = MeasureSpec.makeMeasureSpec( 6486e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka 0, MeasureSpec.UNSPECIFIED); 65923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 668553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard private static final boolean DBG = LatinImeLogger.sDBG; 676f7218627eda110a8454053f8ecb7b80edfdc8cesatok 6886e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka private static final int NUM_CANDIDATES_IN_STRIP = 3; 6908a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka private final ImageView mExpandCandidatesPane; 7008a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka private final ImageView mCloseCandidatesPane; 7186e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka private ViewGroup mCandidatesPane; 7286e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka private ViewGroup mCandidatesPaneContainer; 7386e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka private View mKeyboardView; 741e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka private final ArrayList<TextView> mWords = new ArrayList<TextView>(); 75ddb61ea461b920d87be4ad78c8a36eec1013b965Tadashi G. Takaoka private final ArrayList<View> mDividers = new ArrayList<View>(); 761e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka private final int mCandidatePadding; 7786e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka private final int mCandidateStripHeight; 7882411d47ba7e8133ed2390c6920945e139a738cesatok private final CharacterStyle mInvertedForegroundColorSpan; 7982411d47ba7e8133ed2390c6920945e139a738cesatok private final CharacterStyle mInvertedBackgroundColorSpan; 8008a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka private final int mAutoCorrectHighlight; 8108a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka private static final int AUTO_CORRECT_BOLD = 0x01; 8208a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka private static final int AUTO_CORRECT_UNDERLINE = 0x02; 8308a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka private static final int AUTO_CORRECT_INVERT = 0x04; 8408a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka private static final int AUTO_CORRECT_SPACEBAR_LED = 0x08; 851e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka private final int mColorTypedWord; 861e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka private final int mColorAutoCorrect; 871e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka private final int mColorSuggestedCandidate; 8882411d47ba7e8133ed2390c6920945e139a738cesatok private final PopupWindow mPreviewPopup; 8982411d47ba7e8133ed2390c6920945e139a738cesatok private final TextView mPreviewText; 9066a787b953d703201c6b827abbee74e8cd9bb063Amith Yamasani 91c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaoka private Listener mListener; 927e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka private SuggestedWords mSuggestions = SuggestedWords.EMPTY; 9355b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka private boolean mShowingAutoCorrectionInverted; 94e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka private boolean mShowingAddToDictionary; 956a6075caba3865383eeeb52cccc63a28e4ae5900Amith Yamasani 9655b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka private final UiHandler mHandler = new UiHandler(); 9755b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka 9855b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka private class UiHandler extends Handler { 9955b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka private static final int MSG_HIDE_PREVIEW = 0; 10055b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka private static final int MSG_UPDATE_SUGGESTION = 1; 10155b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka 10255b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka private static final long DELAY_HIDE_PREVIEW = 1000; 10355b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka private static final long DELAY_UPDATE_SUGGESTION = 300; 10455b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka 105e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka @Override 106e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka public void dispatchMessage(Message msg) { 107e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka switch (msg.what) { 108e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka case MSG_HIDE_PREVIEW: 109e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka hidePreview(); 110e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka break; 11155b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka case MSG_UPDATE_SUGGESTION: 1129fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka updateSuggestions(); 11355b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka break; 114e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka } 115e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka } 11655b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka 11755b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka public void postHidePreview() { 11855b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka cancelHidePreview(); 11955b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka sendMessageDelayed(obtainMessage(MSG_HIDE_PREVIEW), DELAY_HIDE_PREVIEW); 12055b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka } 12155b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka 12255b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka public void cancelHidePreview() { 12355b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka removeMessages(MSG_HIDE_PREVIEW); 12455b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka } 12555b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka 1269fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka public void postUpdateSuggestions() { 12755b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka cancelUpdateSuggestions(); 1289fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka sendMessageDelayed(obtainMessage(MSG_UPDATE_SUGGESTION), 12955b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka DELAY_UPDATE_SUGGESTION); 13055b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka } 13155b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka 13255b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka public void cancelUpdateSuggestions() { 13355b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka removeMessages(MSG_UPDATE_SUGGESTION); 13455b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka } 13555b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka 13655b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka public void cancelAllMessages() { 13755b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka cancelHidePreview(); 13855b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka cancelUpdateSuggestions(); 13955b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka } 140e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka } 141179ada958b0bb46c6b9c8eb8b220d84dd3db855aTadashi G. Takaoka 142923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 143923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Construct a CandidateView for showing suggested words for completion. 144923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * @param context 145923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * @param attrs 146923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 147923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project public CandidateView(Context context, AttributeSet attrs) { 14808a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka this(context, attrs, R.attr.candidateViewStyle); 14908a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka } 15008a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka 15108a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka public CandidateView(Context context, AttributeSet attrs, int defStyle) { 15208a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka super(context, attrs, defStyle); 153923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 15466a787b953d703201c6b827abbee74e8cd9bb063Amith Yamasani Resources res = context.getResources(); 155e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka LayoutInflater inflater = LayoutInflater.from(context); 15686e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka inflater.inflate(R.layout.candidates_strip, this); 15786e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka 15886e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka mPreviewPopup = new PopupWindow(context); 159e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka mPreviewText = (TextView) inflater.inflate(R.layout.candidate_preview, null); 160e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka mPreviewPopup.setWindowLayoutMode(ViewGroup.LayoutParams.WRAP_CONTENT, 161e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka ViewGroup.LayoutParams.WRAP_CONTENT); 162923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project mPreviewPopup.setContentView(mPreviewText); 163923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project mPreviewPopup.setBackgroundDrawable(null); 164923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 1651e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka mCandidatePadding = res.getDimensionPixelOffset(R.dimen.candidate_padding); 16686e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka mCandidateStripHeight = res.getDimensionPixelOffset(R.dimen.candidate_strip_height); 167e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka for (int i = 0; i < MAX_SUGGESTIONS; i++) { 16886e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka final TextView tv; 16986e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka switch (i) { 17086e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka case 0: 17186e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka tv = (TextView)findViewById(R.id.candidate_left); 17286e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka tv.setPadding(mCandidatePadding, 0, 0, 0); 17386e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka break; 17486e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka case 1: 17586e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka tv = (TextView)findViewById(R.id.candidate_center); 17686e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka break; 17786e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka case 2: 17886e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka tv = (TextView)findViewById(R.id.candidate_right); 17986e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka break; 18086e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka default: 18186e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka tv = (TextView)inflater.inflate(R.layout.candidate, null); 18286e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka break; 18386e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka } 18486e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka if (i < NUM_CANDIDATES_IN_STRIP) 18586e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka setLayoutWeight(tv, 1.0f); 186e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka tv.setTag(i); 187e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka tv.setOnClickListener(this); 188e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka if (i == 0) 189e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka tv.setOnLongClickListener(this); 1901e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka mWords.add(tv); 191ddb61ea461b920d87be4ad78c8a36eec1013b965Tadashi G. Takaoka if (i > 0) { 192070760dc5abdb9a850a94c509eeb9f486515af59Tadashi G. Takaoka final View divider = inflater.inflate(R.layout.candidate_divider, null); 19386e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka divider.measure(UNSPECIFIED_MEASURESPEC, UNSPECIFIED_MEASURESPEC); 194ddb61ea461b920d87be4ad78c8a36eec1013b965Tadashi G. Takaoka mDividers.add(divider); 195ddb61ea461b920d87be4ad78c8a36eec1013b965Tadashi G. Takaoka } 196c9716b28ce438e06b5cacc07fc002944bcbe24a0Tadashi G. Takaoka } 197179ada958b0bb46c6b9c8eb8b220d84dd3db855aTadashi G. Takaoka 19808a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka final TypedArray a = context.obtainStyledAttributes( 19908a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka attrs, R.styleable.CandidateView, defStyle, R.style.CandidateViewStyle); 20008a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka mAutoCorrectHighlight = a.getInt(R.styleable.CandidateView_autoCorrectHighlight, 0); 20108a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka mColorTypedWord = a.getColor(R.styleable.CandidateView_colorTypedWord, 0); 20208a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka mColorAutoCorrect = a.getColor(R.styleable.CandidateView_colorAutoCorrect, 0); 20308a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka mColorSuggestedCandidate = a.getColor(R.styleable.CandidateView_colorSuggested, 0); 20408a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka mInvertedForegroundColorSpan = new ForegroundColorSpan(mColorTypedWord ^ 0x00ffffff); 20508a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka mInvertedBackgroundColorSpan = new BackgroundColorSpan(mColorTypedWord); 20608a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka 20708a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka mExpandCandidatesPane = (ImageView)findViewById(R.id.expand_candidates_pane); 20808a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka mExpandCandidatesPane.setImageDrawable( 20908a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka a.getDrawable(R.styleable.CandidateView_iconExpandPane)); 21086e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka mExpandCandidatesPane.setOnClickListener(new OnClickListener() { 21186e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka @Override 21286e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka public void onClick(View view) { 21386e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka expandCandidatesPane(); 21486e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka } 21586e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka }); 21608a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka mCloseCandidatesPane = (ImageView)findViewById(R.id.close_candidates_pane); 21708a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka mCloseCandidatesPane.setImageDrawable( 21808a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka a.getDrawable(R.styleable.CandidateView_iconClosePane)); 21986e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka mCloseCandidatesPane.setOnClickListener(new OnClickListener() { 22086e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka @Override 22186e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka public void onClick(View view) { 22286e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka closeCandidatesPane(); 22386e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka } 22486e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka }); 22508a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka 22608a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka a.recycle(); 227923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 228c9716b28ce438e06b5cacc07fc002944bcbe24a0Tadashi G. Takaoka 229923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 230c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaoka * A connection back to the input method. 231923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * @param listener 232923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 23386e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka public void setListener(Listener listener, View inputView) { 234c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaoka mListener = listener; 23586e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka mKeyboardView = inputView.findViewById(R.id.keyboard_view); 23686e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka mCandidatesPane = (ViewGroup)inputView.findViewById(R.id.candidates_pane); 23786e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka mCandidatesPane.setOnClickListener(this); 23886e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka mCandidatesPaneContainer = (ViewGroup)inputView.findViewById( 23986e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka R.id.candidates_pane_container); 240923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 241923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 2427e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka public void setSuggestions(SuggestedWords suggestions) { 2439fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka if (suggestions == null) 244a15e45ef45931ba46678b39c159518d8aa2d4d65Tadashi G. Takaoka return; 2459fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka mSuggestions = suggestions; 24655b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka if (mShowingAutoCorrectionInverted) { 2479fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka mHandler.postUpdateSuggestions(); 24855b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka } else { 2499fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka updateSuggestions(); 25055b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka } 25155b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka } 25255b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka 25386e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka private static void setLayoutWeight(View v, float weight) { 25486e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka ViewGroup.LayoutParams lp = v.getLayoutParams(); 25586e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka if (lp instanceof LinearLayout.LayoutParams) { 25686e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka LinearLayout.LayoutParams llp = (LinearLayout.LayoutParams)lp; 25786e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka llp.width = 0; 25886e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka llp.weight = weight; 25986e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka } 26086e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka } 26186e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka 2621e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka private CharSequence getStyledCandidateWord(CharSequence word, boolean isAutoCorrect) { 2631e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka if (!isAutoCorrect) 2641e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka return word; 2651e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka final Spannable spannedWord = new SpannableString(word); 26608a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka if ((mAutoCorrectHighlight & AUTO_CORRECT_BOLD) != 0) 26708a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka spannedWord.setSpan(BOLD_SPAN, 0, word.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); 26808a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka if ((mAutoCorrectHighlight & AUTO_CORRECT_UNDERLINE) != 0) 26908a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka spannedWord.setSpan(UNDERLINE_SPAN, 0, word.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); 2701e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka return spannedWord; 2711e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka } 2721e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka 2731e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka private int getCandidateTextColor(boolean isAutoCorrect, boolean isSuggestedCandidate, 2741e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka SuggestedWordInfo info) { 2751e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka final int color; 27608a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka if (isAutoCorrect) { 2771e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka color = mColorAutoCorrect; 27808a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka } else if (isSuggestedCandidate) { 2791e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka color = mColorSuggestedCandidate; 2801e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka } else { 2811e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka color = mColorTypedWord; 2821e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka } 2831e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka if (info != null && info.isPreviousSuggestedWord()) { 2841e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka final int newAlpha = (int)(Color.alpha(color) * 0.5f); 2851e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka return Color.argb(newAlpha, Color.red(color), Color.green(color), Color.blue(color)); 2861e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka } else { 2871e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka return color; 2881e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka } 2891e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka } 2901e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka 2919fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka private void updateSuggestions() { 2929fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka final SuggestedWords suggestions = mSuggestions; 2931e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka final List<SuggestedWordInfo> suggestedWordInfoList = suggestions.mSuggestedWordInfoList; 2941e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka 295e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka clear(); 29686e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka final int paneWidth = getWidth(); 29786e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka final int dividerWidth = mDividers.get(0).getMeasuredWidth(); 29886e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka int x = 0; 29986e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka int y = 0; 30086e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka int fromIndex = NUM_CANDIDATES_IN_STRIP; 301ec5ae3a1b6ce6d6c87406def849995ec63816c55Ken Wakasa final int count = Math.min(mWords.size(), suggestions.size()); 30286e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka closeCandidatesPane(); 30386e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka mExpandCandidatesPane.setEnabled(count >= NUM_CANDIDATES_IN_STRIP); 304923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project for (int i = 0; i < count; i++) { 3051e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka final CharSequence word = suggestions.getWord(i); 3067e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka if (word == null) continue; 3071e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka 3081e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka final SuggestedWordInfo info = (suggestedWordInfoList != null) 3091e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka ? suggestedWordInfoList.get(i) : null; 3101e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka final boolean isAutoCorrect = suggestions.mHasMinimalSuggestion 31182411d47ba7e8133ed2390c6920945e139a738cesatok && ((i == 1 && !suggestions.mTypedWordValid) 3121e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka || (i == 0 && suggestions.mTypedWordValid)); 3131e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka // HACK: even if i == 0, we use mColorOther when this suggestion's length is 1 3141e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka // and there are multiple suggestions, such as the default punctuation list. 3151e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka // TODO: Need to revisit this logic with bigram suggestions 3161e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka final boolean isSuggestedCandidate = (i != 0); 3171e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka final boolean isPunctuationSuggestions = (word.length() == 1 && count > 1); 3181e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka 3191e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka final TextView tv = mWords.get(i); 32086e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka // TODO: Reorder candidates in strip as appropriate. The center candidate should hold 32186e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka // the word when space is typed (valid typed word or auto corrected word). 3221e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka tv.setTextColor(getCandidateTextColor(isAutoCorrect, 3231e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka isSuggestedCandidate || isPunctuationSuggestions, info)); 3241e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka tv.setText(getStyledCandidateWord(word, isAutoCorrect)); 32586e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka // TODO: call TextView.setTextScaleX() to fit the candidate in single line. 32686e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka if (i >= NUM_CANDIDATES_IN_STRIP) { 32786e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka tv.measure(UNSPECIFIED_MEASURESPEC, UNSPECIFIED_MEASURESPEC); 32886e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka final int width = tv.getMeasuredWidth(); 32986e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka // TODO: Handle overflow case. 33086e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka if (dividerWidth + x + width >= paneWidth) { 33186e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka centeringCandidates(fromIndex, i - 1, x, paneWidth); 33286e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka x = 0; 33386e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka y += mCandidateStripHeight; 33486e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka fromIndex = i; 33586e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka } 33686e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka if (x != 0) { 33786e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka final View divider = mDividers.get(i - NUM_CANDIDATES_IN_STRIP); 33886e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka mCandidatesPane.addView(divider); 33986e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka placeCandidateAt(divider, x, y); 34086e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka x += dividerWidth; 34186e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka } 34286e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka mCandidatesPane.addView(tv); 34386e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka placeCandidateAt(tv, x, y); 34486e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka x += width; 3457e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka } 3461e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka 3471e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka if (DBG && info != null) { 3481e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka final TextView dv = new TextView(getContext(), null); 3491e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka dv.setTextSize(10.0f); 3501e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka dv.setTextColor(0xff808080); 3511e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka dv.setText(info.getDebugString()); 35286e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka // TODO: debug view for candidate strip needed. 35386e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka mCandidatesPane.addView(dv); 3541e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)dv.getLayoutParams(); 3551e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka lp.gravity = Gravity.BOTTOM; 3561e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka } 357923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 35886e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka if (x != 0) { 35986e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka // Centering last candidates row. 36086e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka centeringCandidates(fromIndex, count - 1, x, paneWidth); 36186e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka } 36286e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka } 36386e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka 36486e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka private void placeCandidateAt(View v, int x, int y) { 36586e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka ViewGroup.LayoutParams lp = v.getLayoutParams(); 36686e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka if (lp instanceof ViewGroup.MarginLayoutParams) { 36786e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams)lp; 36886e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka mlp.width = v.getMeasuredWidth(); 36986e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka mlp.height = v.getMeasuredHeight(); 37086e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka mlp.setMargins(x, y + (mCandidateStripHeight - mlp.height) / 2, 0, 0); 37186e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka } 37286e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka } 37386e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka 37486e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka private void centeringCandidates(int from, int to, int width, int paneWidth) { 37586e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka final ViewGroup pane = mCandidatesPane; 37686e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka final int fromIndex = pane.indexOfChild(mWords.get(from)); 37786e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka final int toIndex = pane.indexOfChild(mWords.get(to)); 37886e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka final int offset = (paneWidth - width) / 2; 37986e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka for (int index = fromIndex; index <= toIndex; index++) { 38086e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka offsetMargin(pane.getChildAt(index), offset, 0); 38186e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka } 38286e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka } 38386e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka 38486e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka private static void offsetMargin(View v, int dx, int dy) { 385862e05a8f0aa03ddc4582d5bf68a3201f348cf1dTadashi G. Takaoka if (v == null) 386862e05a8f0aa03ddc4582d5bf68a3201f348cf1dTadashi G. Takaoka return; 38786e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka ViewGroup.LayoutParams lp = v.getLayoutParams(); 38886e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka if (lp instanceof ViewGroup.MarginLayoutParams) { 38986e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams)lp; 39086e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka mlp.setMargins(mlp.leftMargin + dx, mlp.topMargin + dy, 0, 0); 39186e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka } 39286e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka } 39386e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka 39486e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka private void expandCandidatesPane() { 39586e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka mExpandCandidatesPane.setVisibility(View.GONE); 39686e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka mCloseCandidatesPane.setVisibility(View.VISIBLE); 39786e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka mCandidatesPaneContainer.setMinimumHeight(mKeyboardView.getMeasuredHeight()); 39886e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka mCandidatesPaneContainer.setVisibility(View.VISIBLE); 39986e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka mKeyboardView.setVisibility(View.GONE); 40086e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka } 401e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka 40286e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka private void closeCandidatesPane() { 40386e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka mExpandCandidatesPane.setVisibility(View.VISIBLE); 40486e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka mCloseCandidatesPane.setVisibility(View.GONE); 40586e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka mCandidatesPaneContainer.setVisibility(View.GONE); 40686e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka mKeyboardView.setVisibility(View.VISIBLE); 407923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 408923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 40955b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka public void onAutoCorrectionInverted(CharSequence autoCorrectedWord) { 41008a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka if ((mAutoCorrectHighlight & AUTO_CORRECT_INVERT) == 0) 41155b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka return; 4121e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka final TextView tv = mWords.get(1); 41355b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka final Spannable word = new SpannableString(autoCorrectedWord); 41455b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka final int wordLength = word.length(); 4158553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard word.setSpan(mInvertedBackgroundColorSpan, 0, wordLength, 4168553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard Spanned.SPAN_INCLUSIVE_EXCLUSIVE); 4178553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard word.setSpan(mInvertedForegroundColorSpan, 0, wordLength, 4188553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard Spanned.SPAN_INCLUSIVE_EXCLUSIVE); 41955b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka tv.setText(word); 42055b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka mShowingAutoCorrectionInverted = true; 42155b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka } 42255b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka 42308a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka public boolean needsAutoCorrectionSpacebarLed() { 42408a6f2aea71d998206c47c16dcda4eaa90f8c9eaTadashi G. Takaoka return (mAutoCorrectHighlight & AUTO_CORRECT_SPACEBAR_LED) != 0; 4257e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka } 4267e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka 427b00a1d0c0adbdfc507676772201e979e539a2801Amith Yamasani public boolean isShowingAddToDictionaryHint() { 428b00a1d0c0adbdfc507676772201e979e539a2801Amith Yamasani return mShowingAddToDictionary; 429b00a1d0c0adbdfc507676772201e979e539a2801Amith Yamasani } 430b00a1d0c0adbdfc507676772201e979e539a2801Amith Yamasani 43166a787b953d703201c6b827abbee74e8cd9bb063Amith Yamasani public void showAddToDictionaryHint(CharSequence word) { 4327e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka SuggestedWords.Builder builder = new SuggestedWords.Builder() 4337e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka .addWord(word) 4347e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka .addWord(getContext().getText(R.string.hint_add_to_dictionary)); 4357e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka setSuggestions(builder.build()); 43666a787b953d703201c6b827abbee74e8cd9bb063Amith Yamasani mShowingAddToDictionary = true; 437e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka // Disable R.string.hint_add_to_dictionary button 4381e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka TextView tv = mWords.get(1); 439e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka tv.setClickable(false); 44066a787b953d703201c6b827abbee74e8cd9bb063Amith Yamasani } 44166a787b953d703201c6b827abbee74e8cd9bb063Amith Yamasani 4426558253160e2039c87f424bd814f402ecd31de3bKen Wakasa public boolean dismissAddToDictionaryHint() { 4436558253160e2039c87f424bd814f402ecd31de3bKen Wakasa if (!mShowingAddToDictionary) return false; 4446558253160e2039c87f424bd814f402ecd31de3bKen Wakasa clear(); 4456558253160e2039c87f424bd814f402ecd31de3bKen Wakasa return true; 4466558253160e2039c87f424bd814f402ecd31de3bKen Wakasa } 4476558253160e2039c87f424bd814f402ecd31de3bKen Wakasa 4487e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka public SuggestedWords getSuggestions() { 449979f8690967ff5409fe18f5085858ccdb8e0ccf1satok return mSuggestions; 450979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 451979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 452923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project public void clear() { 45366a787b953d703201c6b827abbee74e8cd9bb063Amith Yamasani mShowingAddToDictionary = false; 45455b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka mShowingAutoCorrectionInverted = false; 45586e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka for (int i = 0; i < NUM_CANDIDATES_IN_STRIP; i++) 45686e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka mWords.get(i).setText(null); 45786e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka mCandidatesPane.removeAllViews(); 458923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 459923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 4606ec55a1599c74150e82ea7e4371ec815f0d2df27Amith Yamasani private void hidePreview() { 461179ada958b0bb46c6b9c8eb8b220d84dd3db855aTadashi G. Takaoka mPreviewPopup.dismiss(); 4626ec55a1599c74150e82ea7e4371ec815f0d2df27Amith Yamasani } 463e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka 464e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka private void showPreview(int index, CharSequence word) { 465e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka if (TextUtils.isEmpty(word)) 466e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka return; 467e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka 468e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka final TextView previewText = mPreviewText; 4691e273fa6d1e8babfebebbbe331d87d3854755e7fTadashi G. Takaoka previewText.setTextColor(mColorTypedWord); 470e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka previewText.setText(word); 471e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka previewText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 472e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); 473f20eb55d4f8063f89c6c0abb3af3029fe23cb845Tadashi G. Takaoka View v = mWords.get(index); 474e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka final int[] offsetInWindow = new int[2]; 475e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka v.getLocationInWindow(offsetInWindow); 476e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka final int posX = offsetInWindow[0]; 477e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka final int posY = offsetInWindow[1] - previewText.getMeasuredHeight(); 478e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka final PopupWindow previewPopup = mPreviewPopup; 479e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka if (previewPopup.isShowing()) { 480e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka previewPopup.update(posX, posY, previewPopup.getWidth(), previewPopup.getHeight()); 481e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka } else { 482e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka previewPopup.showAtLocation(this, Gravity.NO_GRAVITY, posX, posY); 483923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 484e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka previewText.setVisibility(VISIBLE); 48555b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka mHandler.postHidePreview(); 486923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 487fcba53ef7c874a4685c12c01404c91b779cae1e8Tadashi G. Takaoka 488e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka private void addToDictionary(CharSequence word) { 489c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaoka if (mListener.addWordToDictionary(word.toString())) { 490e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka showPreview(0, getContext().getString(R.string.added_word, word)); 491e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka } 492e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka } 493e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka 494e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka @Override 495e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka public boolean onLongClick(View view) { 49686e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka final Object tag = view.getTag(); 49786e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka if (!(tag instanceof Integer)) 49886e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka return true; 49986e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka final int index = (Integer) tag; 50037deb112c727dfed5e94d054cf5f00f5d60c8120Tadashi G. Takaoka if (index >= mSuggestions.size()) 50137deb112c727dfed5e94d054cf5f00f5d60c8120Tadashi G. Takaoka return true; 50286e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka 50337deb112c727dfed5e94d054cf5f00f5d60c8120Tadashi G. Takaoka final CharSequence word = mSuggestions.getWord(index); 504e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka if (word.length() < 2) 505e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka return false; 506e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka addToDictionary(word); 507e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka return true; 508e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka } 509e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka 510e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka @Override 511e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka public void onClick(View view) { 51286e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka final Object tag = view.getTag(); 51386e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka if (!(tag instanceof Integer)) 51486e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka return; 51586e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka final int index = (Integer) tag; 51637deb112c727dfed5e94d054cf5f00f5d60c8120Tadashi G. Takaoka if (index >= mSuggestions.size()) 51737deb112c727dfed5e94d054cf5f00f5d60c8120Tadashi G. Takaoka return; 51886e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka 51937deb112c727dfed5e94d054cf5f00f5d60c8120Tadashi G. Takaoka final CharSequence word = mSuggestions.getWord(index); 520e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka if (mShowingAddToDictionary && index == 0) { 521e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka addToDictionary(word); 522e49bd1c43acad08f103b38430a8bbcba23f325b3Tadashi G. Takaoka } else { 523c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaoka mListener.pickSuggestionManually(index, word); 524923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 52586e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka // Because some punctuation letters are not treated as word separator depending on locale, 52686e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka // {@link #setSuggestions} might not be called and candidates pane left opened. 52786e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka closeCandidatesPane(); 528923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 52982411d47ba7e8133ed2390c6920945e139a738cesatok 5306ec55a1599c74150e82ea7e4371ec815f0d2df27Amith Yamasani @Override 5316ec55a1599c74150e82ea7e4371ec815f0d2df27Amith Yamasani public void onDetachedFromWindow() { 5326ec55a1599c74150e82ea7e4371ec815f0d2df27Amith Yamasani super.onDetachedFromWindow(); 53355b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka mHandler.cancelAllMessages(); 5346ec55a1599c74150e82ea7e4371ec815f0d2df27Amith Yamasani hidePreview(); 5356ec55a1599c74150e82ea7e4371ec815f0d2df27Amith Yamasani } 536923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project} 537