Skip to content

Commit

Permalink
Make TextViewWithClickableSpan activate only if you don't touch a link.
Browse files Browse the repository at this point in the history
BUG=390226,418989,427087

Review URL: https://codereview.chromium.org/697183002

Cr-Commit-Position: refs/heads/master@{#303996}
  • Loading branch information
minorninth authored and Commit bot committed Nov 13, 2014
1 parent eae8afa commit e14afb1
Showing 1 changed file with 73 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,31 @@
package org.chromium.ui.widget;

import android.content.Context;
import android.os.Bundle;
import android.text.Layout;
import android.text.SpannableString;
import android.text.style.ClickableSpan;
import android.util.AttributeSet;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.PopupMenu;
import android.widget.TextView;

/**
* ClickableSpan isn't accessible by default, so we create a simple subclass
* of TextView that addresses this by adding click and longpress handlers.
* If there's only one ClickableSpan, we activate it. If there's more than
* one, we pop up a Spinner to disambiguate.
* ClickableSpan isn't accessible by default, so we create a subclass
* of TextView that tries to handle the case where a user clicks on a view
* and not directly on one of the clickable spans. We do nothing if it's a
* touch event directly on a ClickableSpan. Otherwise if there's only one
* ClickableSpan, we activate it. If there's more than one, we pop up a
* PopupMenu to disambiguate.
*/
public class TextViewWithClickableSpans extends TextView {
private AccessibilityManager mAccessibilityManager;

public TextViewWithClickableSpans(Context context) {
super(context);
init();
Expand All @@ -37,21 +46,74 @@ public TextViewWithClickableSpans(Context context, AttributeSet attrs, int defSt
}

private void init() {
setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
handleClick();
}
});
mAccessibilityManager = (AccessibilityManager)
getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (!mAccessibilityManager.isTouchExplorationEnabled()) {
return false;
}
openDisambiguationMenu();
return true;
}
});
}

@Override
public boolean performAccessibilityAction(int action, Bundle arguments) {
// BrailleBack will generate an accessibility click event directly
// on this view, make sure we handle that correctly.
if (action == AccessibilityNodeInfo.ACTION_CLICK) {
handleAccessibilityClick();
return true;
}
return super.performAccessibilityAction(action, arguments);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
boolean superResult = super.onTouchEvent(event);

if (event.getAction() != MotionEvent.ACTION_UP
&& mAccessibilityManager.isTouchExplorationEnabled()
&& !touchIntersectsAnyClickableSpans(event)) {
handleAccessibilityClick();
return true;
}

return superResult;
}

private boolean touchIntersectsAnyClickableSpans(MotionEvent event) {
// This logic is borrowed from android.text.method.LinkMovementMethod.
//
// ClickableSpan doesn't stop propagation of the event in its click handler,
// so we should only try to simplify clicking on a clickable span if the touch event
// isn't already over a clickable span.

CharSequence text = getText();
if (!(text instanceof SpannableString)) return false;
SpannableString spannable = (SpannableString) text;

int x = (int) event.getX();
int y = (int) event.getY();

x -= getTotalPaddingLeft();
y -= getTotalPaddingTop();

x += getScrollX();
y += getScrollY();

Layout layout = getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);

ClickableSpan[] clickableSpans =
spannable.getSpans(off, off, ClickableSpan.class);
return clickableSpans.length > 0;
}

private ClickableSpan[] getClickableSpans() {
CharSequence text = getText();
if (!(text instanceof SpannableString)) return null;
Expand All @@ -60,7 +122,7 @@ private ClickableSpan[] getClickableSpans() {
return spannable.getSpans(0, spannable.length(), ClickableSpan.class);
}

private void handleClick() {
private void handleAccessibilityClick() {
ClickableSpan[] clickableSpans = getClickableSpans();
if (clickableSpans == null || clickableSpans.length == 0) {
return;
Expand Down

0 comments on commit e14afb1

Please sign in to comment.