1 /* 2 * Copyright (C) 2009 The Android Open Source Project 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 com.android.internal.widget; 18 19 import java.io.InputStream; 20 import java.util.ArrayList; 21 22 import android.app.AlertDialog.Builder; 23 import android.content.Context; 24 import android.content.DialogInterface; 25 import android.graphics.Bitmap; 26 import android.graphics.BitmapFactory; 27 import android.graphics.Canvas; 28 import android.graphics.drawable.BitmapDrawable; 29 import android.graphics.drawable.Drawable; 30 import android.graphics.drawable.ShapeDrawable; 31 import android.graphics.drawable.shapes.RectShape; 32 import android.net.Uri; 33 import android.os.Bundle; 34 import android.text.Editable; 35 import android.text.Html; 36 import android.text.Layout; 37 import android.text.Spannable; 38 import android.text.Spanned; 39 import android.text.method.ArrowKeyMovementMethod; 40 import android.text.style.AbsoluteSizeSpan; 41 import android.text.style.AlignmentSpan; 42 import android.text.style.CharacterStyle; 43 import android.text.style.ForegroundColorSpan; 44 import android.text.style.ImageSpan; 45 import android.text.style.ParagraphStyle; 46 import android.text.style.QuoteSpan; 47 import android.util.AttributeSet; 48 import android.util.Log; 49 import android.view.KeyEvent; 50 import android.view.MotionEvent; 51 import android.view.View; 52 import android.view.inputmethod.InputMethodManager; 53 import android.widget.EditText; 54 import android.widget.TextView; 55 56 /** 57 * EditStyledText extends EditText for managing the flow and status to edit 58 * the styled text. This manages the states and flows of editing, supports 59 * inserting image, import/export HTML. 60 */ 61 public class EditStyledText extends EditText { 62 63 private static final String LOG_TAG = "EditStyledText"; 64 private static final boolean DBG = false; 65 66 /** 67 * The modes of editing actions. 68 */ 69 /** The mode that no editing action is done. */ 70 public static final int MODE_NOTHING = 0; 71 /** The mode of copy. */ 72 public static final int MODE_COPY = 1; 73 /** The mode of paste. */ 74 public static final int MODE_PASTE = 2; 75 /** The mode of changing size. */ 76 public static final int MODE_SIZE = 3; 77 /** The mode of changing color. */ 78 public static final int MODE_COLOR = 4; 79 /** The mode of selection. */ 80 public static final int MODE_SELECT = 5; 81 /** The mode of changing alignment. */ 82 public static final int MODE_ALIGN = 6; 83 /** The mode of changing cut. */ 84 public static final int MODE_CUT = 7; 85 86 /** 87 * The state of selection. 88 */ 89 /** The state that selection isn't started. */ 90 public static final int STATE_SELECT_OFF = 0; 91 /** The state that selection is started. */ 92 public static final int STATE_SELECT_ON = 1; 93 /** The state that selection is done, but not fixed. */ 94 public static final int STATE_SELECTED = 2; 95 /** The state that selection is done and not fixed. */ 96 public static final int STATE_SELECT_FIX = 3; 97 98 /** 99 * The help message strings. 100 */ 101 public static final int HINT_MSG_NULL = 0; 102 public static final int HINT_MSG_COPY_BUF_BLANK = 1; 103 public static final int HINT_MSG_SELECT_START = 2; 104 public static final int HINT_MSG_SELECT_END = 3; 105 public static final int HINT_MSG_PUSH_COMPETE = 4; 106 107 108 /** 109 * The help message strings. 110 */ 111 public static final int DEFAULT_BACKGROUND_COLOR = 0x00FFFFFF; 112 113 /** 114 * EditStyledTextInterface provides functions for notifying messages to 115 * calling class. 116 */ 117 public interface EditStyledTextNotifier { 118 public void notifyHintMsg(int msgId); 119 public void notifyStateChanged(int mode, int state); 120 } 121 122 private EditStyledTextNotifier mESTInterface; 123 124 /** 125 * EditStyledTextEditorManager manages the flow and status of each 126 * function for editing styled text. 127 */ 128 private EditorManager mManager; 129 private StyledTextConverter mConverter; 130 private StyledTextDialog mDialog; 131 private Drawable mDefaultBackground; 132 private int mBackgroundColor; 133 134 /** 135 * EditStyledText extends EditText for managing flow of each editing 136 * action. 137 */ 138 public EditStyledText(Context context, AttributeSet attrs, int defStyle) { 139 super(context, attrs, defStyle); 140 init(); 141 } 142 143 public EditStyledText(Context context, AttributeSet attrs) { 144 super(context, attrs); 145 init(); 146 } 147 148 public EditStyledText(Context context) { 149 super(context); 150 init(); 151 } 152 153 /** 154 * Set Notifier. 155 */ 156 public void setNotifier(EditStyledTextNotifier estInterface) { 157 mESTInterface = estInterface; 158 } 159 160 /** 161 * Set Builder for AlertDialog. 162 * 163 * @param builder 164 * Builder for opening Alert Dialog. 165 */ 166 public void setBuilder(Builder builder) { 167 mDialog.setBuilder(builder); 168 } 169 170 /** 171 * Set Parameters for ColorAlertDialog. 172 * 173 * @param colortitle 174 * Title for Alert Dialog. 175 * @param colornames 176 * List of name of selecting color. 177 * @param colorints 178 * List of int of color. 179 */ 180 public void setColorAlertParams(CharSequence colortitle, 181 CharSequence[] colornames, CharSequence[] colorints) { 182 mDialog.setColorAlertParams(colortitle, colornames, colorints); 183 } 184 185 /** 186 * Set Parameters for SizeAlertDialog. 187 * 188 * @param sizetitle 189 * Title for Alert Dialog. 190 * @param sizenames 191 * List of name of selecting size. 192 * @param sizedisplayints 193 * List of int of size displayed in TextView. 194 * @param sizesendints 195 * List of int of size exported to HTML. 196 */ 197 public void setSizeAlertParams(CharSequence sizetitle, 198 CharSequence[] sizenames, CharSequence[] sizedisplayints, 199 CharSequence[] sizesendints) { 200 mDialog.setSizeAlertParams(sizetitle, sizenames, sizedisplayints, 201 sizesendints); 202 } 203 204 public void setAlignAlertParams(CharSequence aligntitle, 205 CharSequence[] alignnames) { 206 mDialog.setAlignAlertParams(aligntitle, alignnames); 207 } 208 209 @Override 210 public boolean onTouchEvent(MotionEvent event) { 211 if (mManager.isSoftKeyBlocked() && 212 event.getAction() == MotionEvent.ACTION_UP) { 213 cancelLongPress(); 214 } 215 final boolean superResult = super.onTouchEvent(event); 216 if (event.getAction() == MotionEvent.ACTION_UP) { 217 if (DBG) { 218 Log.d(LOG_TAG, "--- onTouchEvent"); 219 } 220 mManager.onCursorMoved(); 221 } 222 return superResult; 223 } 224 225 /** 226 * Start editing. This function have to be called before other editing 227 * actions. 228 */ 229 public void onStartEdit() { 230 mManager.onStartEdit(); 231 } 232 233 /** 234 * End editing. 235 */ 236 public void onEndEdit() { 237 mManager.onEndEdit(); 238 } 239 240 /** 241 * Start "Copy" action. 242 */ 243 public void onStartCopy() { 244 mManager.onStartCopy(); 245 } 246 247 /** 248 * Start "Cut" action. 249 */ 250 public void onStartCut() { 251 mManager.onStartCut(); 252 } 253 254 /** 255 * Start "Paste" action. 256 */ 257 public void onStartPaste() { 258 mManager.onStartPaste(); 259 } 260 261 /** 262 * Start changing "Size" action. 263 */ 264 public void onStartSize() { 265 mManager.onStartSize(); 266 } 267 268 /** 269 * Start changing "Color" action. 270 */ 271 public void onStartColor() { 272 mManager.onStartColor(); 273 } 274 275 /** 276 * Start changing "BackgroundColor" action. 277 */ 278 public void onStartBackgroundColor() { 279 mManager.onStartBackgroundColor(); 280 } 281 282 /** 283 * Start changing "Alignment" action. 284 */ 285 public void onStartAlign() { 286 mManager.onStartAlign(); 287 } 288 289 /** 290 * Start "Select" action. 291 */ 292 public void onStartSelect() { 293 mManager.onStartSelect(); 294 } 295 296 /** 297 * Start "SelectAll" action. 298 */ 299 public void onStartSelectAll() { 300 mManager.onStartSelectAll(); 301 } 302 303 /** 304 * Fix Selected Item. 305 */ 306 public void onFixSelectedItem() { 307 mManager.onFixSelectedItem(); 308 } 309 310 /** 311 * InsertImage to TextView by using URI 312 * 313 * @param uri 314 * URI of the iamge inserted to TextView. 315 */ 316 public void onInsertImage(Uri uri) { 317 mManager.onInsertImage(uri); 318 } 319 320 /** 321 * InsertImage to TextView by using resource ID 322 * 323 * @param resId 324 * Resource ID of the iamge inserted to TextView. 325 */ 326 public void onInsertImage(int resId) { 327 mManager.onInsertImage(resId); 328 } 329 330 public void onInsertHorizontalLine() { 331 mManager.onInsertHorizontalLine(); 332 } 333 334 public void onClearStyles() { 335 mManager.onClearStyles(); 336 } 337 /** 338 * Set Size of the Item. 339 * 340 * @param size 341 * The size of the Item. 342 */ 343 public void setItemSize(int size) { 344 mManager.setItemSize(size); 345 } 346 347 /** 348 * Set Color of the Item. 349 * 350 * @param color 351 * The color of the Item. 352 */ 353 public void setItemColor(int color) { 354 mManager.setItemColor(color); 355 } 356 357 /** 358 * Set Alignment of the Item. 359 * 360 * @param color 361 * The color of the Item. 362 */ 363 public void setAlignment(Layout.Alignment align) { 364 mManager.setAlignment(align); 365 } 366 367 /** 368 * Set Background color of View. 369 * 370 * @param color 371 * The background color of view. 372 */ 373 @Override 374 public void setBackgroundColor(int color) { 375 super.setBackgroundColor(color); 376 mBackgroundColor = color; 377 } 378 379 /** 380 * Set html to EditStyledText. 381 * 382 * @param html 383 * The html to be set. 384 */ 385 public void setHtml(String html) { 386 mConverter.SetHtml(html); 387 } 388 /** 389 * Check whether editing is started or not. 390 * 391 * @return Whether editing is started or not. 392 */ 393 public boolean isEditting() { 394 return mManager.isEditting(); 395 } 396 397 /** 398 * Check whether styled text or not. 399 * 400 * @return Whether styled text or not. 401 */ 402 public boolean isStyledText() { 403 return mManager.isStyledText(); 404 } 405 /** 406 * Check whether SoftKey is Blocked or not. 407 * 408 * @return whether SoftKey is Blocked or not. 409 */ 410 public boolean isSoftKeyBlocked() { 411 return mManager.isSoftKeyBlocked(); 412 } 413 414 /** 415 * Get the mode of the action. 416 * 417 * @return The mode of the action. 418 */ 419 public int getEditMode() { 420 return mManager.getEditMode(); 421 } 422 423 /** 424 * Get the state of the selection. 425 * 426 * @return The state of the selection. 427 */ 428 public int getSelectState() { 429 return mManager.getSelectState(); 430 } 431 432 @Override 433 public Bundle getInputExtras(boolean create) { 434 if (DBG) { 435 Log.d(LOG_TAG, "---getInputExtras"); 436 } 437 Bundle bundle = super.getInputExtras(create); 438 if (bundle != null) { 439 bundle = new Bundle(); 440 } 441 bundle.putBoolean("allowEmoji", true); 442 return bundle; 443 } 444 445 /** 446 * Get the state of the selection. 447 * 448 * @return The state of the selection. 449 */ 450 public String getHtml() { 451 return mConverter.getHtml(); 452 } 453 454 /** 455 * Get the state of the selection. 456 * 457 * @param uris 458 * The array of used uris. 459 * @return The state of the selection. 460 */ 461 public String getHtml(ArrayList<Uri> uris) { 462 mConverter.getUriArray(uris, getText()); 463 return mConverter.getHtml(); 464 } 465 466 /** 467 * Get Background color of View. 468 * 469 * @return The background color of View. 470 */ 471 public int getBackgroundColor() { 472 return mBackgroundColor; 473 } 474 475 /** 476 * Get Foreground color of View. 477 * 478 * @return The background color of View. 479 */ 480 public int getForeGroundColor(int pos) { 481 if (DBG) { 482 Log.d(LOG_TAG, "---getForeGroundColor: " + pos); 483 } 484 if (pos < 0 || pos > getText().length()) { 485 Log.e(LOG_TAG, "---getForeGroundColor: Illigal position."); 486 return DEFAULT_BACKGROUND_COLOR; 487 } else { 488 ForegroundColorSpan[] spans = 489 getText().getSpans(pos, pos, ForegroundColorSpan.class); 490 if (spans.length > 0) { 491 return spans[0].getForegroundColor(); 492 } else { 493 return DEFAULT_BACKGROUND_COLOR; 494 } 495 } 496 } 497 498 /** 499 * Initialize members. 500 */ 501 private void init() { 502 if (DBG) { 503 Log.d(LOG_TAG, "--- init"); 504 } 505 requestFocus(); 506 mDefaultBackground = getBackground(); 507 mBackgroundColor = DEFAULT_BACKGROUND_COLOR; 508 mManager = new EditorManager(this); 509 mConverter = new StyledTextConverter(this); 510 mDialog = new StyledTextDialog(this); 511 setMovementMethod(new StyledTextArrowKeyMethod(mManager)); 512 mManager.blockSoftKey(); 513 mManager.unblockSoftKey(); 514 } 515 516 /** 517 * Show Foreground Color Selecting Dialog. 518 */ 519 private void onShowForegroundColorAlert() { 520 mDialog.onShowForegroundColorAlertDialog(); 521 } 522 523 /** 524 * Show Background Color Selecting Dialog. 525 */ 526 private void onShowBackgroundColorAlert() { 527 mDialog.onShowBackgroundColorAlertDialog(); 528 } 529 530 /** 531 * Show Size Selecting Dialog. 532 */ 533 private void onShowSizeAlert() { 534 mDialog.onShowSizeAlertDialog(); 535 } 536 537 /** 538 * Show Alignment Selecting Dialog. 539 */ 540 private void onShowAlignAlert() { 541 mDialog.onShowAlignAlertDialog(); 542 } 543 544 /** 545 * Notify hint messages what action is expected to calling class. 546 * 547 * @param msgId 548 * Id of the hint message. 549 */ 550 private void setHintMessage(int msgId) { 551 if (mESTInterface != null) { 552 mESTInterface.notifyHintMsg(msgId); 553 } 554 } 555 556 /** 557 * Notify the event that the mode and state are changed. 558 * 559 * @param mode 560 * Mode of the editing action. 561 * @param state 562 * Mode of the selection state. 563 */ 564 private void notifyStateChanged(int mode, int state) { 565 if (mESTInterface != null) { 566 mESTInterface.notifyStateChanged(mode, state); 567 } 568 } 569 570 /** 571 * EditorManager manages the flow and status of editing actions. 572 */ 573 private class EditorManager { 574 private boolean mEditFlag = false; 575 private boolean mSoftKeyBlockFlag = false; 576 private int mMode = 0; 577 private int mState = 0; 578 private int mCurStart = 0; 579 private int mCurEnd = 0; 580 private EditStyledText mEST; 581 582 EditorManager(EditStyledText est) { 583 mEST = est; 584 } 585 586 public void onStartEdit() { 587 if (DBG) { 588 Log.d(LOG_TAG, "--- onStartEdit"); 589 } 590 Log.d(LOG_TAG, "--- onstartedit:"); 591 handleResetEdit(); 592 mEST.notifyStateChanged(mMode, mState); 593 } 594 595 public void onEndEdit() { 596 if (DBG) { 597 Log.d(LOG_TAG, "--- onEndEdit"); 598 } 599 handleCancel(); 600 mEST.notifyStateChanged(mMode, mState); 601 } 602 603 public void onStartCopy() { 604 if (DBG) { 605 Log.d(LOG_TAG, "--- onStartCopy"); 606 } 607 handleCopy(); 608 mEST.notifyStateChanged(mMode, mState); 609 } 610 611 public void onStartCut() { 612 if (DBG) { 613 Log.d(LOG_TAG, "--- onStartCut"); 614 } 615 handleCut(); 616 mEST.notifyStateChanged(mMode, mState); 617 } 618 619 public void onStartPaste() { 620 if (DBG) { 621 Log.d(LOG_TAG, "--- onStartPaste"); 622 } 623 handlePaste(); 624 mEST.notifyStateChanged(mMode, mState); 625 } 626 627 public void onStartSize() { 628 if (DBG) { 629 Log.d(LOG_TAG, "--- onStartSize"); 630 } 631 handleSize(); 632 mEST.notifyStateChanged(mMode, mState); 633 } 634 635 public void onStartAlign() { 636 if (DBG) { 637 Log.d(LOG_TAG, "--- onStartAlignRight"); 638 } 639 handleAlign(); 640 mEST.notifyStateChanged(mMode, mState); 641 } 642 643 public void onStartColor() { 644 if (DBG) { 645 Log.d(LOG_TAG, "--- onClickColor"); 646 } 647 handleColor(); 648 mEST.notifyStateChanged(mMode, mState); 649 } 650 651 public void onStartBackgroundColor() { 652 if (DBG) { 653 Log.d(LOG_TAG, "--- onClickColor"); 654 } 655 mEST.onShowBackgroundColorAlert(); 656 mEST.notifyStateChanged(mMode, mState); 657 } 658 659 public void onStartSelect() { 660 if (DBG) { 661 Log.d(LOG_TAG, "--- onClickSelect"); 662 } 663 mMode = MODE_SELECT; 664 if (mState == STATE_SELECT_OFF) { 665 handleSelect(); 666 } else { 667 unsetSelect(); 668 handleSelect(); 669 } 670 mEST.notifyStateChanged(mMode, mState); 671 } 672 673 public void onCursorMoved() { 674 if (DBG) { 675 Log.d(LOG_TAG, "--- onClickView"); 676 } 677 if (mState == STATE_SELECT_ON || mState == STATE_SELECTED) { 678 handleSelect(); 679 mEST.notifyStateChanged(mMode, mState); 680 } 681 } 682 683 public void onStartSelectAll() { 684 if (DBG) { 685 Log.d(LOG_TAG, "--- onClickSelectAll"); 686 } 687 handleSelectAll(); 688 mEST.notifyStateChanged(mMode, mState); 689 } 690 691 public void onFixSelectedItem() { 692 if (DBG) { 693 Log.d(LOG_TAG, "--- onClickComplete"); 694 } 695 handleComplete(); 696 mEST.notifyStateChanged(mMode, mState); 697 } 698 699 public void onInsertImage(Uri uri) { 700 if (DBG) { 701 Log.d(LOG_TAG, "--- onInsertImage by URI: " + uri.getPath() 702 + "," + uri.toString()); 703 } 704 insertImageSpan(new ImageSpan(mEST.getContext(), uri)); 705 mEST.notifyStateChanged(mMode, mState); 706 } 707 708 public void onInsertImage(int resID) { 709 if (DBG) { 710 Log.d(LOG_TAG, "--- onInsertImage by resID"); 711 } 712 insertImageSpan(new ImageSpan(mEST.getContext(), resID)); 713 mEST.notifyStateChanged(mMode, mState); 714 } 715 716 public void onInsertHorizontalLine() { 717 if (DBG) { 718 Log.d(LOG_TAG, "--- onInsertHorizontalLine:"); 719 } 720 insertImageSpan(new HorizontalLineSpan(0xFF000000, mEST)); 721 mEST.notifyStateChanged(mMode, mState); 722 } 723 724 public void onClearStyles() { 725 if (DBG) { 726 Log.d(LOG_TAG, "--- onClearStyles"); 727 } 728 Editable txt = mEST.getText(); 729 int len = txt.length(); 730 Object[] styles = txt.getSpans(0, len, Object.class); 731 for (Object style : styles) { 732 if (style instanceof ParagraphStyle || 733 style instanceof QuoteSpan || 734 style instanceof CharacterStyle) { 735 if (style instanceof ImageSpan) { 736 int start = txt.getSpanStart(style); 737 int end = txt.getSpanEnd(style); 738 txt.replace(start, end, ""); 739 } 740 txt.removeSpan(style); 741 } 742 } 743 mEST.setBackgroundDrawable(mEST.mDefaultBackground); 744 mEST.mBackgroundColor = DEFAULT_BACKGROUND_COLOR; 745 } 746 747 public void setItemSize(int size) { 748 if (DBG) { 749 Log.d(LOG_TAG, "--- onClickSizeItem"); 750 } 751 if (mState == STATE_SELECTED || mState == STATE_SELECT_FIX) { 752 changeSizeSelectedText(size); 753 handleResetEdit(); 754 } 755 } 756 757 public void setItemColor(int color) { 758 if (DBG) { 759 Log.d(LOG_TAG, "--- onClickColorItem"); 760 } 761 if (mState == STATE_SELECTED || mState == STATE_SELECT_FIX) { 762 changeColorSelectedText(color); 763 handleResetEdit(); 764 } 765 } 766 767 public void setAlignment(Layout.Alignment align) { 768 if (DBG) { 769 Log.d(LOG_TAG, "--- onClickColorItem"); 770 } 771 if (mState == STATE_SELECTED || mState == STATE_SELECT_FIX) { 772 changeAlign(align); 773 handleResetEdit(); 774 } 775 } 776 777 public boolean isEditting() { 778 return mEditFlag; 779 } 780 781 /* If the style of the span is added, add check case for that style */ 782 public boolean isStyledText() { 783 Editable txt = mEST.getText(); 784 int len = txt.length(); 785 if (txt.getSpans(0, len -1, ParagraphStyle.class).length > 0 || 786 txt.getSpans(0, len -1, QuoteSpan.class).length > 0 || 787 txt.getSpans(0, len -1, CharacterStyle.class).length > 0 || 788 mEST.mBackgroundColor != DEFAULT_BACKGROUND_COLOR) { 789 return true; 790 } 791 return false; 792 } 793 794 public boolean isSoftKeyBlocked() { 795 return mSoftKeyBlockFlag; 796 } 797 798 public int getEditMode() { 799 return mMode; 800 } 801 802 public int getSelectState() { 803 return mState; 804 } 805 806 public int getSelectionStart() { 807 return mCurStart; 808 } 809 810 public int getSelectionEnd() { 811 return mCurEnd; 812 } 813 814 private void doNextHandle() { 815 if (DBG) { 816 Log.d(LOG_TAG, "--- doNextHandle: " + mMode + "," + mState); 817 } 818 switch (mMode) { 819 case MODE_COPY: 820 handleCopy(); 821 break; 822 case MODE_CUT: 823 handleCut(); 824 break; 825 case MODE_PASTE: 826 handlePaste(); 827 break; 828 case MODE_SIZE: 829 handleSize(); 830 break; 831 case MODE_COLOR: 832 handleColor(); 833 break; 834 case MODE_ALIGN: 835 handleAlign(); 836 break; 837 default: 838 break; 839 } 840 } 841 842 private void handleCancel() { 843 if (DBG) { 844 Log.d(LOG_TAG, "--- handleCancel"); 845 } 846 mMode = MODE_NOTHING; 847 mState = STATE_SELECT_OFF; 848 mEditFlag = false; 849 Log.d(LOG_TAG, "--- handleCancel:" + mEST.getInputType()); 850 unblockSoftKey(); 851 unsetSelect(); 852 } 853 854 private void handleComplete() { 855 if (DBG) { 856 Log.d(LOG_TAG, "--- handleComplete"); 857 } 858 if (!mEditFlag) { 859 return; 860 } 861 if (mState == STATE_SELECTED) { 862 mState = STATE_SELECT_FIX; 863 } 864 doNextHandle(); 865 } 866 867 private void handleTextViewFunc(int mode, int id) { 868 if (DBG) { 869 Log.d(LOG_TAG, "--- handleTextView: " + mMode + "," + mState + 870 "," + id); 871 } 872 if (!mEditFlag) { 873 return; 874 } 875 if (mMode == MODE_NOTHING || mMode == MODE_SELECT) { 876 mMode = mode; 877 if (mState == STATE_SELECTED) { 878 mState = STATE_SELECT_FIX; 879 handleTextViewFunc(mode, id); 880 } else { 881 handleSelect(); 882 } 883 } else if (mMode != mode) { 884 handleCancel(); 885 mMode = mode; 886 handleTextViewFunc(mode, id); 887 } else if (mState == STATE_SELECT_FIX) { 888 mEST.onTextContextMenuItem(id); 889 handleResetEdit(); 890 } 891 } 892 893 private void handleCopy() { 894 if (DBG) { 895 Log.d(LOG_TAG, "--- handleCopy: " + mMode + "," + mState); 896 } 897 handleTextViewFunc(MODE_COPY, android.R.id.copy); 898 } 899 900 private void handleCut() { 901 if (DBG) { 902 Log.d(LOG_TAG, "--- handleCopy: " + mMode + "," + mState); 903 } 904 handleTextViewFunc(MODE_CUT, android.R.id.cut); 905 } 906 907 private void handlePaste() { 908 if (DBG) { 909 Log.d(LOG_TAG, "--- handlePaste"); 910 } 911 if (!mEditFlag) { 912 return; 913 } 914 mEST.onTextContextMenuItem(android.R.id.paste); 915 } 916 917 private void handleSetSpan(int mode) { 918 if (DBG) { 919 Log.d(LOG_TAG, "--- handleSetSpan:" + mEditFlag + "," 920 + mState + ',' + mMode); 921 } 922 if (!mEditFlag) { 923 Log.e(LOG_TAG, "--- handleSetSpan: Editing is not started."); 924 return; 925 } 926 if (mMode == MODE_NOTHING || mMode == MODE_SELECT) { 927 mMode = mode; 928 if (mState == STATE_SELECTED) { 929 mState = STATE_SELECT_FIX; 930 handleSetSpan(mode); 931 } else { 932 handleSelect(); 933 } 934 } else if (mMode != mode) { 935 handleCancel(); 936 mMode = mode; 937 handleSetSpan(mode); 938 } else { 939 if (mState == STATE_SELECT_FIX) { 940 mEST.setHintMessage(HINT_MSG_NULL); 941 switch (mode) { 942 case MODE_COLOR: 943 mEST.onShowForegroundColorAlert(); 944 break; 945 case MODE_SIZE: 946 mEST.onShowSizeAlert(); 947 break; 948 case MODE_ALIGN: 949 mEST.onShowAlignAlert(); 950 break; 951 default: 952 Log.e(LOG_TAG, "--- handleSetSpan: invalid mode."); 953 break; 954 } 955 } else { 956 Log.d(LOG_TAG, "--- handleSetSpan: do nothing."); 957 } 958 } 959 } 960 961 private void handleSize() { 962 handleSetSpan(MODE_SIZE); 963 } 964 965 private void handleColor() { 966 handleSetSpan(MODE_COLOR); 967 } 968 969 private void handleAlign() { 970 handleSetSpan(MODE_ALIGN); 971 } 972 973 private void handleSelect() { 974 if (DBG) { 975 Log.d(LOG_TAG, "--- handleSelect:" + mEditFlag + "," + mState); 976 } 977 if (!mEditFlag) { 978 return; 979 } 980 if (mState == STATE_SELECT_OFF) { 981 if (isTextSelected()) { 982 Log.e(LOG_TAG, "Selection is off, but selected"); 983 } 984 setSelectStartPos(); 985 blockSoftKey(); 986 mEST.setHintMessage(HINT_MSG_SELECT_END); 987 } else if (mState == STATE_SELECT_ON) { 988 if (isTextSelected()) { 989 Log.e(LOG_TAG, "Selection now start, but selected"); 990 } 991 setSelectedEndPos(); 992 mEST.setHintMessage(HINT_MSG_PUSH_COMPETE); 993 doNextHandle(); 994 } else if (mState == STATE_SELECTED) { 995 if (!isTextSelected()) { 996 Log.e(LOG_TAG, "Selection is done, but not selected"); 997 } 998 setSelectedEndPos(); 999 doNextHandle(); 1000 } 1001 } 1002 1003 private void handleSelectAll() { 1004 if (DBG) { 1005 Log.d(LOG_TAG, "--- handleSelectAll"); 1006 } 1007 if (!mEditFlag) { 1008 return; 1009 } 1010 mEST.selectAll(); 1011 mState = STATE_SELECTED; 1012 } 1013 1014 private void handleResetEdit() { 1015 if (DBG) { 1016 Log.d(LOG_TAG, "Reset Editor"); 1017 } 1018 blockSoftKey(); 1019 handleCancel(); 1020 mEditFlag = true; 1021 mEST.setHintMessage(HINT_MSG_SELECT_START); 1022 } 1023 1024 private void setSelection() { 1025 if (DBG) { 1026 Log.d(LOG_TAG, "--- onSelect:" + mCurStart + "," + mCurEnd); 1027 } 1028 if (mCurStart >= 0 && mCurStart <= mEST.getText().length() 1029 && mCurEnd >= 0 && mCurEnd <= mEST.getText().length()) { 1030 if (mCurStart < mCurEnd) { 1031 mEST.setSelection(mCurStart, mCurEnd); 1032 } else { 1033 mEST.setSelection(mCurEnd, mCurStart); 1034 } 1035 mState = STATE_SELECTED; 1036 } else { 1037 Log.e(LOG_TAG, 1038 "Select is on, but cursor positions are illigal.:" 1039 + mEST.getText().length() + "," + mCurStart 1040 + "," + mCurEnd); 1041 } 1042 } 1043 1044 private void unsetSelect() { 1045 if (DBG) { 1046 Log.d(LOG_TAG, "--- offSelect"); 1047 } 1048 int currpos = mEST.getSelectionStart(); 1049 mEST.setSelection(currpos, currpos); 1050 mState = STATE_SELECT_OFF; 1051 } 1052 1053 private void setSelectStartPos() { 1054 if (DBG) { 1055 Log.d(LOG_TAG, "--- setSelectStartPos"); 1056 } 1057 mCurStart = mEST.getSelectionStart(); 1058 mState = STATE_SELECT_ON; 1059 } 1060 1061 private void setSelectedEndPos() { 1062 if (DBG) { 1063 Log.d(LOG_TAG, "--- setSelectEndPos:"); 1064 } 1065 if (mEST.getSelectionStart() == mCurStart) { 1066 setSelectedEndPos(mEST.getSelectionEnd()); 1067 } else { 1068 setSelectedEndPos(mEST.getSelectionStart()); 1069 } 1070 } 1071 1072 public void setSelectedEndPos(int pos) { 1073 if (DBG) { 1074 Log.d(LOG_TAG, "--- setSelectedEndPos:"); 1075 } 1076 mCurEnd = pos; 1077 setSelection(); 1078 } 1079 1080 private boolean isTextSelected() { 1081 if (DBG) { 1082 Log.d(LOG_TAG, "--- isTextSelected:" + mCurStart + "," 1083 + mCurEnd); 1084 } 1085 return (mCurStart != mCurEnd) 1086 && (mState == STATE_SELECTED || 1087 mState == STATE_SELECT_FIX); 1088 } 1089 1090 private void setStyledTextSpan(Object span, int start, int end) { 1091 if (DBG) { 1092 Log.d(LOG_TAG, "--- setStyledTextSpan:" + mMode + "," 1093 + start + "," + end); 1094 } 1095 if (start < end) { 1096 mEST.getText().setSpan(span, start, end, 1097 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 1098 } else { 1099 mEST.getText().setSpan(span, end, start, 1100 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 1101 } 1102 } 1103 1104 private void changeSizeSelectedText(int size) { 1105 if (DBG) { 1106 Log.d(LOG_TAG, "--- changeSize:" + size); 1107 } 1108 setStyledTextSpan(new AbsoluteSizeSpan(size), 1109 mCurStart, mCurEnd); 1110 } 1111 1112 private void changeColorSelectedText(int color) { 1113 if (DBG) { 1114 Log.d(LOG_TAG, "--- changeColor:" + color); 1115 } 1116 setStyledTextSpan(new ForegroundColorSpan(color), 1117 mCurStart, mCurEnd); 1118 } 1119 1120 private void changeAlign(Layout.Alignment align) { 1121 if (DBG) { 1122 Log.d(LOG_TAG, "--- changeAlign:" + align); 1123 } 1124 setStyledTextSpan(new AlignmentSpan.Standard(align), 1125 findLineStart(mEST.getText(), mCurStart), 1126 findLineEnd(mEST.getText(), mCurEnd)); 1127 } 1128 1129 private int findLineStart(Editable text, int current) { 1130 if (DBG) { 1131 Log.d(LOG_TAG, "--- findLineStart: curr:" + current + 1132 ", length:" + text.length()); 1133 } 1134 int pos = current; 1135 for (; pos > 0; pos--) { 1136 if (text.charAt(pos - 1) == '\n') { 1137 break; 1138 } 1139 } 1140 return pos; 1141 } 1142 1143 private void insertImageSpan(ImageSpan span) { 1144 if (DBG) { 1145 Log.d(LOG_TAG, "--- insertImageSpan"); 1146 } 1147 if (span != null) { 1148 Log.d(LOG_TAG, "--- insertimagespan:" + span.getDrawable().getIntrinsicHeight() + "," + span.getDrawable().getIntrinsicWidth()); 1149 Log.d(LOG_TAG, "--- insertimagespan:" + span.getDrawable().getClass()); 1150 int curpos = mEST.getSelectionStart(); 1151 mEST.getText().insert(curpos, "\uFFFC"); 1152 mEST.getText().setSpan(span, curpos, curpos + 1, 1153 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 1154 mEST.notifyStateChanged(mMode, mState); 1155 } else { 1156 Log.e(LOG_TAG, "--- insertImageSpan: null span was inserted"); 1157 } 1158 } 1159 1160 private int findLineEnd(Editable text, int current) { 1161 if (DBG) { 1162 Log.d(LOG_TAG, "--- findLineEnd: curr:" + current + 1163 ", length:" + text.length()); 1164 } 1165 int pos = current; 1166 for (; pos < text.length(); pos++) { 1167 if (pos > 0 && text.charAt(pos - 1) == '\n') { 1168 break; 1169 } 1170 } 1171 return pos; 1172 } 1173 1174 private void blockSoftKey() { 1175 if (DBG) { 1176 Log.d(LOG_TAG, "--- blockSoftKey:"); 1177 } 1178 InputMethodManager imm = (InputMethodManager) mEST.getContext(). 1179 getSystemService(Context.INPUT_METHOD_SERVICE); 1180 imm.hideSoftInputFromWindow(mEST.getWindowToken(), 0); 1181 mEST.setOnClickListener( 1182 new OnClickListener() { 1183 public void onClick(View v) { 1184 Log.d(LOG_TAG, "--- ontrackballclick:"); 1185 onFixSelectedItem(); 1186 } 1187 }); 1188 mSoftKeyBlockFlag = true; 1189 } 1190 1191 private void unblockSoftKey() { 1192 if (DBG) { 1193 Log.d(LOG_TAG, "--- unblockSoftKey:"); 1194 } 1195 mEST.setOnClickListener(null); 1196 mSoftKeyBlockFlag = false; 1197 } 1198 } 1199 1200 private class StyledTextConverter { 1201 private EditStyledText mEST; 1202 1203 public StyledTextConverter(EditStyledText est) { 1204 mEST = est; 1205 } 1206 1207 public String getHtml() { 1208 String htmlBody = Html.toHtml(mEST.getText()); 1209 if (DBG) { 1210 Log.d(LOG_TAG, "--- getConvertedBody:" + htmlBody); 1211 } 1212 return htmlBody; 1213 } 1214 1215 public void getUriArray(ArrayList<Uri> uris, Editable text) { 1216 uris.clear(); 1217 if (DBG) { 1218 Log.d(LOG_TAG, "--- getUriArray:"); 1219 } 1220 int len = text.length(); 1221 int next; 1222 for (int i = 0; i < text.length(); i = next) { 1223 next = text.nextSpanTransition(i, len, ImageSpan.class); 1224 ImageSpan[] images = text.getSpans(i, next, ImageSpan.class); 1225 for (int j = 0; j < images.length; j++) { 1226 if (DBG) { 1227 Log.d(LOG_TAG, "--- getUriArray: foundArray" + 1228 ((ImageSpan) images[j]).getSource()); 1229 } 1230 uris.add(Uri.parse( 1231 ((ImageSpan) images[j]).getSource())); 1232 } 1233 } 1234 } 1235 1236 public void SetHtml (String html) { 1237 final Spanned spanned = Html.fromHtml(html, new Html.ImageGetter() { 1238 public Drawable getDrawable(String src) { 1239 Log.d(LOG_TAG, "--- sethtml: src="+src); 1240 if (src.startsWith("content://")) { 1241 Uri uri = Uri.parse(src); 1242 try { 1243 InputStream is = mEST.getContext().getContentResolver().openInputStream(uri); 1244 Bitmap bitmap = BitmapFactory.decodeStream(is); 1245 Drawable drawable = new BitmapDrawable( 1246 getContext().getResources(), bitmap); 1247 drawable.setBounds(0, 0, 1248 drawable.getIntrinsicWidth(), 1249 drawable.getIntrinsicHeight()); 1250 is.close(); 1251 return drawable; 1252 } catch (Exception e) { 1253 Log.e(LOG_TAG, "--- set html: Failed to loaded content " + uri, e); 1254 return null; 1255 } 1256 } 1257 Log.d(LOG_TAG, " unknown src="+src); 1258 return null; 1259 } 1260 }, null); 1261 mEST.setText(spanned); 1262 } 1263 } 1264 1265 private class StyledTextDialog { 1266 Builder mBuilder; 1267 CharSequence mColorTitle; 1268 CharSequence mSizeTitle; 1269 CharSequence mAlignTitle; 1270 CharSequence[] mColorNames; 1271 CharSequence[] mColorInts; 1272 CharSequence[] mSizeNames; 1273 CharSequence[] mSizeDisplayInts; 1274 CharSequence[] mSizeSendInts; 1275 CharSequence[] mAlignNames; 1276 EditStyledText mEST; 1277 1278 public StyledTextDialog(EditStyledText est) { 1279 mEST = est; 1280 } 1281 1282 public void setBuilder(Builder builder) { 1283 mBuilder = builder; 1284 } 1285 1286 public void setColorAlertParams(CharSequence colortitle, 1287 CharSequence[] colornames, CharSequence[] colorints) { 1288 mColorTitle = colortitle; 1289 mColorNames = colornames; 1290 mColorInts = colorints; 1291 } 1292 1293 public void setSizeAlertParams(CharSequence sizetitle, 1294 CharSequence[] sizenames, CharSequence[] sizedisplayints, 1295 CharSequence[] sizesendints) { 1296 mSizeTitle = sizetitle; 1297 mSizeNames = sizenames; 1298 mSizeDisplayInts = sizedisplayints; 1299 mSizeSendInts = sizesendints; 1300 } 1301 1302 public void setAlignAlertParams(CharSequence aligntitle, 1303 CharSequence[] alignnames) { 1304 mAlignTitle = aligntitle; 1305 mAlignNames = alignnames; 1306 } 1307 1308 private boolean checkColorAlertParams() { 1309 if (DBG) { 1310 Log.d(LOG_TAG, "--- checkParams"); 1311 } 1312 if (mBuilder == null) { 1313 Log.e(LOG_TAG, "--- builder is null."); 1314 return false; 1315 } else if (mColorTitle == null || mColorNames == null 1316 || mColorInts == null) { 1317 Log.e(LOG_TAG, "--- color alert params are null."); 1318 return false; 1319 } else if (mColorNames.length != mColorInts.length) { 1320 Log.e(LOG_TAG, "--- the length of color alert params are " 1321 + "different."); 1322 return false; 1323 } 1324 return true; 1325 } 1326 1327 private boolean checkSizeAlertParams() { 1328 if (DBG) { 1329 Log.d(LOG_TAG, "--- checkParams"); 1330 } 1331 if (mBuilder == null) { 1332 Log.e(LOG_TAG, "--- builder is null."); 1333 return false; 1334 } else if (mSizeTitle == null || mSizeNames == null 1335 || mSizeDisplayInts == null || mSizeSendInts == null) { 1336 Log.e(LOG_TAG, "--- size alert params are null."); 1337 return false; 1338 } else if (mSizeNames.length != mSizeDisplayInts.length 1339 && mSizeSendInts.length != mSizeDisplayInts.length) { 1340 Log.e(LOG_TAG, "--- the length of size alert params are " 1341 + "different."); 1342 return false; 1343 } 1344 return true; 1345 } 1346 1347 private boolean checkAlignAlertParams() { 1348 if (DBG) { 1349 Log.d(LOG_TAG, "--- checkAlignAlertParams"); 1350 } 1351 if (mBuilder == null) { 1352 Log.e(LOG_TAG, "--- builder is null."); 1353 return false; 1354 } else if (mAlignTitle == null) { 1355 Log.e(LOG_TAG, "--- align alert params are null."); 1356 return false; 1357 } 1358 return true; 1359 } 1360 1361 private void onShowForegroundColorAlertDialog() { 1362 if (DBG) { 1363 Log.d(LOG_TAG, "--- onShowForegroundColorAlertDialog"); 1364 } 1365 if (!checkColorAlertParams()) { 1366 return; 1367 } 1368 mBuilder.setTitle(mColorTitle); 1369 mBuilder.setIcon(0); 1370 mBuilder. 1371 setItems(mColorNames, 1372 new DialogInterface.OnClickListener() { 1373 public void onClick(DialogInterface dialog, int which) { 1374 Log.d("EETVM", "mBuilder.onclick:" + which); 1375 int color = Integer.parseInt( 1376 (String) mColorInts[which], 16) - 0x01000000; 1377 mEST.setItemColor(color); 1378 } 1379 }); 1380 mBuilder.show(); 1381 } 1382 1383 private void onShowBackgroundColorAlertDialog() { 1384 if (DBG) { 1385 Log.d(LOG_TAG, "--- onShowBackgroundColorAlertDialog"); 1386 } 1387 if (!checkColorAlertParams()) { 1388 return; 1389 } 1390 mBuilder.setTitle(mColorTitle); 1391 mBuilder.setIcon(0); 1392 mBuilder. 1393 setItems(mColorNames, 1394 new DialogInterface.OnClickListener() { 1395 public void onClick(DialogInterface dialog, int which) { 1396 Log.d("EETVM", "mBuilder.onclick:" + which); 1397 int color = Integer.parseInt( 1398 (String) mColorInts[which], 16) - 0x01000000; 1399 mEST.setBackgroundColor(color); 1400 } 1401 }); 1402 mBuilder.show(); 1403 } 1404 1405 private void onShowSizeAlertDialog() { 1406 if (DBG) { 1407 Log.d(LOG_TAG, "--- onShowSizeAlertDialog"); 1408 } 1409 if (!checkSizeAlertParams()) { 1410 return; 1411 } 1412 mBuilder.setTitle(mSizeTitle); 1413 mBuilder.setIcon(0); 1414 mBuilder. 1415 setItems(mSizeNames, 1416 new DialogInterface.OnClickListener() { 1417 public void onClick(DialogInterface dialog, int which) { 1418 Log.d(LOG_TAG, "mBuilder.onclick:" + which); 1419 int size = Integer 1420 .parseInt((String) mSizeDisplayInts[which]); 1421 mEST.setItemSize(size); 1422 } 1423 }); 1424 mBuilder.show(); 1425 } 1426 1427 private void onShowAlignAlertDialog() { 1428 if (DBG) { 1429 Log.d(LOG_TAG, "--- onShowAlignAlertDialog"); 1430 } 1431 if (!checkAlignAlertParams()) { 1432 return; 1433 } 1434 mBuilder.setTitle(mAlignTitle); 1435 mBuilder.setIcon(0); 1436 mBuilder. 1437 setItems(mAlignNames, 1438 new DialogInterface.OnClickListener() { 1439 public void onClick(DialogInterface dialog, int which) { 1440 Log.d(LOG_TAG, "mBuilder.onclick:" + which); 1441 Layout.Alignment align = Layout.Alignment.ALIGN_NORMAL; 1442 switch (which) { 1443 case 0: 1444 align = Layout.Alignment.ALIGN_NORMAL; 1445 break; 1446 case 1: 1447 align = Layout.Alignment.ALIGN_CENTER; 1448 break; 1449 case 2: 1450 align = Layout.Alignment.ALIGN_OPPOSITE; 1451 break; 1452 default: 1453 break; 1454 } 1455 mEST.setAlignment(align); 1456 } 1457 }); 1458 mBuilder.show(); 1459 } 1460 } 1461 1462 private class StyledTextArrowKeyMethod extends ArrowKeyMovementMethod { 1463 EditorManager mManager; 1464 StyledTextArrowKeyMethod(EditorManager manager) { 1465 super(); 1466 mManager = manager; 1467 } 1468 1469 @Override 1470 public boolean onKeyDown(TextView widget, Spannable buffer, 1471 int keyCode, KeyEvent event) { 1472 if (!mManager.isSoftKeyBlocked()) { 1473 return super.onKeyDown(widget, buffer, keyCode, event); 1474 } 1475 if (executeDown(widget, buffer, keyCode)) { 1476 return true; 1477 } 1478 return false; 1479 } 1480 1481 private int getEndPos(TextView widget) { 1482 int end; 1483 if (widget.getSelectionStart() == mManager.getSelectionStart()) { 1484 end = widget.getSelectionEnd(); 1485 } else { 1486 end = widget.getSelectionStart(); 1487 } 1488 return end; 1489 } 1490 1491 private boolean up(TextView widget, Spannable buffer) { 1492 if (DBG) { 1493 Log.d(LOG_TAG, "--- up:"); 1494 } 1495 Layout layout = widget.getLayout(); 1496 int end = getEndPos(widget); 1497 int line = layout.getLineForOffset(end); 1498 if (line > 0) { 1499 int to; 1500 if (layout.getParagraphDirection(line) == 1501 layout.getParagraphDirection(line - 1)) { 1502 float h = layout.getPrimaryHorizontal(end); 1503 to = layout.getOffsetForHorizontal(line - 1, h); 1504 } else { 1505 to = layout.getLineStart(line - 1); 1506 } 1507 mManager.setSelectedEndPos(to); 1508 mManager.onCursorMoved(); 1509 return true; 1510 } 1511 return false; 1512 } 1513 1514 private boolean down(TextView widget, Spannable buffer) { 1515 if (DBG) { 1516 Log.d(LOG_TAG, "--- down:"); 1517 } 1518 Layout layout = widget.getLayout(); 1519 int end = getEndPos(widget); 1520 int line = layout.getLineForOffset(end); 1521 if (line < layout.getLineCount() - 1) { 1522 int to; 1523 if (layout.getParagraphDirection(line) == 1524 layout.getParagraphDirection(line + 1)) { 1525 float h = layout.getPrimaryHorizontal(end); 1526 to = layout.getOffsetForHorizontal(line + 1, h); 1527 } else { 1528 to = layout.getLineStart(line + 1); 1529 } 1530 mManager.setSelectedEndPos(to); 1531 mManager.onCursorMoved(); 1532 return true; 1533 } 1534 return false; 1535 } 1536 1537 private boolean left(TextView widget, Spannable buffer) { 1538 if (DBG) { 1539 Log.d(LOG_TAG, "--- left:"); 1540 } 1541 Layout layout = widget.getLayout(); 1542 int to = layout.getOffsetToLeftOf(getEndPos(widget)); 1543 mManager.setSelectedEndPos(to); 1544 mManager.onCursorMoved(); 1545 return true; 1546 } 1547 1548 private boolean right(TextView widget, Spannable buffer) { 1549 if (DBG) { 1550 Log.d(LOG_TAG, "--- right:"); 1551 } 1552 Layout layout = widget.getLayout(); 1553 int to = layout.getOffsetToRightOf(getEndPos(widget)); 1554 mManager.setSelectedEndPos(to); 1555 mManager.onCursorMoved(); 1556 return true; 1557 } 1558 1559 private boolean executeDown(TextView widget, Spannable buffer, 1560 int keyCode) { 1561 if (DBG) { 1562 Log.d(LOG_TAG, "--- executeDown: " + keyCode); 1563 } 1564 boolean handled = false; 1565 1566 switch (keyCode) { 1567 case KeyEvent.KEYCODE_DPAD_UP: 1568 handled |= up(widget, buffer); 1569 break; 1570 case KeyEvent.KEYCODE_DPAD_DOWN: 1571 handled |= down(widget, buffer); 1572 break; 1573 case KeyEvent.KEYCODE_DPAD_LEFT: 1574 handled |= left(widget, buffer); 1575 break; 1576 case KeyEvent.KEYCODE_DPAD_RIGHT: 1577 handled |= right(widget, buffer); 1578 break; 1579 case KeyEvent.KEYCODE_DPAD_CENTER: 1580 mManager.onFixSelectedItem(); 1581 handled = true; 1582 break; 1583 } 1584 return handled; 1585 } 1586 } 1587 1588 public class HorizontalLineSpan extends ImageSpan { 1589 public HorizontalLineSpan(int color, View view) { 1590 super(new HorizontalLineDrawable(color, view)); 1591 } 1592 } 1593 public class HorizontalLineDrawable extends ShapeDrawable { 1594 private View mView; 1595 public HorizontalLineDrawable(int color, View view) { 1596 super(new RectShape()); 1597 mView = view; 1598 renewColor(color); 1599 renewBounds(view); 1600 } 1601 @Override 1602 public void draw(Canvas canvas) { 1603 if (DBG) { 1604 Log.d(LOG_TAG, "--- draw:"); 1605 } 1606 renewColor(); 1607 renewBounds(mView); 1608 super.draw(canvas); 1609 } 1610 1611 private void renewBounds(View view) { 1612 if (DBG) { 1613 int width = mView.getBackground().getBounds().width(); 1614 int height = mView.getBackground().getBounds().height(); 1615 Log.d(LOG_TAG, "--- renewBounds:" + width + "," + height); 1616 Log.d(LOG_TAG, "--- renewBounds:" + mView.getClass()); 1617 } 1618 int width = mView.getWidth(); 1619 if (width > 20) { 1620 width -= 20; 1621 } 1622 setBounds(0, 0, width, 2); 1623 } 1624 private void renewColor(int color) { 1625 if (DBG) { 1626 Log.d(LOG_TAG, "--- renewColor:" + color); 1627 } 1628 getPaint().setColor(color); 1629 } 1630 private void renewColor() { 1631 if (DBG) { 1632 Log.d(LOG_TAG, "--- renewColor:"); 1633 } 1634 if (mView instanceof View) { 1635 ImageSpan parent = getParentSpan(); 1636 Editable text = ((EditStyledText)mView).getText(); 1637 int start = text.getSpanStart(parent); 1638 ForegroundColorSpan[] spans = text.getSpans(start, start, ForegroundColorSpan.class); 1639 if (spans.length > 0) { 1640 renewColor(spans[spans.length - 1].getForegroundColor()); 1641 } 1642 } 1643 } 1644 private ImageSpan getParentSpan() { 1645 if (DBG) { 1646 Log.d(LOG_TAG, "--- getParentSpan:"); 1647 } 1648 if (mView instanceof EditStyledText) { 1649 Editable text = ((EditStyledText)mView).getText(); 1650 ImageSpan[] images = text.getSpans(0, text.length(), ImageSpan.class); 1651 if (images.length > 0) { 1652 for (ImageSpan image: images) { 1653 if (image.getDrawable() == this) { 1654 return image; 1655 } 1656 } 1657 } 1658 } 1659 Log.e(LOG_TAG, "---renewBounds: Couldn't find"); 1660 return null; 1661 } 1662 } 1663 } 1664