Skip to content

Commit 0b7d747

Browse files
Gilles DebunneAndroid (Google) Code Review
authored andcommitted
Merge "Editor uses a SpanWatcher to track EasyEditSpans"
2 parents db41553 + c62589c commit 0b7d747

File tree

4 files changed

+81
-132
lines changed

4 files changed

+81
-132
lines changed

core/java/android/view/inputmethod/ExtractedText.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public class ExtractedText implements Parcelable {
4545
/**
4646
* If the content is a report of a partial text change, this is the offset
4747
* where the change ends. Note that the actual text may be larger or
48-
* smaller than the difference between this and {@link #partialEndOffset},
48+
* smaller than the difference between this and {@link #partialStartOffset},
4949
* meaning a reduction or increase, respectively, in the total text.
5050
*/
5151
public int partialEndOffset;

core/java/android/widget/Editor.java

Lines changed: 69 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@
4747
import android.text.Spanned;
4848
import android.text.StaticLayout;
4949
import android.text.TextUtils;
50-
import android.text.TextWatcher;
5150
import android.text.method.KeyListener;
5251
import android.text.method.MetaKeyKeyListener;
5352
import android.text.method.MovementMethod;
@@ -184,8 +183,6 @@ public class Editor {
184183

185184
Editor(TextView textView) {
186185
mTextView = textView;
187-
mEasyEditSpanController = new EasyEditSpanController();
188-
mTextView.addTextChangedListener(mEasyEditSpanController);
189186
}
190187

191188
void onAttachedToWindow() {
@@ -1121,7 +1118,7 @@ boolean reportExtractedText() {
11211118
if (contentChanged || ims.mSelectionModeChanged) {
11221119
ims.mContentChanged = false;
11231120
ims.mSelectionModeChanged = false;
1124-
final ExtractedTextRequest req = ims.mExtracting;
1121+
final ExtractedTextRequest req = ims.mExtractedTextRequest;
11251122
if (req != null) {
11261123
InputMethodManager imm = InputMethodManager.peekInstance();
11271124
if (imm != null) {
@@ -1133,13 +1130,14 @@ boolean reportExtractedText() {
11331130
ims.mChangedStart = EXTRACT_NOTHING;
11341131
}
11351132
if (extractTextInternal(req, ims.mChangedStart, ims.mChangedEnd,
1136-
ims.mChangedDelta, ims.mTmpExtracted)) {
1133+
ims.mChangedDelta, ims.mExtractedText)) {
11371134
if (TextView.DEBUG_EXTRACT) Log.v(TextView.LOG_TAG,
11381135
"Reporting extracted start=" +
1139-
ims.mTmpExtracted.partialStartOffset +
1140-
" end=" + ims.mTmpExtracted.partialEndOffset +
1141-
": " + ims.mTmpExtracted.text);
1142-
imm.updateExtractedText(mTextView, req.token, ims.mTmpExtracted);
1136+
ims.mExtractedText.partialStartOffset +
1137+
" end=" + ims.mExtractedText.partialEndOffset +
1138+
": " + ims.mExtractedText.text);
1139+
1140+
imm.updateExtractedText(mTextView, req.token, ims.mExtractedText);
11431141
ims.mChangedStart = EXTRACT_UNKNOWN;
11441142
ims.mChangedEnd = EXTRACT_UNKNOWN;
11451143
ims.mChangedDelta = 0;
@@ -1775,138 +1773,92 @@ void onDrop(DragEvent event) {
17751773
}
17761774
}
17771775

1776+
public void addSpanWatchers(Spannable text) {
1777+
final int textLength = text.length();
1778+
1779+
if (mKeyListener != null) {
1780+
text.setSpan(mKeyListener, 0, textLength, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1781+
}
1782+
1783+
if (mEasyEditSpanController == null) {
1784+
mEasyEditSpanController = new EasyEditSpanController();
1785+
}
1786+
text.setSpan(mEasyEditSpanController, 0, textLength, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1787+
}
1788+
17781789
/**
17791790
* Controls the {@link EasyEditSpan} monitoring when it is added, and when the related
17801791
* pop-up should be displayed.
17811792
*/
1782-
class EasyEditSpanController implements TextWatcher {
1793+
class EasyEditSpanController implements SpanWatcher {
17831794

17841795
private static final int DISPLAY_TIMEOUT_MS = 3000; // 3 secs
17851796

17861797
private EasyEditPopupWindow mPopupWindow;
17871798

1788-
private EasyEditSpan mEasyEditSpan;
1789-
17901799
private Runnable mHidePopup;
17911800

1792-
public void hide() {
1793-
if (mPopupWindow != null) {
1794-
mPopupWindow.hide();
1795-
mTextView.removeCallbacks(mHidePopup);
1796-
}
1797-
removeSpans(mTextView.getText());
1798-
mEasyEditSpan = null;
1799-
}
1800-
1801-
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
1802-
// Intentionally empty
1803-
}
1804-
1805-
public void afterTextChanged(Editable s) {
1806-
// Intentionally empty
1807-
}
1801+
@Override
1802+
public void onSpanAdded(Spannable text, Object span, int start, int end) {
1803+
if (span instanceof EasyEditSpan) {
1804+
if (mPopupWindow == null) {
1805+
mPopupWindow = new EasyEditPopupWindow();
1806+
mHidePopup = new Runnable() {
1807+
@Override
1808+
public void run() {
1809+
hide();
1810+
}
1811+
};
1812+
}
18081813

1809-
/**
1810-
* Monitors the changes in the text.
1811-
*
1812-
* <p>{@link SpanWatcher#onSpanAdded(Spannable, Object, int, int)} cannot be used,
1813-
* as the notifications are not sent when a spannable (with spans) is inserted.
1814-
*/
1815-
public void onTextChanged(CharSequence buffer, int start, int before, int after) {
1816-
adjustSpans(buffer, start, after);
1814+
// Make sure there is only at most one EasyEditSpan in the text
1815+
if (mPopupWindow.mEasyEditSpan != null) {
1816+
text.removeSpan(mPopupWindow.mEasyEditSpan);
1817+
}
18171818

1818-
if (mTextView.getWindowVisibility() != View.VISIBLE) {
1819-
// The window is not visible yet, ignore the text change.
1820-
return;
1821-
}
1819+
mPopupWindow.setEasyEditSpan((EasyEditSpan) span);
18221820

1823-
if (mTextView.getLayout() == null) {
1824-
// The view has not been layout yet, ignore the text change
1825-
return;
1826-
}
1827-
1828-
InputMethodManager imm = InputMethodManager.peekInstance();
1829-
if (!(mTextView instanceof ExtractEditText) && imm != null && imm.isFullscreenMode()) {
1830-
// The input is in extract mode. We do not have to handle the easy edit in the
1831-
// original TextView, as the ExtractEditText will do
1832-
return;
1833-
}
1821+
if (mTextView.getWindowVisibility() != View.VISIBLE) {
1822+
// The window is not visible yet, ignore the text change.
1823+
return;
1824+
}
18341825

1835-
// Remove the current easy edit span, as the text changed, and remove the pop-up
1836-
// (if any)
1837-
if (mEasyEditSpan != null) {
1838-
if (buffer instanceof Spannable) {
1839-
((Spannable) buffer).removeSpan(mEasyEditSpan);
1826+
if (mTextView.getLayout() == null) {
1827+
// The view has not been laid out yet, ignore the text change
1828+
return;
18401829
}
1841-
mEasyEditSpan = null;
1842-
}
1843-
if (mPopupWindow != null && mPopupWindow.isShowing()) {
1844-
mPopupWindow.hide();
1845-
}
18461830

1847-
// Display the new easy edit span (if any).
1848-
if (buffer instanceof Spanned) {
1849-
mEasyEditSpan = getSpan((Spanned) buffer);
1850-
if (mEasyEditSpan != null) {
1851-
if (mPopupWindow == null) {
1852-
mPopupWindow = new EasyEditPopupWindow();
1853-
mHidePopup = new Runnable() {
1854-
@Override
1855-
public void run() {
1856-
hide();
1857-
}
1858-
};
1859-
}
1860-
mPopupWindow.show(mEasyEditSpan);
1861-
mTextView.removeCallbacks(mHidePopup);
1862-
mTextView.postDelayed(mHidePopup, DISPLAY_TIMEOUT_MS);
1831+
if (extractedTextModeWillBeStarted()) {
1832+
// The input is in extract mode. Do not handle the easy edit in
1833+
// the original TextView, as the ExtractEditText will do
1834+
return;
18631835
}
1836+
1837+
mPopupWindow.show();
1838+
mTextView.removeCallbacks(mHidePopup);
1839+
mTextView.postDelayed(mHidePopup, DISPLAY_TIMEOUT_MS);
18641840
}
18651841
}
18661842

1867-
/**
1868-
* Adjusts the spans by removing all of them except the last one.
1869-
*/
1870-
private void adjustSpans(CharSequence buffer, int start, int after) {
1871-
// This method enforces that only one easy edit span is attached to the text.
1872-
// A better way to enforce this would be to listen for onSpanAdded, but this method
1873-
// cannot be used in this scenario as no notification is triggered when a text with
1874-
// spans is inserted into a text.
1875-
if (buffer instanceof Spannable) {
1876-
Spannable spannable = (Spannable) buffer;
1877-
EasyEditSpan[] spans = spannable.getSpans(start, start + after, EasyEditSpan.class);
1878-
if (spans.length > 0) {
1879-
// Assuming there was only one EasyEditSpan before, we only need check to
1880-
// check for a duplicate if a new one is found in the modified interval
1881-
spans = spannable.getSpans(0, spannable.length(), EasyEditSpan.class);
1882-
for (int i = 1; i < spans.length; i++) {
1883-
spannable.removeSpan(spans[i]);
1884-
}
1885-
}
1843+
@Override
1844+
public void onSpanRemoved(Spannable text, Object span, int start, int end) {
1845+
if (mPopupWindow != null && span == mPopupWindow.mEasyEditSpan) {
1846+
hide();
18861847
}
18871848
}
18881849

1889-
/**
1890-
* Removes all the {@link EasyEditSpan} currently attached.
1891-
*/
1892-
private void removeSpans(CharSequence buffer) {
1893-
if (buffer instanceof Spannable) {
1894-
Spannable spannable = (Spannable) buffer;
1895-
EasyEditSpan[] spans = spannable.getSpans(0, spannable.length(),
1896-
EasyEditSpan.class);
1897-
for (int i = 0; i < spans.length; i++) {
1898-
spannable.removeSpan(spans[i]);
1899-
}
1850+
@Override
1851+
public void onSpanChanged(Spannable text, Object span, int previousStart, int previousEnd,
1852+
int newStart, int newEnd) {
1853+
if (mPopupWindow != null && span == mPopupWindow.mEasyEditSpan) {
1854+
text.removeSpan(mPopupWindow.mEasyEditSpan);
19001855
}
19011856
}
19021857

1903-
private EasyEditSpan getSpan(Spanned spanned) {
1904-
EasyEditSpan[] easyEditSpans = spanned.getSpans(0, spanned.length(),
1905-
EasyEditSpan.class);
1906-
if (easyEditSpans.length == 0) {
1907-
return null;
1908-
} else {
1909-
return easyEditSpans[0];
1858+
public void hide() {
1859+
if (mPopupWindow != null) {
1860+
mPopupWindow.hide();
1861+
mTextView.removeCallbacks(mHidePopup);
19101862
}
19111863
}
19121864
}
@@ -1951,9 +1903,8 @@ protected void initContentView() {
19511903
mContentView.addView(mDeleteTextView);
19521904
}
19531905

1954-
public void show(EasyEditSpan easyEditSpan) {
1906+
public void setEasyEditSpan(EasyEditSpan easyEditSpan) {
19551907
mEasyEditSpan = easyEditSpan;
1956-
super.show();
19571908
}
19581909

19591910
@Override
@@ -3780,8 +3731,8 @@ static class InputMethodState {
37803731
Rect mCursorRectInWindow = new Rect();
37813732
RectF mTmpRectF = new RectF();
37823733
float[] mTmpOffset = new float[2];
3783-
ExtractedTextRequest mExtracting;
3784-
final ExtractedText mTmpExtracted = new ExtractedText();
3734+
ExtractedTextRequest mExtractedTextRequest;
3735+
final ExtractedText mExtractedText = new ExtractedText();
37853736
int mBatchEditNesting;
37863737
boolean mCursorChanged;
37873738
boolean mSelectionModeChanged;

core/java/android/widget/SpellChecker.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,20 +212,21 @@ public void onSelectionChanged() {
212212

213213
public void spellCheck(int start, int end) {
214214
final Locale locale = mTextView.getTextServicesLocale();
215+
final boolean isSessionActive = isSessionActive();
215216
if (mCurrentLocale == null || (!(mCurrentLocale.equals(locale)))) {
216217
setLocale(locale);
217218
// Re-check the entire text
218219
start = 0;
219220
end = mTextView.getText().length();
220221
} else {
221222
final boolean spellCheckerActivated = mTextServicesManager.isSpellCheckerEnabled();
222-
if (isSessionActive() != spellCheckerActivated) {
223+
if (isSessionActive != spellCheckerActivated) {
223224
// Spell checker has been turned of or off since last spellCheck
224225
resetSession();
225226
}
226227
}
227228

228-
if (!isSessionActive()) return;
229+
if (!isSessionActive) return;
229230

230231
// Find first available SpellParser from pool
231232
final int length = mSpellParsers.length;

core/java/android/widget/TextView.java

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3200,22 +3200,19 @@ private void setText(CharSequence text, BufferType type,
32003200
if (text instanceof Spannable && !mAllowTransformationLengthChange) {
32013201
Spannable sp = (Spannable) text;
32023202

3203-
// Remove any ChangeWatchers that might have come
3204-
// from other TextViews.
3203+
// Remove any ChangeWatchers that might have come from other TextViews.
32053204
final ChangeWatcher[] watchers = sp.getSpans(0, sp.length(), ChangeWatcher.class);
32063205
final int count = watchers.length;
3207-
for (int i = 0; i < count; i++)
3206+
for (int i = 0; i < count; i++) {
32083207
sp.removeSpan(watchers[i]);
3208+
}
32093209

3210-
if (mChangeWatcher == null)
3211-
mChangeWatcher = new ChangeWatcher();
3210+
if (mChangeWatcher == null) mChangeWatcher = new ChangeWatcher();
32123211

32133212
sp.setSpan(mChangeWatcher, 0, textLength, Spanned.SPAN_INCLUSIVE_INCLUSIVE |
32143213
(CHANGE_WATCHER_PRIORITY << Spanned.SPAN_PRIORITY_SHIFT));
32153214

3216-
if (mEditor != null && getEditor().mKeyListener != null) {
3217-
sp.setSpan(getEditor().mKeyListener, 0, textLength, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
3218-
}
3215+
if (mEditor != null) mEditor.addSpanWatchers(sp);
32193216

32203217
if (mTransformation != null) {
32213218
sp.setSpan(mTransformation, 0, textLength, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
@@ -5208,7 +5205,7 @@ public void setExtractedText(ExtractedText text) {
52085205
*/
52095206
public void setExtracting(ExtractedTextRequest req) {
52105207
if (getEditor().mInputMethodState != null) {
5211-
getEditor().mInputMethodState.mExtracting = req;
5208+
getEditor().mInputMethodState.mExtractedTextRequest = req;
52125209
}
52135210
// This would stop a possible selection mode, but no such mode is started in case
52145211
// extracted mode will start. Some text is selected though, and will trigger an action mode
@@ -6856,7 +6853,7 @@ void spanChange(Spanned buf, Object what, int oldStart, int newStart, int oldEnd
68566853
if (what instanceof ParcelableSpan) {
68576854
// If this is a span that can be sent to a remote process,
68586855
// the current extract editor would be interested in it.
6859-
if (ims != null && ims.mExtracting != null) {
6856+
if (ims != null && ims.mExtractedTextRequest != null) {
68606857
if (ims.mBatchEditNesting != 0) {
68616858
if (oldStart >= 0) {
68626859
if (ims.mChangedStart > oldStart) {
@@ -6877,7 +6874,7 @@ void spanChange(Spanned buf, Object what, int oldStart, int newStart, int oldEnd
68776874
} else {
68786875
if (DEBUG_EXTRACT) Log.v(LOG_TAG, "Span change outside of batch: "
68796876
+ oldStart + "-" + oldEnd + ","
6880-
+ newStart + "-" + newEnd + what);
6877+
+ newStart + "-" + newEnd + " " + what);
68816878
ims.mContentChanged = true;
68826879
}
68836880
}

0 commit comments

Comments
 (0)