diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/messages/MessagesUtils.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/messages/MessagesUtils.kt
index 4808d2ef..8a5549bd 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/messages/MessagesUtils.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/messages/MessagesUtils.kt
@@ -1,17 +1,21 @@
package pl.szczodrzynski.edziennik.ui.modules.messages
import android.content.Context
-import android.graphics.*
-import android.os.Build
-import android.text.Html
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.graphics.RectF
import android.text.Spanned
import androidx.core.graphics.ColorUtils
-import pl.szczodrzynski.edziennik.*
+import pl.szczodrzynski.edziennik.App
+import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
+import pl.szczodrzynski.edziennik.fixName
+import pl.szczodrzynski.edziennik.getNameInitials
import pl.szczodrzynski.edziennik.utils.Colors
import pl.szczodrzynski.edziennik.utils.Utils
-import pl.szczodrzynski.navlib.blendColors
+import pl.szczodrzynski.edziennik.utils.html.BetterHtml
import kotlin.math.roundToInt
object MessagesUtils {
@@ -179,39 +183,6 @@ object MessagesUtils {
@JvmStatic
fun htmlToSpannable(context: Context, html: String): Spanned {
- val hexPattern = "(#[a-fA-F0-9]{6})"
- val colorRegex = "(?:color=\"$hexPattern\")|(?:style=\"color: ?${hexPattern})"
- .toRegex(RegexOption.IGNORE_CASE)
-
- var text = html
- .replace("\\[META:[A-z0-9]+;[0-9-]+]".toRegex(), "")
- .replace("background-color: ?$hexPattern;".toRegex(), "")
-
- val colorBackground = android.R.attr.colorBackground.resolveAttr(context)
- val textColorPrimary = android.R.attr.textColorPrimary.resolveAttr(context) and 0xffffff
-
- colorRegex.findAll(text).forEach { result ->
- val group = result.groups.drop(1).firstOrNull { it != null } ?: return@forEach
-
- val color = Color.parseColor(group.value)
- var newColor = 0xff000000.toInt() or color
-
- var blendAmount = 1
- var numIterations = 0
-
- while (numIterations < 100 && ColorUtils.calculateContrast(colorBackground, newColor) < 4.5f) {
- blendAmount += 2
- newColor = blendColors(color, blendAmount shl 24 or textColorPrimary)
- numIterations++
- }
-
- text = text.replaceRange(group.range, "#" + (newColor and 0xffffff).toString(16))
- }
-
- return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- Html.fromHtml(text, Html.FROM_HTML_MODE_LEGACY)
- } else {
- Html.fromHtml(text)
- }
+ return BetterHtml.fromHtml(context, html)
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/utils/html/BetterHtml.kt b/app/src/main/java/pl/szczodrzynski/edziennik/utils/html/BetterHtml.kt
new file mode 100644
index 00000000..cca59802
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/utils/html/BetterHtml.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-3-17.
+ */
+
+package pl.szczodrzynski.edziennik.utils.html
+
+import android.content.Context
+import android.graphics.Color
+import android.os.Build
+import android.text.Html
+import android.text.SpannableStringBuilder
+import android.text.Spanned
+import android.text.style.BulletSpan
+import androidx.core.graphics.ColorUtils
+import pl.szczodrzynski.edziennik.dp
+import pl.szczodrzynski.edziennik.resolveAttr
+import pl.szczodrzynski.navlib.blendColors
+
+object BetterHtml {
+
+ @JvmStatic
+ fun fromHtml(context: Context, html: String): Spanned {
+ val hexPattern = "(#[a-fA-F0-9]{6})"
+ val colorRegex = "(?:color=\"$hexPattern\")|(?:style=\"color: ?${hexPattern})"
+ .toRegex(RegexOption.IGNORE_CASE)
+
+ var text = html
+ .replace("\\[META:[A-z0-9]+;[0-9-]+]".toRegex(), "")
+ .replace("background-color: ?$hexPattern;".toRegex(), "")
+
+ val colorBackground = android.R.attr.colorBackground.resolveAttr(context)
+ val textColorPrimary = android.R.attr.textColorPrimary.resolveAttr(context) and 0xffffff
+
+ colorRegex.findAll(text).forEach { result ->
+ val group = result.groups.drop(1).firstOrNull { it != null } ?: return@forEach
+
+ val color = Color.parseColor(group.value)
+ var newColor = 0xff000000.toInt() or color
+
+ var blendAmount = 1
+ var numIterations = 0
+
+ while (numIterations < 100 && ColorUtils.calculateContrast(colorBackground, newColor) < 4.5f) {
+ blendAmount += 2
+ newColor = blendColors(color, blendAmount shl 24 or textColorPrimary)
+ numIterations++
+ }
+
+ text = text.replaceRange(group.range, "#" + (newColor and 0xffffff).toString(16))
+ }
+
+ /*val olRegex = """
(.+?)\s*?ol>"""
+ .toRegex(setOf(RegexOption.DOT_MATCHES_ALL, RegexOption.IGNORE_CASE))
+ olRegex.findAll(text).forEach {
+ text.replaceRange(
+ it.range,
+ text.slice(it.range).replace("li>", "_li>")
+ )
+ }*/
+
+ @Suppress("DEPRECATION")
+ val htmlSpannable = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ Html.fromHtml(
+ text,
+ Html.FROM_HTML_SEPARATOR_LINE_BREAK_LIST_ITEM or Html.FROM_HTML_SEPARATOR_LINE_BREAK_LIST or Html.FROM_HTML_SEPARATOR_LINE_BREAK_DIV,
+ null,
+ LiTagHandler()
+ )
+ } else {
+ Html.fromHtml(text, null, LiTagHandler())
+ }
+
+ val spannableBuilder = SpannableStringBuilder(htmlSpannable)
+ val bulletSpans = spannableBuilder.getSpans(0, spannableBuilder.length, BulletSpan::class.java)
+ bulletSpans.forEach {
+ val start = spannableBuilder.getSpanStart(it)
+ val end = spannableBuilder.getSpanEnd(it)
+ spannableBuilder.removeSpan(it)
+ spannableBuilder.setSpan(
+ ImprovedBulletSpan(bulletRadius = 3.dp, startWidth = 24.dp, gapWidth = 8.dp),
+ start,
+ end,
+ Spanned.SPAN_INCLUSIVE_EXCLUSIVE
+ )
+ }
+
+ return spannableBuilder
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/utils/html/ImprovedBulletSpan.kt b/app/src/main/java/pl/szczodrzynski/edziennik/utils/html/ImprovedBulletSpan.kt
new file mode 100644
index 00000000..96dcbdfa
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/utils/html/ImprovedBulletSpan.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-3-17.
+ */
+
+/**
+ * https://github.com/davidbilik/bullet-span-sample/blob/master/app/src/main/java/cz/davidbilik/bulletsample/ImprovedBulletSpan.kt
+ */
+
+package pl.szczodrzynski.edziennik.utils.html
+
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.graphics.Path
+import android.graphics.Path.Direction
+import android.text.Layout
+import android.text.Spanned
+import android.text.style.LeadingMarginSpan
+
+/**
+ * Copy of [android.text.style.BulletSpan] from android SDK 28 with removed internal code
+ */
+class ImprovedBulletSpan(
+ val bulletRadius: Int = STANDARD_BULLET_RADIUS,
+ val startWidth: Int = STANDARD_GAP_WIDTH,
+ val gapWidth: Int = STANDARD_GAP_WIDTH,
+ val color: Int = STANDARD_COLOR
+) : LeadingMarginSpan {
+
+ companion object {
+ // Bullet is slightly bigger to avoid aliasing artifacts on mdpi devices.
+ private const val STANDARD_BULLET_RADIUS = 4
+ private const val STANDARD_GAP_WIDTH = 2
+ private const val STANDARD_COLOR = 0
+ }
+
+ private var mBulletPath: Path? = null
+
+ override fun getLeadingMargin(first: Boolean): Int {
+ return startWidth + 2 * bulletRadius + gapWidth
+ }
+
+ override fun drawLeadingMargin(
+ canvas: Canvas, paint: Paint, x: Int, dir: Int,
+ top: Int, baseline: Int, bottom: Int,
+ text: CharSequence, start: Int, end: Int,
+ first: Boolean,
+ layout: Layout?
+ ) {
+ if ((text as Spanned).getSpanStart(this) == start) {
+ val style = paint.style
+ paint.style = Paint.Style.FILL
+
+ val yPosition = if (layout != null) {
+ val line = layout.getLineForOffset(start)
+ layout.getLineBaseline(line).toFloat() - bulletRadius * 2f
+ } else {
+ (top + bottom) / 2f
+ }
+
+ val xPosition = startWidth + (x + dir * bulletRadius).toFloat()
+
+ if (canvas.isHardwareAccelerated) {
+ if (mBulletPath == null) {
+ mBulletPath = Path()
+ mBulletPath!!.addCircle(0.0f, 0.0f, bulletRadius.toFloat(), Direction.CW)
+ }
+
+ canvas.save()
+ canvas.translate(xPosition, yPosition)
+ canvas.drawPath(mBulletPath!!, paint)
+ canvas.restore()
+ } else {
+ canvas.drawCircle(xPosition, yPosition, bulletRadius.toFloat(), paint)
+ }
+
+ paint.style = style
+ }
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/utils/html/LiTagHandler.kt b/app/src/main/java/pl/szczodrzynski/edziennik/utils/html/LiTagHandler.kt
new file mode 100644
index 00000000..36f58d03
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/utils/html/LiTagHandler.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-3-17.
+ */
+
+/**
+ * https://github.com/davidbilik/bullet-span-sample/blob/master/app/src/main/java/cz/davidbilik/bulletsample/LiTagHandler.kt
+ */
+
+package pl.szczodrzynski.edziennik.utils.html
+
+import android.text.Editable
+import android.text.Html
+import android.text.Spannable
+import android.text.Spanned
+import android.text.style.BulletSpan
+import org.xml.sax.XMLReader
+
+/**
+ * [Html.TagHandler] implementation that processes and - tags and creates bullets.
+ *
+ * Note: This class is only applied on SDK < 25 and processes only one-level list, nested lists do not work.
+ */
+class LiTagHandler : Html.TagHandler {
+ /**
+ * Helper marker class. Idea stolen from [Html.fromHtml] implementation
+ */
+ class Bullet
+
+ override fun handleTag(opening: Boolean, tag: String, output: Editable, xmlReader: XMLReader) {
+ if (tag == "li" && opening) {
+ output.setSpan(Bullet(), output.length, output.length, Spannable.SPAN_INCLUSIVE_EXCLUSIVE)
+ }
+ if (tag == "li" && !opening) {
+ output.append("\n")
+ val lastMark = output.getSpans(0, output.length, Bullet::class.java).lastOrNull()
+ lastMark?.let {
+ val start = output.getSpanStart(it)
+ output.removeSpan(it)
+ if (start != output.length) {
+ output.setSpan(BulletSpan(), start, output.length, Spanned.SPAN_INCLUSIVE_EXCLUSIVE)
+ }
+ }
+ }
+ }
+}