The code below renders the following text in both Android Views and in Jetpack Compose UI.
Contact our team on 555 555 555 Opt 3 to activate.
The code below renders the following text in both Android Views and in Jetpack Compose UI.
Contact our team on 555 555 555 Opt 3 to activate.
@Composable | |
fun annotatedStringResource(@StringRes id: Int, vararg formatArgs: Any): AnnotatedString { | |
val resources = LocalContext.current.resources | |
return remember(id) { | |
val text = resources.getText(id, *formatArgs) | |
spannableStringToAnnotatedString(text) | |
} | |
} | |
@Composable | |
fun annotatedStringResource(@StringRes id: Int): AnnotatedString { | |
val resources = LocalContext.current.resources | |
return remember(id) { | |
val text = resources.getText(id) | |
spannableStringToAnnotatedString(text) | |
} | |
} | |
private fun spannableStringToAnnotatedString(text: CharSequence): AnnotatedString { | |
return if (text is Spanned) { | |
val spanStyles = mutableListOf<AnnotatedString.Range<SpanStyle>>() | |
spanStyles.addAll(text.getSpans(0, text.length, UnderlineSpan::class.java).map { | |
AnnotatedString.Range( | |
SpanStyle(textDecoration = TextDecoration.Underline), | |
text.getSpanStart(it), | |
text.getSpanEnd(it) | |
) | |
}) | |
spanStyles.addAll(text.getSpans(0, text.length, StyleSpan::class.java).map { | |
AnnotatedString.Range( | |
SpanStyle(fontWeight = FontWeight.Bold), | |
text.getSpanStart(it), | |
text.getSpanEnd(it) | |
) | |
}) | |
AnnotatedString(text.toString(), spanStyles = spanStyles) | |
} else { | |
AnnotatedString(text.toString()) | |
} | |
} |
<string name="launch_awaiting_instructions">Contact <b>our</b> team on %1$s to activate.</string> | |
<string name="support_contact_phone_number"><b>555 555 555</b> Opt <b>3</b></string> |
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) | |
} |
I think the
@ReadOnlyComposable
should eliminate need forremember
.