From 9d6946aa206d1e2cf876db60ae905d64d65fb241 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 16 Jun 2023 09:40:00 +0200 Subject: [PATCH] Extract method about text to the design system module. --- .../analytics/impl/AnalyticsOptInView.kt | 57 ++----------- .../designsystem/text/AnnotatedStrings.kt | 84 +++++++++++++++++++ 2 files changed, 90 insertions(+), 51 deletions(-) create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/AnnotatedStrings.kt diff --git a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt index 1856cd4d3c..aac025ed68 100644 --- a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt +++ b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt @@ -16,12 +16,6 @@ package io.element.android.features.analytics.impl -import android.graphics.Typeface -import android.text.SpannableString -import android.text.style.ForegroundColorSpan -import android.text.style.StyleSpan -import android.text.style.UnderlineSpan -import androidx.annotation.StringRes import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -44,25 +38,20 @@ import androidx.compose.ui.BiasAlignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.AnnotatedString -import androidx.compose.ui.text.SpanStyle -import androidx.compose.ui.text.buildAnnotatedString -import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import io.element.android.features.analytics.api.AnalyticsOptInEvents import io.element.android.libraries.designsystem.ElementTextStyles -import io.element.android.libraries.designsystem.LinkColor import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight +import io.element.android.libraries.designsystem.text.buildAnnotatedStringWithStyledPart import io.element.android.libraries.designsystem.theme.LocalColors import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.Icon @@ -98,9 +87,12 @@ fun AnalyticsOptInHeader(state: AnalyticsOptInState) { iconImageVector = Icons.Filled.Poll ) Text( - text = buildAnnotatedStringWithColoredPart( + text = buildAnnotatedStringWithStyledPart( R.string.screen_analytics_prompt_read_terms, - R.string.screen_analytics_prompt_read_terms_content_link + R.string.screen_analytics_prompt_read_terms_content_link, + color = Color.Unspecified, + underline = false, + bold = true, ), modifier = Modifier .fillMaxWidth() @@ -198,43 +190,6 @@ fun AnalyticsOptInFooter(eventSink: (AnalyticsOptInEvents) -> Unit) { } } -fun String.toAnnotatedString(): AnnotatedString = buildAnnotatedString { - append(this@toAnnotatedString) - val spannable = SpannableString(this@toAnnotatedString) - spannable.getSpans(0, spannable.length, Any::class.java).forEach { span -> - val start = spannable.getSpanStart(span) - val end = spannable.getSpanEnd(span) - when (span) { - is StyleSpan -> when (span.style) { - Typeface.BOLD -> addStyle(SpanStyle(fontWeight = FontWeight.Bold), start, end) - Typeface.ITALIC -> addStyle(SpanStyle(fontStyle = FontStyle.Italic), start, end) - Typeface.BOLD_ITALIC -> addStyle(SpanStyle(fontWeight = FontWeight.Bold, fontStyle = FontStyle.Italic), start, end) - } - is UnderlineSpan -> addStyle(SpanStyle(textDecoration = TextDecoration.Underline), start, end) - is ForegroundColorSpan -> addStyle(SpanStyle(color = Color(span.foregroundColor)), start, end) - } - } -} - -@Composable -fun buildAnnotatedStringWithColoredPart( - @StringRes fullTextRes: Int, - @StringRes coloredTextRes: Int, - color: Color = LinkColor, - underline: Boolean = true, -) = buildAnnotatedString { - val coloredPart = stringResource(coloredTextRes) - val fullText = stringResource(fullTextRes, coloredPart) - val startIndex = fullText.indexOf(coloredPart) - append(fullText) - addStyle( - style = SpanStyle( - color = color, - textDecoration = if (underline) TextDecoration.Underline else null - ), start = startIndex, end = startIndex + coloredPart.length - ) -} - @Preview @Composable fun AnalyticsOptInViewLightPreview(@PreviewParameter(AnalyticsOptInStateProvider::class) state: AnalyticsOptInState) = ElementPreviewLight { diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/AnnotatedStrings.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/AnnotatedStrings.kt new file mode 100644 index 0000000000..7d4de84301 --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/AnnotatedStrings.kt @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.designsystem.text + +import android.graphics.Typeface +import android.text.SpannableString +import android.text.style.ForegroundColorSpan +import android.text.style.StyleSpan +import android.text.style.UnderlineSpan +import androidx.annotation.StringRes +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextDecoration +import io.element.android.libraries.designsystem.LinkColor + +fun String.toAnnotatedString(): AnnotatedString = buildAnnotatedString { + append(this@toAnnotatedString) + val spannable = SpannableString(this@toAnnotatedString) + spannable.getSpans(0, spannable.length, Any::class.java).forEach { span -> + val start = spannable.getSpanStart(span) + val end = spannable.getSpanEnd(span) + when (span) { + is StyleSpan -> when (span.style) { + Typeface.BOLD -> addStyle(SpanStyle(fontWeight = FontWeight.Bold), start, end) + Typeface.ITALIC -> addStyle(SpanStyle(fontStyle = FontStyle.Italic), start, end) + Typeface.BOLD_ITALIC -> addStyle(SpanStyle(fontWeight = FontWeight.Bold, fontStyle = FontStyle.Italic), start, end) + } + is UnderlineSpan -> addStyle(SpanStyle(textDecoration = TextDecoration.Underline), start, end) + is ForegroundColorSpan -> addStyle(SpanStyle(color = Color(span.foregroundColor)), start, end) + } + } +} + +/** + * Convert a string to an [AnnotatedString] with styles applied. + * + * @param fullTextRes the string resource to use as the full text. Must contain a single %s + * @param coloredTextRes the string resource to use as the colored part of the string + * @param color the color to apply to the string + * @param underline whether to underline the string + * @param bold whether to bold the string + */ +@Composable +fun buildAnnotatedStringWithStyledPart( + @StringRes fullTextRes: Int, + @StringRes coloredTextRes: Int, + color: Color = LinkColor, + underline: Boolean = true, + bold: Boolean = false, +) = buildAnnotatedString { + val coloredPart = stringResource(coloredTextRes) + val fullText = stringResource(fullTextRes, coloredPart) + val startIndex = fullText.indexOf(coloredPart) + append(fullText) + addStyle( + style = SpanStyle( + color = color, + textDecoration = if (underline) TextDecoration.Underline else null, + fontWeight = if (bold) FontWeight.Bold else null, + ), + start = startIndex, + end = startIndex + coloredPart.length, + ) +}