| /* |
| * 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 java.nio.channels.Selector; |
| import java.util.ArrayList; |
| import java.util.LinkedList; |
| import android.content.Context; |
| import android.content.SharedPreferences; |
| import android.content.res.Resources; |
| import android.content.res.Configuration; |
| import android.graphics.drawable.Drawable; |
| import android.graphics.Rect; |
| import android.media.AudioManager; |
| import android.os.Handler; |
| import android.os.Message; |
| import android.os.Vibrator; |
| import android.preference.PreferenceManager; |
| import android.text.TextUtils; |
| import android.text.TextPaint; |
| import android.text.SpannableString; |
| import android.text.Spanned; |
| import android.text.style.ImageSpan; |
| import android.text.style.DynamicDrawableSpan; |
| import android.util.Log; |
| import android.util.DisplayMetrics; |
| import android.util.TypedValue; |
| import android.view.Gravity; |
| import android.view.KeyEvent; |
| import android.view.MotionEvent; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.view.View.OnClickListener; |
| import android.view.View.OnLongClickListener; |
| import android.view.GestureDetector; |
| import android.view.LayoutInflater; |
| import android.widget.Button; |
| import android.widget.LinearLayout; |
| import android.widget.ScrollView; |
| import android.widget.TextView; |
| import android.widget.EditText; |
| import android.widget.AbsoluteLayout; |
| import android.widget.ImageView; |
| |
| /** |
| * The default candidates view manager class using {@link EditText}. |
| * |
| * @author Copyright (C) 2009-2011 OMRON SOFTWARE CO., LTD. All Rights Reserved. |
| */ |
| public class TextCandidatesViewManager extends CandidatesViewManager implements GestureDetector.OnGestureListener { |
| /** Number of lines to display (Portrait) */ |
| public static final int LINE_NUM_PORTRAIT = 2; |
| /** Number of lines to display (Landscape) */ |
| public static final int LINE_NUM_LANDSCAPE = 1; |
| |
| /** Maximum lines */ |
| private static final int DISPLAY_LINE_MAX_COUNT = 1000; |
| /** Maximum number of displaying candidates par one line (full view mode) */ |
| private static final int FULL_VIEW_DIV = 4; |
| /** Maximum number of displaying candidates par one line (full view mode)(symbol)(portrait) */ |
| private static final int FULL_VIEW_SYMBOL_DIV_PORT = 6; |
| /** Maximum number of displaying candidates par one line (full view mode)(symbol)(landscape) */ |
| private static final int FULL_VIEW_SYMBOL_DIV_LAND = 10; |
| /** Delay of set candidate */ |
| private static final int SET_CANDIDATE_DELAY = 50; |
| /** First line count */ |
| private static final int SET_CANDIDATE_FIRST_LINE_COUNT = 7; |
| /** Delay line count */ |
| private static final int SET_CANDIDATE_DELAY_LINE_COUNT = 1; |
| |
| /** Focus is none now */ |
| private static final int FOCUS_NONE = -1; |
| /** Handler for focus Candidate */ |
| private static final int MSG_MOVE_FOCUS = 0; |
| /** Handler for set Candidate */ |
| private static final int MSG_SET_CANDIDATES = 1; |
| /** Handler for select Candidate */ |
| private static final int MSG_SELECT_CANDIDATES = 2; |
| |
| /** NUmber of Candidate display lines */ |
| private static final int SETTING_NUMBER_OF_LINEMAX = 5; |
| |
| /** Body view of the candidates list */ |
| private ViewGroup mViewBody = null; |
| |
| /** The view of the Symbol Tab */ |
| private TextView mViewTabSymbol; |
| /** The view of the Emoticon Tab */ |
| private TextView mViewTabEmoticon; |
| /** Scroller of {@code mViewBodyText} */ |
| private ScrollView mViewBodyScroll; |
| /** Base of {@code mViewCandidateList1st}, {@code mViewCandidateList2nd} */ |
| private ViewGroup mViewCandidateBase; |
| /** Button displayed bottom of the view when there are more candidates. */ |
| private ImageView mReadMoreButton; |
| /** Layout for the candidates list on normal view */ |
| private LinearLayout mViewCandidateList1st; |
| /** Layout for the candidates list on full view */ |
| private AbsoluteLayout mViewCandidateList2nd; |
| /** View for symbol tab */ |
| private LinearLayout mViewCandidateListTab; |
| /** {@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; |
| /** Portrait display({@code true}) or landscape({@code false}) */ |
| private boolean mPortrait; |
| |
| /** Width of the view */ |
| private int mViewWidth; |
| /** Minimum width of a candidate (density support) */ |
| private int mCandidateMinimumWidth; |
| /** Maximum width of a candidate (density support) */ |
| private int mCandidateMinimumHeight; |
| /** Minimum height of the category candidate view */ |
| private int mCandidateCategoryMinimumHeight; |
| /** Left align threshold of the candidate view */ |
| private int mCandidateLeftAlignThreshold; |
| /** Height of keyboard */ |
| private int mKeyboardHeight; |
| /** Height of symbol keyboard */ |
| private int mSymbolKeyboardHeight; |
| /** Height of symbol keyboard tab */ |
| private int mSymbolKeyboardTabHeight; |
| /** Whether being able to use Emoticon */ |
| private boolean mEnableEmoticon = false; |
| |
| /** Whether hide the view if there is no candidate */ |
| private boolean mAutoHideMode = true; |
| /** 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 for 1st */ |
| private int mWordCount1st; |
| /** Number of candidates displaying for 2nd */ |
| private int mWordCount2nd; |
| /** List of candidates for 1st */ |
| private ArrayList<WnnWord> mWnnWordArray1st = new ArrayList<WnnWord>(); |
| /** List of candidates for 2nd */ |
| private ArrayList<WnnWord> mWnnWordArray2nd = new ArrayList<WnnWord>(); |
| /** List of select candidates */ |
| private LinkedList<WnnWord> mWnnWordSelectedList = new LinkedList<WnnWord>(); |
| |
| /** Gesture detector */ |
| private GestureDetector mGestureDetector; |
| /** Character width of the candidate area */ |
| private int mLineLength = 0; |
| /** Number of lines displayed */ |
| private int mLineCount = 1; |
| |
| /** {@code true} if the full screen mode is selected */ |
| private boolean mIsFullView = false; |
| |
| /** The event object for "touch" */ |
| private MotionEvent mMotionEvent = null; |
| |
| /** The offset when the candidates is flowed out the candidate window */ |
| private int mDisplayEndOffset = 0; |
| /** {@code true} if there are more candidates to display. */ |
| private boolean mCanReadMore = false; |
| /** Color of the candidates */ |
| private int mTextColor = 0; |
| /** Template object for each candidate and normal/full view change button */ |
| private TextView mViewCandidateTemplate; |
| /** Number of candidates in full view */ |
| private int mFullViewWordCount; |
| /** Number of candidates in the current line (in full view) */ |
| private int mFullViewOccupyCount; |
| /** View of the previous candidate (in full view) */ |
| private TextView mFullViewPrevView; |
| /** Id of the top line view (in full view) */ |
| private int mFullViewPrevLineTopId; |
| /** Layout of the previous candidate (in full view) */ |
| private ViewGroup.LayoutParams mFullViewPrevParams; |
| /** Whether all candidates are displayed */ |
| private boolean mCreateCandidateDone; |
| /** Number of lines in normal view */ |
| private int mNormalViewWordCountOfLine; |
| |
| /** List of textView for CandiData List 1st for Symbol mode */ |
| private ArrayList<TextView> mTextViewArray1st = new ArrayList<TextView>(); |
| /** List of textView for CandiData List 2st for Symbol mode */ |
| private ArrayList<TextView> mTextViewArray2nd = 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; |
| /** Axis to find next TextView for Up/Down */ |
| private int mFocusAxisX = 0; |
| /** Now focused TextView in mTextViewArray1st */ |
| private boolean mHasFocusedArray1st = true; |
| |
| /** Portrait Number of Lines from Preference */ |
| private int mPortraitNumberOfLine = LINE_NUM_PORTRAIT; |
| /** Landscape Number of Lines from Preference */ |
| private int mLandscapeNumberOfLine = LINE_NUM_LANDSCAPE; |
| |
| /** Coordinates of line */ |
| private int mLineY = 0; |
| |
| /** {@code true} if the candidate is selected */ |
| private boolean mIsSymbolSelected = false; |
| |
| /** Whether candidates is symbol */ |
| private boolean mIsSymbolMode = false; |
| |
| /** Symbol mode */ |
| private int mSymbolMode = OpenWnnJAJP.ENGINE_MODE_SYMBOL; |
| |
| /** Text size of candidates */ |
| private float mCandNormalTextSize; |
| |
| /** Text size of category */ |
| private float mCandCategoryTextSize; |
| |
| /** HardKeyboard hidden({@code true}) or disp({@code false}) */ |
| private boolean mHardKeyboardHidden = true; |
| |
| /** Minimum height of the candidate 1line view */ |
| private int mCandidateOneLineMinimumHeight; |
| |
| /** Whether candidates long click enable */ |
| private boolean mEnableCandidateLongClick = true; |
| |
| /** Keyboard vertical gap value */ |
| private static final float KEYBOARD_VERTICAL_GAP = 0.009f; |
| |
| /** Keyboard vertical gap count */ |
| private static final int KEYBOARD_VERTICAL_GAP_COUNT = 3; |
| |
| /** {@code Handler} Handler for focus Candidate wait delay */ |
| private Handler mHandler = new Handler() { |
| @Override public void handleMessage(Message msg) { |
| switch (msg.what) { |
| case MSG_MOVE_FOCUS: |
| moveFocus(msg.arg1, msg.arg2 == 1); |
| break; |
| |
| case MSG_SET_CANDIDATES: |
| if (mViewType == CandidatesViewManager.VIEW_TYPE_FULL && mIsSymbolMode) { |
| displayCandidates(mConverter, false, SET_CANDIDATE_DELAY_LINE_COUNT); |
| } |
| break; |
| |
| case MSG_SELECT_CANDIDATES: |
| WnnWord word = null; |
| while ((word = mWnnWordSelectedList.poll()) != null) { |
| selectCandidate(word); |
| } |
| break; |
| |
| default: |
| break; |
| } |
| } |
| }; |
| |
| /** Event listener for touching a candidate for 1st */ |
| private OnClickListener mCandidateOnClick1st = new OnClickListener() { |
| public void onClick(View v) { |
| onClickCandidate(v, mWnnWordArray1st); |
| } |
| }; |
| |
| /** Event listener for touching a candidate for 2nd */ |
| private OnClickListener mCandidateOnClick2nd = new OnClickListener() { |
| public void onClick(View v) { |
| onClickCandidate(v, mWnnWordArray2nd); |
| } |
| }; |
| |
| /** Event listener for long-clicking a candidate for 1st */ |
| private OnLongClickListener mCandidateOnLongClick1st = new OnLongClickListener() { |
| public boolean onLongClick(View v) { |
| return onLongClickCandidate(v, mWnnWordArray1st); |
| } |
| }; |
| |
| /** Event listener for long-clicking a candidate for for 2nd */ |
| private OnLongClickListener mCandidateOnLongClick2nd = new OnLongClickListener() { |
| public boolean onLongClick(View v) { |
| return onLongClickCandidate(v, mWnnWordArray2nd); |
| } |
| }; |
| |
| /** Event listener for click a symbol tab */ |
| private OnClickListener mTabOnClick = new OnClickListener() { |
| public void onClick(View v) { |
| if (!v.isShown()) { |
| return; |
| } |
| playSoundAndVibration(); |
| |
| if (v instanceof TextView) { |
| TextView text = (TextView)v; |
| switch (text.getId()) { |
| case R.id.candview_symbol: |
| if (mSymbolMode != OpenWnnJAJP.ENGINE_MODE_SYMBOL) { |
| mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.CHANGE_MODE, |
| OpenWnnJAJP.ENGINE_MODE_SYMBOL)); |
| } |
| break; |
| |
| case R.id.candview_emoticon: |
| if (mSymbolMode != OpenWnnJAJP.ENGINE_MODE_SYMBOL_KAO_MOJI) { |
| mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.CHANGE_MODE, |
| OpenWnnJAJP.ENGINE_MODE_SYMBOL)); |
| } |
| break; |
| |
| default: |
| break; |
| } |
| } |
| } |
| }; |
| |
| /** |
| * Constructor |
| */ |
| public TextCandidatesViewManager() { |
| this(-1); |
| } |
| |
| /** |
| * Constructor |
| * |
| * @param displayLimit The limit of display |
| */ |
| public TextCandidatesViewManager(int displayLimit) { |
| mDisplayLimit = displayLimit; |
| } |
| |
| /** |
| * Handle a click event on the candidate. |
| * @param v View |
| * @param list List of candidates |
| */ |
| private void onClickCandidate(View v, ArrayList<WnnWord> list) { |
| if (!v.isShown()) { |
| return; |
| } |
| playSoundAndVibration(); |
| |
| if (v instanceof TextView) { |
| TextView text = (TextView)v; |
| int wordcount = text.getId(); |
| WnnWord word = list.get(wordcount); |
| |
| if (mHandler.hasMessages(MSG_SET_CANDIDATES)) { |
| mWnnWordSelectedList.add(word); |
| return; |
| } |
| clearFocusCandidate(); |
| selectCandidate(word); |
| } |
| } |
| |
| /** |
| * Handle a long click event on the candidate. |
| * @param v View |
| * @param list List of candidates |
| */ |
| public boolean onLongClickCandidate(View v, ArrayList<WnnWord> list) { |
| if (mViewLongPressDialog == null) { |
| return false; |
| } |
| |
| if (mIsSymbolMode) { |
| return false; |
| } |
| |
| if (!mEnableCandidateLongClick) { |
| return false; |
| } |
| |
| if (!v.isShown()) { |
| return true; |
| } |
| |
| Drawable d = v.getBackground(); |
| if (d != null) { |
| if(d.getState().length == 0){ |
| return true; |
| } |
| } |
| |
| int wordcount = ((TextView)v).getId(); |
| mWord = list.get(wordcount); |
| clearFocusCandidate(); |
| displayDialog(v, mWord); |
| |
| return true; |
| } |
| |
| /** |
| * 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#initView */ |
| public View initView(OpenWnn parent, int width, int height) { |
| mWnn = parent; |
| mViewWidth = width; |
| Resources r = mWnn.getResources(); |
| mCandidateMinimumWidth = r.getDimensionPixelSize(R.dimen.cand_minimum_width); |
| mCandidateMinimumHeight = r.getDimensionPixelSize(R.dimen.cand_minimum_height); |
| if (OpenWnn.isXLarge()) { |
| mCandidateOneLineMinimumHeight = r.getDimensionPixelSize(R.dimen.candidate_layout_height); |
| } |
| mCandidateCategoryMinimumHeight = r.getDimensionPixelSize(R.dimen.cand_category_minimum_height); |
| mCandidateLeftAlignThreshold = r.getDimensionPixelSize(R.dimen.cand_left_align_threshold); |
| mKeyboardHeight = r.getDimensionPixelSize(R.dimen.keyboard_height); |
| if (OpenWnn.isXLarge()) { |
| mKeyboardHeight += Math.round(height * KEYBOARD_VERTICAL_GAP) |
| * KEYBOARD_VERTICAL_GAP_COUNT; |
| } |
| mSymbolKeyboardHeight = r.getDimensionPixelSize(R.dimen.symbol_keyboard_height); |
| Drawable d = r.getDrawable(R.drawable.tab_no_select); |
| mSymbolKeyboardTabHeight = d.getMinimumHeight(); |
| |
| mPortrait = |
| (r.getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE); |
| |
| mCandNormalTextSize = r.getDimensionPixelSize(R.dimen.cand_normal_text_size); |
| mCandCategoryTextSize = r.getDimensionPixelSize(R.dimen.cand_category_text_size); |
| |
| LayoutInflater inflater = parent.getLayoutInflater(); |
| mViewBody = (ViewGroup)inflater.inflate(R.layout.candidates, null); |
| |
| mViewTabSymbol = (TextView)mViewBody.findViewById(R.id.candview_symbol); |
| mViewTabEmoticon = (TextView)mViewBody.findViewById(R.id.candview_emoticon); |
| |
| mViewBodyScroll = (ScrollView)mViewBody.findViewById(R.id.candview_scroll); |
| |
| mViewCandidateBase = (ViewGroup)mViewBody.findViewById(R.id.candview_base); |
| |
| setNumeberOfDisplayLines(); |
| createNormalCandidateView(); |
| mViewCandidateList2nd = (AbsoluteLayout)mViewBody.findViewById(R.id.candidates_2nd_view); |
| |
| mTextColor = r.getColor(R.color.candidate_text); |
| |
| mReadMoreButton = (ImageView)mViewBody.findViewById(R.id.read_more_button); |
| mReadMoreButton.setOnTouchListener(new View.OnTouchListener() { |
| public boolean onTouch(View v, MotionEvent event) { |
| int resid = 0; |
| switch (event.getAction()) { |
| case MotionEvent.ACTION_DOWN: |
| if (mIsFullView) { |
| resid = R.drawable.cand_up_press; |
| } else { |
| resid = R.drawable.cand_down_press; |
| } |
| break; |
| case MotionEvent.ACTION_UP: |
| if (mIsFullView) { |
| resid = R.drawable.cand_up; |
| } else { |
| resid = R.drawable.cand_down; |
| } |
| break; |
| default: |
| break; |
| } |
| |
| if (resid != 0) { |
| mReadMoreButton.setImageResource(resid); |
| } |
| return false; |
| } |
| }); |
| mReadMoreButton.setOnClickListener(new View.OnClickListener() { |
| public void onClick(View v) { |
| if (!v.isShown()) { |
| return; |
| } |
| playSoundAndVibration(); |
| |
| if (mIsFullView) { |
| mIsFullView = false; |
| mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.LIST_CANDIDATES_NORMAL)); |
| } else { |
| mIsFullView = true; |
| mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.LIST_CANDIDATES_FULL)); |
| } |
| } |
| }); |
| |
| setViewType(CandidatesViewManager.VIEW_TYPE_CLOSE); |
| |
| mGestureDetector = new GestureDetector(this); |
| |
| 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(); |
| } |
| }); |
| |
| return mViewBody; |
| } |
| |
| /** |
| * Create the normal candidate view |
| */ |
| private void createNormalCandidateView() { |
| mViewCandidateList1st = (LinearLayout)mViewBody.findViewById(R.id.candidates_1st_view); |
| mViewCandidateList1st.setOnClickListener(mCandidateOnClick1st); |
| |
| mViewCandidateListTab = (LinearLayout)mViewBody.findViewById(R.id.candview_tab); |
| TextView tSymbol = mViewTabSymbol; |
| tSymbol.setOnClickListener(mTabOnClick); |
| TextView tEmoticon = mViewTabEmoticon; |
| tEmoticon.setOnClickListener(mTabOnClick); |
| |
| int line = SETTING_NUMBER_OF_LINEMAX; |
| int width = mViewWidth; |
| for (int i = 0; i < line; i++) { |
| LinearLayout lineView = new LinearLayout(mViewBodyScroll.getContext()); |
| lineView.setOrientation(LinearLayout.HORIZONTAL); |
| LinearLayout.LayoutParams layoutParams = |
| new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, |
| ViewGroup.LayoutParams.WRAP_CONTENT); |
| lineView.setLayoutParams(layoutParams); |
| for (int j = 0; j < (width / getCandidateMinimumWidth()); j++) { |
| TextView tv = createCandidateView(); |
| lineView.addView(tv); |
| } |
| |
| if (i == 0) { |
| TextView tv = createCandidateView(); |
| layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, |
| ViewGroup.LayoutParams.WRAP_CONTENT); |
| layoutParams.weight = 0; |
| layoutParams.gravity = Gravity.RIGHT; |
| tv.setLayoutParams(layoutParams); |
| |
| lineView.addView(tv); |
| mViewCandidateTemplate = tv; |
| } |
| mViewCandidateList1st.addView(lineView); |
| } |
| } |
| |
| /** @see CandidatesViewManager#getCurrentView */ |
| public View getCurrentView() { |
| return mViewBody; |
| } |
| |
| /** @see CandidatesViewManager#setViewType */ |
| public void setViewType(int type) { |
| boolean readMore = setViewLayout(type); |
| |
| if (readMore) { |
| displayCandidates(this.mConverter, false, -1); |
| } else { |
| if (type == CandidatesViewManager.VIEW_TYPE_NORMAL) { |
| mIsFullView = false; |
| if (mDisplayEndOffset > 0) { |
| int maxLine = getMaxLine(); |
| displayCandidates(this.mConverter, false, maxLine); |
| } else { |
| setReadMore(); |
| } |
| } else { |
| if (mViewBody.isShown()) { |
| mWnn.setCandidatesViewShown(false); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Set the view layout |
| * |
| * @param type View type |
| * @return {@code true} if display is updated; {@code false} if otherwise |
| */ |
| private boolean setViewLayout(int type) { |
| |
| ViewGroup.LayoutParams params; |
| int line = (mPortrait) ? mPortraitNumberOfLine : mLandscapeNumberOfLine; |
| |
| if ((mViewType == CandidatesViewManager.VIEW_TYPE_FULL) |
| && (type == CandidatesViewManager.VIEW_TYPE_NORMAL)) { |
| clearFocusCandidate(); |
| } |
| |
| mViewType = type; |
| |
| switch (type) { |
| case CandidatesViewManager.VIEW_TYPE_CLOSE: |
| params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, |
| getCandidateMinimumHeight() * line); |
| mViewBodyScroll.setLayoutParams(params); |
| mViewCandidateListTab.setVisibility(View.GONE); |
| mViewCandidateBase.setMinimumHeight(-1); |
| mHandler.removeMessages(MSG_SET_CANDIDATES); |
| return false; |
| |
| case CandidatesViewManager.VIEW_TYPE_NORMAL: |
| params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, |
| getCandidateMinimumHeight() * line); |
| mViewBodyScroll.setLayoutParams(params); |
| mViewBodyScroll.scrollTo(0, 0); |
| mViewCandidateListTab.setVisibility(View.GONE); |
| mViewCandidateList1st.setVisibility(View.VISIBLE); |
| mViewCandidateList2nd.setVisibility(View.GONE); |
| mViewCandidateBase.setMinimumHeight(-1); |
| return false; |
| |
| case CandidatesViewManager.VIEW_TYPE_FULL: |
| default: |
| params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, |
| getCandidateViewHeight()); |
| mViewBodyScroll.setLayoutParams(params); |
| if (mIsSymbolMode) { |
| updateSymbolType(); |
| mViewCandidateListTab.setVisibility(View.VISIBLE); |
| } else { |
| mViewCandidateListTab.setVisibility(View.GONE); |
| } |
| mViewCandidateList2nd.setVisibility(View.VISIBLE); |
| mViewCandidateBase.setMinimumHeight(-1); |
| return true; |
| } |
| } |
| |
| /** @see CandidatesViewManager#getViewType */ |
| public int getViewType() { |
| return mViewType; |
| } |
| |
| /** @see CandidatesViewManager#displayCandidates */ |
| public void displayCandidates(WnnEngine converter) { |
| |
| mHandler.removeMessages(MSG_SET_CANDIDATES); |
| |
| if (mIsSymbolSelected) { |
| mIsSymbolSelected = false; |
| if (mSymbolMode == OpenWnnJAJP.ENGINE_MODE_SYMBOL_KAO_MOJI) { |
| return; |
| } |
| |
| int prevLineCount = mLineCount; |
| int prevWordCount1st = mWordCount1st; |
| clearNormalViewCandidate(); |
| mWordCount1st = 0; |
| mLineCount = 1; |
| mLineLength = 0; |
| mNormalViewWordCountOfLine = 0; |
| mWnnWordArray1st.clear(); |
| mTextViewArray1st.clear(); |
| if (((prevWordCount1st == 0) && (mWordCount1st == 1)) || |
| (prevLineCount < mLineCount)) { |
| mViewBodyScroll.scrollTo(0, mViewBodyScroll.getScrollY() + getCandidateMinimumHeight()); |
| } |
| if (isFocusCandidate() && mHasFocusedArray1st) { |
| mCurrentFocusIndex = 0; |
| Message m = mHandler.obtainMessage(MSG_MOVE_FOCUS, 0, 0); |
| mHandler.sendMessage(m); |
| } |
| return; |
| } |
| |
| mCanReadMore = false; |
| mDisplayEndOffset = 0; |
| mIsFullView = false; |
| mFullViewWordCount = 0; |
| mFullViewOccupyCount = 0; |
| mFullViewPrevLineTopId = 0; |
| mFullViewPrevView = null; |
| mCreateCandidateDone = false; |
| mNormalViewWordCountOfLine = 0; |
| |
| clearCandidates(); |
| mConverter = converter; |
| mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.LIST_CANDIDATES_NORMAL)); |
| |
| mViewCandidateTemplate.setVisibility(View.VISIBLE); |
| mViewCandidateTemplate.setBackgroundResource(R.drawable.cand_back); |
| |
| displayCandidates(converter, true, getMaxLine()); |
| |
| if (mIsSymbolMode) { |
| mIsFullView = true; |
| mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.LIST_CANDIDATES_FULL)); |
| } |
| } |
| |
| /** @see CandidatesViewManager#getMaxLine */ |
| private int getMaxLine() { |
| int maxLine = (mPortrait) ? mPortraitNumberOfLine : mLandscapeNumberOfLine; |
| return maxLine; |
| } |
| |
| /** |
| * Get categories text. |
| * @param String Source string replacement |
| * @return String Categories text |
| */ |
| private String getCategoriesText(String categoriesString) { |
| String ret = null; |
| |
| Resources r = mWnn.getResources(); |
| if(categoriesString.equals(r.getString(R.string.half_symbol_categories_txt))) { |
| ret = r.getString(R.string.half_symbol_txt); |
| } else if (categoriesString.equals(r.getString(R.string.full_symbol_categories_txt))) { |
| ret = r.getString(R.string.full_symbol_txt); |
| } else { |
| ret = new String(""); |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * Display the candidates. |
| * |
| * @param converter {@link WnnEngine} which holds candidates. |
| * @param dispFirst Whether it is the first time displaying the candidates |
| * @param maxLine The maximum number of displaying lines |
| */ |
| private void displayCandidates(WnnEngine converter, boolean dispFirst, int maxLine) { |
| if (converter == null) { |
| return; |
| } |
| |
| /* Concatenate the candidates already got and the last one in dispFirst mode */ |
| int displayLimit = mDisplayLimit; |
| |
| boolean isDelay = false; |
| boolean isBreak = false; |
| |
| if (converter instanceof SymbolList) { |
| if (!dispFirst) { |
| if (maxLine == -1) { |
| isDelay = true; |
| maxLine = mLineCount + SET_CANDIDATE_FIRST_LINE_COUNT; |
| |
| mHandler.sendEmptyMessageDelayed(MSG_SET_CANDIDATES, SET_CANDIDATE_DELAY); |
| |
| } else if (maxLine == SET_CANDIDATE_DELAY_LINE_COUNT) { |
| isDelay = true; |
| maxLine = mLineCount + SET_CANDIDATE_DELAY_LINE_COUNT; |
| |
| mHandler.sendEmptyMessageDelayed(MSG_SET_CANDIDATES, SET_CANDIDATE_DELAY); |
| } |
| } |
| if (mSymbolMode != OpenWnnJAJP.ENGINE_MODE_SYMBOL_KAO_MOJI) { |
| displayLimit = -1; |
| } |
| } |
| |
| /* Get candidates */ |
| WnnWord result = null; |
| String prevCandidate = null; |
| while ((displayLimit == -1 || getWordCount() < displayLimit)) { |
| for (int i = 0; i < DISPLAY_LINE_MAX_COUNT; i++) { |
| result = converter.getNextCandidate(); |
| if (result == null) { |
| break; |
| } |
| |
| if (converter instanceof SymbolList) { |
| break; |
| } |
| |
| if ((prevCandidate == null) || !prevCandidate.equals(result.candidate)) { |
| break; |
| } |
| } |
| |
| if (result == null) { |
| break; |
| } else { |
| prevCandidate = result.candidate; |
| } |
| |
| if (converter instanceof SymbolList) { |
| if (isCategory(result)) { |
| if (getWordCount() != 0) { |
| createNextLine(false); |
| } |
| result.candidate = getCategoriesText(result.candidate); |
| setCandidate(true, result); |
| createNextLine(true); |
| continue; |
| } |
| } |
| |
| setCandidate(false, result); |
| |
| if ((dispFirst || isDelay) && (maxLine < mLineCount)) { |
| mCanReadMore = true; |
| isBreak = true; |
| break; |
| } |
| } |
| |
| if (!isBreak && !mCreateCandidateDone) { |
| /* align left if necessary */ |
| createNextLine(false); |
| mCreateCandidateDone = true; |
| mHandler.removeMessages(MSG_SET_CANDIDATES); |
| mHandler.sendMessage(mHandler.obtainMessage(MSG_SELECT_CANDIDATES)); |
| } |
| |
| if (getWordCount() < 1) { /* no candidates */ |
| if (mAutoHideMode) { |
| mWnn.setCandidatesViewShown(false); |
| return; |
| } else { |
| mCanReadMore = false; |
| mIsFullView = false; |
| setViewLayout(CandidatesViewManager.VIEW_TYPE_NORMAL); |
| } |
| } |
| |
| setReadMore(); |
| |
| if (!(mViewBody.isShown())) { |
| mWnn.setCandidatesViewShown(true); |
| } |
| return; |
| } |
| |
| /** |
| * Add a candidate into the list. |
| * @param isCategory {@code true}:caption of category, {@code false}:normal word |
| * @param word A candidate word |
| */ |
| private void setCandidate(boolean isCategory, WnnWord word) { |
| int textLength = measureText(word.candidate, 0, word.candidate.length()); |
| TextView template = mViewCandidateTemplate; |
| textLength += template.getPaddingLeft() + template.getPaddingRight(); |
| int maxWidth = mViewWidth; |
| boolean isEmojiSymbol = false; |
| if (mIsSymbolMode && (word.candidate.length() < 3)) { |
| isEmojiSymbol = true; |
| } |
| TextView textView; |
| |
| boolean is2nd = isFirstListOver(mIsFullView, mLineCount, word); |
| if (is2nd) { |
| /* Full view */ |
| int viewDivison = getCandidateViewDivison(); |
| int indentWidth = mViewWidth / viewDivison; |
| int occupyCount = Math.min((textLength + indentWidth + getCandidateSpaceWidth(isEmojiSymbol)) / indentWidth, viewDivison); |
| if (isCategory) { |
| occupyCount = viewDivison; |
| } |
| |
| if (viewDivison < (mFullViewOccupyCount + occupyCount)) { |
| if (viewDivison != mFullViewOccupyCount) { |
| mFullViewPrevParams.width += (viewDivison - mFullViewOccupyCount) * indentWidth; |
| if (mFullViewPrevView != null) { |
| mViewCandidateList2nd.updateViewLayout(mFullViewPrevView, mFullViewPrevParams); |
| } |
| } |
| mFullViewOccupyCount = 0; |
| if (mFullViewPrevView != null) { |
| mFullViewPrevLineTopId = mFullViewPrevView.getId(); |
| } |
| mLineCount++; |
| if (isCategory) { |
| mLineY += mCandidateCategoryMinimumHeight; |
| } else { |
| mLineY += getCandidateMinimumHeight(); |
| } |
| } |
| if (mFullViewWordCount == 0) { |
| mLineY = 0; |
| } |
| |
| ViewGroup layout = mViewCandidateList2nd; |
| |
| int width = indentWidth * occupyCount; |
| int height = getCandidateMinimumHeight(); |
| if (isCategory) { |
| height = mCandidateCategoryMinimumHeight; |
| } |
| |
| ViewGroup.LayoutParams params = buildLayoutParams(mViewCandidateList2nd, width, height); |
| |
| textView = (TextView) layout.getChildAt(mFullViewWordCount); |
| if (textView == null) { |
| textView = createCandidateView(); |
| textView.setLayoutParams(params); |
| |
| mViewCandidateList2nd.addView(textView); |
| } else { |
| mViewCandidateList2nd.updateViewLayout(textView, params); |
| } |
| |
| mFullViewOccupyCount += occupyCount; |
| mFullViewWordCount++; |
| mFullViewPrevView = textView; |
| mFullViewPrevParams = params; |
| |
| } else { |
| int viewDivison = getCandidateViewDivison(); |
| int indentWidth = mViewWidth / viewDivison; |
| textLength = Math.max(textLength, indentWidth); |
| |
| /* Normal view */ |
| int nextEnd = mLineLength + textLength; |
| nextEnd += getCandidateSpaceWidth(isEmojiSymbol); |
| |
| if (mLineCount == 1 && !mIsSymbolMode) { |
| maxWidth -= getCandidateMinimumWidth(); |
| } |
| |
| if ((maxWidth < nextEnd) && (getWordCount() != 0)) { |
| |
| createNextLineFor1st(); |
| if (getMaxLine() < mLineCount) { |
| mLineLength = 0; |
| /* Call this method again to add the candidate in the full view */ |
| if (!mIsSymbolSelected) { |
| setCandidate(isCategory, word); |
| } |
| return; |
| } |
| |
| mLineLength = textLength; |
| mLineLength += getCandidateSpaceWidth(isEmojiSymbol); |
| } else { |
| mLineLength = nextEnd; |
| } |
| |
| LinearLayout lineView = (LinearLayout) mViewCandidateList1st.getChildAt(mLineCount - 1); |
| textView = (TextView) lineView.getChildAt(mNormalViewWordCountOfLine); |
| |
| if (isCategory) { |
| if (mLineCount == 1) { |
| mViewCandidateTemplate.setBackgroundDrawable(null); |
| } |
| mLineLength += mCandidateLeftAlignThreshold; |
| } else { |
| int CompareLength = textLength; |
| CompareLength += getCandidateSpaceWidth(isEmojiSymbol); |
| } |
| |
| mNormalViewWordCountOfLine++; |
| } |
| |
| textView.setText(word.candidate); |
| if (is2nd) { |
| textView.setId(mWordCount2nd); |
| } else { |
| textView.setId(mWordCount1st); |
| } |
| textView.setVisibility(View.VISIBLE); |
| textView.setPressed(false); |
| textView.setFocusable(false); |
| |
| if (isCategory) { |
| textView.setText(" " + word.candidate); |
| |
| textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mCandCategoryTextSize); |
| textView.setBackgroundDrawable(null); |
| textView.setGravity(Gravity.CENTER_VERTICAL); |
| textView.setMinHeight(mCandidateCategoryMinimumHeight); |
| textView.setHeight(mCandidateCategoryMinimumHeight); |
| |
| textView.setOnClickListener(null); |
| textView.setOnLongClickListener(null); |
| textView.setTextColor(mTextColor); |
| } else { |
| textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mCandNormalTextSize); |
| textView.setGravity(Gravity.CENTER); |
| textView.setMinHeight(getCandidateMinimumHeight()); |
| textView.setHeight(getCandidateMinimumHeight()); |
| |
| if (is2nd) { |
| textView.setOnClickListener(mCandidateOnClick2nd); |
| textView.setOnLongClickListener(mCandidateOnLongClick2nd); |
| } else { |
| textView.setOnClickListener(mCandidateOnClick1st); |
| textView.setOnLongClickListener(mCandidateOnLongClick1st); |
| } |
| |
| textView.setBackgroundResource(R.drawable.cand_back); |
| |
| textView.setTextColor(mTextColor); |
| } |
| |
| if (maxWidth < textLength) { |
| textView.setEllipsize(TextUtils.TruncateAt.END); |
| } else { |
| textView.setEllipsize(null); |
| } |
| |
| ImageSpan span = null; |
| if (word.candidate.equals(" ")) { |
| span = new ImageSpan(mWnn, R.drawable.word_half_space, |
| DynamicDrawableSpan.ALIGN_BASELINE); |
| } else if (word.candidate.equals("\u3000" /* full-width space */)) { |
| span = new ImageSpan(mWnn, R.drawable.word_full_space, |
| DynamicDrawableSpan.ALIGN_BASELINE); |
| } |
| |
| if (span != null) { |
| SpannableString spannable = new SpannableString(" "); |
| spannable.setSpan(span, 1, 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); |
| textView.setText(spannable); |
| } |
| textView.setPadding(0, 0, 0, 0); |
| |
| if (is2nd) { |
| mWnnWordArray2nd.add(mWordCount2nd, word); |
| mWordCount2nd++; |
| mTextViewArray2nd.add(textView); |
| } else { |
| mWnnWordArray1st.add(mWordCount1st, word); |
| mWordCount1st++; |
| mTextViewArray1st.add(textView); |
| } |
| } |
| |
| /** |
| * Create AbsoluteLayout.LayoutParams |
| * @param layout AbsoluteLayout |
| * @param width The width of the display |
| * @param height The height of the display |
| * @return Layout parameter |
| */ |
| private ViewGroup.LayoutParams buildLayoutParams(AbsoluteLayout layout, int width, int height) { |
| |
| int viewDivison = getCandidateViewDivison(); |
| int indentWidth = mViewWidth / viewDivison; |
| int x = indentWidth * mFullViewOccupyCount; |
| int y = mLineY; |
| ViewGroup.LayoutParams params |
| = new AbsoluteLayout.LayoutParams(width, height, x, y); |
| |
| return params; |
| } |
| |
| /** |
| * Create a view for a candidate. |
| * @return the view |
| */ |
| private TextView createCandidateView() { |
| TextView text = new CandidateTextView(mViewBodyScroll.getContext()); |
| text.setTextSize(TypedValue.COMPLEX_UNIT_PX, mCandNormalTextSize); |
| text.setBackgroundResource(R.drawable.cand_back); |
| text.setCompoundDrawablePadding(0); |
| text.setGravity(Gravity.CENTER); |
| text.setSingleLine(); |
| text.setPadding(0, 0, 0, 0); |
| text.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, |
| ViewGroup.LayoutParams.WRAP_CONTENT, |
| 1.0f)); |
| text.setMinHeight(getCandidateMinimumHeight()); |
| text.setMinimumWidth(getCandidateMinimumWidth()); |
| text.setSoundEffectsEnabled(false); |
| return text; |
| } |
| |
| /** |
| * Display {@code mReadMoreText} if there are more candidates. |
| */ |
| private void setReadMore() { |
| if (mIsSymbolMode) { |
| mReadMoreButton.setVisibility(View.GONE); |
| mViewCandidateTemplate.setVisibility(View.GONE); |
| return; |
| } |
| |
| int resid = 0; |
| |
| if (mIsFullView) { |
| mReadMoreButton.setVisibility(View.VISIBLE); |
| resid = R.drawable.cand_up; |
| } else { |
| if (mCanReadMore) { |
| mReadMoreButton.setVisibility(View.VISIBLE); |
| resid = R.drawable.cand_down; |
| } else { |
| mReadMoreButton.setVisibility(View.GONE); |
| mViewCandidateTemplate.setVisibility(View.GONE); |
| } |
| } |
| |
| if (resid != 0) { |
| mReadMoreButton.setImageResource(resid); |
| } |
| } |
| |
| /** |
| * Clear the list of the normal candidate view. |
| */ |
| private void clearNormalViewCandidate() { |
| LinearLayout candidateList = mViewCandidateList1st; |
| int lineNum = candidateList.getChildCount(); |
| for (int i = 0; i < lineNum; i++) { |
| |
| LinearLayout lineView = (LinearLayout)candidateList.getChildAt(i); |
| int size = lineView.getChildCount(); |
| for (int j = 0; j < size; j++) { |
| View v = lineView.getChildAt(j); |
| v.setVisibility(View.GONE); |
| } |
| } |
| } |
| |
| /** @see CandidatesViewManager#clearCandidates */ |
| public void clearCandidates() { |
| closeDialog(); |
| clearFocusCandidate(); |
| clearNormalViewCandidate(); |
| |
| ViewGroup layout = mViewCandidateList2nd; |
| int size = layout.getChildCount(); |
| for (int i = 0; i < size; i++) { |
| View v = layout.getChildAt(i); |
| v.setVisibility(View.GONE); |
| } |
| |
| mLineCount = 1; |
| mWordCount1st = 0; |
| mWordCount2nd = 0; |
| mWnnWordArray1st.clear(); |
| mWnnWordArray2nd.clear(); |
| mTextViewArray1st.clear(); |
| mTextViewArray2nd.clear(); |
| mLineLength = 0; |
| |
| mLineY = 0; |
| |
| mIsFullView = false; |
| mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.LIST_CANDIDATES_NORMAL)); |
| if (mAutoHideMode) { |
| setViewLayout(CandidatesViewManager.VIEW_TYPE_CLOSE); |
| } |
| |
| if (mAutoHideMode && mViewBody.isShown()) { |
| mWnn.setCandidatesViewShown(false); |
| } |
| mCanReadMore = false; |
| setReadMore(); |
| } |
| |
| /** @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; |
| } |
| setNumeberOfDisplayLines(); |
| } catch (Exception ex) { |
| Log.e("OpenWnn", "NO VIBRATOR"); |
| } |
| } |
| |
| /** |
| * Set normal mode. |
| */ |
| public void setNormalMode() { |
| setReadMore(); |
| mIsFullView = false; |
| mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.LIST_CANDIDATES_NORMAL)); |
| } |
| |
| /** |
| * Set full mode. |
| */ |
| public void setFullMode() { |
| mIsFullView = true; |
| mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.LIST_CANDIDATES_FULL)); |
| } |
| |
| /** |
| * Set symbol mode. |
| */ |
| public void setSymbolMode(boolean enable, int mode) { |
| if (mIsSymbolMode && !enable) { |
| setViewType(CandidatesViewManager.VIEW_TYPE_CLOSE); |
| } |
| mSymbolMode = mode; |
| mIsSymbolMode = enable; |
| } |
| |
| /** |
| * Set scroll up. |
| */ |
| public void setScrollUp() { |
| if (!mViewBodyScroll.pageScroll(ScrollView.FOCUS_UP)) { |
| mViewBodyScroll.scrollTo(0, mViewBodyScroll.getChildAt(0).getHeight()); |
| } |
| } |
| |
| /** |
| * Set scroll down. |
| */ |
| public void setScrollDown() { |
| if (!mViewBodyScroll.pageScroll(ScrollView.FOCUS_DOWN)) { |
| mViewBodyScroll.scrollTo(0, 0); |
| } |
| } |
| |
| /** |
| * Set scroll full up. |
| */ |
| public void setScrollFullUp() { |
| if (!mViewBodyScroll.fullScroll(ScrollView.FOCUS_UP)) { |
| mViewBodyScroll.scrollTo(0, mViewBodyScroll.getChildAt(0).getHeight()); |
| } |
| } |
| |
| /** |
| * Set scroll full down. |
| */ |
| public void setScrollFullDown() { |
| if (!mViewBodyScroll.fullScroll(ScrollView.FOCUS_DOWN)) { |
| mViewBodyScroll.scrollTo(0, 0); |
| } |
| } |
| |
| /** |
| * Process {@link OpenWnnEvent#CANDIDATE_VIEW_TOUCH} event. |
| * |
| * @return {@code true} if event is processed; {@code false} if otherwise |
| */ |
| public boolean onTouchSync() { |
| return mGestureDetector.onTouchEvent(mMotionEvent); |
| } |
| |
| /** |
| * Select a candidate. |
| * <br> |
| * This method notices the selected word to {@link OpenWnn}. |
| * |
| * @param word The selected word |
| */ |
| private void selectCandidate(WnnWord word) { |
| if (!mIsSymbolMode) { |
| mIsFullView = false; |
| mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.LIST_CANDIDATES_NORMAL)); |
| } |
| mIsSymbolSelected = mIsSymbolMode; |
| mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.SELECT_CANDIDATE, word)); |
| } |
| |
| private void playSoundAndVibration() { |
| if (mVibrator != null) { |
| try { |
| mVibrator.vibrate(5); |
| } catch (Exception ex) { |
| Log.e("OpenWnn", "TextCandidatesViewManager::selectCandidate Vibrator " + ex.toString()); |
| } |
| } |
| if (mSound != null) { |
| try { |
| mSound.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD, -1); |
| } catch (Exception ex) { |
| Log.e("OpenWnn", "TextCandidatesViewManager::selectCandidate Sound " + ex.toString()); |
| } |
| } |
| } |
| |
| /** @see android.view.GestureDetector.OnGestureListener#onDown */ |
| public boolean onDown(MotionEvent arg0) { |
| return false; |
| } |
| |
| /** @see android.view.GestureDetector.OnGestureListener#onFling */ |
| public boolean onFling(MotionEvent arg0, MotionEvent arg1, float arg2, float arg3) { |
| boolean consumed = false; |
| if (arg1 != null && arg0 != null && arg1.getY() < arg0.getY()) { |
| if ((mViewType == CandidatesViewManager.VIEW_TYPE_NORMAL) && mCanReadMore) { |
| if (mVibrator != null) { |
| try { |
| mVibrator.vibrate(5); |
| } catch (Exception ex) { |
| Log.e("iwnn", "TextCandidatesViewManager::onFling Vibrator " + ex.toString()); |
| } |
| } |
| mIsFullView = true; |
| mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.LIST_CANDIDATES_FULL)); |
| consumed = true; |
| } |
| } else { |
| if (mViewBodyScroll.getScrollY() == 0) { |
| if (mVibrator != null) { |
| try { |
| mVibrator.vibrate(5); |
| } catch (Exception ex) { |
| Log.e("iwnn", "TextCandidatesViewManager::onFling Sound " + ex.toString()); |
| } |
| } |
| mIsFullView = false; |
| mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.LIST_CANDIDATES_NORMAL)); |
| consumed = true; |
| } |
| } |
| |
| return consumed; |
| } |
| |
| /** @see android.view.GestureDetector.OnGestureListener#onLongPress */ |
| public void onLongPress(MotionEvent arg0) { |
| return; |
| } |
| |
| /** @see android.view.GestureDetector.OnGestureListener#onScroll */ |
| public boolean onScroll(MotionEvent arg0, MotionEvent arg1, float arg2, float arg3) { |
| return false; |
| } |
| |
| /** @see android.view.GestureDetector.OnGestureListener#onShowPress */ |
| public void onShowPress(MotionEvent arg0) { |
| } |
| |
| /** @see android.view.GestureDetector.OnGestureListener#onSingleTapUp */ |
| public boolean onSingleTapUp(MotionEvent arg0) { |
| return false; |
| } |
| |
| /** |
| * Retrieve the width of string to draw. |
| * |
| * @param text The string |
| * @param start The start position (specified by the number of character) |
| * @param end The end position (specified by the number of character) |
| * @return The width of string to draw |
| */ |
| public int measureText(CharSequence text, int start, int end) { |
| if (end - start < 3) { |
| return getCandidateMinimumWidth(); |
| } |
| |
| TextPaint paint = mViewCandidateTemplate.getPaint(); |
| return (int)paint.measureText(text, start, end); |
| } |
| |
| /** |
| * Create a layout for the next line. |
| */ |
| private void createNextLine(boolean isCategory) { |
| if (isFirstListOver(mIsFullView, mLineCount, null)) { |
| /* Full view */ |
| mFullViewOccupyCount = 0; |
| if (mFullViewPrevView != null) { |
| mFullViewPrevLineTopId = mFullViewPrevView.getId(); |
| } |
| if (isCategory) { |
| mLineY += mCandidateCategoryMinimumHeight; |
| } else { |
| mLineY += getCandidateMinimumHeight(); |
| } |
| mLineCount++; |
| } else { |
| createNextLineFor1st(); |
| } |
| } |
| |
| /** |
| * Create a layout for the next line. |
| */ |
| private void createNextLineFor1st() { |
| LinearLayout lineView = (LinearLayout) mViewCandidateList1st.getChildAt(mLineCount - 1); |
| float weight = 0; |
| if (mLineLength < mCandidateLeftAlignThreshold) { |
| if (mLineCount == 1) { |
| mViewCandidateTemplate.setVisibility(View.GONE); |
| } |
| } else { |
| weight = 1.0f; |
| } |
| |
| LinearLayout.LayoutParams params |
| = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, |
| ViewGroup.LayoutParams.WRAP_CONTENT, |
| weight); |
| |
| int child = lineView.getChildCount(); |
| for (int i = 0; i < child; i++) { |
| View view = lineView.getChildAt(i); |
| |
| if (view != mViewCandidateTemplate) { |
| view.setLayoutParams(params); |
| view.setPadding(0, 0, 0, 0); |
| } |
| } |
| |
| mLineLength = 0; |
| mNormalViewWordCountOfLine = 0; |
| mLineCount++; |
| } |
| |
| /** |
| * Judge if it's a category. |
| * |
| * @return {@code true} if category |
| */ |
| boolean isCategory(WnnWord word) { |
| int length = word.candidate.length(); |
| return ((length > 3) && (word.candidate.charAt(0) == '[')); |
| } |
| |
| /** |
| * Get a minimum width of a candidate view. |
| * |
| * @return the minimum width of a candidate view. |
| */ |
| private int getCandidateMinimumWidth() { |
| return mCandidateMinimumWidth; |
| } |
| |
| /** |
| * @return the minimum height of a candidate view. |
| */ |
| private int getCandidateMinimumHeight() { |
| return mCandidateMinimumHeight; |
| } |
| |
| /** |
| * Get a height of a candidate view. |
| * |
| * @return the height of a candidate view. |
| */ |
| private int getCandidateViewHeight() { |
| if (OpenWnn.isXLarge()) { |
| return mKeyboardHeight + mCandidateOneLineMinimumHeight - mSymbolKeyboardHeight |
| - mSymbolKeyboardTabHeight; |
| } else { |
| int numberOfLine = (mPortrait) ? mPortraitNumberOfLine : mLandscapeNumberOfLine; |
| Resources resource = mWnn.getResources(); |
| Drawable keyboardBackground = resource.getDrawable(R.drawable.keyboard_background); |
| Rect keyboardPadding = new Rect(0 ,0 ,0 ,0); |
| keyboardBackground.getPadding(keyboardPadding); |
| int keyboardTotalPadding = keyboardPadding.top + keyboardPadding.bottom; |
| if (mIsSymbolMode) { |
| return mKeyboardHeight + numberOfLine * getCandidateMinimumHeight() |
| - mSymbolKeyboardHeight - mSymbolKeyboardTabHeight; |
| } else if (!mHardKeyboardHidden) { |
| return mKeyboardHeight + numberOfLine * getCandidateMinimumHeight() |
| - mSymbolKeyboardHeight; |
| } else { |
| return mKeyboardHeight + keyboardTotalPadding |
| + numberOfLine * getCandidateMinimumHeight(); |
| } |
| } |
| } |
| |
| /** |
| * Update symbol type. |
| */ |
| private void updateSymbolType() { |
| switch (mSymbolMode) { |
| case OpenWnnJAJP.ENGINE_MODE_SYMBOL: |
| updateTabStatus(mViewTabSymbol, true, true); |
| updateTabStatus(mViewTabEmoticon, mEnableEmoticon, false); |
| break; |
| |
| case OpenWnnJAJP.ENGINE_MODE_SYMBOL_KAO_MOJI: |
| updateTabStatus(mViewTabSymbol, true, false); |
| updateTabStatus(mViewTabEmoticon, mEnableEmoticon, true); |
| break; |
| |
| default: |
| updateTabStatus(mViewTabSymbol, true, false); |
| updateTabStatus(mViewTabEmoticon, mEnableEmoticon, false); |
| break; |
| } |
| } |
| |
| /** |
| * Update tab status. |
| * |
| * @param tab The tab view. |
| * @param enabled The tab is enabled. |
| * @param selected The tab is selected. |
| */ |
| private void updateTabStatus(TextView tab, boolean enabled, boolean selected) { |
| tab.setVisibility(View.VISIBLE); |
| tab.setEnabled(enabled); |
| int backgroundId = 0; |
| int colorId = 0; |
| if (enabled) { |
| if (selected) { |
| backgroundId = R.drawable.cand_tab; |
| colorId = R.color.tab_textcolor_select; |
| } else { |
| backgroundId = R.drawable.cand_tab_noselect; |
| colorId = R.color.tab_textcolor_no_select; |
| } |
| } else { |
| backgroundId = R.drawable.cand_tab_noselect; |
| colorId = R.color.tab_textcolor_disable; |
| } |
| tab.setBackgroundResource(backgroundId); |
| tab.setTextColor(mWnn.getResources().getColor(colorId)); |
| } |
| |
| /** |
| * Get candidate number of division. |
| * @return Number of division |
| */ |
| private int getCandidateViewDivison() { |
| int viewDivison; |
| |
| if (mIsSymbolMode) { |
| int mode = mSymbolMode; |
| switch (mode) { |
| case OpenWnnJAJP.ENGINE_MODE_SYMBOL: |
| viewDivison = (mPortrait) ? FULL_VIEW_SYMBOL_DIV_PORT : FULL_VIEW_SYMBOL_DIV_LAND; |
| break; |
| case OpenWnnJAJP.ENGINE_MODE_SYMBOL_KAO_MOJI: |
| default: |
| viewDivison = FULL_VIEW_DIV; |
| break; |
| } |
| } else { |
| viewDivison = FULL_VIEW_DIV; |
| } |
| return viewDivison; |
| } |
| |
| /** |
| * @return Word count |
| */ |
| private int getWordCount() { |
| return mWordCount1st + mWordCount2nd; |
| } |
| |
| /** |
| * @return Add second |
| */ |
| private boolean isFirstListOver(boolean isFullView, int lineCount, WnnWord word) { |
| |
| if (mIsSymbolMode) { |
| switch (mSymbolMode) { |
| case OpenWnnJAJP.ENGINE_MODE_SYMBOL_KAO_MOJI: |
| return true; |
| case OpenWnnJAJP.ENGINE_MODE_SYMBOL: |
| return true; |
| default: |
| return (isFullView || getMaxLine() < lineCount); |
| } |
| } else { |
| return (isFullView || getMaxLine() < lineCount); |
| } |
| } |
| |
| /** |
| * @return Candidate space width |
| */ |
| private int getCandidateSpaceWidth(boolean isEmojiSymbol) { |
| Resources r = mWnn.getResources(); |
| if (mPortrait) { |
| if (isEmojiSymbol) { |
| return 0; |
| } else { |
| return r.getDimensionPixelSize(R.dimen.cand_space_width); |
| } |
| } else { |
| if (isEmojiSymbol) { |
| return r.getDimensionPixelSize(R.dimen.cand_space_width_emoji_symbol); |
| } else { |
| return r.getDimensionPixelSize(R.dimen.cand_space_width); |
| } |
| } |
| } |
| |
| /** |
| * 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_UP: |
| moveFocus(-1, true); |
| break; |
| |
| case KeyEvent.KEYCODE_DPAD_DOWN: |
| moveFocus(1, true); |
| break; |
| |
| case KeyEvent.KEYCODE_DPAD_LEFT: |
| moveFocus(-1, false); |
| break; |
| |
| case KeyEvent.KEYCODE_DPAD_RIGHT: |
| moveFocus(1, false); |
| 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. |
| */ |
| public void setViewStatusOfFocusedCandidate(){ |
| View view = mFocusedView; |
| if (view != null) { |
| view.setBackgroundDrawable(mFocusedViewBackground); |
| view.setPadding(0, 0, 0, 0); |
| } |
| |
| TextView v = getFocusedView(); |
| mFocusedView = v; |
| if (v != null) { |
| mFocusedViewBackground = v.getBackground(); |
| v.setBackgroundResource(R.drawable.cand_back_focuse); |
| v.setPadding(0, 0, 0, 0); |
| |
| int viewBodyTop = getViewTopOnScreen(mViewBodyScroll); |
| int viewBodyBottom = viewBodyTop + mViewBodyScroll.getHeight(); |
| int focusedViewTop = getViewTopOnScreen(v); |
| int focusedViewBottom = focusedViewTop + v.getHeight(); |
| |
| if (focusedViewBottom > viewBodyBottom) { |
| mViewBodyScroll.scrollBy(0, (focusedViewBottom - viewBodyBottom)); |
| } else if (focusedViewTop < viewBodyTop) { |
| mViewBodyScroll.scrollBy(0, (focusedViewTop - viewBodyTop)); |
| } |
| } |
| } |
| |
| /** |
| * Clear focus to selected candidate. |
| */ |
| public void clearFocusCandidate(){ |
| View view = mFocusedView; |
| if (view != null) { |
| view.setBackgroundDrawable(mFocusedViewBackground); |
| view.setPadding(0, 0, 0, 0); |
| mFocusedView = null; |
| } |
| |
| mFocusAxisX = 0; |
| mHasFocusedArray1st = true; |
| mCurrentFocusIndex = FOCUS_NONE; |
| mHandler.removeMessages(MSG_MOVE_FOCUS); |
| mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.FOCUS_CANDIDATE_END)); |
| } |
| |
| /** |
| * @see CandidatesViewManager#selectFocusCandidate |
| */ |
| public void selectFocusCandidate(){ |
| if (mCurrentFocusIndex != FOCUS_NONE) { |
| WnnWord word = getFocusedWnnWord(); |
| |
| if (mHandler.hasMessages(MSG_SET_CANDIDATES)) { |
| mWnnWordSelectedList.add(word); |
| } else { |
| selectCandidate(word); |
| } |
| } |
| } |
| |
| /** @see CandidatesViewManager#getFocusedWnnWord */ |
| public WnnWord getFocusedWnnWord() { |
| return getWnnWord(mCurrentFocusIndex); |
| } |
| |
| /** |
| * Get WnnWord. |
| * |
| * @return WnnWord word |
| */ |
| public WnnWord getWnnWord(int index) { |
| WnnWord word = null; |
| if (index < 0) { |
| index = 0; |
| mHandler.removeMessages(MSG_MOVE_FOCUS); |
| Log.i("iwnn", "TextCandidatesViewManager::getWnnWord index < 0 "); |
| } else { |
| int size = mHasFocusedArray1st ? mWnnWordArray1st.size() : mWnnWordArray2nd.size(); |
| if (index >= size) { |
| index = size - 1; |
| mHandler.removeMessages(MSG_MOVE_FOCUS); |
| Log.i("iwnn", "TextCandidatesViewManager::getWnnWord index > candidate max "); |
| } |
| } |
| |
| if (mHasFocusedArray1st) { |
| word = mWnnWordArray1st.get(index); |
| } else { |
| word = mWnnWordArray2nd.get(index); |
| } |
| return word; |
| } |
| |
| /** |
| * Set display candidate line from SharedPreferences. |
| */ |
| private void setNumeberOfDisplayLines(){ |
| SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(mWnn); |
| mPortraitNumberOfLine = Integer.parseInt(pref.getString("setting_portrait", "2")); |
| mLandscapeNumberOfLine = Integer.parseInt(pref.getString("setting_landscape", "1")); |
| } |
| |
| /** |
| * Set emoticon enabled. |
| */ |
| public void setEnableEmoticon(boolean enableEmoticon) { |
| mEnableEmoticon = enableEmoticon; |
| } |
| |
| /** |
| * Get View of focus candidate. |
| */ |
| public TextView getFocusedView() { |
| if (mCurrentFocusIndex == FOCUS_NONE) { |
| return null; |
| } |
| TextView t; |
| if (mHasFocusedArray1st) { |
| t = mTextViewArray1st.get(mCurrentFocusIndex); |
| } else { |
| t = mTextViewArray2nd.get(mCurrentFocusIndex); |
| } |
| return t; |
| } |
| |
| /** |
| * Move the focus to next candidate. |
| * |
| * @param direction The direction of increment or decrement. |
| * @param updown {@code true} if move is up or down. |
| */ |
| public void moveFocus(int direction, boolean updown) { |
| boolean isStart = (mCurrentFocusIndex == FOCUS_NONE); |
| if (direction == 0) { |
| setViewStatusOfFocusedCandidate(); |
| } |
| |
| int size1st = mTextViewArray1st.size(); |
| if (mHasFocusedArray1st && (size1st == 0)) { |
| mHasFocusedArray1st = false; |
| } |
| ArrayList<TextView> list = mHasFocusedArray1st ? mTextViewArray1st : mTextViewArray2nd; |
| int size = list.size(); |
| int start = (mCurrentFocusIndex == FOCUS_NONE) ? 0 : (mCurrentFocusIndex + direction); |
| |
| int index = -1; |
| boolean hasChangedLine = false; |
| for (int i = start; (0 <= i) && (i < size); i += direction) { |
| TextView view = list.get(i); |
| if (!view.isShown()) { |
| break; |
| } |
| |
| if (mIsSymbolMode && (view.getBackground() == null)) { |
| continue; |
| } |
| |
| if (updown) { |
| int left = view.getLeft(); |
| if ((left <= mFocusAxisX) |
| && (mFocusAxisX < view.getRight())) { |
| index = i; |
| break; |
| } |
| |
| if (left == 0) { |
| hasChangedLine = true; |
| } |
| } else { |
| index = i; |
| break; |
| } |
| } |
| |
| if ((index < 0) && hasChangedLine && !mHasFocusedArray1st && (0 < direction)) { |
| index = mTextViewArray2nd.size() - 1; |
| } |
| |
| if (0 <= index) { |
| mCurrentFocusIndex = index; |
| setViewStatusOfFocusedCandidate(); |
| if (!updown) { |
| mFocusAxisX = getFocusedView().getLeft(); |
| } |
| } else { |
| if (mCanReadMore && (0 < size1st)) { |
| |
| if ((mHasFocusedArray1st && (direction < 0)) |
| || (!mHasFocusedArray1st && (0 < direction))) { |
| updown = false; |
| } |
| |
| mHasFocusedArray1st = !mHasFocusedArray1st; |
| |
| if (!mHasFocusedArray1st && !mIsFullView) { |
| setFullMode(); |
| } |
| } |
| |
| if (size1st == 0) { |
| updown = false; |
| } |
| |
| if (0 < direction) { |
| mCurrentFocusIndex = -1; |
| } else { |
| mCurrentFocusIndex = (mHasFocusedArray1st ? size1st : mTextViewArray2nd.size()); |
| } |
| Message m = mHandler.obtainMessage(MSG_MOVE_FOCUS, direction, updown ? 1 : 0); |
| mHandler.sendMessage(m); |
| } |
| |
| if (isStart) { |
| mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.FOCUS_CANDIDATE_START)); |
| } |
| } |
| |
| /** |
| * Set hardkeyboard hidden. |
| * |
| * @param hardKeyboardHidden hardkeyaboard hidden. |
| */ |
| public void setHardKeyboardHidden(boolean hardKeyboardHidden) { |
| mHardKeyboardHidden = hardKeyboardHidden; |
| } |
| |
| /** |
| * Get view top position on screen. |
| * |
| * @param view target view. |
| * @return int view top position on screen |
| */ |
| public int getViewTopOnScreen(View view) { |
| int[] location = new int[2]; |
| view.getLocationOnScreen(location); |
| return location[1]; |
| } |
| |
| |
| /** @see CandidatesViewManager#setCandidateMsgRemove */ |
| public void setCandidateMsgRemove() { |
| mHandler.removeMessages(MSG_SET_CANDIDATES); |
| } |
| } |