package com.fastaccess.provider.timeline.handler; import android.graphics.RectF; import android.text.Layout; import android.text.Selection; import android.text.Spannable; import android.text.Spanned; import android.text.method.LinkMovementMethod; import android.text.style.BackgroundColorSpan; import android.text.style.ClickableSpan; import android.text.style.URLSpan; import android.text.util.Linkify; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import com.fastaccess.helper.Logger; /** * Created by Kosh on 23 Apr 2017, 12:09 PM * <p> * credit to to https://github.com/Saketme/Better-Link-Movement-Method */ public class BetterLinkMovementExtended extends LinkMovementMethod { private static final Class SPAN_CLASS = ClickableSpan.class; private static final int LINKIFY_NONE = -2; private static BetterLinkMovementExtended singleInstance; private BetterLinkMovementExtended.OnLinkClickListener onLinkClickListener; private final RectF touchedLineBounds = new RectF(); private boolean isUrlHighlighted; private boolean touchStartedOverLink; private int activeTextViewHashcode; public static BetterLinkMovementExtended newInstance() { return new BetterLinkMovementExtended(); } private static BetterLinkMovementExtended linkify(int linkifyMask, TextView... textViews) { BetterLinkMovementExtended movementMethod = newInstance(); TextView[] var3 = textViews; int var4 = textViews.length; for (int var5 = 0; var5 < var4; ++var5) { TextView textView = var3[var5]; addLinks(linkifyMask, movementMethod, textView); } return movementMethod; } public static BetterLinkMovementExtended linkifyHtml(TextView... textViews) { return linkify(-2, (TextView[]) textViews); } private static BetterLinkMovementExtended linkify(int linkifyMask, ViewGroup viewGroup) { BetterLinkMovementExtended movementMethod = newInstance(); rAddLinks(linkifyMask, viewGroup, movementMethod); return movementMethod; } public static BetterLinkMovementExtended linkifyHtml(ViewGroup viewGroup) { return linkify(-2, (ViewGroup) viewGroup); } public static BetterLinkMovementExtended getInstance() { if (singleInstance == null) { singleInstance = new BetterLinkMovementExtended(); } return singleInstance; } private BetterLinkMovementExtended() { } public void setOnLinkClickListener(OnLinkClickListener onLinkClickListener) { if (this == singleInstance) { throw new UnsupportedOperationException("Setting a click listener on the instance returned by getInstance() is not supported. Please " + "use newInstance() or any of the linkify() methods instead."); } else { this.onLinkClickListener = onLinkClickListener; } } private static void rAddLinks(int linkifyMask, ViewGroup viewGroup, BetterLinkMovementExtended movementMethod) { for (int i = 0; i < viewGroup.getChildCount(); ++i) { View child = viewGroup.getChildAt(i); if (child instanceof ViewGroup) { rAddLinks(linkifyMask, (ViewGroup) child, movementMethod); } else if (child instanceof TextView) { TextView textView = (TextView) child; addLinks(linkifyMask, movementMethod, textView); } } } private static void addLinks(int linkifyMask, BetterLinkMovementExtended movementMethod, TextView textView) { textView.setMovementMethod(movementMethod); if (linkifyMask != -2) { Linkify.addLinks(textView, linkifyMask); } } public boolean onTouchEvent(TextView view, Spannable text, MotionEvent event) { if (this.activeTextViewHashcode != view.hashCode()) { this.activeTextViewHashcode = view.hashCode(); view.setAutoLinkMask(0); } BetterLinkMovementExtended.ClickableSpanWithText touchedClickableSpan = this.findClickableSpanUnderTouch(view, text, event); if (touchedClickableSpan != null) { this.highlightUrl(view, touchedClickableSpan, text); } else { this.removeUrlHighlightColor(view); } switch (event.getAction()) { case 0: this.touchStartedOverLink = touchedClickableSpan != null; return true; case 1: if (touchedClickableSpan != null && this.touchStartedOverLink) { this.dispatchUrlClick(view, touchedClickableSpan); this.removeUrlHighlightColor(view); } this.touchStartedOverLink = false; return true; case 2: return true; default: return false; } } private BetterLinkMovementExtended.ClickableSpanWithText findClickableSpanUnderTouch(TextView textView, Spannable text, MotionEvent event) { int touchX = (int) event.getX(); int touchY = (int) event.getY(); touchX -= textView.getTotalPaddingLeft(); touchY -= textView.getTotalPaddingTop(); touchX += textView.getScrollX(); touchY += textView.getScrollY(); Layout layout = textView.getLayout(); int touchedLine = layout.getLineForVertical(touchY); int touchOffset = layout.getOffsetForHorizontal(touchedLine, (float) touchX); this.touchedLineBounds.left = layout.getLineLeft(touchedLine); this.touchedLineBounds.top = (float) layout.getLineTop(touchedLine); this.touchedLineBounds.right = layout.getLineWidth(touchedLine) + this.touchedLineBounds.left; this.touchedLineBounds.bottom = (float) layout.getLineBottom(touchedLine); if (this.touchedLineBounds.contains((float) touchX, (float) touchY)) { Object[] spans = text.getSpans(touchOffset, touchOffset, SPAN_CLASS); for (Object span : spans) { if (span instanceof ClickableSpan) { return ClickableSpanWithText.ofSpan(textView, (ClickableSpan) span); } } return null; } else { return null; } } private void highlightUrl(TextView textView, BetterLinkMovementExtended.ClickableSpanWithText spanWithText, Spannable text) { if (!this.isUrlHighlighted) { this.isUrlHighlighted = true; int spanStart = text.getSpanStart(spanWithText.span()); int spanEnd = text.getSpanEnd(spanWithText.span()); Logger.e(text.getClass().getName()); if (text instanceof URLSpan) { text.setSpan(new BackgroundColorSpan(textView.getHighlightColor()), spanStart, spanEnd, 18); textView.setText(text); Selection.setSelection(text, spanStart, spanEnd); } } } private void removeUrlHighlightColor(TextView textView) { if (this.isUrlHighlighted) { this.isUrlHighlighted = false; Spannable text = (Spannable) textView.getText(); BackgroundColorSpan[] highlightSpans = text.getSpans(0, text.length(), BackgroundColorSpan.class); for (BackgroundColorSpan highlightSpan : highlightSpans) { text.removeSpan(highlightSpan); } try { textView.setText(text); Selection.removeSelection(text); } catch (Exception ignored) {} } } private void dispatchUrlClick(TextView textView, BetterLinkMovementExtended.ClickableSpanWithText spanWithText) { String spanUrl = spanWithText.text(); boolean handled = this.onLinkClickListener != null && this.onLinkClickListener.onClick(textView, spanUrl); if (!handled) { spanWithText.span().onClick(textView); } } static class ClickableSpanWithText { private ClickableSpan span; private String text; static BetterLinkMovementExtended.ClickableSpanWithText ofSpan(TextView textView, ClickableSpan span) { Spanned s = (Spanned) textView.getText(); String text; if (span instanceof URLSpan) { text = ((URLSpan) span).getURL(); } else { int start = s.getSpanStart(span); int end = s.getSpanEnd(span); text = s.subSequence(start, end).toString(); } return new BetterLinkMovementExtended.ClickableSpanWithText(span, text); } private ClickableSpanWithText(ClickableSpan span, String text) { this.span = span; this.text = text; } ClickableSpan span() { return this.span; } String text() { return this.text; } } public interface OnLinkClickListener { boolean onClick(TextView var1, String var2); } }