4747import android .text .Spanned ;
4848import android .text .StaticLayout ;
4949import android .text .TextUtils ;
50- import android .text .TextWatcher ;
5150import android .text .method .KeyListener ;
5251import android .text .method .MetaKeyKeyListener ;
5352import android .text .method .MovementMethod ;
@@ -183,8 +182,6 @@ public class Editor {
183182
184183 Editor (TextView textView ) {
185184 mTextView = textView ;
186- mEasyEditSpanController = new EasyEditSpanController ();
187- mTextView .addTextChangedListener (mEasyEditSpanController );
188185 }
189186
190187 void onAttachedToWindow () {
@@ -1120,7 +1117,7 @@ boolean reportExtractedText() {
11201117 if (contentChanged || ims .mSelectionModeChanged ) {
11211118 ims .mContentChanged = false ;
11221119 ims .mSelectionModeChanged = false ;
1123- final ExtractedTextRequest req = ims .mExtracting ;
1120+ final ExtractedTextRequest req = ims .mExtractedTextRequest ;
11241121 if (req != null ) {
11251122 InputMethodManager imm = InputMethodManager .peekInstance ();
11261123 if (imm != null ) {
@@ -1132,13 +1129,14 @@ boolean reportExtractedText() {
11321129 ims .mChangedStart = EXTRACT_NOTHING ;
11331130 }
11341131 if (extractTextInternal (req , ims .mChangedStart , ims .mChangedEnd ,
1135- ims .mChangedDelta , ims .mTmpExtracted )) {
1132+ ims .mChangedDelta , ims .mExtractedText )) {
11361133 if (TextView .DEBUG_EXTRACT ) Log .v (TextView .LOG_TAG ,
11371134 "Reporting extracted start=" +
1138- ims .mTmpExtracted .partialStartOffset +
1139- " end=" + ims .mTmpExtracted .partialEndOffset +
1140- ": " + ims .mTmpExtracted .text );
1141- imm .updateExtractedText (mTextView , req .token , ims .mTmpExtracted );
1135+ ims .mExtractedText .partialStartOffset +
1136+ " end=" + ims .mExtractedText .partialEndOffset +
1137+ ": " + ims .mExtractedText .text );
1138+
1139+ imm .updateExtractedText (mTextView , req .token , ims .mExtractedText );
11421140 ims .mChangedStart = EXTRACT_UNKNOWN ;
11431141 ims .mChangedEnd = EXTRACT_UNKNOWN ;
11441142 ims .mChangedDelta = 0 ;
@@ -1734,138 +1732,92 @@ void onDrop(DragEvent event) {
17341732 }
17351733 }
17361734
1735+ public void addSpanWatchers (Spannable text ) {
1736+ final int textLength = text .length ();
1737+
1738+ if (mKeyListener != null ) {
1739+ text .setSpan (mKeyListener , 0 , textLength , Spanned .SPAN_INCLUSIVE_INCLUSIVE );
1740+ }
1741+
1742+ if (mEasyEditSpanController == null ) {
1743+ mEasyEditSpanController = new EasyEditSpanController ();
1744+ }
1745+ text .setSpan (mEasyEditSpanController , 0 , textLength , Spanned .SPAN_INCLUSIVE_INCLUSIVE );
1746+ }
1747+
17371748 /**
17381749 * Controls the {@link EasyEditSpan} monitoring when it is added, and when the related
17391750 * pop-up should be displayed.
17401751 */
1741- class EasyEditSpanController implements TextWatcher {
1752+ class EasyEditSpanController implements SpanWatcher {
17421753
17431754 private static final int DISPLAY_TIMEOUT_MS = 3000 ; // 3 secs
17441755
17451756 private EasyEditPopupWindow mPopupWindow ;
17461757
1747- private EasyEditSpan mEasyEditSpan ;
1748-
17491758 private Runnable mHidePopup ;
17501759
1751- public void hide () {
1752- if (mPopupWindow != null ) {
1753- mPopupWindow .hide ();
1754- mTextView .removeCallbacks (mHidePopup );
1755- }
1756- removeSpans (mTextView .getText ());
1757- mEasyEditSpan = null ;
1758- }
1759-
1760- public void beforeTextChanged (CharSequence s , int start , int count , int after ) {
1761- // Intentionally empty
1762- }
1763-
1764- public void afterTextChanged (Editable s ) {
1765- // Intentionally empty
1766- }
1760+ @ Override
1761+ public void onSpanAdded (Spannable text , Object span , int start , int end ) {
1762+ if (span instanceof EasyEditSpan ) {
1763+ if (mPopupWindow == null ) {
1764+ mPopupWindow = new EasyEditPopupWindow ();
1765+ mHidePopup = new Runnable () {
1766+ @ Override
1767+ public void run () {
1768+ hide ();
1769+ }
1770+ };
1771+ }
17671772
1768- /**
1769- * Monitors the changes in the text.
1770- *
1771- * <p>{@link SpanWatcher#onSpanAdded(Spannable, Object, int, int)} cannot be used,
1772- * as the notifications are not sent when a spannable (with spans) is inserted.
1773- */
1774- public void onTextChanged (CharSequence buffer , int start , int before , int after ) {
1775- adjustSpans (buffer , start , after );
1773+ // Make sure there is only at most one EasyEditSpan in the text
1774+ if (mPopupWindow .mEasyEditSpan != null ) {
1775+ text .removeSpan (mPopupWindow .mEasyEditSpan );
1776+ }
17761777
1777- if (mTextView .getWindowVisibility () != View .VISIBLE ) {
1778- // The window is not visible yet, ignore the text change.
1779- return ;
1780- }
1778+ mPopupWindow .setEasyEditSpan ((EasyEditSpan ) span );
17811779
1782- if (mTextView .getLayout () == null ) {
1783- // The view has not been layout yet, ignore the text change
1784- return ;
1785- }
1786-
1787- InputMethodManager imm = InputMethodManager .peekInstance ();
1788- if (!(mTextView instanceof ExtractEditText ) && imm != null && imm .isFullscreenMode ()) {
1789- // The input is in extract mode. We do not have to handle the easy edit in the
1790- // original TextView, as the ExtractEditText will do
1791- return ;
1792- }
1780+ if (mTextView .getWindowVisibility () != View .VISIBLE ) {
1781+ // The window is not visible yet, ignore the text change.
1782+ return ;
1783+ }
17931784
1794- // Remove the current easy edit span, as the text changed, and remove the pop-up
1795- // (if any)
1796- if (mEasyEditSpan != null ) {
1797- if (buffer instanceof Spannable ) {
1798- ((Spannable ) buffer ).removeSpan (mEasyEditSpan );
1785+ if (mTextView .getLayout () == null ) {
1786+ // The view has not been laid out yet, ignore the text change
1787+ return ;
17991788 }
1800- mEasyEditSpan = null ;
1801- }
1802- if (mPopupWindow != null && mPopupWindow .isShowing ()) {
1803- mPopupWindow .hide ();
1804- }
18051789
1806- // Display the new easy edit span (if any).
1807- if (buffer instanceof Spanned ) {
1808- mEasyEditSpan = getSpan ((Spanned ) buffer );
1809- if (mEasyEditSpan != null ) {
1810- if (mPopupWindow == null ) {
1811- mPopupWindow = new EasyEditPopupWindow ();
1812- mHidePopup = new Runnable () {
1813- @ Override
1814- public void run () {
1815- hide ();
1816- }
1817- };
1818- }
1819- mPopupWindow .show (mEasyEditSpan );
1820- mTextView .removeCallbacks (mHidePopup );
1821- mTextView .postDelayed (mHidePopup , DISPLAY_TIMEOUT_MS );
1790+ if (extractedTextModeWillBeStarted ()) {
1791+ // The input is in extract mode. Do not handle the easy edit in
1792+ // the original TextView, as the ExtractEditText will do
1793+ return ;
18221794 }
1795+
1796+ mPopupWindow .show ();
1797+ mTextView .removeCallbacks (mHidePopup );
1798+ mTextView .postDelayed (mHidePopup , DISPLAY_TIMEOUT_MS );
18231799 }
18241800 }
18251801
1826- /**
1827- * Adjusts the spans by removing all of them except the last one.
1828- */
1829- private void adjustSpans (CharSequence buffer , int start , int after ) {
1830- // This method enforces that only one easy edit span is attached to the text.
1831- // A better way to enforce this would be to listen for onSpanAdded, but this method
1832- // cannot be used in this scenario as no notification is triggered when a text with
1833- // spans is inserted into a text.
1834- if (buffer instanceof Spannable ) {
1835- Spannable spannable = (Spannable ) buffer ;
1836- EasyEditSpan [] spans = spannable .getSpans (start , start + after , EasyEditSpan .class );
1837- if (spans .length > 0 ) {
1838- // Assuming there was only one EasyEditSpan before, we only need check to
1839- // check for a duplicate if a new one is found in the modified interval
1840- spans = spannable .getSpans (0 , spannable .length (), EasyEditSpan .class );
1841- for (int i = 1 ; i < spans .length ; i ++) {
1842- spannable .removeSpan (spans [i ]);
1843- }
1844- }
1802+ @ Override
1803+ public void onSpanRemoved (Spannable text , Object span , int start , int end ) {
1804+ if (mPopupWindow != null && span == mPopupWindow .mEasyEditSpan ) {
1805+ hide ();
18451806 }
18461807 }
18471808
1848- /**
1849- * Removes all the {@link EasyEditSpan} currently attached.
1850- */
1851- private void removeSpans (CharSequence buffer ) {
1852- if (buffer instanceof Spannable ) {
1853- Spannable spannable = (Spannable ) buffer ;
1854- EasyEditSpan [] spans = spannable .getSpans (0 , spannable .length (),
1855- EasyEditSpan .class );
1856- for (int i = 0 ; i < spans .length ; i ++) {
1857- spannable .removeSpan (spans [i ]);
1858- }
1809+ @ Override
1810+ public void onSpanChanged (Spannable text , Object span , int previousStart , int previousEnd ,
1811+ int newStart , int newEnd ) {
1812+ if (mPopupWindow != null && span == mPopupWindow .mEasyEditSpan ) {
1813+ text .removeSpan (mPopupWindow .mEasyEditSpan );
18591814 }
18601815 }
18611816
1862- private EasyEditSpan getSpan (Spanned spanned ) {
1863- EasyEditSpan [] easyEditSpans = spanned .getSpans (0 , spanned .length (),
1864- EasyEditSpan .class );
1865- if (easyEditSpans .length == 0 ) {
1866- return null ;
1867- } else {
1868- return easyEditSpans [0 ];
1817+ public void hide () {
1818+ if (mPopupWindow != null ) {
1819+ mPopupWindow .hide ();
1820+ mTextView .removeCallbacks (mHidePopup );
18691821 }
18701822 }
18711823 }
@@ -1910,9 +1862,8 @@ protected void initContentView() {
19101862 mContentView .addView (mDeleteTextView );
19111863 }
19121864
1913- public void show (EasyEditSpan easyEditSpan ) {
1865+ public void setEasyEditSpan (EasyEditSpan easyEditSpan ) {
19141866 mEasyEditSpan = easyEditSpan ;
1915- super .show ();
19161867 }
19171868
19181869 @ Override
@@ -3738,8 +3689,8 @@ static class InputMethodState {
37383689 Rect mCursorRectInWindow = new Rect ();
37393690 RectF mTmpRectF = new RectF ();
37403691 float [] mTmpOffset = new float [2 ];
3741- ExtractedTextRequest mExtracting ;
3742- final ExtractedText mTmpExtracted = new ExtractedText ();
3692+ ExtractedTextRequest mExtractedTextRequest ;
3693+ final ExtractedText mExtractedText = new ExtractedText ();
37433694 int mBatchEditNesting ;
37443695 boolean mCursorChanged ;
37453696 boolean mSelectionModeChanged ;
0 commit comments