投稿のHTMLデータの表示を少し改善

This commit is contained in:
tateisu 2023-03-10 02:13:40 +09:00
parent a383ca0657
commit c24aeeadc4
13 changed files with 591 additions and 155 deletions

View File

@ -401,6 +401,8 @@ class TootInstance(parser: TootParser, src: JsonObject) {
*/
private val reOldMisskeyCompatible = """\A[\d.]+:compatible:misskey:""".toRegex()
private val reBothCompatible = """\b(?:misskey|calckey)\b""".toRegex(RegexOption.IGNORE_CASE)
// 疑似アカウントの追加時に、インスタンスの検証を行う
private suspend fun TootApiClient.getInstanceInformation(
forceAccessToken: String? = null,
@ -419,7 +421,9 @@ class TootInstance(parser: TootParser, src: JsonObject) {
// 他、Mastodonではない場合は kids.0px.io が存在する
// https://kids.0px.io/notes/9b628dpesb
// Misskey有効トグルで結果を切り替えたいらしい
version.contains("misskey", ignoreCase = true) &&
//
// Calckey.jp は調査中
reBothCompatible.containsMatchIn(version) &&
PrefB.bpEnableDeprecatedSomething.value -> Unit
// 両方のAPIに応答するサーバは他にないと思う。

View File

@ -1,5 +1,8 @@
package jp.juggler.subwaytooter.mfm
import android.graphics.Typeface
import jp.juggler.subwaytooter.span.BlockCodeSpan
import jp.juggler.subwaytooter.span.BlockQuoteSpan
import jp.juggler.subwaytooter.table.daoAcctColor
import jp.juggler.util.data.encodePercent
import jp.juggler.util.data.notEmpty
@ -51,7 +54,7 @@ enum class NodeType(val render: SpanOutputEnv.(Node) -> Unit) {
spanList.addLast(start, sb.length, android.text.style.BackgroundColorSpan(0x40808080))
spanList.addLast(
start, sb.length,
fontSpan(android.graphics.Typeface.MONOSPACE)
fontSpan(Typeface.MONOSPACE)
)
}
}),
@ -72,12 +75,8 @@ enum class NodeType(val render: SpanOutputEnv.(Node) -> Unit) {
val sp = MisskeySyntaxHighlighter.parse(text)
appendText(text)
spanList.addWithOffset(sp, start)
spanList.addLast(start, sb.length, android.text.style.BackgroundColorSpan(0x40808080))
spanList.addLast(start, sb.length, android.text.style.RelativeSizeSpan(0.7f))
spanList.addLast(
start, sb.length,
fontSpan(android.graphics.Typeface.MONOSPACE)
)
spanList.addLast(start, sb.length, BlockCodeSpan())
closeBlock()
}
}),
@ -96,7 +95,7 @@ enum class NodeType(val render: SpanOutputEnv.(Node) -> Unit) {
spanList.addLast(
start,
sb.length,
fontSpan(android.graphics.Typeface.defaultFromStyle(android.graphics.Typeface.ITALIC))
fontSpan(Typeface.defaultFromStyle(Typeface.ITALIC))
)
}
}),
@ -189,7 +188,7 @@ enum class NodeType(val render: SpanOutputEnv.(Node) -> Unit) {
fireRenderChildNodes(it)
spanList.addLast(
start, sb.length,
fontSpan(android.graphics.Typeface.defaultFromStyle(android.graphics.Typeface.ITALIC))
fontSpan(Typeface.defaultFromStyle(Typeface.ITALIC))
)
}
}),
@ -310,34 +309,21 @@ enum class NodeType(val render: SpanOutputEnv.(Node) -> Unit) {
val bgColor =
MisskeyMarkdownDecoder.quoteNestColors[it.quoteNest % MisskeyMarkdownDecoder.quoteNestColors.size]
// TextView の文字装飾では「ブロック要素の入れ子」を表現できない
// 内容の各行の始端に何か追加するというのがまずキツい
// しかし各行の頭に引用マークをつけないと引用のネストで意味が通じなくなってしまう
val tmp = sb.toString()
//log.d("QUOTE_BLOCK tmp=${tmp} start=$start end=${tmp.length}")
for (i in tmp.length - 1 downTo start) {
val prevChar = when (i) {
start -> '\n'
else -> tmp[i - 1]
}
//log.d("QUOTE_BLOCK prevChar=${ String.format("%x",prevChar.toInt())}")
if (prevChar == '\n') {
//log.d("QUOTE_BLOCK insert! i=$i")
sb.insert(i, "> ")
spanList.insert(i, 2)
spanList.addLast(
i, i + 1,
android.text.style.BackgroundColorSpan(bgColor)
)
}
}
spanList.addLast(
start,
sb.length,
fontSpan(android.graphics.Typeface.defaultFromStyle(android.graphics.Typeface.ITALIC))
BlockQuoteSpan(context = context, blockQuoteColor = bgColor)
)
spanList.addLast(
start,
sb.length,
fontSpan(
Typeface.defaultFromStyle(
Typeface.ITALIC
)
)
)
closeBlock()
}
}),

View File

@ -0,0 +1,81 @@
package jp.juggler.subwaytooter.span
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.Typeface
import android.text.Layout
import android.text.TextPaint
import android.text.style.LeadingMarginSpan
import android.text.style.MetricAffectingSpan
import jp.juggler.subwaytooter.R
import jp.juggler.subwaytooter.pref.lazyContext
import jp.juggler.util.ui.attrColor
import kotlin.math.max
import kotlin.math.min
/**
* コードブロック用の装飾スパン
*/
class BlockCodeSpan (
context: Context = lazyContext,
private var typeface: Typeface = Typeface.MONOSPACE,
private var relativeTextSize: Float = 0.7f,
private var margin: Int = 0,
private var textColor: Int = context.attrColor(R.attr.colorTextContent),
private var backgroundColor: Int = 0x40808080,
) : MetricAffectingSpan(), LeadingMarginSpan {
private val rect = Rect()
private val paint = Paint()
private fun apply(paint: TextPaint) {
paint.color = textColor
paint.typeface = typeface
paint.textSize = paint.textSize * relativeTextSize
}
override fun updateDrawState(tp: TextPaint) {
apply(tp)
}
override fun updateMeasureState(textPaint: TextPaint) {
apply(textPaint)
}
override fun getLeadingMargin(first: Boolean): Int {
return margin
}
override fun drawLeadingMargin(
c: Canvas,
p: Paint,
x: Int,
dir: Int,
top: Int,
baseline: Int,
bottom: Int,
text: CharSequence?,
start: Int,
end: Int,
first: Boolean,
layout: Layout
) {
paint.style = Paint.Style.FILL
paint.color = backgroundColor
val line = layout.getLineForOffset(start)
val left = layout.getParagraphLeft(line)
val right = layout.getParagraphRight(line)
rect.set(
min(left,right),
top,
max(left,right),
bottom,
)
c.drawRect(rect, paint)
}
}

View File

@ -0,0 +1,62 @@
package jp.juggler.subwaytooter.span
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Rect
import android.text.Layout
import android.text.style.LeadingMarginSpan
import jp.juggler.subwaytooter.R
import jp.juggler.util.ui.attrColor
import kotlin.math.max
import kotlin.math.min
/**
* ブロック引用の装飾スパン
*/
class BlockQuoteSpan(
context: Context,
private val margin: Int = (context.resources.displayMetrics.density * 24f + 0.5f).toInt(),
private val quoteWidth: Int = (context.resources.displayMetrics.density * 4f + 0.5f).toInt(),
private val blockQuoteColor: Int = context.attrColor(R.attr.colorTextHint),
) : LeadingMarginSpan {
private val rect = Rect()
private val paint = Paint()
override fun getLeadingMargin(first: Boolean): Int = margin
override fun drawLeadingMargin(
c: Canvas,
p: Paint,
x: Int,
dir: Int,
top: Int,
baseline: Int,
bottom: Int,
text: CharSequence,
start: Int,
end: Int,
first: Boolean,
layout: Layout,
) {
paint.set(p)
paint.style = Paint.Style.FILL
paint.color = blockQuoteColor
val line = layout.getLineForOffset(start)
val edge = if (dir > 0) {
layout.getParagraphLeft(line)
} else {
layout.getParagraphRight(line)
}
val width = quoteWidth
val l = edge - dir * width
val r = edge - dir * width * 2
rect.set(
min(l, r),
top,
max(l, r),
bottom
)
c.drawRect(rect, paint)
}
}

View File

@ -0,0 +1,35 @@
package jp.juggler.subwaytooter.span
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.text.Layout
import android.text.style.LeadingMarginSpan
/**
* dd 要素の字下げスパン
*/
class DdSpan(
context: Context,
marginDp: Float = 24f,
) : LeadingMarginSpan {
private val marginPx = (context.resources.displayMetrics.density * marginDp + 0.5f).toInt()
override fun getLeadingMargin(first: Boolean): Int = marginPx
override fun drawLeadingMargin(
c: Canvas,
p: Paint,
x: Int,
dir: Int,
top: Int,
baseline: Int,
bottom: Int,
text: CharSequence,
start: Int,
end: Int,
first: Boolean,
layout: Layout,
) {
}
}

View File

@ -0,0 +1,72 @@
package jp.juggler.subwaytooter.span
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Rect
import android.text.Layout
import android.text.TextPaint
import android.text.style.LeadingMarginSpan
import android.text.style.MetricAffectingSpan
import jp.juggler.subwaytooter.R
import jp.juggler.subwaytooter.pref.lazyContext
import jp.juggler.util.ui.attrColor
import kotlin.math.max
import kotlin.math.min
/**
* コードブロック用の装飾スパン
*/
class HrSpan(
context: Context = lazyContext,
private var lineColor: Int = context.attrColor(R.attr.colorTextContent),
private var lineHeight: Int = (context.resources.displayMetrics.density * 1f + 0.5f).toInt(),
) : MetricAffectingSpan(), LeadingMarginSpan {
private val rect = Rect()
private val paint = Paint()
private fun apply(paint: TextPaint) {
paint.color = 0
}
override fun updateDrawState(tp: TextPaint) {
apply(tp)
}
override fun updateMeasureState(textPaint: TextPaint) {
apply(textPaint)
}
override fun getLeadingMargin(first: Boolean): Int {
return 0
}
override fun drawLeadingMargin(
c: Canvas,
p: Paint,
x: Int,
dir: Int,
top: Int,
baseline: Int,
bottom: Int,
text: CharSequence?,
start: Int,
end: Int,
first: Boolean,
layout: Layout,
) {
paint.style = Paint.Style.FILL
paint.color = lineColor
val lineY = (top + bottom).shr(1)
val lineT = lineY - lineHeight.shr(1)
val lineB = lineT + lineHeight
val line = layout.getLineForOffset(start)
val left = layout.getParagraphLeft(line)
val right = layout.getParagraphRight(line)
rect.set(min(left, right), lineT, max(left, right), lineB)
c.drawRect(rect, paint)
}
}

View File

@ -0,0 +1,15 @@
package jp.juggler.subwaytooter.span
import android.graphics.Typeface
import android.text.TextPaint
import io.github.inflationx.calligraphy3.CalligraphyTypefaceSpan
class InlineCodeSpan(
tf: Typeface =Typeface.MONOSPACE,
private val bgColor :Int = 0x40808080,
) : CalligraphyTypefaceSpan(tf) {
override fun updateDrawState(drawState: TextPaint) {
super.updateDrawState(drawState)
drawState.bgColor = bgColor
}
}

View File

@ -0,0 +1,71 @@
package jp.juggler.subwaytooter.span
import android.graphics.Canvas
import android.graphics.Paint
import android.text.Layout
import android.text.TextPaint
import android.text.style.LeadingMarginSpan
class OrderedListItemSpan(
private val order: String,
orders: List<String>,
) : LeadingMarginSpan {
companion object {
fun List<String>.longest(paint: TextPaint): String? {
var longestText: String? = null
var longestTextWidth: Int? = null
forEach { text ->
val textWidth = (paint.measureText(text) + .5f).toInt()
if (longestTextWidth?.takeIf { it > textWidth } != null) return@forEach
longestText = text
longestTextWidth = textWidth
}
return longestText
}
}
private val paint = TextPaint()
private val longestOrder = orders.longest(paint) ?: ""
private var longestOrderWidth =
(paint.measureText(longestOrder) + .5f).toInt() +
(paint.measureText(". ") * 2f + .5f).toInt()
override fun getLeadingMargin(first: Boolean): Int = longestOrderWidth
override fun drawLeadingMargin(
c: Canvas,
p: Paint,
x: Int,
dir: Int,
top: Int,
baseline: Int,
bottom: Int,
text: CharSequence,
start: Int,
end: Int,
first: Boolean,
layout: Layout,
) {
// if there was a line break, we don't need to draw anything
if (!first || !selfStart(start, text, this)) {
return
}
paint.set(p)
val longestOrderWidth = (paint.measureText(longestOrder) + .5f).toInt() +
(paint.measureText(".") * 2f + .5f).toInt()
this.longestOrderWidth = longestOrderWidth
val line = layout.getLineForOffset(start)
if (dir > 0) {
paint.textAlign = Paint.Align.LEFT
val left = layout.getParagraphLeft(line) - longestOrderWidth
c.drawText("${order}.", left.toFloat(), baseline.toFloat(), paint)
} else {
paint.textAlign = Paint.Align.RIGHT
val right = layout.getParagraphRight(line) + longestOrderWidth
c.drawText(".${order}", right.toFloat(), baseline.toFloat(), paint)
}
}
}

View File

@ -0,0 +1,7 @@
package jp.juggler.subwaytooter.span
import android.text.Spanned
fun selfStart(start: Int, text: CharSequence?, span: Any?): Boolean {
return text is Spanned && text.getSpanStart(span) == start
}

View File

@ -0,0 +1,86 @@
package jp.juggler.subwaytooter.span
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.RectF
import android.text.Layout
import android.text.TextPaint
import android.text.style.LeadingMarginSpan
import androidx.annotation.IntRange
class UnorderedListItemSpan(
@IntRange(from = 0) private val level: Int,
private var bulletWidth: Int = 0,
) : LeadingMarginSpan {
private val circle = RectF()
private val rectangle = Rect()
private val paint = TextPaint()
private var marginWidth = (paint.descent() - paint.ascent() + .5f).toInt()
override fun getLeadingMargin(first: Boolean) = marginWidth
override fun drawLeadingMargin(
c: Canvas,
p: Paint,
x: Int,
dir: Int,
top: Int,
baseline: Int,
bottom: Int,
text: CharSequence,
start: Int,
end: Int,
first: Boolean,
layout: Layout,
) {
// if there was a line break, we don't need to draw anything
if (!first || !selfStart(start, text, this)) return
paint.set(p)
val save = c.save()
try {
// テキストの高さ
val textLineHeight = (paint.descent() - paint.ascent() + .5f).toInt()
// ドットを含む横マージンの幅
@Suppress("UnnecessaryVariable")
val marginWidth = textLineHeight
this.marginWidth = marginWidth
val marginHalf = marginWidth/2
// ドットの直径
val bulletWidth = textLineHeight / 2
// 行頭からドットの開始端までの距離
val marginLeft = (marginWidth - bulletWidth) / 2
val line = layout.getLineForOffset(start)
val edge = if (dir > 0) {
layout.getParagraphLeft(line)
} else {
layout.getParagraphRight(line)
}
val l = edge - dir * marginHalf - bulletWidth/2
val t =
baseline + ((paint.descent() + paint.ascent()) / 2f + .5f).toInt() - bulletWidth / 2
val r = l + bulletWidth
val b = t + bulletWidth
if (level == 0 || level == 1) {
circle.set(l.toFloat(), t.toFloat(), r.toFloat(), b.toFloat())
paint.style = when (level) {
0 -> Paint.Style.FILL
else -> Paint.Style.STROKE
}
c.drawOval(circle, paint)
} else {
rectangle.set(l, t, r, b)
paint.style = Paint.Style.FILL
c.drawRect(rectangle, paint)
}
} finally {
c.restoreToCount(save)
}
}
}

View File

@ -17,6 +17,7 @@ import jp.juggler.subwaytooter.api.entity.TootMention
import jp.juggler.subwaytooter.api.entity.TootStatus
import jp.juggler.subwaytooter.mfm.MisskeyMarkdownDecoder
import jp.juggler.subwaytooter.pref.PrefB
import jp.juggler.subwaytooter.pref.lazyContext
import jp.juggler.subwaytooter.span.*
import jp.juggler.subwaytooter.table.HighlightWord
import jp.juggler.subwaytooter.table.daoAcctColor
@ -321,13 +322,15 @@ object HTMLDecoder {
val nestLevelDefinition: Int,
val nestLevelQuote: Int,
var order: Int = 0,
val listOrders: List<String>? = null,
) {
fun subOrdered() = ListContext(
fun subOrdered(listOrders:List<String>) = ListContext(
type = ListType.Ordered,
nestLevelOrdered + 1,
nestLevelUnordered,
nestLevelDefinition,
nestLevelQuote
nestLevelQuote,
listOrders= listOrders,
)
fun subUnordered() = ListContext(
@ -354,12 +357,7 @@ object HTMLDecoder {
nestLevelQuote + 1
)
fun increment() = when (type) {
ListType.Ordered -> "${++order}. "
ListType.Unordered -> "${listMarkers[nestLevelUnordered % listMarkers.size]} "
ListType.Definition -> ""
else -> ""
}
fun increment() = ++order
fun inList() = nestLevelOrdered + nestLevelUnordered + nestLevelDefinition > 0
@ -369,43 +367,6 @@ object HTMLDecoder {
}
}
// SpannableStringBuilderを行ごとに分解する
// 行末の改行文字は各行の末尾に残る
// 最終行の長さが0(改行文字もなし)だった場合は出力されない
fun SpannableStringBuilder.splitLines() =
ArrayList<SpannableStringBuilder>().also { dst ->
// 入力の末尾のtrim
var end = this.length
while (end > 0 && CharacterGroup.isWhitespace(this[end - 1].code)) --end
// 入力の最初の非空白文字の位置を調べておく
var firstNonSpace = 0
while (firstNonSpace < end && CharacterGroup.isWhitespace(this[firstNonSpace].code)) ++firstNonSpace
var i = 0
while (i < end) {
val lineStart = i
while (i < end && this[i] != '\n') ++i
val lineEnd = if (i >= end) end else i + 1
++i
// 行頭の空白を削る
// while (lineStart < lineEnd &&
// this[lineStart] != '\n' &&
// CharacterGroup.isWhitespace(this[lineStart].toInt())
// ) ++lineStart
// 最初の非空白文字以降の行を出力する
if (lineEnd > firstNonSpace) {
dst.add(this.subSequence(lineStart, lineEnd) as SpannableStringBuilder)
}
}
if (dst.isEmpty()) {
// ブロック要素は最低1行は存在するので、1行だけの要素を作る
dst.add(SpannableStringBuilder())
}
}
private val reLastLine = """(?:\A|\n)([^\n]*)\z""".toRegex()
private class Node {
@ -413,7 +374,7 @@ object HTMLDecoder {
val child_nodes = ArrayList<Node>()
val tag: String
val text: String
var text: String
private val href: String?
get() {
@ -569,11 +530,13 @@ object HTMLDecoder {
class EncodeSpanEnv(
val options: DecodeOptions,
val listContext: ListContext,
val tag: String,
val node: Node,
val sb: SpannableStringBuilder,
val sbTmp: SpannableStringBuilder,
val spanStart: Int,
)
){
val tag = node.tag
}
val originalFlusher: EncodeSpanEnv.() -> Unit = {
when (tag) {
@ -692,41 +655,31 @@ object HTMLDecoder {
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
"pre" -> {
sb.setSpan(
BackgroundColorSpan(0x40808080),
spanStart,
sb.length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
sb.setSpan(
RelativeSizeSpan(0.7f),
spanStart,
sb.length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
sb.setSpan(
fontSpan(Typeface.MONOSPACE),
spanStart,
sb.length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
"code" -> {
// インラインコード用の装飾
// sb.setSpan(
// BackgroundColorSpan(0x40808080),
// spanStart,
// sb.length,
// Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
// )
sb.setSpan(
BackgroundColorSpan(0x40808080),
spanStart,
sb.length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
sb.setSpan(
fontSpan(Typeface.MONOSPACE),
InlineCodeSpan(),
spanStart,
sb.length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
"hr" -> {
val start =sb.length
sb.append("-")
sb.setSpan(
HrSpan(lazyContext),
spanStart,
sb.length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
"hr" -> sb.append("----------")
}
}
@ -736,6 +689,17 @@ object HTMLDecoder {
for (tag in tags) this[tag] = block
}
fun SpannableStringBuilder.deleteLastSpaces() {
// 最低でも1文字は残す
var last = length - 1
while (last > 0) {
if (!CharacterGroup.isWhitespace(get(last).code)) break
--last
}
// 末尾の空白を除去
if (last != length - 1) delete(last + 1, length)
}
add("a") {
val linkInfo = formatLinkCaption(options, sbTmp, href ?: "")
val caption = linkInfo.caption
@ -780,67 +744,115 @@ object HTMLDecoder {
}
add("blockquote") {
val bg_color = listContext.quoteColor()
// TextView の文字装飾では「ブロック要素の入れ子」を表現できない
// 内容の各行の始端に何か追加するというのがまずキツい
// しかし各行の頭に引用マークをつけないと引用のネストで意味が通じなくなってしまう
val startItalic = sb.length
sbTmp.splitLines().forEach { line ->
val lineStart = sb.length
sb.append("> ")
sb.setSpan(
BackgroundColorSpan(bg_color),
lineStart,
lineStart + 1,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
sb.append(line)
}
val start = sb.length
sbTmp.deleteLastSpaces()
sb.append(sbTmp)
sb.setSpan(
BlockQuoteSpan(
context = lazyContext,
blockQuoteColor = listContext.quoteColor()
),
start,
sb.length,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE,
)
sb.setSpan(
fontSpan(Typeface.defaultFromStyle(Typeface.ITALIC)),
startItalic,
start,
sb.length,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE,
)
}
add("pre") {
val start = sb.length
sbTmp.deleteLastSpaces()
// インラインコード用の装飾を除去する
sbTmp.getSpans(0, sbTmp.length, Any::class.java).forEach { span ->
if (span is BackgroundColorSpan && span.backgroundColor == 0x40808080) {
sbTmp.removeSpan(span)
} else if (span is InlineCodeSpan) {
sbTmp.removeSpan(span)
}
}
sb.append(sbTmp)
sb.setSpan(
BlockCodeSpan(),
start,
sb.length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
add("li") {
val lineHeader1 = listContext.increment()
val lineHeader2 = " ".repeat(lineHeader1.length)
sbTmp.splitLines().forEachIndexed { i, line ->
sb.append(if (i == 0) lineHeader1 else lineHeader2)
sb.append(line)
sbTmp.deleteLastSpaces()
val start = sb.length
sb.append(sbTmp)
when (listContext.type) {
ListType.Unordered -> {
sb.setSpan(
UnorderedListItemSpan(
level = listContext.nestLevelOrdered,
),
start,
sb.length,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
ListType.Ordered -> {
sb.setSpan(
OrderedListItemSpan(
order = node.text,
orders = listContext.listOrders?: listOf(node.text),
),
start,
sb.length,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
else ->Unit
}
}
add("dt") {
val prefix = listContext.increment()
val startBold = sb.length
sbTmp.splitLines().forEach { line ->
sb.append(prefix)
sb.append(line)
}
sbTmp.deleteLastSpaces()
val start = sb.length
sb.append(sbTmp)
sb.setSpan(
DdSpan(lazyContext, marginDp = 3f),
start,
sb.length,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
sb.setSpan(
fontSpan(Typeface.defaultFromStyle(Typeface.BOLD)),
startBold,
start,
sb.length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
add("dd") {
val prefix = listContext.increment() + "  "
sbTmp.splitLines().forEach { line ->
sb.append(prefix)
sb.append(line)
}
sbTmp.deleteLastSpaces()
val start = sb.length
sb.append(sbTmp)
sb.setSpan(
DdSpan(lazyContext, marginDp = 24f),
start,
sb.length,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
}
fun childListContext(tag: String, outerContext: ListContext) = when (tag) {
"ol" -> outerContext.subOrdered()
fun childListContext(node:Node, outerContext: ListContext) = when (node.tag) {
"ol" -> {
var n = 1
val reversed = false
val listItems = node.child_nodes.filter { it.tag == "li" }
(if(reversed ) listItems.reversed() else listItems).forEach { v ->
v.text = (n++).toString()
}
outerContext.subOrdered(listItems.map { it.text })
}
"ul" -> outerContext.subUnordered()
"dl" -> outerContext.subDefinition()
"blockquote" -> outerContext.subQuote()
@ -881,7 +893,7 @@ object HTMLDecoder {
EncodeSpanEnv(
options = options,
listContext = listContext,
tag = tag,
node = this,
sb = sb,
sbTmp = SpannableStringBuilder(),
spanStart = 0,
@ -892,14 +904,14 @@ object HTMLDecoder {
EncodeSpanEnv(
options = options,
listContext = listContext,
tag = tag,
node = this,
sb = sb,
sbTmp = sb,
spanStart = sb.length
)
}
val childListContext = childListContext(tag, listContext)
val childListContext = childListContext(this, listContext)
child_nodes.forEachIndexed { i, child ->
if (!canSkipEncode(

View File

@ -44,6 +44,8 @@
<!-- カスタマイズしない画面のImageButtonの図柄のtint -->
<attr name="colorTextContent" format="color"/>
<attr name="colorTextHint" format="color"/>
<attr name="colorRefreshErrorBg" format="color"/>
<!-- プロフ背景を薄くするマスク #C0FFFFFF -->

View File

@ -36,7 +36,6 @@
<!-- AppCompat ここまで ============================================ -->
<item name="colorTextHelp">@color/Light_colorTextHelp</item>
<item name="colorLink">@color/Light_colorLink</item>
<item name="list_item_bg_pressed_dragged">@color/Light_colotListItemDrag</item>
<item name="color_column_header">@color/Light_colorColumnHeaderBg</item>
@ -54,6 +53,9 @@
<item name="colorColumnListItemText">@color/Light_colorTextColumnListItem</item>
<item name="colorTimeSmall">@color/Light_colorTextTimeSmall</item>
<item name="colorTextContent">@color/Light_colorTextContent</item>
<item name="colorTextHint">@color/Light_colorTextHint</item>
<item name="colorTextHelp">@color/Light_colorTextHelp</item>
<item name="colorProfileBackgroundMask">@color/Light_colorProfileBackgroundMask</item>
<item name="colorShowMediaBackground">@color/Light_colorShowMediaBackground</item>
<item name="colorShowMediaText">@color/Light_colorShowMediaText</item>
@ -131,8 +133,6 @@
<!-- AppCompat ここまで ============================================ -->
<item name="colorTextHelp">@color/Dark_colorTextHelp</item>
<item name="colorLink">@color/Dark_colorLink</item>
<item name="list_item_bg_pressed_dragged">@color/Dark_colorListItemDrag</item>
<item name="color_column_header">@color/Dark_colorColumnHeader</item>
@ -151,6 +151,8 @@
<item name="colorColumnListItemText">@color/Dark_colorTextColumnListItem</item>
<item name="colorTimeSmall">@color/Dark_colorTextTimeSmall</item>
<item name="colorTextContent">@color/Dark_colorTextContent</item>
<item name="colorTextHint">@color/Dark_colorTextHint</item>
<item name="colorTextHelp">@color/Dark_colorTextHelp</item>
<item name="colorProfileBackgroundMask">@color/Dark_colorProfileBackgroundMask</item>
<item name="colorShowMediaBackground">@color/Dark_colorShowMediaBackground</item>
@ -233,7 +235,6 @@
<!-- AppCompat ここまで ============================================ -->
<item name="colorTextHelp">@color/Mastodon_colorTextHelp</item>
<item name="colorLink">@color/Mastodon_colorLink</item>
<item name="list_item_bg_pressed_dragged">@color/Mastodon_colorListItemDrag</item>
@ -253,6 +254,8 @@
<item name="colorColumnListItemText">@color/Mastodon_colorTextColumnListItem</item>
<item name="colorTimeSmall">@color/Mastodon_colorTextTimeSmall</item>
<item name="colorTextContent">@color/Mastodon_colorTextContent</item>
<item name="colorTextHint">@color/Mastodon_colorTextHint</item>
<item name="colorTextHelp">@color/Mastodon_colorTextHelp</item>
<item name="colorProfileBackgroundMask">@color/Mastodon_colorProfileBackgroundMask</item>
<item name="colorShowMediaBackground">@color/Mastodon_colorShowMediaBackground</item>