SmartLinkify - handle keyboard clicks

Bug: 77998709
Test: bit FrameworksCoreTests:android.widget.TextViewActivityTest
Test: bit CtsWidgetTestCases:android.widget.cts.TextViewTest
Change-Id: Ibb95a736248643949a7b521368374084f9f133ca
This commit is contained in:
Abodunrinwa Toki
2018-04-16 10:05:16 +01:00
parent d41e669fca
commit 33fa382b8f
2 changed files with 86 additions and 49 deletions

View File

@@ -25,6 +25,7 @@ import android.text.style.ClickableSpan;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.textclassifier.TextLinks.TextLinkSpan;
import android.widget.TextView;
/**
@@ -130,64 +131,70 @@ public class LinkMovementMethod extends ScrollingMovementMethod {
selStart = selEnd = -1;
switch (what) {
case CLICK:
if (selStart == selEnd) {
return false;
}
case CLICK:
if (selStart == selEnd) {
return false;
}
ClickableSpan[] link = buffer.getSpans(selStart, selEnd, ClickableSpan.class);
ClickableSpan[] links = buffer.getSpans(selStart, selEnd, ClickableSpan.class);
if (link.length != 1)
return false;
if (links.length != 1) {
return false;
}
link[0].onClick(widget);
break;
ClickableSpan link = links[0];
if (link instanceof TextLinkSpan) {
((TextLinkSpan) link).onClick(widget, TextLinkSpan.INVOCATION_METHOD_KEYBOARD);
} else {
link.onClick(widget);
}
break;
case UP:
int bestStart, bestEnd;
case UP:
int bestStart, bestEnd;
bestStart = -1;
bestEnd = -1;
bestStart = -1;
bestEnd = -1;
for (int i = 0; i < candidates.length; i++) {
int end = buffer.getSpanEnd(candidates[i]);
for (int i = 0; i < candidates.length; i++) {
int end = buffer.getSpanEnd(candidates[i]);
if (end < selEnd || selStart == selEnd) {
if (end > bestEnd) {
bestStart = buffer.getSpanStart(candidates[i]);
bestEnd = end;
if (end < selEnd || selStart == selEnd) {
if (end > bestEnd) {
bestStart = buffer.getSpanStart(candidates[i]);
bestEnd = end;
}
}
}
}
if (bestStart >= 0) {
Selection.setSelection(buffer, bestEnd, bestStart);
return true;
}
if (bestStart >= 0) {
Selection.setSelection(buffer, bestEnd, bestStart);
return true;
}
break;
break;
case DOWN:
bestStart = Integer.MAX_VALUE;
bestEnd = Integer.MAX_VALUE;
case DOWN:
bestStart = Integer.MAX_VALUE;
bestEnd = Integer.MAX_VALUE;
for (int i = 0; i < candidates.length; i++) {
int start = buffer.getSpanStart(candidates[i]);
for (int i = 0; i < candidates.length; i++) {
int start = buffer.getSpanStart(candidates[i]);
if (start > selStart || selStart == selEnd) {
if (start < bestStart) {
bestStart = start;
bestEnd = buffer.getSpanEnd(candidates[i]);
if (start > selStart || selStart == selEnd) {
if (start < bestStart) {
bestStart = start;
bestEnd = buffer.getSpanEnd(candidates[i]);
}
}
}
}
if (bestEnd < Integer.MAX_VALUE) {
Selection.setSelection(buffer, bestStart, bestEnd);
return true;
}
if (bestEnd < Integer.MAX_VALUE) {
Selection.setSelection(buffer, bestStart, bestEnd);
return true;
}
break;
break;
}
return false;
@@ -215,8 +222,14 @@ public class LinkMovementMethod extends ScrollingMovementMethod {
ClickableSpan[] links = buffer.getSpans(off, off, ClickableSpan.class);
if (links.length != 0) {
ClickableSpan link = links[0];
if (action == MotionEvent.ACTION_UP) {
links[0].onClick(widget);
if (link instanceof TextLinkSpan) {
((TextLinkSpan) link).onClick(
widget, TextLinkSpan.INVOCATION_METHOD_TOUCH);
} else {
link.onClick(widget);
}
} else if (action == MotionEvent.ACTION_DOWN) {
if (widget.getContext().getApplicationInfo().targetSdkVersion
> Build.VERSION_CODES.O_MR1) {
@@ -225,8 +238,8 @@ public class LinkMovementMethod extends ScrollingMovementMethod {
widget.hideFloatingToolbar(HIDE_FLOATING_TOOLBAR_DELAY_MS);
}
Selection.setSelection(buffer,
buffer.getSpanStart(links[0]),
buffer.getSpanEnd(links[0]));
buffer.getSpanStart(link),
buffer.getSpanEnd(link));
}
return true;
} else {

View File

@@ -503,6 +503,22 @@ public final class TextLinks implements Parcelable {
*/
public static class TextLinkSpan extends ClickableSpan {
/**
* How the clickspan is triggered.
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({INVOCATION_METHOD_UNSPECIFIED, INVOCATION_METHOD_TOUCH,
INVOCATION_METHOD_KEYBOARD})
public @interface InvocationMethod {}
/** @hide */
public static final int INVOCATION_METHOD_UNSPECIFIED = -1;
/** @hide */
public static final int INVOCATION_METHOD_TOUCH = 0;
/** @hide */
public static final int INVOCATION_METHOD_KEYBOARD = 1;
private final TextLink mTextLink;
public TextLinkSpan(@NonNull TextLink textLink) {
@@ -511,16 +527,24 @@ public final class TextLinks implements Parcelable {
@Override
public void onClick(View widget) {
onClick(widget, INVOCATION_METHOD_UNSPECIFIED);
}
/** @hide */
public final void onClick(View widget, @InvocationMethod int invocationMethod) {
if (widget instanceof TextView) {
final TextView textView = (TextView) widget;
final Context context = textView.getContext();
if (TextClassificationManager.getSettings(context).isSmartLinkifyEnabled()) {
if (textView.requestFocus()) {
textView.requestActionMode(this);
} else {
// If textView can not take focus, then simply handle the click as it will
// be difficult to get rid of the floating action mode.
textView.handleClick(this);
switch (invocationMethod) {
case INVOCATION_METHOD_TOUCH:
textView.requestActionMode(this);
break;
case INVOCATION_METHOD_KEYBOARD:// fall though
case INVOCATION_METHOD_UNSPECIFIED: // fall through
default:
textView.handleClick(this);
break;
}
} else {
if (mTextLink.mUrlSpan != null) {