Merge "Fix random SmartLinkify-related TextView bugs." into pi-dev

This commit is contained in:
TreeHugger Robot
2018-03-26 23:21:37 +00:00
committed by Android (Google) Code Review
4 changed files with 59 additions and 22 deletions

View File

@@ -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]));

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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);