@@ -100,4 +100,74 @@ object EnhancedMovementMethod : ArrowKeyMovementMethod() {
100
100
101
101
return super .onTouchEvent(widget, text, event)
102
102
}
103
+
104
+ fun handleLinkTouchEvent (widget : TextView , text : Spannable , event : MotionEvent ): LinkTouchEventResult {
105
+ var x = event.x.toInt()
106
+ var y = event.y.toInt()
107
+
108
+ x - = widget.totalPaddingLeft
109
+ y - = widget.totalPaddingTop
110
+
111
+ x + = widget.scrollX
112
+ y + = widget.scrollY
113
+
114
+ val layout = widget.layout
115
+ val line = layout.getLineForVertical(y)
116
+ val off = layout.getOffsetForHorizontal(line, x.toFloat())
117
+
118
+ // get the character's position. This may be the left or the right edge of the character so, find the
119
+ // other edge by inspecting nearby characters (if they exist)
120
+ val charX = layout.getPrimaryHorizontal(off)
121
+ val charPrevX = if (off > 0 ) layout.getPrimaryHorizontal(off - 1 ) else charX
122
+ val charNextX = if (off < text.length) layout.getPrimaryHorizontal(off + 1 ) else charX
123
+
124
+ val lineRect = Rect ()
125
+ layout.getLineBounds(line, lineRect)
126
+
127
+ val clickedWithinLineHeight = y >= lineRect.top && y <= lineRect.bottom
128
+ val clickedOnSpanToTheLeftOfCursor = x.toFloat() in charPrevX.. charX
129
+ val clickedOnSpanToTheRightOfCursor = x.toFloat() in charX.. charNextX
130
+
131
+ val clickedOnSpan = clickedWithinLineHeight &&
132
+ (clickedOnSpanToTheLeftOfCursor || clickedOnSpanToTheRightOfCursor)
133
+
134
+ val clickedSpanBordersAnotherOne = (text.getSpans(off, off, ClickableSpan ::class .java).size == 1 &&
135
+ text.getSpans(off + 1 , off + 1 , ClickableSpan ::class .java).isNotEmpty())
136
+
137
+ val isClickedSpanAmbiguous = text.getSpans(off, off, ClickableSpan ::class .java).size > 1
138
+
139
+ val failedToPinpointClickedSpan = (isClickedSpanAmbiguous || clickedSpanBordersAnotherOne)
140
+ && ! clickedOnSpanToTheLeftOfCursor && ! clickedOnSpanToTheRightOfCursor
141
+
142
+ var link: ClickableSpan ? = null
143
+
144
+ if (clickedOnSpan) {
145
+ link = if (isClickedSpanAmbiguous) {
146
+ if (clickedOnSpanToTheLeftOfCursor) {
147
+ text.getSpans(off, off, ClickableSpan ::class .java)[0 ]
148
+ } else {
149
+ text.getSpans(off, off, ClickableSpan ::class .java)[1 ]
150
+ }
151
+ } else {
152
+ text.getSpans(off, off, ClickableSpan ::class .java).firstOrNull()
153
+ }
154
+ } else if (failedToPinpointClickedSpan) {
155
+ link = text.getSpans(off, off, ClickableSpan ::class .java).firstOrNull { text.getSpanStart(it) == off }
156
+ }
157
+
158
+ if (link != null ) {
159
+ if (link is AztecMediaClickableSpan || link is UnknownClickableSpan ) {
160
+ link.onClick(widget)
161
+ return LinkTouchEventResult (true )
162
+ } else if (link is AztecURLSpan && isLinkTapEnabled) {
163
+ linkTappedListenerRef.get()?.onLinkTapped(widget, link.url) ? : link.onClick(widget)
164
+ return LinkTouchEventResult (true )
165
+ }
166
+ }
167
+
168
+ return LinkTouchEventResult (false )
169
+ }
103
170
}
171
+
172
+ @JvmInline
173
+ value class LinkTouchEventResult (val handled : Boolean )
0 commit comments