| /* |
| * Copyright (C) 2008-2012 OMRON SOFTWARE Co., Ltd. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package jp.co.omronsoft.openwnn; |
| |
| import android.app.AlertDialog; |
| import android.content.Context; |
| import android.content.DialogInterface; |
| import android.content.SharedPreferences; |
| import android.content.res.Resources; |
| import android.graphics.drawable.Drawable; |
| import android.media.AudioManager; |
| import android.os.Handler; |
| import android.os.Message; |
| import android.os.Vibrator; |
| import android.text.Layout; |
| import android.text.SpannableString; |
| import android.text.Spanned; |
| import android.text.style.AbsoluteSizeSpan; |
| import android.text.style.AlignmentSpan; |
| import android.util.DisplayMetrics; |
| import android.util.Log; |
| import android.view.KeyEvent; |
| import android.view.LayoutInflater; |
| import android.view.MotionEvent; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.view.View.OnClickListener; |
| import android.view.View.OnLongClickListener; |
| import android.widget.Button; |
| import android.widget.ImageView; |
| import android.widget.LinearLayout; |
| import android.widget.HorizontalScrollView; |
| import android.widget.TextView; |
| |
| import java.util.ArrayList; |
| |
| /** |
| * The default candidates view manager using {@link android.widget.EditText}. |
| * |
| * @author Copyright (C) 2011 OMRON SOFTWARE CO., LTD. All Rights Reserved. |
| */ |
| public class TextCandidates1LineViewManager extends CandidatesViewManager { |
| |
| /** displayCandidates() normal display */ |
| private static final int IS_NEXTCANDIDATE_NORMAL = 1; |
| /** displayCandidates() delay display */ |
| private static final int IS_NEXTCANDIDATE_DELAY = 2; |
| /** displayCandidates() display end */ |
| private static final int IS_NEXTCANDIDATE_END = 3; |
| |
| /** Delay of set candidate */ |
| private static final int SET_CANDIDATE_DELAY = 50; |
| /** Delay Millis */ |
| private static final int CANDIDATE_DELAY_MILLIS = 500; |
| |
| /** Scroll distance */ |
| private static final float SCROLL_DISTANCE = 0.9f; |
| |
| /** Body view of the candidates list */ |
| private ViewGroup mViewBody; |
| /** Scroller */ |
| private HorizontalScrollView mViewBodyScroll; |
| /** Left more button */ |
| private ImageView mLeftMoreButton; |
| /** Right more button */ |
| private ImageView mRightMoreButton; |
| /** Candidate view */ |
| private LinearLayout mViewCandidateList; |
| |
| /** {@link OpenWnn} instance using this manager */ |
| private OpenWnn mWnn; |
| /** View type (VIEW_TYPE_NORMAL or VIEW_TYPE_FULL or VIEW_TYPE_CLOSE) */ |
| private int mViewType; |
| |
| /** view width */ |
| private int mViewWidth; |
| /** Minimum width of candidate view */ |
| private int mCandidateMinimumWidth; |
| /** Minimum height of candidate view */ |
| private int mCandidateMinimumHeight; |
| |
| /** Minimum width of candidate view */ |
| private static final int CANDIDATE_MINIMUM_WIDTH = 48; |
| |
| /** Whether hide the view if there is no candidate */ |
| private boolean mAutoHideMode; |
| /** The converter to get candidates from and notice the selected candidate to. */ |
| private WnnEngine mConverter; |
| /** Limitation of displaying candidates */ |
| private int mDisplayLimit; |
| |
| /** Vibrator for touch vibration */ |
| private Vibrator mVibrator = null; |
| /** AudioManager for click sound */ |
| private AudioManager mSound = null; |
| |
| /** Number of candidates displaying */ |
| private int mWordCount; |
| /** List of candidates */ |
| private ArrayList<WnnWord> mWnnWordArray; |
| |
| /** Character width of the candidate area */ |
| private int mLineLength = 0; |
| /** Maximum width of candidate view */ |
| private int mCandidateMaxWidth = 0; |
| /** general information about a display */ |
| private final DisplayMetrics mMetrics = new DisplayMetrics(); |
| /** Focus is none now */ |
| private static final int FOCUS_NONE = -1; |
| /** Handler for set Candidate */ |
| private static final int MSG_SET_CANDIDATES = 1; |
| /** List of textView for CandiData List */ |
| private ArrayList<TextView> mTextViewArray = new ArrayList<TextView>(); |
| /** Now focus textView index */ |
| private int mCurrentFocusIndex = FOCUS_NONE; |
| /** Focused View */ |
| private View mFocusedView = null; |
| /** Focused View Background */ |
| private Drawable mFocusedViewBackground = null; |
| /** Scale up text size */ |
| private AbsoluteSizeSpan mSizeSpan; |
| /** Scale up text alignment */ |
| private AlignmentSpan.Standard mCenterSpan; |
| /** Whether candidates long click enable */ |
| private boolean mEnableCandidateLongClick = true; |
| |
| /** {@code Handler} Handler for focus Candidate wait delay */ |
| private Handler mHandler = new Handler() { |
| @Override public void handleMessage(Message msg) { |
| |
| switch (msg.what) { |
| case MSG_SET_CANDIDATES: |
| displayCandidatesDelay(mConverter); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| }; |
| |
| /** Event listener for touching a candidate */ |
| private OnClickListener mCandidateOnClick = new OnClickListener() { |
| public void onClick(View v) { |
| if (!v.isShown()) { |
| return; |
| } |
| playSoundAndVibration(); |
| |
| if (v instanceof CandidateTextView) { |
| CandidateTextView text = (CandidateTextView)v; |
| int wordcount = text.getId(); |
| WnnWord word = getWnnWord(wordcount); |
| clearFocusCandidate(); |
| selectCandidate(word); |
| } |
| } |
| }; |
| |
| /** Event listener for long-clicking a candidate */ |
| private OnLongClickListener mCandidateOnLongClick = new OnLongClickListener() { |
| public boolean onLongClick(View v) { |
| if (!v.isShown()) { |
| return true; |
| } |
| |
| if (!mEnableCandidateLongClick) { |
| return false; |
| } |
| |
| clearFocusCandidate(); |
| |
| int wordcount = ((TextView)v).getId(); |
| mWord = mWnnWordArray.get(wordcount); |
| |
| displayDialog(v, mWord); |
| return true; |
| } |
| }; |
| |
| /** |
| * Constructor |
| */ |
| public TextCandidates1LineViewManager() { |
| this(300); |
| } |
| |
| /** |
| * Constructor |
| * |
| * @param displayLimit The limit of display |
| */ |
| public TextCandidates1LineViewManager(int displayLimit) { |
| mDisplayLimit = displayLimit; |
| mWnnWordArray = new ArrayList<WnnWord>(); |
| mAutoHideMode = true; |
| mMetrics.setToDefaults(); |
| } |
| |
| /** |
| * Set auto-hide mode. |
| * @param hide {@code true} if the view will hidden when no candidate exists; |
| * {@code false} if the view is always shown. |
| */ |
| public void setAutoHide(boolean hide) { |
| mAutoHideMode = hide; |
| } |
| |
| /** @see CandidatesViewManager */ |
| public View initView(OpenWnn parent, int width, int height) { |
| mWnn = parent; |
| mViewWidth = width; |
| |
| Resources r = mWnn.getResources(); |
| |
| mCandidateMinimumWidth = (int)(CANDIDATE_MINIMUM_WIDTH * mMetrics.density); |
| mCandidateMinimumHeight = r.getDimensionPixelSize(R.dimen.candidate_layout_height); |
| |
| LayoutInflater inflater = parent.getLayoutInflater(); |
| mViewBody = (ViewGroup)inflater.inflate(R.layout.candidates_1line, null); |
| mViewBodyScroll = (HorizontalScrollView)mViewBody.findViewById(R.id.candview_scroll_1line); |
| mViewBodyScroll.setOverScrollMode(View.OVER_SCROLL_NEVER); |
| mViewBodyScroll.setOnTouchListener(new View.OnTouchListener() { |
| public boolean onTouch(View v, MotionEvent event) { |
| switch (event.getAction()) { |
| case MotionEvent.ACTION_DOWN: |
| case MotionEvent.ACTION_MOVE: |
| if (mHandler.hasMessages(MSG_SET_CANDIDATES)) { |
| mHandler.removeMessages(MSG_SET_CANDIDATES); |
| mHandler.sendEmptyMessageDelayed(MSG_SET_CANDIDATES, CANDIDATE_DELAY_MILLIS); |
| } |
| break; |
| |
| default: |
| break; |
| |
| } |
| return false; |
| } |
| }); |
| |
| mLeftMoreButton = (ImageView)mViewBody.findViewById(R.id.left_more_imageview); |
| mLeftMoreButton.setOnClickListener(new View.OnClickListener() { |
| public void onClick(View v) { |
| if (!v.isShown()) { |
| return; |
| } |
| playSoundAndVibration(); |
| if (mViewBodyScroll.getScrollX() > 0) { |
| mViewBodyScroll.smoothScrollBy( |
| (int)(mViewBodyScroll.getWidth() * -SCROLL_DISTANCE), 0); |
| } |
| } |
| }); |
| mLeftMoreButton.setOnLongClickListener(new View.OnLongClickListener() { |
| public boolean onLongClick(View v) { |
| if (!v.isShown()) { |
| return false; |
| } |
| if (!mViewBodyScroll.fullScroll(View.FOCUS_LEFT)) { |
| mViewBodyScroll.scrollTo(mViewBodyScroll.getChildAt(0).getWidth(), 0); |
| } |
| return true; |
| } |
| }); |
| |
| mRightMoreButton = (ImageView)mViewBody.findViewById(R.id.right_more_imageview); |
| mRightMoreButton.setOnClickListener(new View.OnClickListener() { |
| public void onClick(View v) { |
| if (!v.isShown()) { |
| return; |
| } |
| int width = mViewBodyScroll.getWidth(); |
| int scrollMax = mViewBodyScroll.getChildAt(0).getRight(); |
| |
| if ((mViewBodyScroll.getScrollX() + width) < scrollMax) { |
| mViewBodyScroll.smoothScrollBy((int)(width * SCROLL_DISTANCE), 0); |
| } |
| } |
| }); |
| mRightMoreButton.setOnLongClickListener(new View.OnLongClickListener() { |
| public boolean onLongClick(View v) { |
| if (!v.isShown()) { |
| return false; |
| } |
| if (!mViewBodyScroll.fullScroll(View.FOCUS_RIGHT)) { |
| mViewBodyScroll.scrollTo(0, 0); |
| } |
| return true; |
| } |
| }); |
| |
| mViewLongPressDialog = (View)inflater.inflate(R.layout.candidate_longpress_dialog, null); |
| |
| /* select button */ |
| Button longPressDialogButton = (Button)mViewLongPressDialog.findViewById(R.id.candidate_longpress_dialog_select); |
| longPressDialogButton.setOnClickListener(new View.OnClickListener() { |
| public void onClick(View v) { |
| playSoundAndVibration(); |
| clearFocusCandidate(); |
| selectCandidate(mWord); |
| closeDialog(); |
| } |
| }); |
| |
| /* cancel button */ |
| longPressDialogButton = (Button)mViewLongPressDialog.findViewById(R.id.candidate_longpress_dialog_cancel); |
| longPressDialogButton.setOnClickListener(new View.OnClickListener() { |
| public void onClick(View v) { |
| playSoundAndVibration(); |
| mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.LIST_CANDIDATES_NORMAL)); |
| mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.UPDATE_CANDIDATE)); |
| closeDialog(); |
| } |
| }); |
| |
| int buttonWidth = r.getDimensionPixelSize(R.dimen.candidate_layout_width); |
| mCandidateMaxWidth = (mViewWidth - buttonWidth * 2) / 2; |
| |
| mSizeSpan = new AbsoluteSizeSpan(r.getDimensionPixelSize(R.dimen.candidate_delete_word_size)); |
| mCenterSpan = new AlignmentSpan.Standard(Layout.Alignment.ALIGN_CENTER); |
| |
| createNormalCandidateView(); |
| |
| setViewType(CandidatesViewManager.VIEW_TYPE_CLOSE); |
| |
| return mViewBody; |
| } |
| |
| /** |
| * Create normal candidate view |
| */ |
| private void createNormalCandidateView() { |
| mViewCandidateList = (LinearLayout)mViewBody.findViewById(R.id.candidates_view_1line); |
| |
| Context context = mViewBodyScroll.getContext(); |
| for (int i = 0; i < mDisplayLimit; i++) { |
| mViewCandidateList.addView(new CandidateTextView(context, |
| mCandidateMinimumHeight, |
| mCandidateMinimumWidth, |
| mCandidateMaxWidth)); |
| } |
| } |
| |
| /** @see CandidatesViewManager#getCurrentView */ |
| public View getCurrentView() { |
| return mViewBody; |
| } |
| |
| /** @see CandidatesViewManager#setViewType */ |
| public void setViewType(int type) { |
| mViewType = type; |
| |
| if (type == CandidatesViewManager.VIEW_TYPE_NORMAL) { |
| mViewCandidateList.setMinimumHeight(mCandidateMinimumHeight); |
| } else { |
| mViewCandidateList.setMinimumHeight(-1); |
| mHandler.removeMessages(MSG_SET_CANDIDATES); |
| |
| if (mViewBody.isShown()) { |
| mWnn.setCandidatesViewShown(false); |
| } |
| } |
| } |
| |
| /** @see CandidatesViewManager#getViewType */ |
| public int getViewType() { |
| return mViewType; |
| } |
| |
| /** @see CandidatesViewManager#displayCandidates */ |
| public void displayCandidates(WnnEngine converter) { |
| |
| mHandler.removeMessages(MSG_SET_CANDIDATES); |
| |
| closeDialog(); |
| clearCandidates(); |
| mConverter = converter; |
| |
| int isNextCandidate = IS_NEXTCANDIDATE_NORMAL; |
| while(isNextCandidate == IS_NEXTCANDIDATE_NORMAL) { |
| isNextCandidate = displayCandidatesNormal(converter); |
| } |
| |
| if (isNextCandidate == IS_NEXTCANDIDATE_DELAY) { |
| isNextCandidate = displayCandidatesDelay(converter); |
| } |
| |
| mViewBodyScroll.scrollTo(0,0); |
| } |
| |
| |
| /** |
| * Display the candidates. |
| * @param converter {@link WnnEngine} which holds candidates. |
| */ |
| private int displayCandidatesNormal(WnnEngine converter) { |
| int isNextCandidate = IS_NEXTCANDIDATE_NORMAL; |
| |
| if (converter == null) { |
| return IS_NEXTCANDIDATE_END; |
| } |
| |
| /* Get candidates */ |
| WnnWord result = converter.getNextCandidate(); |
| if (result == null) { |
| return IS_NEXTCANDIDATE_END; |
| } |
| |
| mLineLength += setCandidate(result); |
| if (mLineLength >= mViewWidth) { |
| isNextCandidate = IS_NEXTCANDIDATE_DELAY; |
| } |
| |
| if (mWordCount < 1) { /* no candidates */ |
| if (mAutoHideMode) { |
| mWnn.setCandidatesViewShown(false); |
| return IS_NEXTCANDIDATE_END; |
| } |
| } |
| |
| if (mWordCount > mDisplayLimit) { |
| return IS_NEXTCANDIDATE_END; |
| } |
| |
| if (!(mViewBody.isShown())) { |
| mWnn.setCandidatesViewShown(true); |
| } |
| return isNextCandidate; |
| } |
| |
| /** |
| * Display the candidates. |
| * @param converter {@link WnnEngine} which holds candidates. |
| */ |
| private int displayCandidatesDelay(WnnEngine converter) { |
| int isNextCandidate = IS_NEXTCANDIDATE_DELAY; |
| |
| if (converter == null) { |
| return IS_NEXTCANDIDATE_END; |
| } |
| |
| /* Get candidates */ |
| WnnWord result = converter.getNextCandidate(); |
| if (result == null) { |
| return IS_NEXTCANDIDATE_END; |
| } |
| |
| setCandidate(result); |
| |
| if (mWordCount > mDisplayLimit) { |
| return IS_NEXTCANDIDATE_END; |
| } |
| |
| mHandler.sendEmptyMessageDelayed(MSG_SET_CANDIDATES, SET_CANDIDATE_DELAY); |
| |
| return isNextCandidate; |
| } |
| |
| /** |
| * Set the candidate for candidate view |
| * @param word set word |
| * @return int Set width |
| */ |
| private int setCandidate(WnnWord word) { |
| CandidateTextView candidateTextView = |
| (CandidateTextView) mViewCandidateList.getChildAt(mWordCount); |
| candidateTextView.setCandidateTextView(word, mWordCount, mCandidateOnClick, |
| mCandidateOnLongClick); |
| mWnnWordArray.add(mWordCount, word); |
| mWordCount++; |
| mTextViewArray.add(candidateTextView); |
| |
| return candidateTextView.getWidth(); |
| } |
| |
| /** |
| * Clear the candidate view |
| */ |
| private void clearNormalViewCandidate() { |
| int candidateNum = mViewCandidateList.getChildCount(); |
| for (int i = 0; i < candidateNum; i++) { |
| View v = mViewCandidateList.getChildAt(i); |
| v.setVisibility(View.GONE); |
| } |
| } |
| |
| /** @see CandidatesViewManager#clearCandidates */ |
| public void clearCandidates() { |
| clearFocusCandidate(); |
| clearNormalViewCandidate(); |
| |
| mLineLength = 0; |
| |
| mWordCount = 0; |
| mWnnWordArray.clear(); |
| mTextViewArray.clear(); |
| |
| if (mAutoHideMode && mViewBody.isShown()) { |
| mWnn.setCandidatesViewShown(false); |
| } |
| } |
| |
| /** @see CandidatesViewManager#setPreferences */ |
| public void setPreferences(SharedPreferences pref) { |
| try { |
| if (pref.getBoolean("key_vibration", false)) { |
| mVibrator = (Vibrator)mWnn.getSystemService(Context.VIBRATOR_SERVICE); |
| } else { |
| mVibrator = null; |
| } |
| if (pref.getBoolean("key_sound", false)) { |
| mSound = (AudioManager)mWnn.getSystemService(Context.AUDIO_SERVICE); |
| } else { |
| mSound = null; |
| } |
| } catch (Exception ex) { |
| Log.d("OpenWnn", "NO VIBRATOR"); |
| } |
| } |
| |
| /** |
| * Select a candidate. |
| * <br> |
| * This method notices the selected word to {@link OpenWnn}. |
| * |
| * @param word The selected word |
| */ |
| private void selectCandidate(WnnWord word) { |
| mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.SELECT_CANDIDATE, word)); |
| } |
| |
| private void playSoundAndVibration() { |
| if (mVibrator != null) { |
| try { |
| mVibrator.vibrate(5); |
| } catch (Exception ex) { |
| Log.e("OpenWnn", "TextCandidates1LineViewManager::selectCandidate Vibrator " + ex.toString()); |
| } |
| } |
| if (mSound != null) { |
| try { |
| mSound.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD, 1.0f); |
| } catch (Exception ex) { |
| Log.e("OpenWnn", "TextCandidates1LineViewManager::selectCandidate Sound " + ex.toString()); |
| } |
| } |
| } |
| |
| /** |
| * KeyEvent action for the candidate view. |
| * |
| * @param key Key event |
| */ |
| public void processMoveKeyEvent(int key) { |
| if (!mViewBody.isShown()) { |
| return; |
| } |
| |
| switch (key) { |
| case KeyEvent.KEYCODE_DPAD_LEFT: |
| moveFocus(-1); |
| break; |
| |
| case KeyEvent.KEYCODE_DPAD_RIGHT: |
| moveFocus(1); |
| break; |
| |
| case KeyEvent.KEYCODE_DPAD_UP: |
| moveFocus(-1); |
| break; |
| |
| case KeyEvent.KEYCODE_DPAD_DOWN: |
| moveFocus(1); |
| break; |
| |
| default: |
| break; |
| |
| } |
| } |
| |
| /** |
| * Get a flag candidate is focused now. |
| * |
| * @return the Candidate is focused of a flag. |
| */ |
| public boolean isFocusCandidate(){ |
| if (mCurrentFocusIndex != FOCUS_NONE) { |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Give focus to View of candidate. |
| */ |
| private void setViewStatusOfFocusedCandidate() { |
| View view = mFocusedView; |
| if (view != null) { |
| view.setBackgroundDrawable(mFocusedViewBackground); |
| } |
| |
| TextView v = getFocusedView(); |
| mFocusedView = v; |
| if (v != null) { |
| mFocusedViewBackground = v.getBackground(); |
| v.setBackgroundResource(R.drawable.cand_back_focuse); |
| |
| int viewBodyLeft = getViewLeftOnScreen(mViewBodyScroll); |
| int viewBodyRight = viewBodyLeft + mViewBodyScroll.getWidth(); |
| int focusedViewLeft = getViewLeftOnScreen(v); |
| int focusedViewRight = focusedViewLeft + v.getWidth(); |
| |
| if (focusedViewRight > viewBodyRight) { |
| mViewBodyScroll.scrollBy((focusedViewRight - viewBodyRight), 0); |
| } else if (focusedViewLeft < viewBodyLeft) { |
| mViewBodyScroll.scrollBy((focusedViewLeft - viewBodyLeft), 0); |
| } |
| } |
| } |
| |
| /** |
| * Clear focus to selected candidate. |
| */ |
| private void clearFocusCandidate(){ |
| View view = mFocusedView; |
| if (view != null) { |
| view.setBackgroundDrawable(mFocusedViewBackground); |
| mFocusedView = null; |
| } |
| |
| mCurrentFocusIndex = FOCUS_NONE; |
| |
| mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.FOCUS_CANDIDATE_END)); |
| } |
| |
| /** |
| * Select candidate that has focus. |
| */ |
| public void selectFocusCandidate(){ |
| if (mCurrentFocusIndex != FOCUS_NONE) { |
| selectCandidate(getFocusedWnnWord()); |
| } |
| } |
| |
| /** |
| * Get View of focus candidate. |
| */ |
| private TextView getFocusedView() { |
| if (mCurrentFocusIndex == FOCUS_NONE) { |
| return null; |
| } |
| return mTextViewArray.get(mCurrentFocusIndex); |
| } |
| |
| /** |
| * Move the focus to next candidate. |
| * |
| * @param direction The direction of increment or decrement. |
| */ |
| private void moveFocus(int direction) { |
| boolean isStart = (mCurrentFocusIndex == FOCUS_NONE); |
| int size = mTextViewArray.size(); |
| int index = isStart ? 0 : (mCurrentFocusIndex + direction); |
| |
| if (index < 0) { |
| index = size - 1; |
| } else { |
| if (index >= size) { |
| index = 0; |
| } |
| } |
| |
| mCurrentFocusIndex = index; |
| setViewStatusOfFocusedCandidate(); |
| |
| if (isStart) { |
| mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.FOCUS_CANDIDATE_START)); |
| } |
| } |
| |
| /** |
| * Get view top position on screen. |
| * |
| * @param view target view. |
| * @return int view top position on screen |
| */ |
| private int getViewLeftOnScreen(View view) { |
| int[] location = new int[2]; |
| view.getLocationOnScreen(location); |
| return location[0]; |
| } |
| |
| /** @see CandidatesViewManager#getFocusedWnnWord */ |
| public WnnWord getFocusedWnnWord() { |
| return getWnnWord(mCurrentFocusIndex); |
| } |
| |
| /** |
| * Get WnnWord. |
| * |
| * @return WnnWord word |
| */ |
| public WnnWord getWnnWord(int index) { |
| return mWnnWordArray.get(index); |
| } |
| |
| /** @see CandidatesViewManager#setCandidateMsgRemove */ |
| public void setCandidateMsgRemove() { |
| mHandler.removeMessages(MSG_SET_CANDIDATES); |
| } |
| } |