Styling
parent
50f9864a8b
commit
3c36f1247b
@ -0,0 +1,180 @@
|
||||
package org.thoughtcrime.securesms.ui.components
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Typeface
|
||||
import android.text.Spanned
|
||||
import android.text.SpannedString
|
||||
import android.text.style.*
|
||||
import android.util.Log
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.BaselineShift
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.unit.Density
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.em
|
||||
import androidx.core.text.HtmlCompat
|
||||
|
||||
// TODO Remove this file once we update to composeVersion=1.7.0-alpha06 fixes https://issuetracker.google.com/issues/139320238?pli=1
|
||||
// which allows Stylized string in string resources
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun resources(): Resources {
|
||||
LocalConfiguration.current
|
||||
return LocalContext.current.resources
|
||||
}
|
||||
|
||||
fun Spanned.toHtmlWithoutParagraphs(): String {
|
||||
return HtmlCompat.toHtml(this, HtmlCompat.TO_HTML_PARAGRAPH_LINES_CONSECUTIVE)
|
||||
.substringAfter("<p dir=\"ltr\">").substringBeforeLast("</p>")
|
||||
}
|
||||
|
||||
fun Resources.getText(@StringRes id: Int, vararg args: Any): CharSequence {
|
||||
val escapedArgs = args.map {
|
||||
if (it is Spanned) it.toHtmlWithoutParagraphs() else it
|
||||
}.toTypedArray()
|
||||
val resource = SpannedString(getText(id))
|
||||
val htmlResource = resource.toHtmlWithoutParagraphs()
|
||||
val formattedHtml = String.format(htmlResource, *escapedArgs)
|
||||
return HtmlCompat.fromHtml(formattedHtml, HtmlCompat.FROM_HTML_MODE_LEGACY)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun annotatedStringResource(@StringRes id: Int, vararg formatArgs: Any): AnnotatedString {
|
||||
val resources = resources()
|
||||
val density = LocalDensity.current
|
||||
return remember(id, formatArgs) {
|
||||
val text = resources.getText(id, *formatArgs)
|
||||
spannableStringToAnnotatedString(text, density)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun annotatedStringResource(@StringRes id: Int): AnnotatedString {
|
||||
val resources = resources()
|
||||
val density = LocalDensity.current
|
||||
return remember(id) {
|
||||
val text = resources.getText(id)
|
||||
spannableStringToAnnotatedString(text, density)
|
||||
}
|
||||
}
|
||||
|
||||
private fun spannableStringToAnnotatedString(
|
||||
text: CharSequence,
|
||||
density: Density
|
||||
): AnnotatedString {
|
||||
return if (text is Spanned) {
|
||||
with(density) {
|
||||
buildAnnotatedString {
|
||||
append((text.toString()))
|
||||
text.getSpans(0, text.length, Any::class.java).forEach {
|
||||
val start = text.getSpanStart(it)
|
||||
val end = text.getSpanEnd(it)
|
||||
when (it) {
|
||||
is StyleSpan -> when (it.style) {
|
||||
Typeface.NORMAL -> addStyle(
|
||||
SpanStyle(
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontStyle = FontStyle.Normal
|
||||
),
|
||||
start,
|
||||
end
|
||||
)
|
||||
Typeface.BOLD -> addStyle(
|
||||
SpanStyle(
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontStyle = FontStyle.Normal
|
||||
),
|
||||
start,
|
||||
end
|
||||
)
|
||||
Typeface.ITALIC -> addStyle(
|
||||
SpanStyle(
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontStyle = FontStyle.Italic
|
||||
),
|
||||
start,
|
||||
end
|
||||
)
|
||||
Typeface.BOLD_ITALIC -> addStyle(
|
||||
SpanStyle(
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontStyle = FontStyle.Italic
|
||||
),
|
||||
start,
|
||||
end
|
||||
)
|
||||
}
|
||||
is TypefaceSpan -> addStyle(
|
||||
SpanStyle(
|
||||
fontFamily = when (it.family) {
|
||||
FontFamily.SansSerif.name -> FontFamily.SansSerif
|
||||
FontFamily.Serif.name -> FontFamily.Serif
|
||||
FontFamily.Monospace.name -> FontFamily.Monospace
|
||||
FontFamily.Cursive.name -> FontFamily.Cursive
|
||||
else -> FontFamily.Default
|
||||
}
|
||||
),
|
||||
start,
|
||||
end
|
||||
)
|
||||
is BulletSpan -> {
|
||||
Log.d("StringResources", "BulletSpan not supported yet")
|
||||
addStyle(SpanStyle(), start, end)
|
||||
}
|
||||
is AbsoluteSizeSpan -> addStyle(
|
||||
SpanStyle(fontSize = if (it.dip) it.size.dp.toSp() else it.size.toSp()),
|
||||
start,
|
||||
end
|
||||
)
|
||||
is RelativeSizeSpan -> addStyle(
|
||||
SpanStyle(fontSize = it.sizeChange.em),
|
||||
start,
|
||||
end
|
||||
)
|
||||
is StrikethroughSpan -> addStyle(
|
||||
SpanStyle(textDecoration = TextDecoration.LineThrough),
|
||||
start,
|
||||
end
|
||||
)
|
||||
is UnderlineSpan -> addStyle(
|
||||
SpanStyle(textDecoration = TextDecoration.Underline),
|
||||
start,
|
||||
end
|
||||
)
|
||||
is SuperscriptSpan -> addStyle(
|
||||
SpanStyle(baselineShift = BaselineShift.Superscript),
|
||||
start,
|
||||
end
|
||||
)
|
||||
is SubscriptSpan -> addStyle(
|
||||
SpanStyle(baselineShift = BaselineShift.Subscript),
|
||||
start,
|
||||
end
|
||||
)
|
||||
is ForegroundColorSpan -> addStyle(
|
||||
SpanStyle(color = Color(it.foregroundColor)),
|
||||
start,
|
||||
end
|
||||
)
|
||||
else -> addStyle(SpanStyle(), start, end)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
AnnotatedString(text.toString())
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="12dp"
|
||||
android:height="12dp"
|
||||
android:viewportWidth="12"
|
||||
android:viewportHeight="12">
|
||||
<path
|
||||
android:pathData="M6,10.56C8.497,10.56 10.559,8.495 10.559,6.002C10.559,3.504 8.494,1.443 5.996,1.443C3.503,1.443 1.441,3.504 1.441,6.002C1.441,8.495 3.507,10.56 6,10.56ZM6,9.662C3.968,9.662 2.343,8.033 2.343,6.002C2.343,3.97 3.965,2.341 5.996,2.341C8.027,2.341 9.661,3.97 9.661,6.002C9.661,8.033 8.031,9.662 6,9.662Z"
|
||||
android:fillColor="#A1A2A1"/>
|
||||
<path
|
||||
android:pathData="M6.262,5.758C6.436,5.758 6.523,5.712 6.648,5.592C6.759,5.464 6.807,5.337 6.811,5.125L6.81,5.095C6.809,4.98 6.798,4.907 6.759,4.802C6.718,4.703 6.68,4.632 6.609,4.557C6.535,4.48 6.467,4.426 6.362,4.384C6.258,4.337 6.152,4.313 6.024,4.313C5.743,4.317 5.575,4.386 5.413,4.55C5.261,4.721 5.192,4.907 5.19,5.183L5.192,5.484L4.582,5.485V5.151C4.582,4.973 4.609,4.791 4.667,4.628C4.729,4.466 4.826,4.295 4.942,4.171C5.058,4.047 5.227,3.928 5.401,3.855C5.576,3.781 5.803,3.738 6.031,3.738C6.225,3.738 6.43,3.776 6.593,3.842C6.755,3.908 6.908,4.004 7.024,4.12C7.14,4.233 7.252,4.392 7.318,4.543C7.383,4.69 7.419,4.87 7.419,5.036L7.418,5.149C7.418,5.3 7.389,5.459 7.335,5.598C7.284,5.733 7.201,5.868 7.109,5.969C7.016,6.07 6.898,6.162 6.77,6.224C6.646,6.282 6.476,6.327 6.324,6.322L6.244,6.32C6.174,6.325 6.144,6.341 6.143,6.437L6.142,6.928L5.557,6.926V6.315C5.557,6.168 5.605,6.021 5.704,5.926C5.793,5.821 5.959,5.755 6.116,5.758H6.262Z"
|
||||
android:fillColor="#A1A2A1"/>
|
||||
<path
|
||||
android:pathData="M6.325,7.62C6.325,7.879 6.116,8.088 5.857,8.088C5.599,8.088 5.389,7.879 5.389,7.62C5.389,7.362 5.599,7.152 5.857,7.152C6.116,7.152 6.325,7.362 6.325,7.62Z"
|
||||
android:fillColor="#A1A2A1"/>
|
||||
</vector>
|
Loading…
Reference in New Issue