@@ -47,57 +47,82 @@ object EnhancedMovementMethod : ArrowKeyMovementMethod() {
4747 return true
4848 }
4949
50- // get the character's position. This may be the left or the right edge of the character so, find the
51- // other edge by inspecting nearby characters (if they exist)
52- val charX = layout.getPrimaryHorizontal(off)
53- val charPrevX = if (off > 0 ) layout.getPrimaryHorizontal(off - 1 ) else charX
54- val charNextX = if (off < text.length) layout.getPrimaryHorizontal(off + 1 ) else charX
50+ // If we find that the touch event was on a link, then, we handle it and return immediately.
51+ if (handleLinkTouchEvent(widget = widget, text = text, event = event).handled) {
52+ return true
53+ }
54+ }
55+
56+ return super .onTouchEvent(widget, text, event)
57+ }
58+
59+ fun handleLinkTouchEvent (widget : TextView , text : Spannable , event : MotionEvent ): LinkTouchEventResult {
60+ var x = event.x.toInt()
61+ var y = event.y.toInt()
62+
63+ x - = widget.totalPaddingLeft
64+ y - = widget.totalPaddingTop
5565
56- val lineRect = Rect ()
57- layout.getLineBounds(line, lineRect)
66+ x + = widget.scrollX
67+ y + = widget.scrollY
5868
59- val clickedWithinLineHeight = y >= lineRect.top && y <= lineRect.bottom
60- val clickedOnSpanToTheLeftOfCursor = x.toFloat() in charPrevX .. charX
61- val clickedOnSpanToTheRightOfCursor = x.toFloat() in charX .. charNextX
69+ val layout = widget.layout
70+ val line = layout.getLineForVertical(y)
71+ val off = layout.getOffsetForHorizontal(line, x.toFloat())
6272
63- val clickedOnSpan = clickedWithinLineHeight &&
64- (clickedOnSpanToTheLeftOfCursor || clickedOnSpanToTheRightOfCursor)
73+ // get the character's position. This may be the left or the right edge of the character so, find the
74+ // other edge by inspecting nearby characters (if they exist)
75+ val charX = layout.getPrimaryHorizontal(off)
76+ val charPrevX = if (off > 0 ) layout.getPrimaryHorizontal(off - 1 ) else charX
77+ val charNextX = if (off < text.length) layout.getPrimaryHorizontal(off + 1 ) else charX
6578
66- val clickedSpanBordersAnotherOne = (text.getSpans(off, off, ClickableSpan :: class .java).size == 1 &&
67- text.getSpans(off + 1 , off + 1 , ClickableSpan :: class .java).isNotEmpty() )
79+ val lineRect = Rect ()
80+ layout.getLineBounds(line, lineRect )
6881
69- val isClickedSpanAmbiguous = text.getSpans(off, off, ClickableSpan ::class .java).size > 1
82+ val clickedWithinLineHeight = y >= lineRect.top && y <= lineRect.bottom
83+ val clickedOnSpanToTheLeftOfCursor = x.toFloat() in charPrevX.. charX
84+ val clickedOnSpanToTheRightOfCursor = x.toFloat() in charX.. charNextX
7085
71- val failedToPinpointClickedSpan = (isClickedSpanAmbiguous || clickedSpanBordersAnotherOne)
72- && ! clickedOnSpanToTheLeftOfCursor && ! clickedOnSpanToTheRightOfCursor
86+ val clickedOnSpan = clickedWithinLineHeight &&
87+ ( clickedOnSpanToTheLeftOfCursor || clickedOnSpanToTheRightOfCursor)
7388
74- var link: ClickableSpan ? = null
89+ val clickedSpanBordersAnotherOne = (text.getSpans(off, off, ClickableSpan ::class .java).size == 1 &&
90+ text.getSpans(off + 1 , off + 1 , ClickableSpan ::class .java).isNotEmpty())
7591
76- if (clickedOnSpan) {
77- if (isClickedSpanAmbiguous) {
78- if (clickedOnSpanToTheLeftOfCursor) {
79- link = text.getSpans(off, off, ClickableSpan ::class .java)[0 ]
80- } else if (clickedOnSpanToTheRightOfCursor) {
81- link = text.getSpans(off, off, ClickableSpan ::class .java)[1 ]
82- }
92+ val isClickedSpanAmbiguous = text.getSpans(off, off, ClickableSpan ::class .java).size > 1
93+
94+ val failedToPinpointClickedSpan = (isClickedSpanAmbiguous || clickedSpanBordersAnotherOne)
95+ && ! clickedOnSpanToTheLeftOfCursor && ! clickedOnSpanToTheRightOfCursor
96+
97+ var link: ClickableSpan ? = null
98+
99+ if (clickedOnSpan) {
100+ link = if (isClickedSpanAmbiguous) {
101+ if (clickedOnSpanToTheLeftOfCursor) {
102+ text.getSpans(off, off, ClickableSpan ::class .java)[0 ]
83103 } else {
84- link = text.getSpans(off, off, ClickableSpan ::class .java).firstOrNull()
104+ text.getSpans(off, off, ClickableSpan ::class .java)[ 1 ]
85105 }
86- } else if (failedToPinpointClickedSpan) {
87- link = text.getSpans(off, off, ClickableSpan ::class .java).firstOrNull { text.getSpanStart(it) == off }
106+ } else {
107+ text.getSpans(off, off, ClickableSpan ::class .java).firstOrNull()
88108 }
109+ } else if (failedToPinpointClickedSpan) {
110+ link = text.getSpans(off, off, ClickableSpan ::class .java).firstOrNull { text.getSpanStart(it) == off }
111+ }
89112
90- if (link != null ) {
91- if (link is AztecMediaClickableSpan || link is UnknownClickableSpan ) {
92- link.onClick(widget)
93- return true
94- } else if (link is AztecURLSpan && isLinkTapEnabled) {
95- linkTappedListenerRef.get()?.onLinkTapped(widget, link.url) ? : link.onClick(widget)
96- return true
97- }
113+ if (link != null ) {
114+ if (link is AztecMediaClickableSpan || link is UnknownClickableSpan ) {
115+ link.onClick(widget)
116+ return LinkTouchEventResult (true )
117+ } else if (link is AztecURLSpan && isLinkTapEnabled) {
118+ linkTappedListenerRef.get()?.onLinkTapped(widget, link.url) ? : link.onClick(widget)
119+ return LinkTouchEventResult (true )
98120 }
99121 }
100122
101- return super .onTouchEvent(widget, text, event )
123+ return LinkTouchEventResult ( false )
102124 }
103125}
126+
127+ @JvmInline
128+ value class LinkTouchEventResult (val handled : Boolean )
0 commit comments