mirror of
https://github.com/vector-im/element-android.git
synced 2024-11-15 01:35:07 +08:00
Import source from https://github.com/2dxgujun/Kpan
This commit is contained in:
parent
e9b9434671
commit
69680a9856
@ -240,7 +240,6 @@ ext.groups = [
|
||||
],
|
||||
group: [
|
||||
'me.dm7.barcodescanner',
|
||||
'me.gujun.android',
|
||||
]
|
||||
]
|
||||
]
|
||||
|
4
library/external/jsonviewer/build.gradle
vendored
4
library/external/jsonviewer/build.gradle
vendored
@ -58,9 +58,7 @@ dependencies {
|
||||
|
||||
implementation libs.airbnb.mavericks
|
||||
// Span utils
|
||||
implementation('me.gujun.android:span:1.7') {
|
||||
exclude group: 'com.android.support', module: 'support-annotations'
|
||||
}
|
||||
implementation project(":library:external:span")
|
||||
|
||||
implementation libs.jetbrains.coroutinesCore
|
||||
implementation libs.jetbrains.coroutinesAndroid
|
||||
|
20
library/external/span/build.gradle
vendored
Normal file
20
library/external/span/build.gradle
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
android {
|
||||
namespace "me.gujun.android.span"
|
||||
compileSdk versions.compileSdk
|
||||
|
||||
defaultConfig {
|
||||
minSdk versions.minSdk
|
||||
targetSdk versions.targetSdk
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.android.support:support-annotations:28.0.0'
|
||||
}
|
316
library/external/span/src/main/kotlin/me/gujun/android/span/span.kt
vendored
Normal file
316
library/external/span/src/main/kotlin/me/gujun/android/span/span.kt
vendored
Normal file
@ -0,0 +1,316 @@
|
||||
package me.gujun.android.span
|
||||
|
||||
import android.graphics.Typeface
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.text.Layout
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.TextUtils
|
||||
import android.text.style.AbsoluteSizeSpan
|
||||
import android.text.style.AlignmentSpan
|
||||
import android.text.style.BackgroundColorSpan
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.text.style.ImageSpan
|
||||
import android.text.style.QuoteSpan
|
||||
import android.text.style.StyleSpan
|
||||
import android.text.style.SubscriptSpan
|
||||
import android.text.style.SuperscriptSpan
|
||||
import android.text.style.TypefaceSpan
|
||||
import android.text.style.URLSpan
|
||||
import android.view.View
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.annotation.Dimension
|
||||
import me.gujun.android.span.style.CustomTypefaceSpan
|
||||
import me.gujun.android.span.style.LineSpacingSpan
|
||||
import me.gujun.android.span.style.SimpleClickableSpan
|
||||
import me.gujun.android.span.style.TextDecorationLineSpan
|
||||
import me.gujun.android.span.style.VerticalPaddingSpan
|
||||
|
||||
class Span(val parent: Span? = null) : SpannableStringBuilder() {
|
||||
|
||||
companion object {
|
||||
val EMPTY_STYLE = Span()
|
||||
|
||||
var globalStyle: Span = EMPTY_STYLE
|
||||
}
|
||||
|
||||
var text: CharSequence = ""
|
||||
|
||||
@ColorInt var textColor: Int? = parent?.textColor
|
||||
|
||||
@ColorInt var backgroundColor: Int? = parent?.backgroundColor
|
||||
|
||||
@Dimension(unit = Dimension.PX) var textSize: Int? = parent?.textSize
|
||||
|
||||
var fontFamily: String? = parent?.fontFamily
|
||||
|
||||
var typeface: Typeface? = parent?.typeface
|
||||
|
||||
var textStyle: String? = parent?.textStyle
|
||||
|
||||
var alignment: String? = parent?.alignment
|
||||
|
||||
var textDecorationLine: String? = parent?.textDecorationLine
|
||||
|
||||
@Dimension(unit = Dimension.PX) var lineSpacing: Int? = null
|
||||
|
||||
@Dimension(unit = Dimension.PX) var paddingTop: Int? = null
|
||||
|
||||
@Dimension(unit = Dimension.PX) var paddingBottom: Int? = null
|
||||
|
||||
@Dimension(unit = Dimension.PX) var verticalPadding: Int? = null
|
||||
|
||||
var onClick: (() -> Unit)? = null
|
||||
|
||||
var spans: ArrayList<Any> = ArrayList()
|
||||
|
||||
var style: Span = EMPTY_STYLE
|
||||
|
||||
private fun buildCharacterStyle(builder: ArrayList<Any>) {
|
||||
if (textColor != null) {
|
||||
builder.add(ForegroundColorSpan(textColor!!))
|
||||
}
|
||||
|
||||
if (backgroundColor != null) {
|
||||
builder.add(BackgroundColorSpan(backgroundColor!!))
|
||||
}
|
||||
|
||||
if (textSize != null) {
|
||||
builder.add(AbsoluteSizeSpan(textSize!!))
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(fontFamily)) {
|
||||
builder.add(TypefaceSpan(fontFamily))
|
||||
}
|
||||
|
||||
if (typeface != null) {
|
||||
builder.add(CustomTypefaceSpan(typeface!!))
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(textStyle)) {
|
||||
builder.add(StyleSpan(when (textStyle) {
|
||||
"normal" -> Typeface.NORMAL
|
||||
"bold" -> Typeface.BOLD
|
||||
"italic" -> Typeface.ITALIC
|
||||
"bold_italic" -> Typeface.BOLD_ITALIC
|
||||
else -> throw RuntimeException("Unknown text style")
|
||||
}))
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(textDecorationLine)) {
|
||||
builder.add(TextDecorationLineSpan(textDecorationLine!!))
|
||||
}
|
||||
|
||||
if (onClick != null) {
|
||||
builder.add(object : SimpleClickableSpan() {
|
||||
override fun onClick(widget: View) {
|
||||
onClick?.invoke()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildParagraphStyle(builder: ArrayList<Any>) {
|
||||
if (!TextUtils.isEmpty(alignment)) {
|
||||
builder.add(AlignmentSpan.Standard(when (alignment) {
|
||||
"normal" -> Layout.Alignment.ALIGN_NORMAL
|
||||
"opposite" -> Layout.Alignment.ALIGN_OPPOSITE
|
||||
"center" -> Layout.Alignment.ALIGN_CENTER
|
||||
else -> throw RuntimeException("Unknown text alignment")
|
||||
}))
|
||||
}
|
||||
|
||||
if (lineSpacing != null) {
|
||||
builder.add(LineSpacingSpan(lineSpacing!!))
|
||||
}
|
||||
|
||||
paddingTop = when {
|
||||
paddingTop != null -> paddingTop
|
||||
verticalPadding != null -> verticalPadding
|
||||
else -> 0
|
||||
}
|
||||
paddingBottom = when {
|
||||
paddingBottom != null -> paddingBottom
|
||||
verticalPadding != null -> verticalPadding
|
||||
else -> 0
|
||||
}
|
||||
if (paddingTop != 0 || paddingBottom != 0) {
|
||||
builder.add(VerticalPaddingSpan(paddingTop!!, paddingBottom!!))
|
||||
}
|
||||
}
|
||||
|
||||
private fun prebuild() {
|
||||
override(style)
|
||||
}
|
||||
|
||||
fun build(): Span {
|
||||
prebuild()
|
||||
val builder = ArrayList<Any>()
|
||||
if (!TextUtils.isEmpty(text)) {
|
||||
var p = this.parent
|
||||
while (p != null) {
|
||||
if (!TextUtils.isEmpty(p.text)) {
|
||||
throw RuntimeException("Can't nest \"$text\" in spans")
|
||||
}
|
||||
p = p.parent
|
||||
}
|
||||
append(text)
|
||||
buildCharacterStyle(builder)
|
||||
buildParagraphStyle(builder)
|
||||
} else {
|
||||
buildParagraphStyle(builder)
|
||||
}
|
||||
|
||||
builder.addAll(spans)
|
||||
builder.forEach {
|
||||
setSpan(it, 0, length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
fun override(style: Span) {
|
||||
if (textColor == null) {
|
||||
textColor = style.textColor
|
||||
}
|
||||
if (backgroundColor == null) {
|
||||
backgroundColor = style.backgroundColor
|
||||
}
|
||||
if (textSize == null) {
|
||||
textSize = style.textSize
|
||||
}
|
||||
if (fontFamily == null) {
|
||||
fontFamily = style.fontFamily
|
||||
}
|
||||
if (typeface == null) {
|
||||
typeface = style.typeface
|
||||
}
|
||||
if (textStyle == null) {
|
||||
textStyle = style.textStyle
|
||||
}
|
||||
if (alignment == null) {
|
||||
alignment = style.alignment
|
||||
}
|
||||
if (textDecorationLine == null) {
|
||||
textDecorationLine = style.textDecorationLine
|
||||
}
|
||||
if (lineSpacing == null) {
|
||||
lineSpacing = style.lineSpacing
|
||||
}
|
||||
if (paddingTop == null) {
|
||||
paddingTop = style.paddingTop
|
||||
}
|
||||
if (paddingBottom == null) {
|
||||
paddingBottom = style.paddingBottom
|
||||
}
|
||||
if (verticalPadding == null) {
|
||||
verticalPadding = style.verticalPadding
|
||||
}
|
||||
if (onClick == null) {
|
||||
onClick = style.onClick
|
||||
}
|
||||
spans.addAll(style.spans)
|
||||
}
|
||||
|
||||
operator fun CharSequence.unaryPlus(): CharSequence {
|
||||
return append(Span(parent = this@Span).apply {
|
||||
text = this@unaryPlus
|
||||
build()
|
||||
})
|
||||
}
|
||||
|
||||
operator fun Span.plus(other: CharSequence): CharSequence {
|
||||
return append(Span(parent = this).apply {
|
||||
text = other
|
||||
build()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fun span(init: Span.() -> Unit): Span = Span().apply {
|
||||
override(Span.globalStyle)
|
||||
init()
|
||||
build()
|
||||
}
|
||||
|
||||
fun span(text: CharSequence, init: Span.() -> Unit): Span = Span().apply {
|
||||
override(Span.globalStyle)
|
||||
this.text = text
|
||||
init()
|
||||
build()
|
||||
}
|
||||
|
||||
fun style(init: Span.() -> Unit): Span = Span().apply {
|
||||
init()
|
||||
}
|
||||
|
||||
fun Span.span(init: Span.() -> Unit = {}): Span = apply {
|
||||
append(Span(parent = this).apply {
|
||||
init()
|
||||
build()
|
||||
})
|
||||
}
|
||||
|
||||
fun Span.span(text: CharSequence, init: Span.() -> Unit = {}): Span = apply {
|
||||
append(Span(parent = this).apply {
|
||||
this.text = text
|
||||
init()
|
||||
build()
|
||||
})
|
||||
}
|
||||
|
||||
fun Span.link(url: String, text: CharSequence = "",
|
||||
init: Span.() -> Unit = {}): Span = apply {
|
||||
append(Span(parent = this).apply {
|
||||
this.text = text
|
||||
this.spans.add(URLSpan(url))
|
||||
init()
|
||||
build()
|
||||
})
|
||||
}
|
||||
|
||||
fun Span.quote(@ColorInt color: Int, text: CharSequence = "",
|
||||
init: Span.() -> Unit = {}): Span = apply {
|
||||
append(Span(parent = this).apply {
|
||||
this.text = text
|
||||
this.spans.add(QuoteSpan(color))
|
||||
init()
|
||||
build()
|
||||
})
|
||||
}
|
||||
|
||||
fun Span.superscript(text: CharSequence = "", init: Span.() -> Unit = {}): Span = apply {
|
||||
append(Span(parent = this).apply {
|
||||
this.text = text
|
||||
this.spans.add(SuperscriptSpan())
|
||||
init()
|
||||
build()
|
||||
})
|
||||
}
|
||||
|
||||
fun Span.subscript(text: CharSequence = "", init: Span.() -> Unit = {}): Span = apply {
|
||||
append(Span(parent = this).apply {
|
||||
this.text = text
|
||||
this.spans.add(SubscriptSpan())
|
||||
init()
|
||||
build()
|
||||
})
|
||||
}
|
||||
|
||||
fun Span.image(drawable: Drawable, alignment: String = "bottom",
|
||||
init: Span.() -> Unit = {}): Span = apply {
|
||||
drawable.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight)
|
||||
append(Span(parent = this).apply {
|
||||
this.text = " "
|
||||
this.spans.add(ImageSpan(drawable, when (alignment) {
|
||||
"bottom" -> ImageSpan.ALIGN_BOTTOM
|
||||
"baseline" -> ImageSpan.ALIGN_BASELINE
|
||||
else -> throw RuntimeException("Unknown image alignment")
|
||||
}))
|
||||
init()
|
||||
build()
|
||||
})
|
||||
}
|
||||
|
||||
fun Span.addSpan(what: Any) = apply {
|
||||
this.spans.add(what)
|
||||
}
|
36
library/external/span/src/main/kotlin/me/gujun/android/span/style/CustomTypefaceSpan.kt
vendored
Normal file
36
library/external/span/src/main/kotlin/me/gujun/android/span/style/CustomTypefaceSpan.kt
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
package me.gujun.android.span.style
|
||||
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Typeface
|
||||
import android.text.TextPaint
|
||||
import android.text.style.MetricAffectingSpan
|
||||
|
||||
class CustomTypefaceSpan(private val tf: Typeface) : MetricAffectingSpan() {
|
||||
|
||||
override fun updateMeasureState(paint: TextPaint) {
|
||||
apply(paint, tf)
|
||||
}
|
||||
|
||||
override fun updateDrawState(ds: TextPaint) {
|
||||
apply(ds, tf)
|
||||
}
|
||||
|
||||
private fun apply(paint: Paint, tf: Typeface) {
|
||||
val oldStyle: Int
|
||||
|
||||
val old = paint.typeface
|
||||
oldStyle = old?.style ?: 0
|
||||
|
||||
val fake = oldStyle and tf.style.inv()
|
||||
|
||||
if (fake and Typeface.BOLD != 0) {
|
||||
paint.isFakeBoldText = true
|
||||
}
|
||||
|
||||
if (fake and Typeface.ITALIC != 0) {
|
||||
paint.textSkewX = -0.25f
|
||||
}
|
||||
|
||||
paint.typeface = tf
|
||||
}
|
||||
}
|
31
library/external/span/src/main/kotlin/me/gujun/android/span/style/LineSpacingSpan.kt
vendored
Normal file
31
library/external/span/src/main/kotlin/me/gujun/android/span/style/LineSpacingSpan.kt
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
package me.gujun.android.span.style
|
||||
|
||||
import android.graphics.Paint.FontMetricsInt
|
||||
import android.text.Spanned
|
||||
import android.text.style.LineHeightSpan
|
||||
|
||||
class LineSpacingSpan(private val add: Int) : LineHeightSpan {
|
||||
|
||||
override fun chooseHeight(text: CharSequence, start: Int, end: Int, spanstartv: Int, v: Int,
|
||||
fm: FontMetricsInt) {
|
||||
text as Spanned
|
||||
/*val spanStart =*/ text.getSpanStart(this)
|
||||
val spanEnd = text.getSpanEnd(this)
|
||||
|
||||
// Log.d("DEBUG", "Text: start=$start end=$end v=$v") // end may include the \n character
|
||||
// Log.d("DEBUG", "${text.slice(start until end)}".replace("\n", "#"))
|
||||
// Log.d("DEBUG", "LineSpacingSpan: spanStart=$spanStart spanEnd=$spanEnd spanstartv=$spanstartv")
|
||||
// Log.d("DEBUG", "$fm")
|
||||
// Log.d("DEBUG", "-----------------------")
|
||||
|
||||
if (spanstartv == v) {
|
||||
fm.descent += add
|
||||
} else if (text[start - 1] == '\n') {
|
||||
fm.descent += add
|
||||
}
|
||||
|
||||
if (end == spanEnd || end - 1 == spanEnd) {
|
||||
fm.descent -= add
|
||||
}
|
||||
}
|
||||
}
|
10
library/external/span/src/main/kotlin/me/gujun/android/span/style/SimpleClickableSpan.kt
vendored
Normal file
10
library/external/span/src/main/kotlin/me/gujun/android/span/style/SimpleClickableSpan.kt
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
package me.gujun.android.span.style
|
||||
|
||||
import android.text.TextPaint
|
||||
import android.text.style.ClickableSpan
|
||||
|
||||
abstract class SimpleClickableSpan : ClickableSpan() {
|
||||
override fun updateDrawState(ds: TextPaint) {
|
||||
// no-op
|
||||
}
|
||||
}
|
29
library/external/span/src/main/kotlin/me/gujun/android/span/style/TextDecorationLineSpan.kt
vendored
Normal file
29
library/external/span/src/main/kotlin/me/gujun/android/span/style/TextDecorationLineSpan.kt
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
package me.gujun.android.span.style
|
||||
|
||||
import android.text.TextPaint
|
||||
import android.text.style.CharacterStyle
|
||||
|
||||
class TextDecorationLineSpan(private val textDecorationLine: String) : CharacterStyle() {
|
||||
|
||||
override fun updateDrawState(tp: TextPaint) {
|
||||
when (textDecorationLine) {
|
||||
"none" -> {
|
||||
tp.isUnderlineText = false
|
||||
tp.isStrikeThruText = false
|
||||
}
|
||||
"underline" -> {
|
||||
tp.isUnderlineText = true
|
||||
tp.isStrikeThruText = false
|
||||
}
|
||||
"line-through" -> {
|
||||
tp.isUnderlineText = false
|
||||
tp.isStrikeThruText = true
|
||||
}
|
||||
"underline line-through" -> {
|
||||
tp.isUnderlineText = true
|
||||
tp.isStrikeThruText = true
|
||||
}
|
||||
else -> throw RuntimeException("Unknown text decoration line")
|
||||
}
|
||||
}
|
||||
}
|
41
library/external/span/src/main/kotlin/me/gujun/android/span/style/VerticalPaddingSpan.kt
vendored
Normal file
41
library/external/span/src/main/kotlin/me/gujun/android/span/style/VerticalPaddingSpan.kt
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
package me.gujun.android.span.style
|
||||
|
||||
import android.graphics.Paint.FontMetricsInt
|
||||
import android.text.Spanned
|
||||
import android.text.style.LineHeightSpan
|
||||
|
||||
class VerticalPaddingSpan(private val paddingTop: Int,
|
||||
private val paddingBottom: Int) : LineHeightSpan {
|
||||
|
||||
private var flag: Boolean = true
|
||||
|
||||
override fun chooseHeight(text: CharSequence, start: Int, end: Int, spanstartv: Int, v: Int,
|
||||
fm: FontMetricsInt) {
|
||||
text as Spanned
|
||||
/*val spanStart =*/ text.getSpanStart(this)
|
||||
val spanEnd = text.getSpanEnd(this)
|
||||
|
||||
// Log.d("DEBUG", "Text: start=$start end=$end v=$v") // end may include the \n character
|
||||
// Log.d("DEBUG", "${text.slice(start until end)}".replace("\n", "#"))
|
||||
// Log.d("DEBUG", "VerticalPadding: spanStart=$spanStart spanEnd=$spanEnd spanstartv=$spanstartv")
|
||||
// Log.d("DEBUG", "$fm")
|
||||
// Log.d("DEBUG", "-----------------------")
|
||||
|
||||
if (spanstartv == v) {
|
||||
fm.top -= paddingTop
|
||||
fm.ascent -= paddingTop
|
||||
flag = true
|
||||
} else if (flag && text[start - 1] != '\n') {
|
||||
fm.top += paddingTop
|
||||
fm.ascent += paddingTop
|
||||
flag = false
|
||||
} else {
|
||||
flag = false
|
||||
}
|
||||
|
||||
if (end == spanEnd || end - 1 == spanEnd) {
|
||||
fm.descent += paddingBottom
|
||||
fm.bottom += paddingBottom
|
||||
}
|
||||
}
|
||||
}
|
@ -14,6 +14,7 @@ include ':library:external:dialpad'
|
||||
include ':library:external:textdrawable'
|
||||
include ':library:external:autocomplete'
|
||||
include ':library:external:realmfieldnameshelper'
|
||||
include ':library:external:span'
|
||||
|
||||
include ':library:rustCrypto'
|
||||
include ':matrix-sdk-android'
|
||||
|
@ -396,6 +396,7 @@ dependencies {
|
||||
implementation project(':vector')
|
||||
implementation project(':vector-config')
|
||||
implementation project(':library:core-utils')
|
||||
debugImplementation project(':library:external:span')
|
||||
debugImplementation project(':library:ui-styles')
|
||||
implementation libs.dagger.hilt
|
||||
implementation 'androidx.multidex:multidex:2.0.1'
|
||||
|
@ -187,9 +187,7 @@ dependencies {
|
||||
|
||||
// UI
|
||||
implementation libs.google.material
|
||||
api('me.gujun.android:span:1.7') {
|
||||
exclude group: 'com.android.support', module: 'support-annotations'
|
||||
}
|
||||
implementation project(":library:external:span")
|
||||
implementation libs.markwon.core
|
||||
implementation libs.markwon.extLatex
|
||||
implementation libs.markwon.imageGlide
|
||||
|
Loading…
Reference in New Issue
Block a user