1 /* 2 * Copyright (C) 2008,2009 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.util.ArrayList; 20 import android.content.Context; 21 import android.content.SharedPreferences; 22 import android.content.res.Configuration; 23 import android.content.res.Resources; 24 import android.media.MediaPlayer; 25 import android.os.Vibrator; 26 import android.text.TextUtils; 27 import android.text.TextPaint; 28 import android.text.SpannableString; 29 import android.text.Spanned; 30 import android.text.style.ImageSpan; 31 import android.text.style.DynamicDrawableSpan; 32 import android.util.Log; 33 import android.util.DisplayMetrics; 34 import android.view.Gravity; 35 import android.view.MotionEvent; 36 import android.view.View; 37 import android.view.ViewGroup; 38 import android.view.View.OnClickListener; 39 import android.view.View.OnLongClickListener; 40 import android.view.View.OnTouchListener; 41 import android.view.GestureDetector; 42 import android.view.LayoutInflater; 43 import android.widget.Button; 44 import android.widget.LinearLayout; 45 import android.widget.ScrollView; 46 import android.widget.TextView; 47 import android.widget.EditText; 48 import android.widget.AbsoluteLayout; 49 import android.widget.ImageView; 50 import android.graphics.drawable.Drawable; 51 52 /** 53 * The default candidates view manager class using {@link EditText}. 54 * 55 * @author Copyright (C) 2009 OMRON SOFTWARE CO., LTD. All Rights Reserved. 56 */ 57 public class TextCandidatesViewManager implements CandidatesViewManager, GestureDetector.OnGestureListener { 58 /** Height of a line */ 59 public static final int LINE_HEIGHT = 34; 60 /** Number of lines to display (Portrait) */ 61 public static final int LINE_NUM_PORTRAIT = 2; 62 /** Number of lines to display (Landscape) */ 63 public static final int LINE_NUM_LANDSCAPE = 1; 64 65 /** Maximum lines */ 66 private static final int DISPLAY_LINE_MAX_COUNT = 1000; 67 /** Width of the view */ 68 private static final int CANDIDATE_MINIMUM_WIDTH = 48; 69 /** Height of the view */ 70 private static final int CANDIDATE_MINIMUM_HEIGHT = 35; 71 /** Align the candidate left if the width of the string exceeds this threshold */ 72 private static final int CANDIDATE_LEFT_ALIGN_THRESHOLD = 120; 73 /** Maximum number of displaying candidates par one line (full view mode) */ 74 private static final int FULL_VIEW_DIV = 4; 75 76 /** Body view of the candidates list */ 77 private ViewGroup mViewBody; 78 /** Scroller of {@code mViewBodyText} */ 79 private ScrollView mViewBodyScroll; 80 /** Base of {@code mViewCandidateList1st}, {@code mViewCandidateList2nd} */ 81 private ViewGroup mViewCandidateBase; 82 /** Button displayed bottom of the view when there are more candidates. */ 83 private ImageView mReadMoreButton; 84 /** The view of the scaling up candidate */ 85 private View mViewScaleUp; 86 /** Layout for the candidates list on normal view */ 87 private LinearLayout mViewCandidateList1st; 88 /** Layout for the candidates list on full view */ 89 private AbsoluteLayout mViewCandidateList2nd; 90 /** {@link OpenWnn} instance using this manager */ 91 private OpenWnn mWnn; 92 /** View type (VIEW_TYPE_NORMAL or VIEW_TYPE_FULL or VIEW_TYPE_CLOSE) */ 93 private int mViewType; 94 /** Portrait display({@code true}) or landscape({@code false}) */ 95 private boolean mPortrait; 96 97 /** Width of the view */ 98 private int mViewWidth; 99 /** Height of the view */ 100 private int mViewHeight; 101 /** Minimum width of a candidate (density support) */ 102 private int mCandidateMinimumWidth; 103 /** Maximum width of a candidate (density support) */ 104 private int mCandidateMinimumHeight; 105 106 /** Whether hide the view if there is no candidates */ 107 private boolean mAutoHideMode; 108 /** The converter to be get candidates from and notice the selected candidate to. */ 109 private WnnEngine mConverter; 110 /** Limitation of displaying candidates */ 111 private int mDisplayLimit; 112 113 /** Vibrator for touch vibration */ 114 private Vibrator mVibrator = null; 115 /** MediaPlayer for click sound */ 116 private MediaPlayer mSound = null; 117 118 /** Number of candidates displaying */ 119 private int mWordCount; 120 /** List of candidates */ 121 private ArrayList<WnnWord> mWnnWordArray; 122 123 /** Gesture detector */ 124 private GestureDetector mGestureDetector; 125 /** The word pressed */ 126 private WnnWord mWord; 127 /** Character width of the candidate area */ 128 private int mLineLength = 0; 129 /** Number of lines displayed */ 130 private int mLineCount = 1; 131 132 /** {@code true} if the candidate delete state is selected */ 133 private boolean mIsScaleUp = false; 134 135 /** {@code true} if the full screen mode is selected */ 136 private boolean mIsFullView = false; 137 138 /** The event object for "touch" */ 139 private MotionEvent mMotionEvent = null; 140 141 /** The offset when the candidates is flowed out the candidate window */ 142 private int mDisplayEndOffset = 0; 143 /** {@code true} if there are more candidates to display. */ 144 private boolean mCanReadMore = false; 145 /** Width of {@code mReadMoreButton} */ 146 private int mReadMoreButtonWidth = 0; 147 /** Color of the candidates */ 148 private int mTextColor = 0; 149 /** Template object for each candidate and normal/full view change button */ 150 private TextView mViewCandidateTemplate; 151 /** Number of candidates in full view */ 152 private int mFullViewWordCount; 153 /** Number of candidates in the current line (in full view) */ 154 private int mFullViewOccupyCount; 155 /** View of the previous candidate (in full view) */ 156 private TextView mFullViewPrevView; 157 /** Id of the top line view (in full view) */ 158 private int mFullViewPrevLineTopId; 159 /** Layout of the previous candidate (in full view) */ 160 private ViewGroup.LayoutParams mFullViewPrevParams; 161 /** Whether all candidates is displayed */ 162 private boolean mCreateCandidateDone; 163 /** Number of lines in normal view */ 164 private int mNormalViewWordCountOfLine; 165 /** general infomation about a display */ 166 private final DisplayMetrics mMetrics = new DisplayMetrics(); 167 168 /** Event listener for touching a candidate */ 169 private OnTouchListener mCandidateOnTouch = new OnTouchListener() { 170 public boolean onTouch(View v, MotionEvent event) { 171 if (mMotionEvent != null) { 172 return true; 173 } 174 175 if ((event.getAction() == MotionEvent.ACTION_UP) 176 && (v instanceof TextView)) { 177 Drawable d = v.getBackground(); 178 if (d != null) { 179 d.setState(new int[] {}); 180 } 181 } 182 183 mMotionEvent = event; 184 boolean ret = mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.CANDIDATE_VIEW_TOUCH)); 185 mMotionEvent = null; 186 return ret; 187 } 188 }; 189 190 191 /** Event listener for clicking a candidate */ 192 private OnClickListener mCandidateOnClick = new OnClickListener() { 193 public void onClick(View v) { 194 if (!v.isShown()) { 195 return; 196 } 197 198 if (v instanceof TextView) { 199 TextView text = (TextView)v; 200 int wordcount = text.getId(); 201 WnnWord word = null; 202 word = mWnnWordArray.get(wordcount); 203 selectCandidate(word); 204 } 205 } 206 }; 207 208 /** Event listener for long-clicking a candidate */ 209 private OnLongClickListener mCandidateOnLongClick = new OnLongClickListener() { 210 public boolean onLongClick(View v) { 211 if (mViewScaleUp == null) { 212 return false; 213 } 214 215 if (!v.isShown()) { 216 return true; 217 } 218 219 Drawable d = v.getBackground(); 220 if (d != null) { 221 if(d.getState().length == 0){ 222 return true; 223 } 224 } 225 226 int wordcount = ((TextView)v).getId(); 227 mWord = mWnnWordArray.get(wordcount); 228 setViewScaleUp(true, mWord); 229 230 return true; 231 } 232 }; 233 234 235 /** 236 * Constructor 237 */ 238 public TextCandidatesViewManager() { 239 this(-1); 240 } 241 242 /** 243 * Constructor 244 * 245 * @param displayLimit The limit of display 246 */ 247 public TextCandidatesViewManager(int displayLimit) { 248 this.mDisplayLimit = displayLimit; 249 this.mWnnWordArray = new ArrayList<WnnWord>(); 250 this.mAutoHideMode = true; 251 mMetrics.setToDefaults(); 252 } 253 254 /** 255 * Set auto-hide mode. 256 * @param hide {@code true} if the view will hidden when no candidate exists; 257 * {@code false} if the view is always shown. 258 */ 259 public void setAutoHide(boolean hide) { 260 mAutoHideMode = hide; 261 } 262 263 /** @see CandidatesViewManager */ 264 public View initView(OpenWnn parent, int width, int height) { 265 mWnn = parent; 266 mViewWidth = width; 267 mViewHeight = height; 268 mCandidateMinimumWidth = (int)(CANDIDATE_MINIMUM_WIDTH * mMetrics.density); 269 mCandidateMinimumHeight = (int)(CANDIDATE_MINIMUM_HEIGHT * mMetrics.density); 270 mPortrait = 271 (parent.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE); 272 273 Resources r = mWnn.getResources(); 274 275 LayoutInflater inflater = parent.getLayoutInflater(); 276 mViewBody = (ViewGroup)inflater.inflate(R.layout.candidates, null); 277 278 mViewBodyScroll = (ScrollView)mViewBody.findViewById(R.id.candview_scroll); 279 mViewBodyScroll.setOnTouchListener(mCandidateOnTouch); 280 281 mViewCandidateBase = (ViewGroup)mViewBody.findViewById(R.id.candview_base); 282 283 createNormalCandidateView(); 284 mViewCandidateList2nd = (AbsoluteLayout)mViewBody.findViewById(R.id.candidates_2nd_view); 285 286 mReadMoreButtonWidth = r.getDrawable(R.drawable.cand_up).getMinimumWidth(); 287 288 mTextColor = r.getColor(R.color.candidate_text); 289 290 mReadMoreButton = (ImageView)mViewBody.findViewById(R.id.read_more_text); 291 mReadMoreButton.setOnTouchListener(new View.OnTouchListener() { 292 public boolean onTouch(View v, MotionEvent event) { 293 switch (event.getAction()) { 294 case MotionEvent.ACTION_DOWN: 295 if (mIsFullView) { 296 mReadMoreButton.setImageResource(R.drawable.cand_down_press); 297 } else { 298 mReadMoreButton.setImageResource(R.drawable.cand_up_press); 299 } 300 break; 301 case MotionEvent.ACTION_UP: 302 if (mIsFullView) { 303 mReadMoreButton.setImageResource(R.drawable.cand_down); 304 } else { 305 mReadMoreButton.setImageResource(R.drawable.cand_up); 306 } 307 break; 308 default: 309 break; 310 } 311 return false; 312 } 313 }); 314 mReadMoreButton.setOnClickListener(new View.OnClickListener() { 315 public void onClick(View v) { 316 if (!v.isShown()) { 317 return; 318 } 319 320 if (mIsFullView) { 321 mIsFullView = false; 322 mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.LIST_CANDIDATES_NORMAL)); 323 } else { 324 mIsFullView = true; 325 mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.LIST_CANDIDATES_FULL)); 326 } 327 } 328 }); 329 330 setViewType(CandidatesViewManager.VIEW_TYPE_CLOSE); 331 332 mGestureDetector = new GestureDetector(this); 333 334 View scaleUp = (View)inflater.inflate(R.layout.candidate_scale_up, null); 335 mViewScaleUp = scaleUp; 336 337 /* select button */ 338 Button b = (Button)scaleUp.findViewById(R.id.candidate_select); 339 b.setOnClickListener(new View.OnClickListener() { 340 public void onClick(View v) { 341 selectCandidate(mWord); 342 } 343 }); 344 345 /* cancel button */ 346 b = (Button)scaleUp.findViewById(R.id.candidate_cancel); 347 b.setOnClickListener(new View.OnClickListener() { 348 public void onClick(View v) { 349 setViewLayout(CandidatesViewManager.VIEW_TYPE_NORMAL); 350 mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.UPDATE_CANDIDATE)); 351 } 352 }); 353 354 return mViewBody; 355 } 356 357 /** 358 * Create the normal candidate view 359 */ 360 private void createNormalCandidateView() { 361 mViewCandidateList1st = (LinearLayout)mViewBody.findViewById(R.id.candidates_1st_view); 362 mViewCandidateList1st.setOnTouchListener(mCandidateOnTouch); 363 mViewCandidateList1st.setOnClickListener(mCandidateOnClick); 364 365 int line = getMaxLine(); 366 int width = mViewWidth; 367 for (int i = 0; i < line; i++) { 368 LinearLayout lineView = new LinearLayout(mViewBodyScroll.getContext()); 369 lineView.setOrientation(LinearLayout.HORIZONTAL); 370 LinearLayout.LayoutParams layoutParams = 371 new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 372 ViewGroup.LayoutParams.WRAP_CONTENT); 373 lineView.setLayoutParams(layoutParams); 374 for (int j = 0; j < (width / getCandidateMinimumWidth()); j++) { 375 TextView tv = createCandidateView(); 376 lineView.addView(tv); 377 } 378 379 if (i == 0) { 380 TextView tv = createCandidateView(); 381 layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 382 ViewGroup.LayoutParams.WRAP_CONTENT); 383 layoutParams.weight = 0; 384 layoutParams.gravity = Gravity.RIGHT; 385 tv.setLayoutParams(layoutParams); 386 387 lineView.addView(tv); 388 mViewCandidateTemplate = tv; 389 } 390 mViewCandidateList1st.addView(lineView); 391 } 392 } 393 394 /** @see CandidatesViewManager#getCurrentView */ 395 public View getCurrentView() { 396 return mViewBody; 397 } 398 399 /** @see CandidatesViewManager#setViewType */ 400 public void setViewType(int type) { 401 boolean readMore = setViewLayout(type); 402 403 if (readMore) { 404 displayCandidates(this.mConverter, false, -1); 405 } else { 406 if (type == CandidatesViewManager.VIEW_TYPE_NORMAL) { 407 mIsFullView = false; 408 if (mDisplayEndOffset > 0) { 409 int maxLine = getMaxLine(); 410 displayCandidates(this.mConverter, false, maxLine); 411 } else { 412 setReadMore(); 413 } 414 } else { 415 if (mViewBody.isShown()) { 416 mWnn.setCandidatesViewShown(false); 417 } 418 } 419 } 420 } 421 422 /** 423 * Set the view layout 424 * 425 * @param type View type 426 * @return {@code true} if display is updated; {@code false} if otherwise 427 */ 428 private boolean setViewLayout(int type) { 429 mViewType = type; 430 setViewScaleUp(false, null); 431 432 switch (type) { 433 case CandidatesViewManager.VIEW_TYPE_CLOSE: 434 mViewCandidateBase.setMinimumHeight(-1); 435 return false; 436 437 case CandidatesViewManager.VIEW_TYPE_NORMAL: 438 mViewBodyScroll.scrollTo(0, 0); 439 mViewCandidateList1st.setVisibility(View.VISIBLE); 440 mViewCandidateList2nd.setVisibility(View.GONE); 441 mViewCandidateBase.setMinimumHeight(-1); 442 int line = (mPortrait) ? LINE_NUM_PORTRAIT : LINE_NUM_LANDSCAPE; 443 mViewCandidateList1st.setMinimumHeight(getCandidateMinimumHeight() * line); 444 return false; 445 446 case CandidatesViewManager.VIEW_TYPE_FULL: 447 default: 448 mViewCandidateList2nd.setVisibility(View.VISIBLE); 449 mViewCandidateBase.setMinimumHeight(mViewHeight); 450 return true; 451 } 452 } 453 454 /** @see CandidatesViewManager#getViewType */ 455 public int getViewType() { 456 return mViewType; 457 } 458 459 /** @see CandidatesViewManager#displayCandidates */ 460 public void displayCandidates(WnnEngine converter) { 461 462 mCanReadMore = false; 463 mDisplayEndOffset = 0; 464 mIsFullView = false; 465 mFullViewWordCount = 0; 466 mFullViewOccupyCount = 0; 467 mFullViewPrevLineTopId = 0; 468 mCreateCandidateDone = false; 469 mNormalViewWordCountOfLine = 0; 470 471 clearCandidates(); 472 mConverter = converter; 473 setViewLayout(CandidatesViewManager.VIEW_TYPE_NORMAL); 474 475 mViewCandidateTemplate.setVisibility(View.VISIBLE); 476 mViewCandidateTemplate.setBackgroundResource(R.drawable.cand_back); 477 478 displayCandidates(converter, true, getMaxLine()); 479 } 480 481 /** @see CandidatesViewManager#getMaxLine */ 482 private int getMaxLine() { 483 int maxLine = (mPortrait) ? LINE_NUM_PORTRAIT : LINE_NUM_LANDSCAPE; 484 return maxLine; 485 } 486 487 /** 488 * Display the candidates. 489 * 490 * @param converter {@link WnnEngine} which holds candidates. 491 * @param dispFirst Whether it is the first time displaying the candidates 492 * @param maxLine The maximum number of displaying lines 493 */ 494 synchronized private void displayCandidates(WnnEngine converter, boolean dispFirst, int maxLine) { 495 if (converter == null) { 496 return; 497 } 498 499 /* Concatenate the candidates already got and the last one in dispFirst mode */ 500 int displayLimit = mDisplayLimit; 501 502 boolean isHistorySequence = false; 503 boolean isBreak = false; 504 505 /* Get candidates */ 506 WnnWord result = null; 507 while ((displayLimit == -1 || mWordCount < displayLimit)) { 508 result = converter.getNextCandidate(); 509 510 if (result == null) { 511 break; 512 } 513 514 setCandidate(false, result); 515 516 if (dispFirst && (maxLine < mLineCount)) { 517 mCanReadMore = true; 518 isBreak = true; 519 break; 520 } 521 } 522 523 if (!isBreak && !mCreateCandidateDone) { 524 /* align left if necessary */ 525 createNextLine(); 526 mCreateCandidateDone = true; 527 } 528 529 if (mWordCount < 1) { /* no candidates */ 530 if (mAutoHideMode) { 531 mWnn.setCandidatesViewShown(false); 532 return; 533 } else { 534 mCanReadMore = false; 535 mIsFullView = false; 536 setViewLayout(CandidatesViewManager.VIEW_TYPE_NORMAL); 537 } 538 } 539 540 setReadMore(); 541 542 if (!(mViewBody.isShown())) { 543 mWnn.setCandidatesViewShown(true); 544 } 545 return; 546 } 547 548 /** 549 * Add a candidate into the list. 550 * @param isCategory {@code true}:caption of category, {@code false}:normal word 551 * @param word A candidate word 552 */ 553 private void setCandidate(boolean isCategory, WnnWord word) { 554 int textLength = measureText(word.candidate, 0, word.candidate.length()); 555 TextView template = mViewCandidateTemplate; 556 textLength += template.getPaddingLeft() + template.getPaddingRight(); 557 int maxWidth = mViewWidth; 558 559 TextView textView; 560 if (mIsFullView || getMaxLine() < mLineCount) { 561 /* Full view */ 562 int indentWidth = mViewWidth / FULL_VIEW_DIV; 563 int occupyCount = Math.min((textLength + indentWidth) / indentWidth, FULL_VIEW_DIV); 564 if (isCategory) { 565 occupyCount = FULL_VIEW_DIV; 566 } 567 568 if (FULL_VIEW_DIV < (mFullViewOccupyCount + occupyCount)) { 569 if (FULL_VIEW_DIV != mFullViewOccupyCount) { 570 mFullViewPrevParams.width += (FULL_VIEW_DIV - mFullViewOccupyCount) * indentWidth; 571 mViewCandidateList2nd.updateViewLayout(mFullViewPrevView, mFullViewPrevParams); 572 } 573 mFullViewOccupyCount = 0; 574 mFullViewPrevLineTopId = mFullViewPrevView.getId(); 575 mLineCount++; 576 } 577 578 ViewGroup layout = mViewCandidateList2nd; 579 580 int width = indentWidth * occupyCount; 581 int height = getCandidateMinimumHeight(); 582 583 584 ViewGroup.LayoutParams params = buildLayoutParams(mViewCandidateList2nd, width, height); 585 586 textView = (TextView) layout.getChildAt(mFullViewWordCount); 587 if (textView == null) { 588 textView = createCandidateView(); 589 textView.setLayoutParams(params); 590 591 mViewCandidateList2nd.addView(textView); 592 } else { 593 mViewCandidateList2nd.updateViewLayout(textView, params); 594 } 595 596 mFullViewOccupyCount += occupyCount; 597 mFullViewWordCount++; 598 mFullViewPrevView = textView; 599 mFullViewPrevParams = params; 600 601 } else { 602 textLength = Math.max(textLength, getCandidateMinimumWidth()); 603 604 /* Normal view */ 605 int nextEnd = mLineLength + textLength; 606 if (mLineCount == 1) { 607 maxWidth -= getCandidateMinimumWidth(); 608 } 609 610 if ((maxWidth < nextEnd) && (mWordCount != 0)) { 611 createNextLine(); 612 if (getMaxLine() < mLineCount) { 613 mLineLength = 0; 614 /* Call this method again to add the candidate in the full view */ 615 setCandidate(isCategory, word); 616 return; 617 } 618 619 mLineLength = textLength; 620 } else { 621 mLineLength = nextEnd; 622 } 623 624 LinearLayout lineView = (LinearLayout) mViewCandidateList1st.getChildAt(mLineCount - 1); 625 textView = (TextView) lineView.getChildAt(mNormalViewWordCountOfLine); 626 627 if (isCategory) { 628 if (mLineCount == 1) { 629 mViewCandidateTemplate.setBackgroundDrawable(null); 630 } 631 mLineLength += CANDIDATE_LEFT_ALIGN_THRESHOLD; 632 } 633 634 mNormalViewWordCountOfLine++; 635 } 636 637 textView.setText(word.candidate); 638 textView.setTextColor(mTextColor); 639 textView.setId(mWordCount); 640 textView.setVisibility(View.VISIBLE); 641 textView.setPressed(false); 642 643 if (isCategory) { 644 textView.setOnClickListener(null); 645 textView.setOnLongClickListener(null); 646 textView.setBackgroundDrawable(null); 647 } else { 648 textView.setOnClickListener(mCandidateOnClick); 649 textView.setOnLongClickListener(mCandidateOnLongClick); 650 textView.setBackgroundResource(R.drawable.cand_back); 651 } 652 textView.setOnTouchListener(mCandidateOnTouch); 653 654 if (maxWidth < textLength) { 655 textView.setEllipsize(TextUtils.TruncateAt.END); 656 } else { 657 textView.setEllipsize(null); 658 } 659 660 ImageSpan span = null; 661 if (word.candidate.equals(" ")) { 662 span = new ImageSpan(mWnn, R.drawable.word_half_space, 663 DynamicDrawableSpan.ALIGN_BASELINE); 664 } else if (word.candidate.equals("\u3000" /* full-width space */)) { 665 span = new ImageSpan(mWnn, R.drawable.word_full_space, 666 DynamicDrawableSpan.ALIGN_BASELINE); 667 } 668 669 if (span != null) { 670 SpannableString spannable = new SpannableString(" "); 671 spannable.setSpan(span, 1, 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 672 textView.setText(spannable); 673 } 674 675 mWnnWordArray.add(mWordCount, word); 676 mWordCount++; 677 } 678 679 /** 680 * Create AbsoluteLayout.LayoutParams 681 * @param layout AbsoluteLayout 682 * @param width 683 * @param height 684 * @return ViewGroup.LayoutParams 685 */ 686 private ViewGroup.LayoutParams buildLayoutParams(AbsoluteLayout layout, int width, int height) { 687 688 int indentWidth = mViewWidth / FULL_VIEW_DIV; 689 int x = indentWidth * mFullViewOccupyCount; 690 int nomalLine = (mPortrait) ? LINE_NUM_PORTRAIT : LINE_NUM_LANDSCAPE; 691 int y = getCandidateMinimumHeight() * (mLineCount - nomalLine - 1); 692 ViewGroup.LayoutParams params 693 = new AbsoluteLayout.LayoutParams(width, height, x, y); 694 695 return params; 696 } 697 698 699 700 701 702 703 /** 704 * Create a view for a candidate. 705 * @return the view 706 */ 707 private TextView createCandidateView() { 708 TextView text = new TextView(mViewBodyScroll.getContext()); 709 text.setTextSize(20); 710 text.setBackgroundResource(R.drawable.cand_back); 711 text.setGravity(Gravity.CENTER); 712 text.setSingleLine(); 713 text.setPadding(4, 4, 4, 4); 714 text.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 715 ViewGroup.LayoutParams.WRAP_CONTENT, 716 1.0f)); 717 text.setMinHeight(getCandidateMinimumHeight()); 718 text.setMinimumWidth(getCandidateMinimumWidth()); 719 return text; 720 } 721 722 /** 723 * Display {@code mReadMoreText} if there are more candidates. 724 */ 725 private void setReadMore() { 726 if (mIsScaleUp) { 727 mReadMoreButton.setVisibility(View.GONE); 728 mViewCandidateTemplate.setVisibility(View.GONE); 729 return; 730 } 731 732 if (mIsFullView) { 733 mReadMoreButton.setVisibility(View.VISIBLE); 734 mReadMoreButton.setImageResource(R.drawable.cand_down); 735 } else { 736 if (mCanReadMore) { 737 mReadMoreButton.setVisibility(View.VISIBLE); 738 mReadMoreButton.setImageResource(R.drawable.cand_up); 739 } else { 740 mReadMoreButton.setVisibility(View.GONE); 741 mViewCandidateTemplate.setVisibility(View.GONE); 742 } 743 } 744 } 745 746 /** 747 * Clear the list of the normal candidate view. 748 */ 749 private void clearNormalViewCandidate() { 750 LinearLayout candidateList = mViewCandidateList1st; 751 int lineNum = candidateList.getChildCount(); 752 for (int i = 0; i < lineNum; i++) { 753 754 LinearLayout lineView = (LinearLayout)candidateList.getChildAt(i); 755 int size = lineView.getChildCount(); 756 for (int j = 0; j < size; j++) { 757 View v = lineView.getChildAt(j); 758 v.setVisibility(View.GONE); 759 } 760 } 761 } 762 763 /** @see CandidatesViewManager#clearCandidates */ 764 public void clearCandidates() { 765 clearNormalViewCandidate(); 766 767 ViewGroup layout = mViewCandidateList2nd; 768 int size = layout.getChildCount(); 769 for (int i = 0; i < size; i++) { 770 View v = layout.getChildAt(i); 771 v.setVisibility(View.GONE); 772 } 773 774 mLineCount = 1; 775 mWordCount = 0; 776 mWnnWordArray.clear(); 777 778 mLineLength = 0; 779 780 mIsFullView = false; 781 setViewLayout(CandidatesViewManager.VIEW_TYPE_NORMAL); 782 if (mAutoHideMode) { 783 setViewLayout(CandidatesViewManager.VIEW_TYPE_CLOSE); 784 } 785 786 if (mAutoHideMode && mViewBody.isShown()) { 787 mWnn.setCandidatesViewShown(false); 788 } 789 mCanReadMore = false; 790 setReadMore(); 791 } 792 793 /** @see CandidatesViewManager#setPreferences */ 794 public void setPreferences(SharedPreferences pref) { 795 try { 796 if (pref.getBoolean("key_vibration", false)) { 797 mVibrator = (Vibrator)mWnn.getSystemService(Context.VIBRATOR_SERVICE); 798 } else { 799 mVibrator = null; 800 } 801 if (pref.getBoolean("key_sound", false)) { 802 mSound = MediaPlayer.create(mWnn, R.raw.type); 803 } else { 804 mSound = null; 805 } 806 } catch (Exception ex) { 807 Log.d("iwnn", "NO VIBRATOR"); 808 } 809 } 810 811 /** 812 * Process {@code OpenWnnEvent.CANDIDATE_VIEW_TOUCH} event. 813 * 814 * @return {@code true} if event is processed; {@code false} if otherwise 815 */ 816 public boolean onTouchSync() { 817 return mGestureDetector.onTouchEvent(mMotionEvent); 818 } 819 820 /** 821 * Select a candidate. 822 * <br> 823 * This method notices the selected word to {@link OpenWnn}. 824 * 825 * @param word The selected word 826 */ 827 private void selectCandidate(WnnWord word) { 828 setViewLayout(CandidatesViewManager.VIEW_TYPE_NORMAL); 829 if (mVibrator != null) { 830 try { mVibrator.vibrate(30); } catch (Exception ex) { } 831 } 832 if (mSound != null) { 833 try { mSound.seekTo(0); mSound.start(); } catch (Exception ex) { } 834 } 835 mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.SELECT_CANDIDATE, word)); 836 } 837 838 /** @see android.view.GestureDetector.OnGestureListener#onDown */ 839 public boolean onDown(MotionEvent arg0) { 840 return false; 841 } 842 843 /** @see android.view.GestureDetector.OnGestureListener#onFling */ 844 public boolean onFling(MotionEvent arg0, MotionEvent arg1, float arg2, float arg3) { 845 if (mIsScaleUp) { 846 return false; 847 } 848 849 boolean consumed = false; 850 if (arg1 != null && arg0 != null && arg1.getY() < arg0.getY()) { 851 if ((mViewType == CandidatesViewManager.VIEW_TYPE_NORMAL) && mCanReadMore) { 852 if (mVibrator != null) { 853 try { mVibrator.vibrate(30); } catch (Exception ex) { } 854 } 855 mIsFullView = true; 856 mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.LIST_CANDIDATES_FULL)); 857 consumed = true; 858 } 859 } else { 860 if (mViewBodyScroll.getScrollY() == 0) { 861 if (mVibrator != null) { 862 try { mVibrator.vibrate(30); } catch (Exception ex) { } 863 } 864 mIsFullView = false; 865 mWnn.onEvent(new OpenWnnEvent(OpenWnnEvent.LIST_CANDIDATES_NORMAL)); 866 consumed = true; 867 } 868 } 869 870 return consumed; 871 } 872 873 /** @see android.view.GestureDetector.OnGestureListener#onLongPress */ 874 public void onLongPress(MotionEvent arg0) { 875 return; 876 } 877 878 /** @see android.view.GestureDetector.OnGestureListener#onScroll */ 879 public boolean onScroll(MotionEvent arg0, MotionEvent arg1, float arg2, float arg3) { 880 return false; 881 } 882 883 /** @see android.view.GestureDetector.OnGestureListener#onShowPress */ 884 public void onShowPress(MotionEvent arg0) { 885 } 886 887 /** @see android.view.GestureDetector.OnGestureListener#onSingleTapUp */ 888 public boolean onSingleTapUp(MotionEvent arg0) { 889 return false; 890 } 891 892 /** 893 * Retrieve the width of string to draw. 894 * 895 * @param text The string 896 * @param start The start position (specified by the number of character) 897 * @param end The end position (specified by the number of character) 898 * @return The width of string to draw 899 */ 900 public int measureText(CharSequence text, int start, int end) { 901 if (end - start < 3) { 902 return getCandidateMinimumWidth(); 903 } 904 905 TextPaint paint = mViewCandidateTemplate.getPaint(); 906 return (int)paint.measureText(text, start, end); 907 } 908 909 /** 910 * Switch list/enlarge view mode. 911 * @param up {@code true}:enlarge, {@code false}:list 912 * @param word The candidate word to be enlarged. 913 */ 914 private void setViewScaleUp(boolean up, WnnWord word) { 915 if (up == mIsScaleUp || (mViewScaleUp == null)) { 916 return; 917 } 918 919 if (up) { 920 setViewLayout(CandidatesViewManager.VIEW_TYPE_NORMAL); 921 mViewCandidateList1st.setVisibility(View.GONE); 922 mViewCandidateBase.setMinimumHeight(-1); 923 mViewCandidateBase.addView(mViewScaleUp); 924 TextView text = (TextView)mViewScaleUp.findViewById(R.id.candidate_scale_up_text); 925 text.setText(word.candidate); 926 if (!mPortrait) { 927 Resources r = mViewBodyScroll.getContext().getResources(); 928 text.setTextSize(r.getDimensionPixelSize(R.dimen.candidate_delete_word_size_landscape)); 929 } 930 931 mIsScaleUp = true; 932 setReadMore(); 933 } else { 934 mIsScaleUp = false; 935 mViewCandidateBase.removeView(mViewScaleUp); 936 } 937 } 938 939 /** 940 * Create a layout for the next line. 941 */ 942 private void createNextLine() { 943 int lineCount = mLineCount; 944 if (mIsFullView || getMaxLine() < lineCount) { 945 /* Full view */ 946 mFullViewOccupyCount = 0; 947 mFullViewPrevLineTopId = mFullViewPrevView.getId(); 948 } else { 949 /* Normal view */ 950 LinearLayout lineView = (LinearLayout) mViewCandidateList1st.getChildAt(lineCount - 1); 951 float weight = 0; 952 if (mLineLength < CANDIDATE_LEFT_ALIGN_THRESHOLD) { 953 if (lineCount == 1) { 954 mViewCandidateTemplate.setVisibility(View.GONE); 955 } 956 } else { 957 weight = 1.0f; 958 } 959 960 LinearLayout.LayoutParams params 961 = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 962 ViewGroup.LayoutParams.WRAP_CONTENT, 963 weight); 964 965 int child = lineView.getChildCount(); 966 for (int i = 0; i < child; i++) { 967 View view = lineView.getChildAt(i); 968 969 if (view != mViewCandidateTemplate) { 970 view.setLayoutParams(params); 971 } 972 } 973 974 mLineLength = 0; 975 mNormalViewWordCountOfLine = 0; 976 } 977 mLineCount++; 978 } 979 980 /** 981 * @return the minimum width of a candidate view. 982 */ 983 private int getCandidateMinimumWidth() { 984 return mCandidateMinimumWidth; 985 } 986 987 /** 988 * @return the minimum height of a candidate view. 989 */ 990 private int getCandidateMinimumHeight() { 991 return mCandidateMinimumHeight; 992 } 993 } 994