Home | History | Annotate | Download | only in openwnn
      1 /*
      2  * Copyright (C) 2008-2012  OMRON SOFTWARE Co., Ltd.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package jp.co.omronsoft.openwnn;
     18 
     19 import java.nio.channels.Selector;
     20 import java.util.ArrayList;
     21 import java.util.LinkedList;
     22 import android.content.Context;
     23 import android.content.SharedPreferences;
     24 import android.content.res.Resources;
     25 import android.content.res.Configuration;
     26 import android.graphics.drawable.Drawable;
     27 import android.graphics.Rect;
     28 import android.media.AudioManager;
     29 import android.os.Handler;
     30 import android.os.Message;
     31 import android.os.Vibrator;
     32 import android.preference.PreferenceManager;
     33 import android.text.TextUtils;
     34 import android.text.TextPaint;
     35 import android.text.SpannableString;
     36 import android.text.Spanned;
     37 import android.text.style.ImageSpan;
     38 import android.text.style.DynamicDrawableSpan;
     39 import android.util.Log;
     40 import android.util.DisplayMetrics;
     41 import android.util.TypedValue;
     42 import android.view.Gravity;
     43 import android.view.KeyEvent;
     44 import android.view.MotionEvent;
     45 import android.view.View;
     46 import android.view.ViewGroup;
     47 import android.view.View.OnClickListener;
     48 import android.view.View.OnLongClickListener;
     49 import android.view.GestureDetector;
     50 import android.view.LayoutInflater;
     51 import android.widget.Button;
     52 import android.widget.LinearLayout;
     53 import android.widget.ScrollView;
     54 import android.widget.TextView;
     55 import android.widget.EditText;
     56 import android.widget.AbsoluteLayout;
     57 import android.widget.ImageView;
     58 
     59 /**
     60  * The default candidates view manager class using {@link EditText}.
     61  *
     62  * @author Copyright (C) 2009-2011 OMRON SOFTWARE CO., LTD.  All Rights Reserved.
     63  */
     64 public class TextCandidatesViewManager extends CandidatesViewManager implements GestureDetector.OnGestureListener {
     65     /** Number of lines to display (Portrait) */
     66     public static final int LINE_NUM_PORTRAIT       = 2;
     67     /** Number of lines to display (Landscape) */
     68     public static final int LINE_NUM_LANDSCAPE      = 1;
     69 
     70     /** Maximum lines */
     71     private static final int DISPLAY_LINE_MAX_COUNT = 1000;
     72     /** Maximum number of displaying candidates par one line (full view mode) */
     73     private static final int FULL_VIEW_DIV = 4;
     74     /** Maximum number of displaying candidates par one line (full view mode)(symbol)(portrait) */
     75     private static final int FULL_VIEW_SYMBOL_DIV_PORT = 6;
     76     /** Maximum number of displaying candidates par one line (full view mode)(symbol)(landscape) */
     77     private static final int FULL_VIEW_SYMBOL_DIV_LAND = 10;
     78     /** Delay of set candidate */
     79     private static final int SET_CANDIDATE_DELAY = 50;
     80     /** First line count */
     81     private static final int SET_CANDIDATE_FIRST_LINE_COUNT = 7;
     82     /** Delay line count */
     83     private static final int SET_CANDIDATE_DELAY_LINE_COUNT = 1;
     84 
     85     /** Focus is none now */
     86     private static final int FOCUS_NONE = -1;
     87     /** Handler for focus Candidate */
     88     private static final int MSG_MOVE_FOCUS = 0;
     89     /** Handler for set  Candidate */
     90     private static final int MSG_SET_CANDIDATES = 1;
     91     /** Handler for select Candidate */
     92     private static final int MSG_SELECT_CANDIDATES = 2;
     93 
     94     /** NUmber of Candidate display lines */
     95     private static final int SETTING_NUMBER_OF_LINEMAX = 5;
     96 
     97     /** Body view of the candidates list */
     98     private ViewGroup  mViewBody = null;
     99 
    100     /** The view of the Symbol Tab */
    101     private TextView mViewTabSymbol;
    102     /** The view of the Emoticon Tab */
    103     private TextView mViewTabEmoticon;
    104     /** Scroller of {@code mViewBodyText} */
    105     private ScrollView mViewBodyScroll;
    106     /** Base of {@code mViewCandidateList1st}, {@code mViewCandidateList2nd} */
    107     private ViewGroup mViewCandidateBase;
    108     /** Button displayed bottom of the view when there are more candidates. */
    109     private ImageView mReadMoreButton;
    110     /** Layout for the candidates list on normal view */
    111     private LinearLayout mViewCandidateList1st;
    112     /** Layout for the candidates list on full view */
    113     private AbsoluteLayout mViewCandidateList2nd;
    114     /** View for symbol tab */
    115     private LinearLayout mViewCandidateListTab;
    116     /** {@link OpenWnn} instance using this manager */
    117     private OpenWnn mWnn;
    118     /** View type (VIEW_TYPE_NORMAL or VIEW_TYPE_FULL or VIEW_TYPE_CLOSE) */
    119     private int mViewType;
    120     /** Portrait display({@code true}) or landscape({@code false}) */
    121     private boolean mPortrait;
    122 
    123     /** Width of the view */
    124     private int mViewWidth;
    125     /** Minimum width of a candidate (density support) */
    126     private int mCandidateMinimumWidth;
    127     /** Maximum width of a candidate (density support) */
    128     private int mCandidateMinimumHeight;
    129     /** Minimum height of the category candidate view */
    130     private int mCandidateCategoryMinimumHeight;
    131     /** Left align threshold of the candidate view */
    132     private int mCandidateLeftAlignThreshold;
    133     /** Height of keyboard */
    134     private int mKeyboardHeight;
    135     /** Height of symbol keyboard */
    136     private int mSymbolKeyboardHeight;
    137     /** Height of symbol keyboard tab */
    138     private int mSymbolKeyboardTabHeight;
    139     /** Whether being able to use Emoticon */
    140     private boolean mEnableEmoticon = false;
    141 
    142     /** Whether hide the view if there is no candidate */
    143     private boolean mAutoHideMode = true;
    144     /** The converter to get candidates from and notice the selected candidate to. */
    145     private WnnEngine mConverter;
    146     /** Limitation of displaying candidates */
    147     private int mDisplayLimit;
    148 
    149     /** Vibrator for touch vibration */
    150     private Vibrator mVibrator = null;
    151     /** AudioManager for click sound */
    152     private AudioManager mSound = null;
    153 
    154     /** Number of candidates displaying for 1st */
    155     private int mWordCount1st;
    156     /** Number of candidates displaying for 2nd */
    157     private int mWordCount2nd;
    158     /** List of candidates for 1st */
    159     private ArrayList<WnnWord> mWnnWordArray1st = new ArrayList<WnnWord>();
    160     /** List of candidates for 2nd */
    161     private ArrayList<WnnWord> mWnnWordArray2nd = new ArrayList<WnnWord>();
    162     /** List of select candidates */
    163     private LinkedList<WnnWord> mWnnWordSelectedList = new LinkedList<WnnWord>();
    164 
    165     /** Gesture detector */
    166     private GestureDetector mGestureDetector;
    167     /** Character width of the candidate area */
    168     private int mLineLength = 0;
    169     /** Number of lines displayed */
    170     private int mLineCount = 1;
    171 
    172     /** {@code true} if the full screen mode is selected */
    173     private boolean mIsFullView = false;
    174 
    175     /** The event object for "touch" */
    176     private MotionEvent mMotionEvent = null;
    177 
    178     /** The offset when the candidates is flowed out the candidate window */
    179     private int mDisplayEndOffset = 0;
    180     /** {@code true} if there are more candidates to display. */
    181     private boolean mCanReadMore = false;
    182     /** Color of the candidates */
    183     private int mTextColor = 0;
    184     /** Template object for each candidate and normal/full view change button */
    185     private TextView mViewCandidateTemplate;
    186     /** Number of candidates in full view */
    187     private int mFullViewWordCount;
    188     /** Number of candidates in the current line (in full view) */
    189     private int mFullViewOccupyCount;
    190     /** View of the previous candidate (in full view) */
    191     private TextView mFullViewPrevView;
    192     /** Id of the top line view (in full view) */
    193     private int mFullViewPrevLineTopId;
    194     /** Layout of the previous candidate (in full view) */
    195     private ViewGroup.LayoutParams mFullViewPrevParams;
    196     /** Whether all candidates are displayed */
    197     private boolean mCreateCandidateDone;
    198     /** Number of lines in normal view */
    199     private int mNormalViewWordCountOfLine;
    200 
    201     /** List of textView for CandiData List 1st for Symbol mode */
    202     private ArrayList<TextView> mTextViewArray1st = new ArrayList<TextView>();
    203     /** List of textView for CandiData List 2st for Symbol mode */
    204     private ArrayList<TextView> mTextViewArray2nd = new ArrayList<TextView>();
    205     /** Now focus textView index */
    206     private int mCurrentFocusIndex = FOCUS_NONE;
    207     /** Focused View */
    208     private View mFocusedView = null;
    209     /** Focused View Background */
    210     private Drawable mFocusedViewBackground = null;
    211     /** Axis to find next TextView for Up/Down */
    212     private int mFocusAxisX = 0;
    213     /** Now focused TextView in mTextViewArray1st */
    214     private boolean mHasFocusedArray1st = true;
    215 
    216     /** Portrait Number of Lines from Preference */
    217     private int mPortraitNumberOfLine = LINE_NUM_PORTRAIT;
    218     /** Landscape Number of Lines from Preference */
    219     private int mLandscapeNumberOfLine = LINE_NUM_LANDSCAPE;
    220 
    221     /** Coordinates of line */
    222     private int mLineY = 0;
    223 
    224     /** {@code true} if the candidate is selected */
    225     private boolean mIsSymbolSelected = false;
    226 
    227     /** Whether candidates is symbol */
    228     private boolean mIsSymbolMode = false;
    229 
    230     /** Symbol mode */
    231     private int mSymbolMode = OpenWnnJAJP.ENGINE_MODE_SYMBOL;
    232 
    233     /** Text size of candidates */
    234     private float mCandNormalTextSize;
    235 
    236     /** Text size of category */
    237     private float mCandCategoryTextSize;
    238 
    239     /** HardKeyboard hidden({@code true}) or disp({@code false}) */
    240     private boolean mHardKeyboardHidden = true;
    241 
    242     /** Minimum height of the candidate 1line view */
    243     private int mCandidateOneLineMinimumHeight;
    244 
    245     /** Whether candidates long click enable */
    246     private boolean mEnableCandidateLongClick = true;
    247 
    248     /** Keyboard vertical gap value */
    249     private static final float KEYBOARD_VERTICAL_GAP = 0.009f;
    250 
    251     /** Keyboard vertical gap count */
    252     private static final int KEYBOARD_VERTICAL_GAP_COUNT = 3;
    253 
    254     /** {@code Handler} Handler for focus Candidate wait delay */
    255     private Handler mHandler = new Handler() {
    256             @Override public void handleMessage(Message msg) {
    257                 switch (msg.what) {
    258                 case MSG_MOVE_FOCUS:
    259                     moveFocus(msg.arg1, msg.arg2 == 1);
    260                     break;
    261 
    262                 case MSG_SET_CANDIDATES:
    263                     if (mViewType == CandidatesViewManager.VIEW_TYPE_FULL && mIsSymbolMode) {
    264                         displayCandidates(mConverter, false, SET_CANDIDATE_DELAY_LINE_COUNT);
    265                     }
    266                     break;
    267 
    268                 case MSG_SELECT_CANDIDATES:
    269                     WnnWord word = null;
    270                     while ((word = mWnnWordSelectedList.poll()) != null) {
    271                         selectCandidate(word);
    272                     }
    273                     break;
    274 
    275                 default:
    276                     break;
    277                 }
    278             }
    279         };
    280 
    281     /** Event listener for touching a candidate for 1st */
    282     private OnClickListener mCandidateOnClick1st = new OnClickListener() {
    283         public void onClick(View v) {
    284             onClickCandidate(v, mWnnWordArray1st);
    285         }
    286     };
    287 
    288     /** Event listener for touching a candidate for 2nd */
    289     private OnClickListener mCandidateOnClick2nd = new OnClickListener() {
    290         public void onClick(View v) {
    291             onClickCandidate(v, mWnnWordArray2nd);
    292         }
    293     };
    294 
    295     /** Event listener for long-clicking a candidate for 1st */
    296     private OnLongClickListener mCandidateOnLongClick1st = new OnLongClickListener() {
    297         public boolean onLongClick(View v) {
    298             return onLongClickCandidate(v, mWnnWordArray1st);
    299         }
    300     };
    301 
    302     /** Event listener for long-clicking a candidate for for 2nd */
    303     private OnLongClickListener mCandidateOnLongClick2nd = new OnLongClickListener() {
    304         public boolean onLongClick(View v) {
    305             return onLongClickCandidate(v, mWnnWordArray2nd);
    306         }
    307     };
    308 
    309     /** Event listener for click a symbol tab */
    310     private OnClickListener mTabOnClick = new OnClickListener() {
    311         public void onClick(View v) {
    312             if (!v.isShown()) {
    313                 return;
    314             }
    315             playSoundAndVibration();
    316 
    317             if (v instanceof TextView) {
    318                 TextView text = (TextView)v;
    319                 switch (text.getId()) {
    320                 case R.id.candview_symbol:
    321                     if (mSymbolMode != OpenWnnJAJP.ENGINE_MODE_SYMBOL) {
    322                         mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.CHANGE_MODE,
    323                                                       OpenWnnJAJP.ENGINE_MODE_SYMBOL));
    324                     }
    325                     break;
    326 
    327                 case R.id.candview_emoticon:
    328                     if (mSymbolMode != OpenWnnJAJP.ENGINE_MODE_SYMBOL_KAO_MOJI) {
    329                         mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.CHANGE_MODE,
    330                                 OpenWnnJAJP.ENGINE_MODE_SYMBOL));
    331                     }
    332                     break;
    333 
    334                 default:
    335                     break;
    336                 }
    337             }
    338         }
    339     };
    340 
    341     /**
    342      * Constructor
    343      */
    344     public TextCandidatesViewManager() {
    345         this(-1);
    346     }
    347 
    348     /**
    349      * Constructor
    350      *
    351      * @param displayLimit      The limit of display
    352      */
    353     public TextCandidatesViewManager(int displayLimit) {
    354         mDisplayLimit = displayLimit;
    355     }
    356 
    357     /**
    358      * Handle a click event on the candidate.
    359      * @param v  View
    360      * @param list  List of candidates
    361      */
    362     private void onClickCandidate(View v, ArrayList<WnnWord> list) {
    363         if (!v.isShown()) {
    364             return;
    365         }
    366         playSoundAndVibration();
    367 
    368         if (v instanceof TextView) {
    369             TextView text = (TextView)v;
    370             int wordcount = text.getId();
    371             WnnWord word = list.get(wordcount);
    372 
    373             if (mHandler.hasMessages(MSG_SET_CANDIDATES)) {
    374                 mWnnWordSelectedList.add(word);
    375                 return;
    376             }
    377             clearFocusCandidate();
    378             selectCandidate(word);
    379         }
    380     }
    381 
    382     /**
    383      * Handle a long click event on the candidate.
    384      * @param v  View
    385      * @param list  List of candidates
    386      */
    387     public boolean onLongClickCandidate(View v, ArrayList<WnnWord> list) {
    388         if (mViewLongPressDialog == null) {
    389             return false;
    390         }
    391 
    392         if (mIsSymbolMode) {
    393             return false;
    394         }
    395 
    396         if (!mEnableCandidateLongClick) {
    397             return false;
    398         }
    399 
    400         if (!v.isShown()) {
    401             return true;
    402         }
    403 
    404         Drawable d = v.getBackground();
    405         if (d != null) {
    406             if(d.getState().length == 0){
    407                 return true;
    408             }
    409         }
    410 
    411         int wordcount = ((TextView)v).getId();
    412         mWord = list.get(wordcount);
    413         clearFocusCandidate();
    414         displayDialog(v, mWord);
    415 
    416         return true;
    417     }
    418 
    419     /**
    420      * Set auto-hide mode.
    421      * @param hide      {@code true} if the view will hidden when no candidate exists;
    422      *                  {@code false} if the view is always shown.
    423      */
    424     public void setAutoHide(boolean hide) {
    425         mAutoHideMode = hide;
    426     }
    427 
    428     /** @see CandidatesViewManager#initView */
    429     public View initView(OpenWnn parent, int width, int height) {
    430         mWnn = parent;
    431         mViewWidth = width;
    432         Resources r = mWnn.getResources();
    433         mCandidateMinimumWidth = r.getDimensionPixelSize(R.dimen.cand_minimum_width);
    434         mCandidateMinimumHeight = r.getDimensionPixelSize(R.dimen.cand_minimum_height);
    435         if (OpenWnn.isXLarge()) {
    436             mCandidateOneLineMinimumHeight = r.getDimensionPixelSize(R.dimen.candidate_layout_height);
    437         }
    438         mCandidateCategoryMinimumHeight = r.getDimensionPixelSize(R.dimen.cand_category_minimum_height);
    439         mCandidateLeftAlignThreshold = r.getDimensionPixelSize(R.dimen.cand_left_align_threshold);
    440         mKeyboardHeight = r.getDimensionPixelSize(R.dimen.keyboard_height);
    441         if (OpenWnn.isXLarge()) {
    442             mKeyboardHeight += Math.round(height * KEYBOARD_VERTICAL_GAP)
    443                                 * KEYBOARD_VERTICAL_GAP_COUNT;
    444         }
    445         mSymbolKeyboardHeight = r.getDimensionPixelSize(R.dimen.symbol_keyboard_height);
    446         Drawable d = r.getDrawable(R.drawable.tab_no_select);
    447         mSymbolKeyboardTabHeight = d.getMinimumHeight();
    448 
    449         mPortrait =
    450             (r.getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE);
    451 
    452         mCandNormalTextSize = r.getDimensionPixelSize(R.dimen.cand_normal_text_size);
    453         mCandCategoryTextSize = r.getDimensionPixelSize(R.dimen.cand_category_text_size);
    454 
    455         LayoutInflater inflater = parent.getLayoutInflater();
    456         mViewBody = (ViewGroup)inflater.inflate(R.layout.candidates, null);
    457 
    458         mViewTabSymbol = (TextView)mViewBody.findViewById(R.id.candview_symbol);
    459         mViewTabEmoticon = (TextView)mViewBody.findViewById(R.id.candview_emoticon);
    460 
    461         mViewBodyScroll = (ScrollView)mViewBody.findViewById(R.id.candview_scroll);
    462 
    463         mViewCandidateBase = (ViewGroup)mViewBody.findViewById(R.id.candview_base);
    464 
    465         setNumeberOfDisplayLines();
    466         createNormalCandidateView();
    467         mViewCandidateList2nd = (AbsoluteLayout)mViewBody.findViewById(R.id.candidates_2nd_view);
    468 
    469         mTextColor = r.getColor(R.color.candidate_text);
    470 
    471         mReadMoreButton = (ImageView)mViewBody.findViewById(R.id.read_more_button);
    472         mReadMoreButton.setOnTouchListener(new View.OnTouchListener() {
    473                 public boolean onTouch(View v, MotionEvent event) {
    474                     int resid = 0;
    475                     switch (event.getAction()) {
    476                     case MotionEvent.ACTION_DOWN:
    477                         if (mIsFullView) {
    478                             resid = R.drawable.cand_up_press;
    479                         } else {
    480                             resid = R.drawable.cand_down_press;
    481                         }
    482                         break;
    483                     case MotionEvent.ACTION_UP:
    484                         if (mIsFullView) {
    485                             resid = R.drawable.cand_up;
    486                         } else {
    487                             resid = R.drawable.cand_down;
    488                         }
    489                         break;
    490                     default:
    491                         break;
    492                     }
    493 
    494                     if (resid != 0) {
    495                         mReadMoreButton.setImageResource(resid);
    496                     }
    497                     return false;
    498                 }
    499             });
    500         mReadMoreButton.setOnClickListener(new View.OnClickListener() {
    501                 public void onClick(View v) {
    502                     if (!v.isShown()) {
    503                         return;
    504                     }
    505                     playSoundAndVibration();
    506 
    507                     if (mIsFullView) {
    508                         mIsFullView = false;
    509                         mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.LIST_CANDIDATES_NORMAL));
    510                     } else {
    511                         mIsFullView = true;
    512                         mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.LIST_CANDIDATES_FULL));
    513                     }
    514                 }
    515             });
    516 
    517         setViewType(CandidatesViewManager.VIEW_TYPE_CLOSE);
    518 
    519         mGestureDetector = new GestureDetector(this);
    520 
    521         mViewLongPressDialog = (View)inflater.inflate(R.layout.candidate_longpress_dialog, null);
    522 
    523         /* select button */
    524         Button longPressDialogButton = (Button)mViewLongPressDialog.findViewById(R.id.candidate_longpress_dialog_select);
    525         longPressDialogButton.setOnClickListener(new View.OnClickListener() {
    526                 public void onClick(View v) {
    527                     playSoundAndVibration();
    528                     clearFocusCandidate();
    529                     selectCandidate(mWord);
    530                     closeDialog();
    531                 }
    532             });
    533 
    534         /* cancel button */
    535         longPressDialogButton = (Button)mViewLongPressDialog.findViewById(R.id.candidate_longpress_dialog_cancel);
    536         longPressDialogButton.setOnClickListener(new View.OnClickListener() {
    537                 public void onClick(View v) {
    538                     playSoundAndVibration();
    539                     mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.LIST_CANDIDATES_NORMAL));
    540                     mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.UPDATE_CANDIDATE));
    541                     closeDialog();
    542                 }
    543             });
    544 
    545         return mViewBody;
    546     }
    547 
    548     /**
    549      * Create the normal candidate view
    550      */
    551     private void createNormalCandidateView() {
    552         mViewCandidateList1st = (LinearLayout)mViewBody.findViewById(R.id.candidates_1st_view);
    553         mViewCandidateList1st.setOnClickListener(mCandidateOnClick1st);
    554 
    555         mViewCandidateListTab = (LinearLayout)mViewBody.findViewById(R.id.candview_tab);
    556         TextView tSymbol = mViewTabSymbol;
    557         tSymbol.setOnClickListener(mTabOnClick);
    558         TextView tEmoticon = mViewTabEmoticon;
    559         tEmoticon.setOnClickListener(mTabOnClick);
    560 
    561         int line = SETTING_NUMBER_OF_LINEMAX;
    562         int width = mViewWidth;
    563         for (int i = 0; i < line; i++) {
    564             LinearLayout lineView = new LinearLayout(mViewBodyScroll.getContext());
    565             lineView.setOrientation(LinearLayout.HORIZONTAL);
    566             LinearLayout.LayoutParams layoutParams =
    567                 new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
    568                                               ViewGroup.LayoutParams.WRAP_CONTENT);
    569             lineView.setLayoutParams(layoutParams);
    570             for (int j = 0; j < (width / getCandidateMinimumWidth()); j++) {
    571                 TextView tv = createCandidateView();
    572                 lineView.addView(tv);
    573             }
    574 
    575             if (i == 0) {
    576                 TextView tv = createCandidateView();
    577                 layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
    578                                                              ViewGroup.LayoutParams.WRAP_CONTENT);
    579                 layoutParams.weight = 0;
    580                 layoutParams.gravity = Gravity.RIGHT;
    581                 tv.setLayoutParams(layoutParams);
    582 
    583                 lineView.addView(tv);
    584                 mViewCandidateTemplate = tv;
    585             }
    586             mViewCandidateList1st.addView(lineView);
    587         }
    588     }
    589 
    590     /** @see CandidatesViewManager#getCurrentView */
    591     public View getCurrentView() {
    592         return mViewBody;
    593     }
    594 
    595     /** @see CandidatesViewManager#setViewType */
    596     public void setViewType(int type) {
    597         boolean readMore = setViewLayout(type);
    598 
    599         if (readMore) {
    600             displayCandidates(this.mConverter, false, -1);
    601         } else {
    602             if (type == CandidatesViewManager.VIEW_TYPE_NORMAL) {
    603                 mIsFullView = false;
    604                 if (mDisplayEndOffset > 0) {
    605                     int maxLine = getMaxLine();
    606                     displayCandidates(this.mConverter, false, maxLine);
    607                 } else {
    608                     setReadMore();
    609                 }
    610             } else {
    611                 if (mViewBody.isShown()) {
    612                     mWnn.setCandidatesViewShown(false);
    613                 }
    614             }
    615         }
    616     }
    617 
    618     /**
    619      * Set the view layout
    620      *
    621      * @param type      View type
    622      * @return          {@code true} if display is updated; {@code false} if otherwise
    623      */
    624     private boolean setViewLayout(int type) {
    625 
    626         ViewGroup.LayoutParams params;
    627         int line = (mPortrait) ? mPortraitNumberOfLine : mLandscapeNumberOfLine;
    628 
    629         if ((mViewType == CandidatesViewManager.VIEW_TYPE_FULL)
    630                 && (type == CandidatesViewManager.VIEW_TYPE_NORMAL)) {
    631             clearFocusCandidate();
    632         }
    633 
    634         mViewType = type;
    635 
    636         switch (type) {
    637         case CandidatesViewManager.VIEW_TYPE_CLOSE:
    638             params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
    639                                                    getCandidateMinimumHeight() * line);
    640             mViewBodyScroll.setLayoutParams(params);
    641             mViewCandidateListTab.setVisibility(View.GONE);
    642             mViewCandidateBase.setMinimumHeight(-1);
    643             mHandler.removeMessages(MSG_SET_CANDIDATES);
    644             return false;
    645 
    646         case CandidatesViewManager.VIEW_TYPE_NORMAL:
    647             params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
    648                                                    getCandidateMinimumHeight() * line);
    649             mViewBodyScroll.setLayoutParams(params);
    650             mViewBodyScroll.scrollTo(0, 0);
    651             mViewCandidateListTab.setVisibility(View.GONE);
    652             mViewCandidateList1st.setVisibility(View.VISIBLE);
    653             mViewCandidateList2nd.setVisibility(View.GONE);
    654             mViewCandidateBase.setMinimumHeight(-1);
    655             return false;
    656 
    657         case CandidatesViewManager.VIEW_TYPE_FULL:
    658         default:
    659             params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
    660                                                    getCandidateViewHeight());
    661             mViewBodyScroll.setLayoutParams(params);
    662             if (mIsSymbolMode) {
    663                 updateSymbolType();
    664                 mViewCandidateListTab.setVisibility(View.VISIBLE);
    665             } else {
    666                 mViewCandidateListTab.setVisibility(View.GONE);
    667             }
    668             mViewCandidateList2nd.setVisibility(View.VISIBLE);
    669             mViewCandidateBase.setMinimumHeight(-1);
    670             return true;
    671         }
    672     }
    673 
    674     /** @see CandidatesViewManager#getViewType */
    675     public int getViewType() {
    676         return mViewType;
    677     }
    678 
    679     /** @see CandidatesViewManager#displayCandidates */
    680     public void displayCandidates(WnnEngine converter) {
    681 
    682         mHandler.removeMessages(MSG_SET_CANDIDATES);
    683 
    684         if (mIsSymbolSelected) {
    685             mIsSymbolSelected = false;
    686             if (mSymbolMode == OpenWnnJAJP.ENGINE_MODE_SYMBOL_KAO_MOJI) {
    687                 return;
    688             }
    689 
    690             int prevLineCount = mLineCount;
    691             int prevWordCount1st = mWordCount1st;
    692             clearNormalViewCandidate();
    693             mWordCount1st = 0;
    694             mLineCount = 1;
    695             mLineLength = 0;
    696             mNormalViewWordCountOfLine = 0;
    697             mWnnWordArray1st.clear();
    698             mTextViewArray1st.clear();
    699             if (((prevWordCount1st == 0) && (mWordCount1st == 1)) ||
    700                 (prevLineCount < mLineCount)) {
    701                 mViewBodyScroll.scrollTo(0, mViewBodyScroll.getScrollY() + getCandidateMinimumHeight());
    702             }
    703             if (isFocusCandidate() && mHasFocusedArray1st) {
    704                 mCurrentFocusIndex = 0;
    705                 Message m = mHandler.obtainMessage(MSG_MOVE_FOCUS, 0, 0);
    706                 mHandler.sendMessage(m);
    707             }
    708             return;
    709         }
    710 
    711         mCanReadMore = false;
    712         mDisplayEndOffset = 0;
    713         mIsFullView = false;
    714         mFullViewWordCount = 0;
    715         mFullViewOccupyCount = 0;
    716         mFullViewPrevLineTopId = 0;
    717         mFullViewPrevView = null;
    718         mCreateCandidateDone = false;
    719         mNormalViewWordCountOfLine = 0;
    720 
    721         clearCandidates();
    722         mConverter = converter;
    723         mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.LIST_CANDIDATES_NORMAL));
    724 
    725         mViewCandidateTemplate.setVisibility(View.VISIBLE);
    726         mViewCandidateTemplate.setBackgroundResource(R.drawable.cand_back);
    727 
    728         displayCandidates(converter, true, getMaxLine());
    729 
    730         if (mIsSymbolMode) {
    731             mIsFullView = true;
    732             mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.LIST_CANDIDATES_FULL));
    733         }
    734     }
    735 
    736     /** @see CandidatesViewManager#getMaxLine */
    737     private int getMaxLine() {
    738         int maxLine = (mPortrait) ? mPortraitNumberOfLine : mLandscapeNumberOfLine;
    739         return maxLine;
    740     }
    741 
    742     /**
    743      * Get categories text.
    744      * @param String Source string replacement
    745      * @return String Categories text
    746      */
    747     private String getCategoriesText(String categoriesString) {
    748         String ret = null;
    749 
    750         Resources r = mWnn.getResources();
    751         if(categoriesString.equals(r.getString(R.string.half_symbol_categories_txt))) {
    752             ret = r.getString(R.string.half_symbol_txt);
    753         } else if (categoriesString.equals(r.getString(R.string.full_symbol_categories_txt))) {
    754             ret = r.getString(R.string.full_symbol_txt);
    755         } else {
    756             ret = new String("");
    757         }
    758 
    759         return ret;
    760     }
    761 
    762     /**
    763      * Display the candidates.
    764      *
    765      * @param converter  {@link WnnEngine} which holds candidates.
    766      * @param dispFirst  Whether it is the first time displaying the candidates
    767      * @param maxLine    The maximum number of displaying lines
    768      */
    769     private void displayCandidates(WnnEngine converter, boolean dispFirst, int maxLine) {
    770         if (converter == null) {
    771             return;
    772         }
    773 
    774         /* Concatenate the candidates already got and the last one in dispFirst mode */
    775         int displayLimit = mDisplayLimit;
    776 
    777         boolean isDelay = false;
    778         boolean isBreak = false;
    779 
    780         if (converter instanceof SymbolList) {
    781             if (!dispFirst) {
    782                 if (maxLine == -1) {
    783                     isDelay = true;
    784                     maxLine = mLineCount + SET_CANDIDATE_FIRST_LINE_COUNT;
    785 
    786                     mHandler.sendEmptyMessageDelayed(MSG_SET_CANDIDATES, SET_CANDIDATE_DELAY);
    787 
    788                 } else if (maxLine == SET_CANDIDATE_DELAY_LINE_COUNT) {
    789                     isDelay = true;
    790                     maxLine = mLineCount + SET_CANDIDATE_DELAY_LINE_COUNT;
    791 
    792                     mHandler.sendEmptyMessageDelayed(MSG_SET_CANDIDATES, SET_CANDIDATE_DELAY);
    793                 }
    794             }
    795             if (mSymbolMode != OpenWnnJAJP.ENGINE_MODE_SYMBOL_KAO_MOJI) {
    796                 displayLimit = -1;
    797             }
    798         }
    799 
    800         /* Get candidates */
    801         WnnWord result = null;
    802         String prevCandidate = null;
    803         while ((displayLimit == -1 || getWordCount() < displayLimit)) {
    804             for (int i = 0; i < DISPLAY_LINE_MAX_COUNT; i++) {
    805                 result = converter.getNextCandidate();
    806                 if (result == null) {
    807                     break;
    808                 }
    809 
    810                 if (converter instanceof SymbolList) {
    811                     break;
    812                 }
    813 
    814                 if ((prevCandidate == null) || !prevCandidate.equals(result.candidate)) {
    815                     break;
    816                 }
    817             }
    818 
    819             if (result == null) {
    820                 break;
    821             } else {
    822                 prevCandidate = result.candidate;
    823             }
    824 
    825             if (converter instanceof SymbolList) {
    826                 if (isCategory(result)) {
    827                     if (getWordCount() != 0) {
    828                         createNextLine(false);
    829                     }
    830                     result.candidate = getCategoriesText(result.candidate);
    831                     setCandidate(true, result);
    832                     createNextLine(true);
    833                     continue;
    834                 }
    835             }
    836 
    837             setCandidate(false, result);
    838 
    839             if ((dispFirst || isDelay) && (maxLine < mLineCount)) {
    840                 mCanReadMore = true;
    841                 isBreak = true;
    842                 break;
    843             }
    844         }
    845 
    846         if (!isBreak && !mCreateCandidateDone) {
    847             /* align left if necessary */
    848             createNextLine(false);
    849             mCreateCandidateDone = true;
    850             mHandler.removeMessages(MSG_SET_CANDIDATES);
    851             mHandler.sendMessage(mHandler.obtainMessage(MSG_SELECT_CANDIDATES));
    852         }
    853 
    854         if (getWordCount() < 1) { /* no candidates */
    855             if (mAutoHideMode) {
    856                 mWnn.setCandidatesViewShown(false);
    857                 return;
    858             } else {
    859                 mCanReadMore = false;
    860                 mIsFullView = false;
    861                 setViewLayout(CandidatesViewManager.VIEW_TYPE_NORMAL);
    862             }
    863         }
    864 
    865         setReadMore();
    866 
    867         if (!(mViewBody.isShown())) {
    868             mWnn.setCandidatesViewShown(true);
    869         }
    870         return;
    871     }
    872 
    873     /**
    874      * Add a candidate into the list.
    875      * @param isCategory  {@code true}:caption of category, {@code false}:normal word
    876      * @param word        A candidate word
    877      */
    878     private void setCandidate(boolean isCategory, WnnWord word) {
    879         int textLength = measureText(word.candidate, 0, word.candidate.length());
    880         TextView template = mViewCandidateTemplate;
    881         textLength += template.getPaddingLeft() + template.getPaddingRight();
    882         int maxWidth = mViewWidth;
    883         boolean isEmojiSymbol = false;
    884         if (mIsSymbolMode && (word.candidate.length() < 3)) {
    885             isEmojiSymbol = true;
    886         }
    887         TextView textView;
    888 
    889         boolean is2nd = isFirstListOver(mIsFullView, mLineCount, word);
    890         if (is2nd) {
    891             /* Full view */
    892             int viewDivison = getCandidateViewDivison();
    893             int indentWidth = mViewWidth / viewDivison;
    894             int occupyCount = Math.min((textLength + indentWidth + getCandidateSpaceWidth(isEmojiSymbol)) / indentWidth, viewDivison);
    895             if (isCategory) {
    896                 occupyCount = viewDivison;
    897             }
    898 
    899             if (viewDivison < (mFullViewOccupyCount + occupyCount)) {
    900                 if (viewDivison != mFullViewOccupyCount) {
    901                     mFullViewPrevParams.width += (viewDivison - mFullViewOccupyCount) * indentWidth;
    902                     if (mFullViewPrevView != null) {
    903                         mViewCandidateList2nd.updateViewLayout(mFullViewPrevView, mFullViewPrevParams);
    904                     }
    905                 }
    906                 mFullViewOccupyCount = 0;
    907                 if (mFullViewPrevView != null) {
    908                     mFullViewPrevLineTopId = mFullViewPrevView.getId();
    909                 }
    910                 mLineCount++;
    911                 if (isCategory) {
    912                     mLineY += mCandidateCategoryMinimumHeight;
    913                 } else {
    914                     mLineY += getCandidateMinimumHeight();
    915                 }
    916             }
    917             if (mFullViewWordCount == 0) {
    918                 mLineY = 0;
    919             }
    920 
    921             ViewGroup layout = mViewCandidateList2nd;
    922 
    923             int width = indentWidth * occupyCount;
    924             int height = getCandidateMinimumHeight();
    925             if (isCategory) {
    926                 height = mCandidateCategoryMinimumHeight;
    927             }
    928 
    929             ViewGroup.LayoutParams params = buildLayoutParams(mViewCandidateList2nd, width, height);
    930 
    931             textView = (TextView) layout.getChildAt(mFullViewWordCount);
    932             if (textView == null) {
    933                 textView = createCandidateView();
    934                 textView.setLayoutParams(params);
    935 
    936                 mViewCandidateList2nd.addView(textView);
    937             } else {
    938                 mViewCandidateList2nd.updateViewLayout(textView, params);
    939             }
    940 
    941             mFullViewOccupyCount += occupyCount;
    942             mFullViewWordCount++;
    943             mFullViewPrevView = textView;
    944             mFullViewPrevParams = params;
    945 
    946         } else {
    947             int viewDivison = getCandidateViewDivison();
    948             int indentWidth = mViewWidth / viewDivison;
    949             textLength = Math.max(textLength, indentWidth);
    950 
    951             /* Normal view */
    952             int nextEnd = mLineLength + textLength;
    953             nextEnd += getCandidateSpaceWidth(isEmojiSymbol);
    954 
    955             if (mLineCount == 1 && !mIsSymbolMode) {
    956                 maxWidth -= getCandidateMinimumWidth();
    957             }
    958 
    959             if ((maxWidth < nextEnd) && (getWordCount() != 0)) {
    960 
    961                 createNextLineFor1st();
    962                 if (getMaxLine() < mLineCount) {
    963                     mLineLength = 0;
    964                     /* Call this method again to add the candidate in the full view */
    965                     if (!mIsSymbolSelected) {
    966                         setCandidate(isCategory, word);
    967                     }
    968                     return;
    969                 }
    970 
    971                 mLineLength = textLength;
    972                 mLineLength += getCandidateSpaceWidth(isEmojiSymbol);
    973             } else {
    974                 mLineLength = nextEnd;
    975             }
    976 
    977             LinearLayout lineView = (LinearLayout) mViewCandidateList1st.getChildAt(mLineCount - 1);
    978             textView = (TextView) lineView.getChildAt(mNormalViewWordCountOfLine);
    979 
    980             if (isCategory) {
    981                 if (mLineCount == 1) {
    982                     mViewCandidateTemplate.setBackgroundDrawable(null);
    983                 }
    984                 mLineLength += mCandidateLeftAlignThreshold;
    985             } else {
    986                 int CompareLength = textLength;
    987                 CompareLength += getCandidateSpaceWidth(isEmojiSymbol);
    988             }
    989 
    990             mNormalViewWordCountOfLine++;
    991         }
    992 
    993         textView.setText(word.candidate);
    994         if (is2nd) {
    995             textView.setId(mWordCount2nd);
    996         } else {
    997             textView.setId(mWordCount1st);
    998         }
    999         textView.setVisibility(View.VISIBLE);
   1000         textView.setPressed(false);
   1001         textView.setFocusable(false);
   1002 
   1003         if (isCategory) {
   1004             textView.setText("      " + word.candidate);
   1005 
   1006             textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mCandCategoryTextSize);
   1007             textView.setBackgroundDrawable(null);
   1008             textView.setGravity(Gravity.CENTER_VERTICAL);
   1009             textView.setMinHeight(mCandidateCategoryMinimumHeight);
   1010             textView.setHeight(mCandidateCategoryMinimumHeight);
   1011 
   1012             textView.setOnClickListener(null);
   1013             textView.setOnLongClickListener(null);
   1014             textView.setTextColor(mTextColor);
   1015         } else {
   1016             textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mCandNormalTextSize);
   1017             textView.setGravity(Gravity.CENTER);
   1018             textView.setMinHeight(getCandidateMinimumHeight());
   1019             textView.setHeight(getCandidateMinimumHeight());
   1020 
   1021             if (is2nd) {
   1022                 textView.setOnClickListener(mCandidateOnClick2nd);
   1023                 textView.setOnLongClickListener(mCandidateOnLongClick2nd);
   1024             } else {
   1025                 textView.setOnClickListener(mCandidateOnClick1st);
   1026                 textView.setOnLongClickListener(mCandidateOnLongClick1st);
   1027             }
   1028 
   1029             textView.setBackgroundResource(R.drawable.cand_back);
   1030 
   1031             textView.setTextColor(mTextColor);
   1032         }
   1033 
   1034         if (maxWidth < textLength) {
   1035             textView.setEllipsize(TextUtils.TruncateAt.END);
   1036         } else {
   1037             textView.setEllipsize(null);
   1038         }
   1039 
   1040         ImageSpan span = null;
   1041         if (word.candidate.equals(" ")) {
   1042             span = new ImageSpan(mWnn, R.drawable.word_half_space,
   1043                                  DynamicDrawableSpan.ALIGN_BASELINE);
   1044         } else if (word.candidate.equals("\u3000" /* full-width space */)) {
   1045             span = new ImageSpan(mWnn, R.drawable.word_full_space,
   1046                                  DynamicDrawableSpan.ALIGN_BASELINE);
   1047         }
   1048 
   1049         if (span != null) {
   1050             SpannableString spannable = new SpannableString("   ");
   1051             spannable.setSpan(span, 1, 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
   1052             textView.setText(spannable);
   1053         }
   1054         textView.setPadding(0, 0, 0, 0);
   1055 
   1056         if (is2nd) {
   1057             mWnnWordArray2nd.add(mWordCount2nd, word);
   1058             mWordCount2nd++;
   1059             mTextViewArray2nd.add(textView);
   1060         } else {
   1061             mWnnWordArray1st.add(mWordCount1st, word);
   1062             mWordCount1st++;
   1063             mTextViewArray1st.add(textView);
   1064         }
   1065     }
   1066 
   1067     /**
   1068      * Create AbsoluteLayout.LayoutParams
   1069      * @param layout AbsoluteLayout
   1070      * @param width  The width of the display
   1071      * @param height The height of the display
   1072      * @return Layout parameter
   1073      */
   1074     private ViewGroup.LayoutParams buildLayoutParams(AbsoluteLayout layout, int width, int height) {
   1075 
   1076         int viewDivison = getCandidateViewDivison();
   1077         int indentWidth = mViewWidth / viewDivison;
   1078         int x         = indentWidth * mFullViewOccupyCount;
   1079         int y         = mLineY;
   1080         ViewGroup.LayoutParams params
   1081               = new AbsoluteLayout.LayoutParams(width, height, x, y);
   1082 
   1083         return params;
   1084     }
   1085 
   1086     /**
   1087      * Create a view for a candidate.
   1088      * @return the view
   1089      */
   1090     private TextView createCandidateView() {
   1091         TextView text = new CandidateTextView(mViewBodyScroll.getContext());
   1092         text.setTextSize(TypedValue.COMPLEX_UNIT_PX, mCandNormalTextSize);
   1093         text.setBackgroundResource(R.drawable.cand_back);
   1094         text.setCompoundDrawablePadding(0);
   1095         text.setGravity(Gravity.CENTER);
   1096         text.setSingleLine();
   1097         text.setPadding(0, 0, 0, 0);
   1098         text.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
   1099                                                            ViewGroup.LayoutParams.WRAP_CONTENT,
   1100                                                            1.0f));
   1101         text.setMinHeight(getCandidateMinimumHeight());
   1102         text.setMinimumWidth(getCandidateMinimumWidth());
   1103         text.setSoundEffectsEnabled(false);
   1104         return text;
   1105     }
   1106 
   1107     /**
   1108      * Display {@code mReadMoreText} if there are more candidates.
   1109      */
   1110     private void setReadMore() {
   1111         if (mIsSymbolMode) {
   1112             mReadMoreButton.setVisibility(View.GONE);
   1113             mViewCandidateTemplate.setVisibility(View.GONE);
   1114             return;
   1115         }
   1116 
   1117         int resid = 0;
   1118 
   1119         if (mIsFullView) {
   1120             mReadMoreButton.setVisibility(View.VISIBLE);
   1121             resid = R.drawable.cand_up;
   1122         } else {
   1123             if (mCanReadMore) {
   1124                 mReadMoreButton.setVisibility(View.VISIBLE);
   1125                 resid = R.drawable.cand_down;
   1126             } else {
   1127                 mReadMoreButton.setVisibility(View.GONE);
   1128                 mViewCandidateTemplate.setVisibility(View.GONE);
   1129             }
   1130         }
   1131 
   1132         if (resid != 0) {
   1133             mReadMoreButton.setImageResource(resid);
   1134         }
   1135     }
   1136 
   1137     /**
   1138      * Clear the list of the normal candidate view.
   1139      */
   1140     private void clearNormalViewCandidate() {
   1141         LinearLayout candidateList = mViewCandidateList1st;
   1142         int lineNum = candidateList.getChildCount();
   1143         for (int i = 0; i < lineNum; i++) {
   1144 
   1145             LinearLayout lineView = (LinearLayout)candidateList.getChildAt(i);
   1146             int size = lineView.getChildCount();
   1147             for (int j = 0; j < size; j++) {
   1148                 View v = lineView.getChildAt(j);
   1149                 v.setVisibility(View.GONE);
   1150             }
   1151         }
   1152     }
   1153 
   1154     /** @see CandidatesViewManager#clearCandidates */
   1155     public void clearCandidates() {
   1156         closeDialog();
   1157         clearFocusCandidate();
   1158         clearNormalViewCandidate();
   1159 
   1160         ViewGroup layout = mViewCandidateList2nd;
   1161         int size = layout.getChildCount();
   1162         for (int i = 0; i < size; i++) {
   1163             View v = layout.getChildAt(i);
   1164             v.setVisibility(View.GONE);
   1165         }
   1166 
   1167         mLineCount = 1;
   1168         mWordCount1st = 0;
   1169         mWordCount2nd = 0;
   1170         mWnnWordArray1st.clear();
   1171         mWnnWordArray2nd.clear();
   1172         mTextViewArray1st.clear();
   1173         mTextViewArray2nd.clear();
   1174         mLineLength = 0;
   1175 
   1176         mLineY = 0;
   1177 
   1178         mIsFullView = false;
   1179         mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.LIST_CANDIDATES_NORMAL));
   1180         if (mAutoHideMode) {
   1181             setViewLayout(CandidatesViewManager.VIEW_TYPE_CLOSE);
   1182         }
   1183 
   1184         if (mAutoHideMode && mViewBody.isShown()) {
   1185             mWnn.setCandidatesViewShown(false);
   1186         }
   1187         mCanReadMore = false;
   1188         setReadMore();
   1189     }
   1190 
   1191     /** @see CandidatesViewManager#setPreferences */
   1192     public void setPreferences(SharedPreferences pref) {
   1193         try {
   1194             if (pref.getBoolean("key_vibration", false)) {
   1195                 mVibrator = (Vibrator)mWnn.getSystemService(Context.VIBRATOR_SERVICE);
   1196             } else {
   1197                 mVibrator = null;
   1198             }
   1199             if (pref.getBoolean("key_sound", false)) {
   1200                 mSound = (AudioManager)mWnn.getSystemService(Context.AUDIO_SERVICE);
   1201             } else {
   1202                 mSound = null;
   1203             }
   1204             setNumeberOfDisplayLines();
   1205         } catch (Exception ex) {
   1206             Log.e("OpenWnn", "NO VIBRATOR");
   1207         }
   1208     }
   1209 
   1210     /**
   1211      * Set normal mode.
   1212      */
   1213     public void setNormalMode() {
   1214         setReadMore();
   1215         mIsFullView = false;
   1216         mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.LIST_CANDIDATES_NORMAL));
   1217     }
   1218 
   1219     /**
   1220      * Set full mode.
   1221      */
   1222     public void setFullMode() {
   1223         mIsFullView = true;
   1224         mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.LIST_CANDIDATES_FULL));
   1225     }
   1226 
   1227     /**
   1228      * Set symbol mode.
   1229      */
   1230     public void setSymbolMode(boolean enable, int mode) {
   1231         if (mIsSymbolMode && !enable) {
   1232             setViewType(CandidatesViewManager.VIEW_TYPE_CLOSE);
   1233         }
   1234         mSymbolMode = mode;
   1235         mIsSymbolMode = enable;
   1236     }
   1237 
   1238     /**
   1239      * Set scroll up.
   1240      */
   1241     public void setScrollUp() {
   1242         if (!mViewBodyScroll.pageScroll(ScrollView.FOCUS_UP)) {
   1243             mViewBodyScroll.scrollTo(0, mViewBodyScroll.getChildAt(0).getHeight());
   1244         }
   1245     }
   1246 
   1247     /**
   1248      * Set scroll down.
   1249      */
   1250     public void setScrollDown() {
   1251         if (!mViewBodyScroll.pageScroll(ScrollView.FOCUS_DOWN)) {
   1252             mViewBodyScroll.scrollTo(0, 0);
   1253         }
   1254     }
   1255 
   1256     /**
   1257      * Set scroll full up.
   1258      */
   1259     public void setScrollFullUp() {
   1260         if (!mViewBodyScroll.fullScroll(ScrollView.FOCUS_UP)) {
   1261             mViewBodyScroll.scrollTo(0, mViewBodyScroll.getChildAt(0).getHeight());
   1262         }
   1263     }
   1264 
   1265     /**
   1266      * Set scroll full down.
   1267      */
   1268     public void setScrollFullDown() {
   1269         if (!mViewBodyScroll.fullScroll(ScrollView.FOCUS_DOWN)) {
   1270             mViewBodyScroll.scrollTo(0, 0);
   1271         }
   1272     }
   1273 
   1274     /**
   1275      * Process {@link OpenWnnEvent#CANDIDATE_VIEW_TOUCH} event.
   1276      *
   1277      * @return      {@code true} if event is processed; {@code false} if otherwise
   1278      */
   1279     public boolean onTouchSync() {
   1280         return mGestureDetector.onTouchEvent(mMotionEvent);
   1281     }
   1282 
   1283     /**
   1284      * Select a candidate.
   1285      * <br>
   1286      * This method notices the selected word to {@link OpenWnn}.
   1287      *
   1288      * @param word  The selected word
   1289      */
   1290     private void selectCandidate(WnnWord word) {
   1291         if (!mIsSymbolMode) {
   1292             mIsFullView = false;
   1293             mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.LIST_CANDIDATES_NORMAL));
   1294         }
   1295         mIsSymbolSelected = mIsSymbolMode;
   1296         mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.SELECT_CANDIDATE, word));
   1297     }
   1298 
   1299     private void playSoundAndVibration() {
   1300         if (mVibrator != null) {
   1301             try {
   1302                 mVibrator.vibrate(5);
   1303             } catch (Exception ex) {
   1304                 Log.e("OpenWnn", "TextCandidatesViewManager::selectCandidate Vibrator " + ex.toString());
   1305             }
   1306         }
   1307         if (mSound != null) {
   1308             try {
   1309                 mSound.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD, -1);
   1310             } catch (Exception ex) {
   1311                 Log.e("OpenWnn", "TextCandidatesViewManager::selectCandidate Sound " + ex.toString());
   1312             }
   1313         }
   1314     }
   1315 
   1316     /** @see android.view.GestureDetector.OnGestureListener#onDown */
   1317     public boolean onDown(MotionEvent arg0) {
   1318         return false;
   1319     }
   1320 
   1321     /** @see android.view.GestureDetector.OnGestureListener#onFling */
   1322     public boolean onFling(MotionEvent arg0, MotionEvent arg1, float arg2, float arg3) {
   1323         boolean consumed = false;
   1324         if (arg1 != null && arg0 != null && arg1.getY() < arg0.getY()) {
   1325             if ((mViewType == CandidatesViewManager.VIEW_TYPE_NORMAL) && mCanReadMore) {
   1326                 if (mVibrator != null) {
   1327                     try {
   1328                         mVibrator.vibrate(5);
   1329                     } catch (Exception ex) {
   1330                         Log.e("iwnn", "TextCandidatesViewManager::onFling Vibrator " + ex.toString());
   1331                     }
   1332                 }
   1333                 mIsFullView = true;
   1334                 mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.LIST_CANDIDATES_FULL));
   1335                 consumed = true;
   1336             }
   1337         } else {
   1338             if (mViewBodyScroll.getScrollY() == 0) {
   1339                 if (mVibrator != null) {
   1340                     try {
   1341                         mVibrator.vibrate(5);
   1342                     } catch (Exception ex) {
   1343                         Log.e("iwnn", "TextCandidatesViewManager::onFling Sound " + ex.toString());
   1344                     }
   1345                 }
   1346                 mIsFullView = false;
   1347                 mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.LIST_CANDIDATES_NORMAL));
   1348                 consumed = true;
   1349             }
   1350         }
   1351 
   1352         return consumed;
   1353     }
   1354 
   1355     /** @see android.view.GestureDetector.OnGestureListener#onLongPress */
   1356     public void onLongPress(MotionEvent arg0) {
   1357         return;
   1358     }
   1359 
   1360     /** @see android.view.GestureDetector.OnGestureListener#onScroll */
   1361     public boolean onScroll(MotionEvent arg0, MotionEvent arg1, float arg2, float arg3) {
   1362         return false;
   1363     }
   1364 
   1365     /** @see android.view.GestureDetector.OnGestureListener#onShowPress */
   1366     public void onShowPress(MotionEvent arg0) {
   1367     }
   1368 
   1369     /** @see android.view.GestureDetector.OnGestureListener#onSingleTapUp */
   1370     public boolean onSingleTapUp(MotionEvent arg0) {
   1371         return false;
   1372     }
   1373 
   1374     /**
   1375      * Retrieve the width of string to draw.
   1376      *
   1377      * @param text          The string
   1378      * @param start         The start position (specified by the number of character)
   1379      * @param end           The end position (specified by the number of character)
   1380      * @return          The width of string to draw
   1381      */
   1382     public int measureText(CharSequence text, int start, int end) {
   1383         if (end - start < 3) {
   1384             return getCandidateMinimumWidth();
   1385         }
   1386 
   1387         TextPaint paint = mViewCandidateTemplate.getPaint();
   1388         return (int)paint.measureText(text, start, end);
   1389     }
   1390 
   1391     /**
   1392      * Create a layout for the next line.
   1393      */
   1394     private void createNextLine(boolean isCategory) {
   1395         if (isFirstListOver(mIsFullView, mLineCount, null)) {
   1396             /* Full view */
   1397             mFullViewOccupyCount = 0;
   1398             if (mFullViewPrevView != null) {
   1399                 mFullViewPrevLineTopId = mFullViewPrevView.getId();
   1400             }
   1401             if (isCategory) {
   1402                 mLineY += mCandidateCategoryMinimumHeight;
   1403             } else {
   1404                 mLineY += getCandidateMinimumHeight();
   1405             }
   1406             mLineCount++;
   1407         } else {
   1408             createNextLineFor1st();
   1409         }
   1410     }
   1411 
   1412     /**
   1413      * Create a layout for the next line.
   1414      */
   1415     private void createNextLineFor1st() {
   1416         LinearLayout lineView = (LinearLayout) mViewCandidateList1st.getChildAt(mLineCount - 1);
   1417         float weight = 0;
   1418         if (mLineLength < mCandidateLeftAlignThreshold) {
   1419             if (mLineCount == 1) {
   1420                 mViewCandidateTemplate.setVisibility(View.GONE);
   1421             }
   1422         } else {
   1423             weight = 1.0f;
   1424         }
   1425 
   1426         LinearLayout.LayoutParams params
   1427             = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
   1428                                             ViewGroup.LayoutParams.WRAP_CONTENT,
   1429                                             weight);
   1430 
   1431         int child = lineView.getChildCount();
   1432         for (int i = 0; i < child; i++) {
   1433             View view = lineView.getChildAt(i);
   1434 
   1435             if (view != mViewCandidateTemplate) {
   1436                 view.setLayoutParams(params);
   1437                 view.setPadding(0, 0, 0, 0);
   1438             }
   1439         }
   1440 
   1441         mLineLength = 0;
   1442         mNormalViewWordCountOfLine = 0;
   1443         mLineCount++;
   1444     }
   1445 
   1446     /**
   1447      * Judge if it's a category.
   1448      *
   1449      * @return {@code true} if category
   1450      */
   1451     boolean isCategory(WnnWord word) {
   1452         int length = word.candidate.length();
   1453         return ((length > 3) && (word.candidate.charAt(0) == '['));
   1454     }
   1455 
   1456     /**
   1457      * Get a minimum width of a candidate view.
   1458      *
   1459      * @return the minimum width of a candidate view.
   1460      */
   1461     private int getCandidateMinimumWidth() {
   1462         return mCandidateMinimumWidth;
   1463     }
   1464 
   1465     /**
   1466      * @return the minimum height of a candidate view.
   1467      */
   1468     private int getCandidateMinimumHeight() {
   1469         return mCandidateMinimumHeight;
   1470     }
   1471 
   1472     /**
   1473      * Get a height of a candidate view.
   1474      *
   1475      * @return the height of a candidate view.
   1476      */
   1477     private int getCandidateViewHeight() {
   1478         if (OpenWnn.isXLarge()) {
   1479            return mKeyboardHeight + mCandidateOneLineMinimumHeight - mSymbolKeyboardHeight
   1480                          - mSymbolKeyboardTabHeight;
   1481         } else {
   1482             int numberOfLine = (mPortrait) ? mPortraitNumberOfLine : mLandscapeNumberOfLine;
   1483             Resources resource = mWnn.getResources();
   1484             Drawable keyboardBackground = resource.getDrawable(R.drawable.keyboard_background);
   1485             Rect keyboardPadding = new Rect(0 ,0 ,0 ,0);
   1486             keyboardBackground.getPadding(keyboardPadding);
   1487             int keyboardTotalPadding = keyboardPadding.top + keyboardPadding.bottom;
   1488             if (mIsSymbolMode) {
   1489                 return mKeyboardHeight + numberOfLine * getCandidateMinimumHeight()
   1490                        - mSymbolKeyboardHeight - mSymbolKeyboardTabHeight;
   1491             } else if (!mHardKeyboardHidden) {
   1492                 return mKeyboardHeight + numberOfLine * getCandidateMinimumHeight()
   1493                        - mSymbolKeyboardHeight;
   1494             } else {
   1495                 return mKeyboardHeight + keyboardTotalPadding
   1496                        + numberOfLine * getCandidateMinimumHeight();
   1497             }
   1498         }
   1499     }
   1500 
   1501     /**
   1502      * Update symbol type.
   1503      */
   1504     private void updateSymbolType() {
   1505         switch (mSymbolMode) {
   1506         case OpenWnnJAJP.ENGINE_MODE_SYMBOL:
   1507             updateTabStatus(mViewTabSymbol, true, true);
   1508             updateTabStatus(mViewTabEmoticon, mEnableEmoticon, false);
   1509             break;
   1510 
   1511         case OpenWnnJAJP.ENGINE_MODE_SYMBOL_KAO_MOJI:
   1512             updateTabStatus(mViewTabSymbol, true, false);
   1513             updateTabStatus(mViewTabEmoticon, mEnableEmoticon, true);
   1514             break;
   1515 
   1516         default:
   1517             updateTabStatus(mViewTabSymbol, true, false);
   1518             updateTabStatus(mViewTabEmoticon, mEnableEmoticon, false);
   1519             break;
   1520         }
   1521     }
   1522 
   1523     /**
   1524      * Update tab status.
   1525      *
   1526      * @param tab           The tab view.
   1527      * @param enabled       The tab is enabled.
   1528      * @param selected      The tab is selected.
   1529      */
   1530     private void updateTabStatus(TextView tab, boolean enabled, boolean selected) {
   1531         tab.setVisibility(View.VISIBLE);
   1532         tab.setEnabled(enabled);
   1533         int backgroundId = 0;
   1534         int colorId = 0;
   1535         if (enabled) {
   1536             if (selected) {
   1537                 backgroundId = R.drawable.cand_tab;
   1538                 colorId = R.color.tab_textcolor_select;
   1539             } else {
   1540                 backgroundId = R.drawable.cand_tab_noselect;
   1541                 colorId = R.color.tab_textcolor_no_select;
   1542             }
   1543         } else {
   1544             backgroundId = R.drawable.cand_tab_noselect;
   1545             colorId = R.color.tab_textcolor_disable;
   1546         }
   1547         tab.setBackgroundResource(backgroundId);
   1548         tab.setTextColor(mWnn.getResources().getColor(colorId));
   1549     }
   1550 
   1551     /**
   1552      * Get candidate number of division.
   1553      * @return Number of division
   1554      */
   1555     private int getCandidateViewDivison() {
   1556         int viewDivison;
   1557 
   1558         if (mIsSymbolMode) {
   1559             int mode = mSymbolMode;
   1560             switch (mode) {
   1561             case OpenWnnJAJP.ENGINE_MODE_SYMBOL:
   1562                 viewDivison = (mPortrait) ? FULL_VIEW_SYMBOL_DIV_PORT : FULL_VIEW_SYMBOL_DIV_LAND;
   1563                 break;
   1564             case OpenWnnJAJP.ENGINE_MODE_SYMBOL_KAO_MOJI:
   1565             default:
   1566                 viewDivison = FULL_VIEW_DIV;
   1567                 break;
   1568             }
   1569         } else {
   1570              viewDivison = FULL_VIEW_DIV;
   1571         }
   1572         return viewDivison;
   1573     }
   1574 
   1575     /**
   1576      * @return Word count
   1577      */
   1578     private int getWordCount() {
   1579         return mWordCount1st + mWordCount2nd;
   1580     }
   1581 
   1582     /**
   1583      * @return Add second
   1584      */
   1585     private boolean isFirstListOver(boolean isFullView, int lineCount, WnnWord word) {
   1586 
   1587         if (mIsSymbolMode) {
   1588             switch (mSymbolMode) {
   1589             case OpenWnnJAJP.ENGINE_MODE_SYMBOL_KAO_MOJI:
   1590                 return true;
   1591             case OpenWnnJAJP.ENGINE_MODE_SYMBOL:
   1592 				return true;
   1593             default:
   1594                 return (isFullView || getMaxLine() < lineCount);
   1595             }
   1596         } else {
   1597             return (isFullView || getMaxLine() < lineCount);
   1598         }
   1599     }
   1600 
   1601     /**
   1602      * @return Candidate space width
   1603      */
   1604     private int getCandidateSpaceWidth(boolean isEmojiSymbol) {
   1605         Resources r = mWnn.getResources();
   1606         if (mPortrait) {
   1607             if (isEmojiSymbol) {
   1608                 return 0;
   1609             } else {
   1610                 return r.getDimensionPixelSize(R.dimen.cand_space_width);
   1611             }
   1612         } else {
   1613             if (isEmojiSymbol) {
   1614                 return r.getDimensionPixelSize(R.dimen.cand_space_width_emoji_symbol);
   1615             } else {
   1616                 return r.getDimensionPixelSize(R.dimen.cand_space_width);
   1617             }
   1618         }
   1619     }
   1620 
   1621     /**
   1622      * KeyEvent action for the candidate view.
   1623      *
   1624      * @param key    Key event
   1625      */
   1626     public void processMoveKeyEvent(int key) {
   1627         if (!mViewBody.isShown()) {
   1628             return;
   1629         }
   1630 
   1631         switch (key) {
   1632         case KeyEvent.KEYCODE_DPAD_UP:
   1633             moveFocus(-1, true);
   1634             break;
   1635 
   1636         case KeyEvent.KEYCODE_DPAD_DOWN:
   1637             moveFocus(1, true);
   1638             break;
   1639 
   1640         case KeyEvent.KEYCODE_DPAD_LEFT:
   1641             moveFocus(-1, false);
   1642             break;
   1643 
   1644         case KeyEvent.KEYCODE_DPAD_RIGHT:
   1645             moveFocus(1, false);
   1646             break;
   1647 
   1648         default:
   1649             break;
   1650         }
   1651     }
   1652 
   1653     /**
   1654      * Get a flag candidate is focused now.
   1655      *
   1656      * @return the Candidate is focused of a flag.
   1657      */
   1658     public boolean isFocusCandidate(){
   1659         if (mCurrentFocusIndex != FOCUS_NONE) {
   1660             return true;
   1661         }
   1662         return false;
   1663     }
   1664 
   1665     /**
   1666      * Give focus to View of candidate.
   1667      */
   1668     public void setViewStatusOfFocusedCandidate(){
   1669         View view = mFocusedView;
   1670         if (view != null) {
   1671             view.setBackgroundDrawable(mFocusedViewBackground);
   1672             view.setPadding(0, 0, 0, 0);
   1673         }
   1674 
   1675         TextView v = getFocusedView();
   1676         mFocusedView = v;
   1677         if (v != null) {
   1678             mFocusedViewBackground = v.getBackground();
   1679             v.setBackgroundResource(R.drawable.cand_back_focuse);
   1680             v.setPadding(0, 0, 0, 0);
   1681 
   1682             int viewBodyTop = getViewTopOnScreen(mViewBodyScroll);
   1683             int viewBodyBottom = viewBodyTop + mViewBodyScroll.getHeight();
   1684             int focusedViewTop = getViewTopOnScreen(v);
   1685             int focusedViewBottom = focusedViewTop + v.getHeight();
   1686 
   1687             if (focusedViewBottom > viewBodyBottom) {
   1688                 mViewBodyScroll.scrollBy(0, (focusedViewBottom - viewBodyBottom));
   1689             } else if (focusedViewTop < viewBodyTop) {
   1690                 mViewBodyScroll.scrollBy(0, (focusedViewTop - viewBodyTop));
   1691             }
   1692         }
   1693     }
   1694 
   1695     /**
   1696      * Clear focus to selected candidate.
   1697      */
   1698     public void clearFocusCandidate(){
   1699         View view = mFocusedView;
   1700         if (view != null) {
   1701             view.setBackgroundDrawable(mFocusedViewBackground);
   1702             view.setPadding(0, 0, 0, 0);
   1703             mFocusedView = null;
   1704         }
   1705 
   1706         mFocusAxisX = 0;
   1707         mHasFocusedArray1st = true;
   1708         mCurrentFocusIndex = FOCUS_NONE;
   1709         mHandler.removeMessages(MSG_MOVE_FOCUS);
   1710         mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.FOCUS_CANDIDATE_END));
   1711     }
   1712 
   1713     /**
   1714      * @see CandidatesViewManager#selectFocusCandidate
   1715      */
   1716     public void selectFocusCandidate(){
   1717         if (mCurrentFocusIndex != FOCUS_NONE) {
   1718             WnnWord word = getFocusedWnnWord();
   1719 
   1720             if (mHandler.hasMessages(MSG_SET_CANDIDATES)) {
   1721                 mWnnWordSelectedList.add(word);
   1722             } else {
   1723                 selectCandidate(word);
   1724             }
   1725         }
   1726     }
   1727 
   1728     /** @see CandidatesViewManager#getFocusedWnnWord */
   1729     public WnnWord getFocusedWnnWord() {
   1730         return getWnnWord(mCurrentFocusIndex);
   1731     }
   1732 
   1733     /**
   1734      * Get WnnWord.
   1735      *
   1736      * @return WnnWord word
   1737      */
   1738     public WnnWord getWnnWord(int index) {
   1739         WnnWord word = null;
   1740         if (index < 0) {
   1741             index = 0;
   1742             mHandler.removeMessages(MSG_MOVE_FOCUS);
   1743             Log.i("iwnn", "TextCandidatesViewManager::getWnnWord  index < 0 ");
   1744         } else {
   1745             int size = mHasFocusedArray1st ? mWnnWordArray1st.size() : mWnnWordArray2nd.size();
   1746             if (index >= size) {
   1747                 index = size - 1;
   1748                 mHandler.removeMessages(MSG_MOVE_FOCUS);
   1749                 Log.i("iwnn", "TextCandidatesViewManager::getWnnWord  index > candidate max ");
   1750             }
   1751         }
   1752 
   1753         if (mHasFocusedArray1st) {
   1754             word = mWnnWordArray1st.get(index);
   1755         } else {
   1756             word = mWnnWordArray2nd.get(index);
   1757         }
   1758         return word;
   1759     }
   1760 
   1761     /**
   1762      * Set display candidate line from SharedPreferences.
   1763      */
   1764     private void setNumeberOfDisplayLines(){
   1765         SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(mWnn);
   1766         mPortraitNumberOfLine = Integer.parseInt(pref.getString("setting_portrait", "2"));
   1767         mLandscapeNumberOfLine = Integer.parseInt(pref.getString("setting_landscape", "1"));
   1768     }
   1769 
   1770     /**
   1771      * Set emoticon enabled.
   1772      */
   1773     public void setEnableEmoticon(boolean enableEmoticon) {
   1774         mEnableEmoticon = enableEmoticon;
   1775     }
   1776 
   1777     /**
   1778      * Get View of focus candidate.
   1779      */
   1780     public TextView getFocusedView() {
   1781         if (mCurrentFocusIndex == FOCUS_NONE) {
   1782             return null;
   1783         }
   1784         TextView t;
   1785         if (mHasFocusedArray1st) {
   1786             t = mTextViewArray1st.get(mCurrentFocusIndex);
   1787         } else {
   1788             t = mTextViewArray2nd.get(mCurrentFocusIndex);
   1789         }
   1790         return t;
   1791     }
   1792 
   1793     /**
   1794      * Move the focus to next candidate.
   1795      *
   1796      * @param direction  The direction of increment or decrement.
   1797      * @param updown     {@code true} if move is up or down.
   1798      */
   1799     public void moveFocus(int direction, boolean updown) {
   1800         boolean isStart = (mCurrentFocusIndex == FOCUS_NONE);
   1801         if (direction == 0) {
   1802             setViewStatusOfFocusedCandidate();
   1803         }
   1804 
   1805         int size1st = mTextViewArray1st.size();
   1806         if (mHasFocusedArray1st && (size1st == 0)) {
   1807             mHasFocusedArray1st = false;
   1808         }
   1809         ArrayList<TextView> list = mHasFocusedArray1st ? mTextViewArray1st : mTextViewArray2nd;
   1810         int size = list.size();
   1811         int start = (mCurrentFocusIndex == FOCUS_NONE) ? 0 : (mCurrentFocusIndex + direction);
   1812 
   1813         int index = -1;
   1814         boolean hasChangedLine = false;
   1815         for (int i = start; (0 <= i) && (i < size); i += direction) {
   1816             TextView view = list.get(i);
   1817             if (!view.isShown()) {
   1818                 break;
   1819             }
   1820 
   1821             if (mIsSymbolMode && (view.getBackground() == null)) {
   1822                 continue;
   1823             }
   1824 
   1825             if (updown) {
   1826                 int left = view.getLeft();
   1827                 if ((left <= mFocusAxisX)
   1828                         && (mFocusAxisX < view.getRight())) {
   1829                     index = i;
   1830                     break;
   1831                 }
   1832 
   1833                 if (left == 0) {
   1834                     hasChangedLine = true;
   1835                 }
   1836             } else {
   1837                 index = i;
   1838                 break;
   1839             }
   1840         }
   1841 
   1842         if ((index < 0) && hasChangedLine && !mHasFocusedArray1st && (0 < direction)) {
   1843             index = mTextViewArray2nd.size() - 1;
   1844         }
   1845 
   1846         if (0 <= index) {
   1847             mCurrentFocusIndex = index;
   1848             setViewStatusOfFocusedCandidate();
   1849             if (!updown) {
   1850                 mFocusAxisX = getFocusedView().getLeft();
   1851             }
   1852         } else {
   1853             if (mCanReadMore && (0 < size1st)) {
   1854 
   1855                 if ((mHasFocusedArray1st && (direction < 0))
   1856                         || (!mHasFocusedArray1st && (0 < direction))) {
   1857                     updown = false;
   1858                 }
   1859 
   1860                 mHasFocusedArray1st = !mHasFocusedArray1st;
   1861 
   1862                 if (!mHasFocusedArray1st && !mIsFullView) {
   1863                     setFullMode();
   1864                 }
   1865             }
   1866 
   1867             if (size1st == 0) {
   1868                 updown = false;
   1869             }
   1870 
   1871             if (0 < direction) {
   1872                 mCurrentFocusIndex = -1;
   1873             } else {
   1874                 mCurrentFocusIndex = (mHasFocusedArray1st ? size1st : mTextViewArray2nd.size());
   1875             }
   1876             Message m = mHandler.obtainMessage(MSG_MOVE_FOCUS, direction, updown ? 1 : 0);
   1877             mHandler.sendMessage(m);
   1878         }
   1879 
   1880         if (isStart) {
   1881             mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.FOCUS_CANDIDATE_START));
   1882         }
   1883     }
   1884 
   1885     /**
   1886      * Set hardkeyboard hidden.
   1887      *
   1888      * @param hardKeyboardHidden hardkeyaboard hidden.
   1889      */
   1890     public void setHardKeyboardHidden(boolean hardKeyboardHidden) {
   1891         mHardKeyboardHidden = hardKeyboardHidden;
   1892     }
   1893 
   1894     /**
   1895      * Get view top position on screen.
   1896      *
   1897      * @param view target view.
   1898      * @return int view top position on screen
   1899      */
   1900     public int getViewTopOnScreen(View view) {
   1901         int[] location = new int[2];
   1902         view.getLocationOnScreen(location);
   1903         return location[1];
   1904     }
   1905 
   1906 
   1907     /** @see CandidatesViewManager#setCandidateMsgRemove */
   1908     public void setCandidateMsgRemove() {
   1909         mHandler.removeMessages(MSG_SET_CANDIDATES);
   1910     }
   1911 }
   1912