Skip to content

Instantly share code, notes, and snippets.

@rubenwardy
Last active August 31, 2017 11:16
Show Gist options
  • Save rubenwardy/a503aaaee25f02566dc98654809d55cb to your computer and use it in GitHub Desktop.
Save rubenwardy/a503aaaee25f02566dc98654809d55cb to your computer and use it in GitHub Desktop.
License: Apache-2.0
package com.example.app
import android.content.Context
import android.text.Html
import android.text.SpannableString
import android.text.Spanned
import android.text.style.URLSpan
import android.text.util.Linkify
import android.util.AttributeSet
import org.sufficientlysecure.htmltextview.*
import java.lang.reflect.Field
import java.lang.reflect.Method
import android.util.Log
import android.view.View
// Waiting on https://github.com/PrivacyApps/html-textview/issues/87
// (I'd rather not fork the library, or include it in the project)
class MyHtmlTextView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : HtmlTextView(context, attrs, defStyleAttr) {
// Callback
var onLinkClicked: ((String) -> Boolean)? = null
//
// Expose private members and methods :(
//
companion object {
private val clickableTableSpanField: Field = HtmlTextView::class.java.getDeclaredField("clickableTableSpan")
private val drawTableLinkSpanField: Field = HtmlTextView::class.java.getDeclaredField("drawTableLinkSpan")
private val removeFromHtmlSpaceField: Field = HtmlTextView::class.java.getDeclaredField("removeFromHtmlSpace")
private val removeHtmlBottomPaddingMethod: Method = HtmlTextView::class.java.getDeclaredMethod("removeHtmlBottomPadding", CharSequence::class.java)
}
init {
clickableTableSpanField.isAccessible = true
drawTableLinkSpanField.isAccessible = true
removeFromHtmlSpaceField.isAccessible = true
removeHtmlBottomPaddingMethod.isAccessible = true
}
private val clickableTableSpan
get() = clickableTableSpanField.get(this) as ClickableTableSpan?
private val drawTableLinkSpan
get() = drawTableLinkSpanField.get(this) as DrawTableLinkSpan?
private val removeFromHtmlSpace
get() = removeFromHtmlSpaceField.get(this) as Boolean
private fun removeHtmlBottomPadding(text: CharSequence?): CharSequence? =
removeHtmlBottomPaddingMethod.invoke(null, text) as CharSequence?
private fun callOverrideTagsMethod(htmlTagHandler: HtmlTagHandler, html: String): String {
val overrideTagsMethod = htmlTagHandler::class.java.getDeclaredMethod("overrideTags", String::class.java)
overrideTagsMethod.isAccessible = true
return overrideTagsMethod.invoke(htmlTagHandler, html) as String
}
//
// Override function
//
override fun setHtml(html: String, imageGetter: Html.ImageGetter?) {
val htmlTagHandler = HtmlTagHandler(paint)
htmlTagHandler.setClickableTableSpan(clickableTableSpan)
htmlTagHandler.setDrawTableLinkSpan(drawTableLinkSpan)
val htmlCorrected = callOverrideTagsMethod(htmlTagHandler, html)
val spans =
if (removeFromHtmlSpace) {
removeHtmlBottomPadding(Html.fromHtml(htmlCorrected, imageGetter, htmlTagHandler)) as Spanned
} else {
Html.fromHtml(htmlCorrected, imageGetter, htmlTagHandler)
}
text = addLinkCallbacksToSpanned(spans)
// make links work
movementMethod = LocalLinkMovementMethod.getInstance()
}
private fun addLinkCallbacksToSpanned(text: Spanned): Spanned {
val buffer = SpannableString(text)
Linkify.addLinks(buffer, Linkify.WEB_URLS)
val spans = text.getSpans(0, text.length, URLSpan::class.java) as Array<URLSpan>
for (us in spans) {
val url = us.url
val end = text.getSpanEnd(us) //Getting the spans position
val start = text.getSpanStart(us)
buffer.setSpan(object : URLSpan(us.url) { // A new span with the same URL
override fun onClick(widget: View) {
if (onLinkClicked?.invoke(url) != true) {
super.onClick(widget)
}
}
}, start, end, 0) //Overwriting the old span
}
return buffer
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment