Merge "Fix random SmartLinkify-related TextView bugs." into pi-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
d982f561cc
@@ -16,6 +16,7 @@
|
||||
|
||||
package android.text.method;
|
||||
|
||||
import android.os.Build;
|
||||
import android.text.Layout;
|
||||
import android.text.NoCopySpan;
|
||||
import android.text.Selection;
|
||||
@@ -35,6 +36,8 @@ public class LinkMovementMethod extends ScrollingMovementMethod {
|
||||
private static final int UP = 2;
|
||||
private static final int DOWN = 3;
|
||||
|
||||
private static final int HIDE_FLOATING_TOOLBAR_DELAY_MS = 200;
|
||||
|
||||
@Override
|
||||
public boolean canSelectArbitrarily() {
|
||||
return true;
|
||||
@@ -65,7 +68,7 @@ public class LinkMovementMethod extends ScrollingMovementMethod {
|
||||
|
||||
return super.up(widget, buffer);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean down(TextView widget, Spannable buffer) {
|
||||
if (action(DOWN, widget, buffer)) {
|
||||
@@ -215,6 +218,12 @@ public class LinkMovementMethod extends ScrollingMovementMethod {
|
||||
if (action == MotionEvent.ACTION_UP) {
|
||||
links[0].onClick(widget);
|
||||
} else if (action == MotionEvent.ACTION_DOWN) {
|
||||
if (widget.getContext().getApplicationInfo().targetSdkVersion
|
||||
> Build.VERSION_CODES.O_MR1) {
|
||||
// Selection change will reposition the toolbar. Hide it for a few ms for a
|
||||
// smoother transition.
|
||||
widget.hideFloatingToolbar(HIDE_FLOATING_TOOLBAR_DELAY_MS);
|
||||
}
|
||||
Selection.setSelection(buffer,
|
||||
buffer.getSpanStart(links[0]),
|
||||
buffer.getSpanEnd(links[0]));
|
||||
|
||||
@@ -289,6 +289,7 @@ public class Editor {
|
||||
boolean mShowSoftInputOnFocus = true;
|
||||
private boolean mPreserveSelection;
|
||||
private boolean mRestartActionModeOnNextRefresh;
|
||||
private boolean mRequestingLinkActionMode;
|
||||
|
||||
private SelectionActionModeHelper mSelectionActionModeHelper;
|
||||
|
||||
@@ -2127,6 +2128,7 @@ public class Editor {
|
||||
return;
|
||||
}
|
||||
stopTextActionMode();
|
||||
mRequestingLinkActionMode = true;
|
||||
getSelectionActionModeHelper().startLinkActionModeAsync(start, end);
|
||||
}
|
||||
|
||||
@@ -2212,7 +2214,9 @@ public class Editor {
|
||||
mTextActionMode = mTextView.startActionMode(actionModeCallback, ActionMode.TYPE_FLOATING);
|
||||
|
||||
final boolean selectionStarted = mTextActionMode != null;
|
||||
if (selectionStarted && !mTextView.isTextSelectable() && mShowSoftInputOnFocus) {
|
||||
if (selectionStarted
|
||||
&& mTextView.isTextEditable() && !mTextView.isTextSelectable()
|
||||
&& mShowSoftInputOnFocus) {
|
||||
// Show the IME to be able to replace text, except when selecting non editable text.
|
||||
final InputMethodManager imm = InputMethodManager.peekInstance();
|
||||
if (imm != null) {
|
||||
@@ -2322,10 +2326,14 @@ public class Editor {
|
||||
if (!selectAllGotFocus && text.length() > 0) {
|
||||
// Move cursor
|
||||
final int offset = mTextView.getOffsetForPosition(event.getX(), event.getY());
|
||||
Selection.setSelection((Spannable) text, offset);
|
||||
if (mSpellChecker != null) {
|
||||
// When the cursor moves, the word that was typed may need spell check
|
||||
mSpellChecker.onSelectionChanged();
|
||||
|
||||
final boolean shouldInsertCursor = !mRequestingLinkActionMode;
|
||||
if (shouldInsertCursor) {
|
||||
Selection.setSelection((Spannable) text, offset);
|
||||
if (mSpellChecker != null) {
|
||||
// When the cursor moves, the word that was typed may need spell check
|
||||
mSpellChecker.onSelectionChanged();
|
||||
}
|
||||
}
|
||||
|
||||
if (!extractedTextModeWillBeStarted()) {
|
||||
@@ -2335,16 +2343,17 @@ public class Editor {
|
||||
mTextView.removeCallbacks(mInsertionActionModeRunnable);
|
||||
}
|
||||
|
||||
mShowSuggestionRunnable = new Runnable() {
|
||||
public void run() {
|
||||
replace();
|
||||
}
|
||||
};
|
||||
mShowSuggestionRunnable = this::replace;
|
||||
|
||||
// removeCallbacks is performed on every touch
|
||||
mTextView.postDelayed(mShowSuggestionRunnable,
|
||||
ViewConfiguration.getDoubleTapTimeout());
|
||||
} else if (hasInsertionController()) {
|
||||
getInsertionController().show();
|
||||
if (shouldInsertCursor) {
|
||||
getInsertionController().show();
|
||||
} else {
|
||||
getInsertionController().hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4170,6 +4179,7 @@ public class Editor {
|
||||
}
|
||||
|
||||
mAssistClickHandlers.clear();
|
||||
mRequestingLinkActionMode = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -71,7 +71,7 @@ public final class SelectionActionModeHelper {
|
||||
private final TextClassificationHelper mTextClassificationHelper;
|
||||
private final TextClassificationConstants mTextClassificationSettings;
|
||||
|
||||
private TextClassification mTextClassification;
|
||||
@Nullable private TextClassification mTextClassification;
|
||||
private AsyncTask mTextClassificationAsyncTask;
|
||||
|
||||
private final SelectionTracker mSelectionTracker;
|
||||
@@ -124,7 +124,8 @@ public final class SelectionActionModeHelper {
|
||||
: mTextClassificationHelper::classifyText,
|
||||
mSmartSelectSprite != null
|
||||
? this::startSelectionActionModeWithSmartSelectAnimation
|
||||
: this::startSelectionActionMode)
|
||||
: this::startSelectionActionMode,
|
||||
mTextClassificationHelper::getOriginalSelection)
|
||||
.execute();
|
||||
}
|
||||
}
|
||||
@@ -143,7 +144,8 @@ public final class SelectionActionModeHelper {
|
||||
mTextView,
|
||||
mTextClassificationHelper.getTimeoutDuration(),
|
||||
mTextClassificationHelper::classifyText,
|
||||
this::startLinkActionMode)
|
||||
this::startLinkActionMode,
|
||||
mTextClassificationHelper::getOriginalSelection)
|
||||
.execute();
|
||||
}
|
||||
}
|
||||
@@ -158,7 +160,8 @@ public final class SelectionActionModeHelper {
|
||||
mTextView,
|
||||
mTextClassificationHelper.getTimeoutDuration(),
|
||||
mTextClassificationHelper::classifyText,
|
||||
this::invalidateActionMode)
|
||||
this::invalidateActionMode,
|
||||
mTextClassificationHelper::getOriginalSelection)
|
||||
.execute();
|
||||
}
|
||||
}
|
||||
@@ -822,6 +825,7 @@ public final class SelectionActionModeHelper {
|
||||
private final int mTimeOutDuration;
|
||||
private final Supplier<SelectionResult> mSelectionResultSupplier;
|
||||
private final Consumer<SelectionResult> mSelectionResultCallback;
|
||||
private final Supplier<SelectionResult> mTimeOutResultSupplier;
|
||||
private final TextView mTextView;
|
||||
private final String mOriginalText;
|
||||
|
||||
@@ -830,16 +834,19 @@ public final class SelectionActionModeHelper {
|
||||
* @param timeOut time in milliseconds to timeout the query if it has not completed
|
||||
* @param selectionResultSupplier fetches the selection results. Runs on a background thread
|
||||
* @param selectionResultCallback receives the selection results. Runs on the UiThread
|
||||
* @param timeOutResultSupplier default result if the task times out
|
||||
*/
|
||||
TextClassificationAsyncTask(
|
||||
@NonNull TextView textView, int timeOut,
|
||||
@NonNull Supplier<SelectionResult> selectionResultSupplier,
|
||||
@NonNull Consumer<SelectionResult> selectionResultCallback) {
|
||||
@NonNull Consumer<SelectionResult> selectionResultCallback,
|
||||
@NonNull Supplier<SelectionResult> timeOutResultSupplier) {
|
||||
super(textView != null ? textView.getHandler() : null);
|
||||
mTextView = Preconditions.checkNotNull(textView);
|
||||
mTimeOutDuration = timeOut;
|
||||
mSelectionResultSupplier = Preconditions.checkNotNull(selectionResultSupplier);
|
||||
mSelectionResultCallback = Preconditions.checkNotNull(selectionResultCallback);
|
||||
mTimeOutResultSupplier = Preconditions.checkNotNull(timeOutResultSupplier);
|
||||
// Make a copy of the original text.
|
||||
mOriginalText = getText(mTextView).toString();
|
||||
}
|
||||
@@ -863,7 +870,7 @@ public final class SelectionActionModeHelper {
|
||||
|
||||
private void onTimeOut() {
|
||||
if (getStatus() == Status.RUNNING) {
|
||||
onPostExecute(null);
|
||||
onPostExecute(mTimeOutResultSupplier.get());
|
||||
}
|
||||
cancel(true);
|
||||
}
|
||||
@@ -963,6 +970,10 @@ public final class SelectionActionModeHelper {
|
||||
return performClassification(selection);
|
||||
}
|
||||
|
||||
public SelectionResult getOriginalSelection() {
|
||||
return new SelectionResult(mSelectionStart, mSelectionEnd, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maximum time (in milliseconds) to wait for a textclassifier result before timing out.
|
||||
*/
|
||||
@@ -1025,14 +1036,14 @@ public final class SelectionActionModeHelper {
|
||||
private static final class SelectionResult {
|
||||
private final int mStart;
|
||||
private final int mEnd;
|
||||
private final TextClassification mClassification;
|
||||
@Nullable private final TextClassification mClassification;
|
||||
@Nullable private final TextSelection mSelection;
|
||||
|
||||
SelectionResult(int start, int end,
|
||||
TextClassification classification, @Nullable TextSelection selection) {
|
||||
@Nullable TextClassification classification, @Nullable TextSelection selection) {
|
||||
mStart = start;
|
||||
mEnd = end;
|
||||
mClassification = Preconditions.checkNotNull(classification);
|
||||
mClassification = classification;
|
||||
mSelection = selection;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11593,6 +11593,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
}
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void hideFloatingToolbar(int durationMs) {
|
||||
if (mEditor != null) {
|
||||
mEditor.hideFloatingToolbar(durationMs);
|
||||
}
|
||||
}
|
||||
|
||||
boolean canUndo() {
|
||||
return mEditor != null && mEditor.canUndo();
|
||||
}
|
||||
@@ -11687,7 +11694,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
boolean selectAllText() {
|
||||
if (mEditor != null) {
|
||||
// Hide the toolbar before changing the selection to avoid flickering.
|
||||
mEditor.hideFloatingToolbar(FLOATING_TOOLBAR_SELECT_ALL_REFRESH_DELAY);
|
||||
hideFloatingToolbar(FLOATING_TOOLBAR_SELECT_ALL_REFRESH_DELAY);
|
||||
}
|
||||
final int length = mText.length();
|
||||
Selection.setSelection((Spannable) mText, 0, length);
|
||||
|
||||
Reference in New Issue
Block a user