Skip to content

Commit

Permalink
fix: make learn more links clickable for automation v2 [WPB-5888]
Browse files Browse the repository at this point in the history
  • Loading branch information
saleniuk committed Jun 3, 2024
1 parent aee1880 commit e93eccc
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@ package com.wire.android.ui.common
import androidx.compose.foundation.layout.width
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.rememberTextMeasurer
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.LayoutDirection
import com.wire.android.ui.theme.WireTheme
import com.wire.android.ui.theme.wireTypography
import com.wire.android.util.ui.PreviewMultipleThemes
Expand All @@ -52,6 +55,8 @@ private fun PreviewTextWithLinkSuffixBuilder(
)
}

// ----- LTR -----

@PreviewMultipleThemes
@Composable
fun PreviewTextWithLinkSuffixWithoutALink() = WireTheme {
Expand Down Expand Up @@ -87,3 +92,51 @@ fun PreviewTextWithLinkSuffixMultilineNotFittingInLastLine() = WireTheme {
linkText = "link"
) { lastTextLineWidthDp, linkWidthDp -> lastTextLineWidthDp + (linkWidthDp / 2) }
}

// ----- RTL -----

@PreviewMultipleThemes
@Composable
fun PreviewTextWithLinkSuffixWithoutALinkRtl() = WireTheme {
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
TextWithLinkSuffix(text = AnnotatedString("This is a text without a link"))
}
}

@PreviewMultipleThemes
@Composable
fun PreviewTextWithLinkSuffixFittingInSameLineRtl() = WireTheme {
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
PreviewTextWithLinkSuffixBuilder { lastTextLineWidthDp, linkWidthDp -> lastTextLineWidthDp + linkWidthDp }
}
}

@PreviewMultipleThemes
@Composable
fun PreviewTextWithLinkSuffixNotFittingInSameLineRtl() = WireTheme {
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
PreviewTextWithLinkSuffixBuilder { lastTextLineWidthDp, linkWidthDp -> lastTextLineWidthDp + (linkWidthDp / 2) }
}
}

@PreviewMultipleThemes
@Composable
fun PreviewTextWithLinkSuffixMultilineFittingInLastLineRtl() = WireTheme {
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
PreviewTextWithLinkSuffixBuilder(
textLines = listOf("This is a text with a link", "This is a text with a"),
linkText = "link",
) { lastTextLineWidthDp, linkWidthDp -> lastTextLineWidthDp + linkWidthDp }
}
}

@PreviewMultipleThemes
@Composable
fun PreviewTextWithLinkSuffixMultilineNotFittingInLastLineRtl() = WireTheme {
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
PreviewTextWithLinkSuffixBuilder(
textLines = listOf("This is a text with a", "This is a text with a"),
linkText = "link"
) { lastTextLineWidthDp, linkWidthDp -> lastTextLineWidthDp + (linkWidthDp / 2) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,25 @@
package com.wire.android.ui.common

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.text.InlineTextContent
import androidx.compose.foundation.text.appendInlineContent
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.positionInParent
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.Placeholder
import androidx.compose.ui.text.PlaceholderVerticalAlign
Expand All @@ -35,23 +45,78 @@ import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.rememberTextMeasurer
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.DpSize
import com.wire.android.ui.theme.wireColorScheme
import com.wire.android.ui.theme.wireTypography

@Composable
fun TextWithLinkSuffix(
text: AnnotatedString,
modifier: Modifier = Modifier,
linkText: String? = null,
onLinkClick: () -> Unit = {},
linkTag: String = "link",
textStyle: TextStyle = MaterialTheme.wireTypography.body01,
textColor: Color = MaterialTheme.wireColorScheme.onBackground,
linkStyle: TextStyle = MaterialTheme.wireTypography.body02,
linkColor: Color = MaterialTheme.wireColorScheme.primary,
linkDecoration: TextDecoration = TextDecoration.Underline,
onTextLayout: (TextLayoutResult) -> Unit = {},
modifier: Modifier = Modifier,
onTextLayout: (TextLayoutResult) -> Unit = {}
) {
var linkPosition by remember { mutableStateOf(Offset(0f, 0f)) }
val (inlineText, inlineContent) = buildInlineText(
text = text,
linkText = linkText,
linkStyle = linkStyle,
linkDecoration = linkDecoration,
onLinkPositionCalculated = { linkPosition = it }
)

// For some reason automation tests can't find inlined text content, so it needs to be added directly in layout. Inlined text content
// is still used to get the size of the link text and its position so that it works no matter what locale is used. Position of the link
// is then used in this layout and the proper text composable that can be found by automation tests is placed where it should be.
Layout(
modifier = modifier,
content = {
Text(
text = inlineText,
style = textStyle,
color = textColor,
inlineContent = inlineContent,
onTextLayout = onTextLayout,
modifier = Modifier.layoutId("text")
)

if (linkText != null) {
Text(
text = linkText,
style = linkStyle,
color = linkColor,
textDecoration = linkDecoration,
modifier = Modifier.layoutId("link")
.clickable(onClick = onLinkClick)
)
}
},
measurePolicy = { measurables, constraints ->
val measureConstraints = constraints.copy(minWidth = 0, minHeight = 0)
val textPlaceable = measurables.first { it.layoutId == "text" }.measure(measureConstraints)
val linkPlaceable = measurables.firstOrNull { it.layoutId == "link" }?.measure(measureConstraints)
layout(width = textPlaceable.width, height = textPlaceable.height) {
textPlaceable.placeRelative(0, 0)
linkPlaceable?.place(linkPosition.x.toInt(), linkPosition.y.toInt())
}
}
)
}

@Composable
private fun buildInlineText(
text: AnnotatedString,
linkText: String?,
linkStyle: TextStyle,
linkDecoration: TextDecoration,
onLinkPositionCalculated: (Offset) -> Unit
): Pair<AnnotatedString, Map<String, InlineTextContent>> {
val textMeasurer = rememberTextMeasurer()
val linkId = "link"
val inlineText = linkText?.let {
Expand All @@ -68,42 +133,31 @@ fun TextWithLinkSuffix(
text = linkText,
style = linkStyle.copy(textDecoration = linkDecoration),
)
val textSize = textLayoutResult.size
val density = LocalDensity.current
val (linkWidthSp, linkHeightSp) = with(density) {
textSize.width.toSp() to textSize.height.toSp()
textLayoutResult.size.width.toSp() to textLayoutResult.size.height.toSp()
}
val linkSizeDp = with(density) {
DpSize(textLayoutResult.size.width.toDp(), textLayoutResult.size.height.toDp())
}

put(
linkId,
InlineTextContent(
placeholder = Placeholder(
width = linkWidthSp,
height = linkHeightSp,
placeholderVerticalAlign = PlaceholderVerticalAlign.Bottom
),
children = {
Text(
text = linkText,
style = linkStyle,
color = linkColor,
textDecoration = linkDecoration,
modifier = Modifier
.testTag(linkTag)
.clickable(onClick = onLinkClick)
)
}
)
placeholder = Placeholder(
width = linkWidthSp,
height = linkHeightSp,
placeholderVerticalAlign = PlaceholderVerticalAlign.Bottom
),
children = {
Box(
modifier = Modifier // It's only a placeholder as well, just to get the real size and position of the link.
.size(linkSizeDp)
.onGloballyPositioned { it.parentLayoutCoordinates?.let { onLinkPositionCalculated(it.positionInParent()) } }
)
}
)
)
}
}

Text(
text = inlineText,
style = textStyle,
color = textColor,
inlineContent = inlineContent,
onTextLayout = onTextLayout,
modifier = modifier,
)
return inlineText to inlineContent
}

0 comments on commit e93eccc

Please sign in to comment.