package com.orgzly.android.ui.views; import android.content.Context; import android.support.annotation.NonNull; import android.support.v7.widget.AppCompatTextView; import android.text.Layout; import android.text.Spanned; import android.text.style.ClickableSpan; import android.util.AttributeSet; import android.view.MotionEvent; import android.widget.TextView; import com.orgzly.BuildConfig; import com.orgzly.android.util.LogUtils; /** * {@link TextView} with links support. */ public class TextViewWithLinks extends AppCompatTextView { public static final String TAG = TextViewWithLinks.class.getName(); public TextViewWithLinks(Context context) { super(context); } public TextViewWithLinks(Context context, AttributeSet attrs) { super(context, attrs); } public TextViewWithLinks(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public boolean onTouchEvent(@NonNull MotionEvent event) { if (BuildConfig.LOG_DEBUG) LogUtils.d(TAG, event); Layout layout = this.getLayout(); if (layout != null) { int line = layout.getLineForVertical((int) event.getY() - getTotalPaddingTop()); int offset = getOffsetForPosition(event.getX(), event.getY()); if (isEventOnText(event, layout, line) && getText() != null && getText() instanceof Spanned) { Spanned spanned = (Spanned) getText(); ClickableSpan[] links = spanned.getSpans(offset, offset, ClickableSpan.class); if (links.length > 0) { if (event.getAction() == MotionEvent.ACTION_DOWN) { return true; } else if (event.getAction() == MotionEvent.ACTION_UP) { links[0].onClick(this); } else { return super.onTouchEvent(event); } } } } return super.onTouchEvent(event); } /** * Check if event's coordinates are within line's text. * * Needed as getOffsetForHorizontal will return closest character, * which is an issue when clicking the empty space next to the text. */ private boolean isEventOnText(MotionEvent event, Layout layout, int line) { float left = layout.getLineLeft(line) + getTotalPaddingLeft(); float right = layout.getLineRight(line) + getTotalPaddingRight(); float bottom = layout.getLineBottom(line) + getTotalPaddingTop(); float top = layout.getLineTop(line) + getTotalPaddingTop(); return left <= event.getX() && event.getX() <= right && top <= event.getY() && event.getY() <= bottom; } }