| /* |
| * Copyright (C) 2008,2009 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.util.ArrayList; |
| import android.content.Context; |
| import android.content.SharedPreferences; |
| import android.content.res.Configuration; |
| import android.content.res.Resources; |
| import android.media.MediaPlayer; |
| import android.os.Vibrator; |
| 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.view.Gravity; |
| 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.View.OnTouchListener; |
| 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.RelativeLayout; |
| import android.widget.ImageView; |
| import android.graphics.drawable.Drawable; |
| |
| /** |
| * The default candidates view manager class using {@link EditText}. |
| * |
| * @author Copyright (C) 2009 OMRON SOFTWARE CO., LTD. All Rights Reserved. |
| */ |
| public class TextCandidatesViewManager implements CandidatesViewManager, GestureDetector.OnGestureListener { |
| /** Height of a line */ |
| public static final int LINE_HEIGHT = 34; |
| /** 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; |
| /** Width of the view */ |
| private static final int CANDIDATE_MINIMUM_WIDTH = 48; |
| /** Height of the view */ |
| private static final int CANDIDATE_MINIMUM_HEIGHT = 35; |
| /** Align the candidate left if the width of the string exceeds this threshold */ |
| private static final int CANDIDATE_LEFT_ALIGN_THRESHOLD = 120; |
| /** Maximum number of displaying candidates par one line (full view mode) */ |
| private static final int FULL_VIEW_DIV = 4; |
| |
| /** Body view of the candidates list */ |
| private ViewGroup mViewBody; |
| /** 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; |
| /** The view of the scaling up candidate */ |
| private View mViewScaleUp; |
| /** Layout for the candidates list on normal view */ |
| private LinearLayout mViewCandidateList1st; |
| /** Layout for the candidates list on full view */ |
| private RelativeLayout mViewCandidateList2nd; |
| /** {@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; |
| /** Height of the view */ |
| private int mViewHeight; |
| /** Whether hide the view if there is no candidates */ |
| private boolean mAutoHideMode; |
| /** The converter to be 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; |
| /** MediaPlayer for click sound */ |
| private MediaPlayer mSound = null; |
| |
| /** Number of candidates displaying */ |
| private int mWordCount; |
| /** List of candidates */ |
| private ArrayList<WnnWord> mWnnWordArray; |
| |
| /** Gesture detector */ |
| private GestureDetector mGestureDetector; |
| /** The word pressed */ |
| private WnnWord mWord; |
| /** Character width of the candidate area */ |
| private int mLineLength = 0; |
| /** Number of lines displayed */ |
| private int mLineCount = 1; |
| |
| /** {@code true} if the candidate delete state is selected */ |
| private boolean mIsScaleUp = false; |
| |
| /** {@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; |
| /** Width of {@code mReadMoreButton} */ |
| private int mReadMoreButtonWidth = 0; |
| /** 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 RelativeLayout.LayoutParams mFullViewPrevParams; |
| /** Whether all candidates is displayed */ |
| private boolean mCreateCandidateDone; |
| /** Number of lines in normal view */ |
| private int mNormalViewWordCountOfLine; |
| /** general infomation about a display */ |
| private final DisplayMetrics mMetrics = new DisplayMetrics(); |
| |
| /** Event listener for touching a candidate */ |
| private OnTouchListener mCandidateOnTouch = new OnTouchListener() { |
| public boolean onTouch(View v, MotionEvent event) { |
| if (mMotionEvent != null) { |
| return true; |
| } |
| |
| if ((event.getAction() == MotionEvent.ACTION_UP) |
| && (v instanceof TextView)) { |
| Drawable d = v.getBackground(); |
| if (d != null) { |
| d.setState(new int[] {}); |
| } |
| } |
| |
| mMotionEvent = event; |
| boolean ret = mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.CANDIDATE_VIEW_TOUCH)); |
| mMotionEvent = null; |
| return ret; |
| } |
| }; |
| |
| |
| /** Event listener for clicking a candidate */ |
| private OnClickListener mCandidateOnClick = new OnClickListener() { |
| public void onClick(View v) { |
| if (!v.isShown()) { |
| return; |
| } |
| |
| if (v instanceof TextView) { |
| TextView text = (TextView)v; |
| int wordcount = text.getId(); |
| WnnWord word = null; |
| word = mWnnWordArray.get(wordcount); |
| selectCandidate(word); |
| } |
| } |
| }; |
| |
| /** Event listener for long-clicking a candidate */ |
| private OnLongClickListener mCandidateOnLongClick = new OnLongClickListener() { |
| public boolean onLongClick(View v) { |
| if (mViewScaleUp == null) { |
| 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 = mWnnWordArray.get(wordcount); |
| setViewScaleUp(true, mWord); |
| |
| return true; |
| } |
| }; |
| |
| |
| /** |
| * Constructor |
| */ |
| public TextCandidatesViewManager() { |
| this(-1); |
| } |
| |
| /** |
| * Constructor |
| * |
| * @param displayLimit The limit of display |
| */ |
| public TextCandidatesViewManager(int displayLimit) { |
| this.mDisplayLimit = displayLimit; |
| this.mWnnWordArray = new ArrayList<WnnWord>(); |
| this.mAutoHideMode = true; |
| mMetrics.setToDefaults(); |
| } |
| |
| /** |
| * Set auto-hide mode. |
| * @param hide {@code true} if the view will hidden when no candidate exists; |
| * {@code false} if the view is always shown. |
| */ |
| public void setAutoHide(boolean hide) { |
| mAutoHideMode = hide; |
| } |
| |
| /** @see CandidatesViewManager */ |
| public View initView(OpenWnn parent, int width, int height) { |
| mWnn = parent; |
| mViewWidth = width; |
| mViewHeight = height; |
| mPortrait = |
| (parent.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE); |
| |
| Resources r = mWnn.getResources(); |
| |
| LayoutInflater inflater = parent.getLayoutInflater(); |
| mViewBody = (ViewGroup)inflater.inflate(R.layout.candidates, null); |
| |
| mViewBodyScroll = (ScrollView)mViewBody.findViewById(R.id.candview_scroll); |
| mViewBodyScroll.setOnTouchListener(mCandidateOnTouch); |
| |
| mViewCandidateBase = (ViewGroup)mViewBody.findViewById(R.id.candview_base); |
| |
| createNormalCandidateView(); |
| mViewCandidateList2nd = (RelativeLayout)mViewBody.findViewById(R.id.candidates_2nd_view); |
| |
| mReadMoreButtonWidth = r.getDrawable(R.drawable.cand_up).getMinimumWidth(); |
| |
| mTextColor = r.getColor(R.color.candidate_text); |
| |
| mReadMoreButton = (ImageView)mViewBody.findViewById(R.id.read_more_text); |
| mReadMoreButton.setOnTouchListener(new View.OnTouchListener() { |
| public boolean onTouch(View v, MotionEvent event) { |
| switch (event.getAction()) { |
| case MotionEvent.ACTION_DOWN: |
| if (mIsFullView) { |
| mReadMoreButton.setImageResource(R.drawable.cand_down_press); |
| } else { |
| mReadMoreButton.setImageResource(R.drawable.cand_up_press); |
| } |
| break; |
| case MotionEvent.ACTION_UP: |
| if (mIsFullView) { |
| mReadMoreButton.setImageResource(R.drawable.cand_down); |
| } else { |
| mReadMoreButton.setImageResource(R.drawable.cand_up); |
| } |
| break; |
| default: |
| break; |
| } |
| return false; |
| } |
| }); |
| mReadMoreButton.setOnClickListener(new View.OnClickListener() { |
| public void onClick(View v) { |
| if (!v.isShown()) { |
| return; |
| } |
| |
| 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); |
| |
| View scaleUp = (View)inflater.inflate(R.layout.candidate_scale_up, null); |
| mViewScaleUp = scaleUp; |
| |
| /* select button */ |
| Button b = (Button)scaleUp.findViewById(R.id.candidate_select); |
| b.setOnClickListener(new View.OnClickListener() { |
| public void onClick(View v) { |
| selectCandidate(mWord); |
| } |
| }); |
| |
| /* cancel button */ |
| b = (Button)scaleUp.findViewById(R.id.candidate_cancel); |
| b.setOnClickListener(new View.OnClickListener() { |
| public void onClick(View v) { |
| setViewLayout(CandidatesViewManager.VIEW_TYPE_NORMAL); |
| mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.UPDATE_CANDIDATE)); |
| } |
| }); |
| |
| return mViewBody; |
| } |
| |
| /** |
| * Create the normal candidate view |
| */ |
| private void createNormalCandidateView() { |
| mViewCandidateList1st = (LinearLayout)mViewBody.findViewById(R.id.candidates_1st_view); |
| mViewCandidateList1st.setOnTouchListener(mCandidateOnTouch); |
| mViewCandidateList1st.setOnClickListener(mCandidateOnClick); |
| |
| int line = getMaxLine(); |
| 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.FILL_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) { |
| mViewType = type; |
| setViewScaleUp(false, null); |
| |
| switch (type) { |
| case CandidatesViewManager.VIEW_TYPE_CLOSE: |
| mViewCandidateBase.setMinimumHeight(-1); |
| return false; |
| |
| case CandidatesViewManager.VIEW_TYPE_NORMAL: |
| mViewBodyScroll.scrollTo(0, 0); |
| mViewCandidateList1st.setVisibility(View.VISIBLE); |
| mViewCandidateList2nd.setVisibility(View.GONE); |
| mViewCandidateBase.setMinimumHeight(-1); |
| int line = (mPortrait) ? LINE_NUM_PORTRAIT : LINE_NUM_LANDSCAPE; |
| mViewCandidateList1st.setMinimumHeight(getCandidateMinimumHeight() * line); |
| return false; |
| |
| case CandidatesViewManager.VIEW_TYPE_FULL: |
| default: |
| mViewCandidateList2nd.setVisibility(View.VISIBLE); |
| mViewCandidateBase.setMinimumHeight(mViewHeight); |
| return true; |
| } |
| } |
| |
| /** @see CandidatesViewManager#getViewType */ |
| public int getViewType() { |
| return mViewType; |
| } |
| |
| /** @see CandidatesViewManager#displayCandidates */ |
| public void displayCandidates(WnnEngine converter) { |
| |
| mCanReadMore = false; |
| mDisplayEndOffset = 0; |
| mIsFullView = false; |
| mFullViewWordCount = 0; |
| mFullViewOccupyCount = 0; |
| mFullViewPrevLineTopId = 0; |
| mCreateCandidateDone = false; |
| mNormalViewWordCountOfLine = 0; |
| |
| clearCandidates(); |
| mConverter = converter; |
| setViewLayout(CandidatesViewManager.VIEW_TYPE_NORMAL); |
| |
| mViewCandidateTemplate.setVisibility(View.VISIBLE); |
| mViewCandidateTemplate.setBackgroundResource(R.drawable.cand_back); |
| |
| displayCandidates(converter, true, getMaxLine()); |
| } |
| |
| /** @see CandidatesViewManager#getMaxLine */ |
| private int getMaxLine() { |
| int maxLine = (mPortrait) ? LINE_NUM_PORTRAIT : LINE_NUM_LANDSCAPE; |
| return maxLine; |
| } |
| |
| /** |
| * 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 |
| */ |
| synchronized 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 isHistorySequence = false; |
| boolean isBreak = false; |
| |
| /* Get candidates */ |
| WnnWord result = null; |
| while ((displayLimit == -1 || mWordCount < displayLimit)) { |
| result = converter.getNextCandidate(); |
| |
| if (result == null) { |
| break; |
| } |
| |
| setCandidate(false, result); |
| |
| if (dispFirst && (maxLine < mLineCount)) { |
| mCanReadMore = true; |
| isBreak = true; |
| break; |
| } |
| } |
| |
| if (!isBreak && !mCreateCandidateDone) { |
| /* align left if necessary */ |
| createNextLine(); |
| mCreateCandidateDone = true; |
| } |
| |
| if (mWordCount < 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; |
| |
| TextView textView; |
| if (mIsFullView || getMaxLine() < mLineCount) { |
| /* Full view */ |
| int indentWidth = mViewWidth / FULL_VIEW_DIV; |
| int occupyCount = Math.min((textLength + indentWidth) / indentWidth, FULL_VIEW_DIV); |
| if (isCategory) { |
| occupyCount = FULL_VIEW_DIV; |
| } |
| |
| if (FULL_VIEW_DIV < (mFullViewOccupyCount + occupyCount)) { |
| if (FULL_VIEW_DIV != mFullViewOccupyCount) { |
| mFullViewPrevParams.width += (FULL_VIEW_DIV - mFullViewOccupyCount) * indentWidth; |
| mViewCandidateList2nd.updateViewLayout(mFullViewPrevView, mFullViewPrevParams); |
| } |
| mFullViewOccupyCount = 0; |
| mFullViewPrevLineTopId = mFullViewPrevView.getId(); |
| mLineCount++; |
| } |
| |
| RelativeLayout layout = mViewCandidateList2nd; |
| |
| int width = indentWidth * occupyCount; |
| int height = getCandidateMinimumHeight(); |
| RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(width, height); |
| |
| if (mFullViewPrevLineTopId == 0) { |
| params.addRule(RelativeLayout.ALIGN_PARENT_TOP); |
| } else { |
| params.addRule(RelativeLayout.BELOW, mFullViewPrevLineTopId); |
| } |
| |
| if (mFullViewOccupyCount == 0) { |
| params.addRule(RelativeLayout.ALIGN_PARENT_LEFT); |
| } else { |
| params.addRule(RelativeLayout.RIGHT_OF, (mWordCount - 1)); |
| } |
| |
| 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 { |
| textLength = Math.max(textLength, getCandidateMinimumWidth()); |
| |
| /* Normal view */ |
| int nextEnd = mLineLength + textLength; |
| if (mLineCount == 1) { |
| maxWidth -= getCandidateMinimumWidth(); |
| } |
| |
| if ((maxWidth < nextEnd) && (mWordCount != 0)) { |
| createNextLine(); |
| if (getMaxLine() < mLineCount) { |
| mLineLength = 0; |
| /* Call this method again to add the candidate in the full view */ |
| setCandidate(isCategory, word); |
| return; |
| } |
| |
| mLineLength = textLength; |
| } else { |
| mLineLength = nextEnd; |
| } |
| |
| LinearLayout lineView = (LinearLayout) mViewCandidateList1st.getChildAt(mLineCount - 1); |
| textView = (TextView) lineView.getChildAt(mNormalViewWordCountOfLine); |
| |
| if (isCategory) { |
| if (mLineCount == 1) { |
| mViewCandidateTemplate.setBackgroundDrawable(null); |
| } |
| mLineLength += CANDIDATE_LEFT_ALIGN_THRESHOLD; |
| } |
| |
| mNormalViewWordCountOfLine++; |
| } |
| |
| textView.setText(word.candidate); |
| textView.setTextColor(mTextColor); |
| textView.setId(mWordCount); |
| textView.setVisibility(View.VISIBLE); |
| textView.setPressed(false); |
| |
| if (isCategory) { |
| textView.setOnClickListener(null); |
| textView.setOnLongClickListener(null); |
| textView.setBackgroundDrawable(null); |
| } else { |
| textView.setOnClickListener(mCandidateOnClick); |
| textView.setOnLongClickListener(mCandidateOnLongClick); |
| textView.setBackgroundResource(R.drawable.cand_back); |
| } |
| textView.setOnTouchListener(mCandidateOnTouch); |
| |
| 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); |
| } |
| |
| mWnnWordArray.add(mWordCount, word); |
| mWordCount++; |
| } |
| |
| /** |
| * Create a view for a candidate. |
| * @return the view |
| */ |
| private TextView createCandidateView() { |
| TextView text = new TextView(mViewBodyScroll.getContext()); |
| text.setTextSize(20); |
| text.setBackgroundResource(R.drawable.cand_back); |
| text.setGravity(Gravity.CENTER); |
| text.setSingleLine(); |
| text.setPadding(4, 4, 4, 4); |
| text.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, |
| ViewGroup.LayoutParams.WRAP_CONTENT, |
| 1.0f)); |
| text.setMinHeight(getCandidateMinimumHeight()); |
| text.setMinimumWidth(getCandidateMinimumWidth()); |
| return text; |
| } |
| |
| /** |
| * Display {@code mReadMoreText} if there are more candidates. |
| */ |
| private void setReadMore() { |
| if (mIsScaleUp) { |
| mReadMoreButton.setVisibility(View.GONE); |
| mViewCandidateTemplate.setVisibility(View.GONE); |
| return; |
| } |
| |
| if (mIsFullView) { |
| mReadMoreButton.setVisibility(View.VISIBLE); |
| mReadMoreButton.setImageResource(R.drawable.cand_down); |
| } else { |
| if (mCanReadMore) { |
| mReadMoreButton.setVisibility(View.VISIBLE); |
| mReadMoreButton.setImageResource(R.drawable.cand_up); |
| } else { |
| mReadMoreButton.setVisibility(View.GONE); |
| mViewCandidateTemplate.setVisibility(View.GONE); |
| } |
| } |
| } |
| |
| /** |
| * 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() { |
| clearNormalViewCandidate(); |
| |
| RelativeLayout layout = mViewCandidateList2nd; |
| int size = layout.getChildCount(); |
| for (int i = 0; i < size; i++) { |
| View v = layout.getChildAt(i); |
| v.setVisibility(View.GONE); |
| } |
| |
| mLineCount = 1; |
| mWordCount = 0; |
| mWnnWordArray.clear(); |
| |
| mLineLength = 0; |
| |
| mIsFullView = false; |
| setViewLayout(CandidatesViewManager.VIEW_TYPE_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 = MediaPlayer.create(mWnn, R.raw.type); |
| } else { |
| mSound = null; |
| } |
| } catch (Exception ex) { |
| Log.d("iwnn", "NO VIBRATOR"); |
| } |
| } |
| |
| /** |
| * Process {@code 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) { |
| setViewLayout(CandidatesViewManager.VIEW_TYPE_NORMAL); |
| if (mVibrator != null) { |
| try { mVibrator.vibrate(30); } catch (Exception ex) { } |
| } |
| if (mSound != null) { |
| try { mSound.seekTo(0); mSound.start(); } catch (Exception ex) { } |
| } |
| mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.SELECT_CANDIDATE, word)); |
| } |
| |
| /** @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) { |
| if (mIsScaleUp) { |
| return false; |
| } |
| |
| 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(30); } catch (Exception ex) { } |
| } |
| mIsFullView = true; |
| mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.LIST_CANDIDATES_FULL)); |
| consumed = true; |
| } |
| } else { |
| if (mViewBodyScroll.getScrollY() == 0) { |
| if (mVibrator != null) { |
| try { mVibrator.vibrate(30); } catch (Exception ex) { } |
| } |
| 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) { |
| TextPaint paint = mViewCandidateTemplate.getPaint(); |
| return (int)paint.measureText(text, start, end); |
| } |
| |
| /** |
| * Switch list/enlarge view mode. |
| * @param up {@code true}:enlarge, {@code false}:list |
| * @param word The candidate word to be enlarged. |
| */ |
| private void setViewScaleUp(boolean up, WnnWord word) { |
| if (up == mIsScaleUp || (mViewScaleUp == null)) { |
| return; |
| } |
| |
| if (up) { |
| setViewLayout(CandidatesViewManager.VIEW_TYPE_NORMAL); |
| mViewCandidateList1st.setVisibility(View.GONE); |
| mViewCandidateBase.setMinimumHeight(-1); |
| mViewCandidateBase.addView(mViewScaleUp); |
| TextView text = (TextView)mViewScaleUp.findViewById(R.id.candidate_scale_up_text); |
| text.setText(word.candidate); |
| if (!mPortrait) { |
| Resources r = mViewBodyScroll.getContext().getResources(); |
| text.setTextSize(r.getDimensionPixelSize(R.dimen.candidate_delete_word_size_landscape)); |
| } |
| |
| mIsScaleUp = true; |
| setReadMore(); |
| } else { |
| mIsScaleUp = false; |
| mViewCandidateBase.removeView(mViewScaleUp); |
| } |
| } |
| |
| /** |
| * Create a layout for the next line. |
| */ |
| private void createNextLine() { |
| int lineCount = mLineCount; |
| if (mIsFullView || getMaxLine() < lineCount) { |
| /* Full view */ |
| mFullViewOccupyCount = 0; |
| mFullViewPrevLineTopId = mFullViewPrevView.getId(); |
| } else { |
| /* Normal view */ |
| LinearLayout lineView = (LinearLayout) mViewCandidateList1st.getChildAt(lineCount - 1); |
| float weight = 0; |
| if (mLineLength < CANDIDATE_LEFT_ALIGN_THRESHOLD) { |
| if (lineCount == 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); |
| } |
| } |
| |
| mLineLength = 0; |
| mNormalViewWordCountOfLine = 0; |
| } |
| mLineCount++; |
| } |
| |
| /** |
| * @return the minimum width of a candidate view. |
| */ |
| private int getCandidateMinimumWidth() { |
| return (int)(CANDIDATE_MINIMUM_WIDTH * mMetrics.density); |
| } |
| |
| /** |
| * @return the minimum height of a candidate view. |
| */ |
| private int getCandidateMinimumHeight() { |
| return (int)(CANDIDATE_MINIMUM_HEIGHT * mMetrics.density); |
| } |
| } |