package ani.dantotsu.others import android.graphics.Color import android.text.Spannable import android.text.Spanned import android.text.TextPaint import android.text.style.CharacterStyle import android.text.style.ClickableSpan import android.view.View import android.widget.TextView import io.noties.markwon.AbstractMarkwonPlugin import io.noties.markwon.utils.ColorUtils import java.util.regex.Pattern class SpoilerPlugin : AbstractMarkwonPlugin() { override fun beforeSetText(textView: TextView, markdown: Spanned) { applySpoilerSpans(markdown as Spannable) } private class RedditSpoilerSpan : CharacterStyle() { private var revealed = false override fun updateDrawState(tp: TextPaint) { if (!revealed) { // use the same text color tp.bgColor = Color.DKGRAY tp.color = Color.DKGRAY } else { // for example keep a bit of black background to remind that it is a spoiler tp.bgColor = ColorUtils.applyAlpha(Color.DKGRAY, 25) } } fun setRevealed(revealed: Boolean) { this.revealed = revealed } } // we also could make text size smaller (but then MetricAffectingSpan should be used) private class HideSpoilerSyntaxSpan : CharacterStyle() { override fun updateDrawState(tp: TextPaint) { // set transparent color tp.color = 0 } } companion object { private val RE = Pattern.compile("\\|\\|.+?\\|\\|") private fun applySpoilerSpans(spannable: Spannable) { val text = spannable.toString() val matcher = RE.matcher(text) while (matcher.find()) { val spoilerSpan = RedditSpoilerSpan() val clickableSpan: ClickableSpan = object : ClickableSpan() { override fun onClick(widget: View) { spoilerSpan.setRevealed(true) widget.postInvalidateOnAnimation() } override fun updateDrawState(ds: TextPaint) { // no op } } val s = matcher.start() val e = matcher.end() spannable.setSpan(spoilerSpan, s, e, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) spannable.setSpan(clickableSpan, s, e, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) // we also can hide original syntax spannable.setSpan( HideSpoilerSyntaxSpan(), s, s + 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE ) spannable.setSpan( HideSpoilerSyntaxSpan(), e - 2, e, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE ) } } } }