70

How can i add hyperlink to some section of the text of Text component?

With buildAnnotatedString i can set link section blue and underlined as in image below, but how can i also turn that section into link?

enter image description here

   val annotatedLinkString = buildAnnotatedString {
        val str = "Click this link to go to web site"
        val startIndex = str.indexOf("link")
        val endIndex = startIndex + 4
        append(str)
        addStyle(
            style = SpanStyle(
                color = Color(0xff64B5F6),
                textDecoration = TextDecoration.Underline
            ), start = startIndex, end = endIndex
        )
    }

    Text(
        modifier = modifier
            .padding(16.dp)
            .fillMaxWidth(),
        text = annotatedLinkString
    )

I can also get Spanned but is there any way to use it with Text?

val str: Spanned = HtmlCompat.fromHtml(
    "<a href=\"http://www.github.com\">Github</a>", HtmlCompat.FROM_HTML_MODE_LEGACY
)
Thracian
  • 43,021
  • 16
  • 133
  • 222
  • 1
    Also consider size of touch target: https://slack-chats.kotlinlang.org/t/2651976 – Uli Nov 02 '22 at 10:55

12 Answers12

68

The marked answer confuses novices, I give a complete example

Please don't forget to end pushStringAnnotation with pop()

val annotatedString = buildAnnotatedString {
    append("By joining, you agree to the ")

    pushStringAnnotation(tag = "policy", annotation = "https://google.com/policy")
    withStyle(style = SpanStyle(color = MaterialTheme.colors.primary)) {
        append("privacy policy")
    }
    pop()

    append(" and ")

    pushStringAnnotation(tag = "terms", annotation = "https://google.com/terms")

    withStyle(style = SpanStyle(color = MaterialTheme.colors.primary)) {
        append("terms of use")
    }

    pop()
}

ClickableText(text = annotatedString, style = MaterialTheme.typography.body1, onClick = { offset ->
    annotatedString.getStringAnnotations(tag = "policy", start = offset, end = offset).firstOrNull()?.let {
        Log.d("policy URL", it.item)
    }

    annotatedString.getStringAnnotations(tag = "terms", start = offset, end = offset).firstOrNull()?.let {
        Log.d("terms URL", it.item)
    }
})

final effect

enter image description here

If you need #tags and @mentions refer to my other answer

enter image description here

gaohomway
  • 2,132
  • 1
  • 20
  • 37
65

For a complete answer you can use ClickableText which returns the position of text, and UriHandler to open URI in a browser.

val annotatedLinkString: AnnotatedString = buildAnnotatedString {

    val str = "Click this link to go to web site"
    val startIndex = str.indexOf("link")
    val endIndex = startIndex + 4
    append(str)
    addStyle(
        style = SpanStyle(
            color = Color(0xff64B5F6),
            fontSize = 18.sp,
            textDecoration = TextDecoration.Underline
        ), start = startIndex, end = endIndex
    )

    // attach a string annotation that stores a URL to the text "link"
    addStringAnnotation(
        tag = "URL",
        annotation = "https://github.com",
        start = startIndex,
        end = endIndex
    )

}

// UriHandler parse and opens URI inside AnnotatedString Item in Browse
val uriHandler = LocalUriHandler.current

//  Clickable text returns position of text that is clicked in onClick callback
ClickableText(
    modifier = modifier
        .padding(16.dp)
        .fillMaxWidth(),
    text = annotatedLinkString,
    onClick = {
        annotatedLinkString
            .getStringAnnotations("URL", it, it)
            .firstOrNull()?.let { stringAnnotation ->
                uriHandler.openUri(stringAnnotation.item)
            }
    }
)
C. Hellmann
  • 556
  • 6
  • 13
Thracian
  • 43,021
  • 16
  • 133
  • 222
  • 4
    How can I make this work with string resources, seems like a great approach for hardcoded strings. – Guanaco Devs Oct 07 '21 at 20:24
  • 1
    @GuanacoDevs check out [my answer below](https://stackoverflow.com/a/72369830/4560689)! – Chantell Osejo May 24 '22 at 21:50
  • @ChantellOsejo That seems to be a way to go, you may gain a more control. However this answer led to me a more simplified way. – Guanaco Devs May 26 '22 at 23:20
  • Great answer. Just want to point out that if you need to simply run a function (like to move to another fragment) in your app, you can omit the `addStringAnnotation` and just directly call the function from your `onClick` method in the `ClickableText` constructor. – AndroidDev Nov 22 '22 at 19:52
  • @Thracian It would be hard to calculate `start` `end` index if we have multiple locale in app. Do you know how can i achieve that in that case? – Arpit Patel Feb 19 '23 at 17:51
  • I understand it's just an example, but you mixed up `str.indexOf` and hardcoded index - `4`, just choose one way – user924 Jun 12 '23 at 09:05
21

For anyone looking for a reusable copy-paste solution,

Create a new file LinkText.kt and copy-paste this code,

data class LinkTextData(
    val text: String,
    val tag: String? = null,
    val annotation: String? = null,
    val onClick: ((str: AnnotatedString.Range<String>) -> Unit)? = null,
)

@Composable
fun LinkText(
    linkTextData: List<LinkTextData>,
    modifier: Modifier = Modifier,
) {
    val annotatedString = createAnnotatedString(linkTextData)

    ClickableText(
        text = annotatedString,
        style = MaterialTheme.typography.body1,
        onClick = { offset ->
            linkTextData.forEach { annotatedStringData ->
                if (annotatedStringData.tag != null && annotatedStringData.annotation != null) {
                    annotatedString.getStringAnnotations(
                        tag = annotatedStringData.tag,
                        start = offset,
                        end = offset,
                    ).firstOrNull()?.let {
                        annotatedStringData.onClick?.invoke(it)
                    }
                }
            }
        },
        modifier = modifier,
    )
}

@Composable
private fun createAnnotatedString(data: List<LinkTextData>): AnnotatedString {
    return buildAnnotatedString {
        data.forEach { linkTextData ->
            if (linkTextData.tag != null && linkTextData.annotation != null) {
                pushStringAnnotation(
                    tag = linkTextData.tag,
                    annotation = linkTextData.annotation,
                )
                withStyle(
                    style = SpanStyle(
                        color = MaterialTheme.colors.primary,
                        textDecoration = TextDecoration.Underline,
                    ),
                ) {
                    append(linkTextData.text)
                }
                pop()
            } else {
                append(linkTextData.text)
            }
        }
    }
}

Usage

LinkText(
    linkTextData = listOf(
        LinkTextData(
            text = "Icons made by ",
        ),
        LinkTextData(
            text = "smalllikeart",
            tag = "icon_1_author",
            annotation = "https://www.flaticon.com/authors/smalllikeart",
            onClick = {
                Log.d("Link text", "${it.tag} ${it.item}")
            },
        ),
        LinkTextData(
            text = " from ",
        ),
        LinkTextData(
            text = "Flaticon",
            tag = "icon_1_source",
            annotation = "https://www.flaticon.com/",
            onClick = {
                Log.d("Link text", "${it.tag} ${it.item}")
            },
        )
    ),
    modifier = Modifier
        .padding(
            all = 16.dp,
        ),
)

Screenshot,

Screenshot

Note

  1. I am handling web pages manually using a composable. Use UriHandler or other alternatives if manual control is not required.
  2. Style clickable and other text as required in LinkText.
Abhimanyu
  • 11,351
  • 7
  • 51
  • 121
10

You can use https://github.com/firefinchdev/linkify-text

Its a single file, you can directly copy it to your project.

Also, it uses Android's Linkify for link detection, which is same as that of TextView's autoLink.

Anirudh Gupta
  • 573
  • 5
  • 12
8

The easiest and cleanest solution:

enter image description here

@Composable
fun AnnotatedClickableText() {
  val termsUrl = "https://example.com/terms"
  val privacyUrl = "https://example.com/privacy"
  val annotatedText = buildAnnotatedString {
    append("You agree to our ")
    withStyle(style = SpanStyle(color = Color.Blue, fontWeight = FontWeight.Bold)) {
      appendLink("Terms of Use", termsUrl)
    }
    append(" and ")
    withStyle(style = SpanStyle(color = Color.Blue, fontWeight = FontWeight.Bold)) {
      appendLink("Privacy Policy", privacyUrl)
    }
  }

  ClickableText(
    text = annotatedText,
    onClick = { offset ->
      annotatedText.onLinkClick(offset) { link ->
        println("Clicked URL: $link")
        // Open link in WebView.
      }
    }
  )
}

fun AnnotatedString.Builder.appendLink(linkText: String, linkUrl: String) {
  pushStringAnnotation(tag = linkUrl, annotation = linkUrl)
  append(linkText)
  pop()
}

fun AnnotatedString.onLinkClick(offset: Int, onClick: (String) -> Unit) {
  getStringAnnotations(start = offset, end = offset).firstOrNull()?.let {
    onClick(it.item)
  }
}

Notice the 2 extension functions which make link creation much more simple.

vovahost
  • 34,185
  • 17
  • 113
  • 116
7

How can i add hyperlink to some section of the text of Text component?

with(AnnotatedString.Builder()) {
    append("link: Jetpack Compose")
    // attach a string annotation that stores a URL to the text "Jetpack Compose".
    addStringAnnotation(
        tag = "URL",
        annotation = "https://developer.android.com/jetpack/compose",
        start = 6,
        end = 21
    )
}

tag:The tag used to distinguish annotations

annotation: The string annotation that is attached

start: The inclusive starting offset of the range

end: The exclusive end offset of the

Source

Arda Kazancı
  • 8,341
  • 4
  • 28
  • 50
5

If you want to use @StringRes from strings.xml file you can use the code below

enter image description here

Lets say you have the following string resources:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="disclaimer">By joining you agree to the privacy policy and terms of use.</string>
    <string name="privacy_policy">privacy policy</string>
    <string name="terms_of_use">terms of use</string>
</resources>

You can use it like this:

HighlightedText(
    text = stringResource(id = R.string.disclaimer),
    highlights = listOf(
        Highlight(
            text = stringResource(id = R.string.privacy_policy),
            data = "https://stackoverflow.com/legal/privacy-policy",
            onClick = { link ->
                // do something with link
            }
        ),
        Highlight(
            text = stringResource(id = R.string.terms_of_use),
            data = "https://stackoverflow.com/legal/terms-of-use",
            onClick = { link ->
                // do something with link
            }
        )
    )
)

Here is the source code for the Composable:

data class Highlight(
    val text: String,
    val data: String,
    val onClick: (data: String) -> Unit
)

@Composable
fun HighlightedText(
    text: String,
    highlights: List<Highlight>,
    modifier: Modifier = Modifier
) {
    data class TextData(
        val text: String,
        val tag: String? = null,
        val data: String? = null,
        val onClick: ((data: AnnotatedString.Range<String>) -> Unit)? = null
    )

    val textData = mutableListOf<TextData>()
    if (highlights.isEmpty()) {
        textData.add(
            TextData(
                text = text
            )
        )
    } else {
        var startIndex = 0
        highlights.forEachIndexed { i, link ->
            val endIndex = text.indexOf(link.text)
            if (endIndex == -1) {
                throw Exception("Highlighted text mismatch")
            }
            textData.add(
                TextData(
                    text = text.substring(startIndex, endIndex)
                )
            )
            textData.add(
                TextData(
                    text = link.text,
                    tag = "${link.text}_TAG",
                    data = link.data,
                    onClick = {
                        link.onClick(it.item)
                    }
                )
            )
            startIndex = endIndex + link.text.length
            if (i == highlights.lastIndex && startIndex < text.length) {
                textData.add(
                    TextData(
                        text = text.substring(startIndex, text.length)
                    )
                )
            }
        }
    }

    val annotatedString = buildAnnotatedString {
        textData.forEach { linkTextData ->
            if (linkTextData.tag != null && linkTextData.data != null) {
                pushStringAnnotation(
                    tag = linkTextData.tag,
                    annotation = linkTextData.data,
                )
                withStyle(
                    style = SpanStyle(
                        color = infoLinkTextColor
                    ),
                ) {
                    append(linkTextData.text)
                }
                pop()
            } else {
                append(linkTextData.text)
            }
        }
    }
    ClickableText(
        text = annotatedString,
        style = TextStyle(
            fontSize = 30.sp,
            fontWeight = FontWeight.Normal,
            color = infoTextColor,
            textAlign = TextAlign.Start
        ),
        onClick = { offset ->
            textData.forEach { annotatedStringData ->
                if (annotatedStringData.tag != null && annotatedStringData.data != null) {
                    annotatedString.getStringAnnotations(
                        tag = annotatedStringData.tag,
                        start = offset,
                        end = offset,
                    ).firstOrNull()?.let {
                        annotatedStringData.onClick?.invoke(it)
                    }
                }
            }
        },
        modifier = modifier
    )
}
slaviboy
  • 1,407
  • 17
  • 27
4

EDIT: There is a bug that prevents accessibility services from properly reading embedded links such as these prior to Jetpack Compose 1.3.0. Even after 1.3.0, there is another bug where the onClick() function is not called by the Accessibility Service (Talkback). See this Google Issue. I would recommend utilizing the AndroidView + old-school TextView option I outline below if your app needs to be accessible, at least until the linked issue is addressed.

--

The answers here are all great if you are using hardcoded strings, but they're not very useful for string resources. Here's some code to give you similar functionality to how old-school TextViews would work with HTML built entirely using Jetpack Compose (no interop APIs). Credit for 99% of this answer goes to the comment on this issue, which I extended to use the Android String resource Annotation tag to support URLs. [Note: BulletSpan is not currently supported in this solution as it is not needed for my use case and I didn't take the time to address its absence in the solution I extended]

const val URL_ANNOTATION_KEY = "url"

/**
 * Much of this class comes from
 * https://issuetracker.google.com/issues/139320238#comment11
 * which seeks to correct the gap in Jetpack Compose wherein HTML style tags in string resources
 * are not respected.
 */
@Composable
@ReadOnlyComposable
private fun resources(): Resources {
    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(
                                style = SpanStyle(
                                    fontWeight = FontWeight.Normal,
                                    fontStyle = FontStyle.Normal
                                ),
                                start = start,
                                end = end
                            )
                            Typeface.BOLD -> addStyle(
                                style = SpanStyle(
                                    fontWeight = FontWeight.Bold,
                                    fontStyle = FontStyle.Normal
                                ),
                                start = start,
                                end = end
                            )
                            Typeface.ITALIC -> addStyle(
                                style = SpanStyle(
                                    fontWeight = FontWeight.Normal,
                                    fontStyle = FontStyle.Italic
                                ),
                                start = start,
                                end = end
                            )
                            Typeface.BOLD_ITALIC -> addStyle(
                                style = SpanStyle(
                                    fontWeight = FontWeight.Bold,
                                    fontStyle = FontStyle.Italic
                                ),
                                start = start,
                                end = end
                            )
                        }
                        is TypefaceSpan -> addStyle(
                            style = 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 = start,
                            end = end
                        )
                        is BulletSpan -> {
                            Log.d("StringResources", "BulletSpan not supported yet")
                            addStyle(style = SpanStyle(), start = start, end = end)
                        }
                        is AbsoluteSizeSpan -> addStyle(
                            style = SpanStyle(fontSize = if (it.dip) it.size.dp.toSp() else it.size.toSp()),
                            start = start,
                            end = end
                        )
                        is RelativeSizeSpan -> addStyle(
                            style = SpanStyle(fontSize = it.sizeChange.em),
                            start = start,
                            end = end
                        )
                        is StrikethroughSpan -> addStyle(
                            style = SpanStyle(textDecoration = TextDecoration.LineThrough),
                            start = start,
                            end = end
                        )
                        is UnderlineSpan -> addStyle(
                            style = SpanStyle(textDecoration = TextDecoration.Underline),
                            start = start,
                            end = end
                        )
                        is SuperscriptSpan -> addStyle(
                            style = SpanStyle(baselineShift = BaselineShift.Superscript),
                            start = start,
                            end = end
                        )
                        is SubscriptSpan -> addStyle(
                            style = SpanStyle(baselineShift = BaselineShift.Subscript),
                            start = start,
                            end = end
                        )
                        is ForegroundColorSpan -> addStyle(
                            style = SpanStyle(color = Color(it.foregroundColor)),
                            start = start,
                            end = end
                        )
                        is Annotation -> {
                            if (it.key == URL_ANNOTATION_KEY) {
                                addStyle(
                                    style = SpanStyle(color = Color.Blue),
                                    start = start,
                                    end = end
                                )
                                addUrlAnnotation(
                                    annotation = UrlAnnotation(it.value),
                                    start = start,
                                    end = end
                                )
                            }
                        }
                        else -> addStyle(style = SpanStyle(), start = start, end = end)
                    }
                }
            }
        }
    } else {
        AnnotatedString(text = text.toString())
    }
}

@Composable
fun LinkableTextView(
    @StringRes id: Int,
    modifier: Modifier = Modifier,
    style: TextStyle = MaterialTheme.typography.body1
) {
    val uriHandler = LocalUriHandler.current
    
    val annotatedString = annotatedStringResource(id)
    
    ClickableText(
        text = annotatedString,
        style = style,
        onClick = { offset ->
            annotatedString.getStringAnnotations(
                tag = "URL",
                start = offset,
                end = offset
            ).firstOrNull()?.let {
                uriHandler.openUri(it.item)
            }
        },
        modifier = modifier,
    )
}

Usage:

@Composable
fun MyComposableView {
    LinkableTextView(
        id = R.string.my_link_string
    )
}

String resource:

<string name="my_link_string">Click this
    <annotation url="https://www.stackoverflow.com">link</annotation>
    to go to web site
</string>

There is also the "dumb" way of just falling back to using android.widget.TextView which has the behavior you're seeking, and works with accessibility services properly:

@Composable
fun CompatHtmlTextView(@StringRes htmlStringResource: Int) {
    val html = stringResourceWithStyling(htmlStringResource).toString()

    AndroidView(factory = { context ->
        android.widget.TextView(context).apply {
            text = fromHtml(html)
        }
    })
}

@Composable
@ReadOnlyComposable
fun stringResWithStyling(@StringRes id: Int): CharSequence =
    LocalContext.current.resources.getText(id = id) 

/**
 * Compat method that will use the deprecated fromHtml method 
 * prior to Android N and the new one after Android N
 */
@Suppress("DEPRECATION")
fun fromHtml(html: String?): Spanned {
    return when {
        html == null -> {
            // return an empty spannable if the html is null
            SpannableString("")
        } 
        Build.VERSION.SDK_INT >= Build.VERSION_CODES.N -> {
            // FROM_HTML_MODE_LEGACY is the behaviour that was used for versions below android N
            // we are using this flag to give a consistent behaviour
            Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY)
        }
        else -> {
            Html.fromHtml(html)
        }
    }
}

For the Compat option, it's important that you retrieve the string resource as outlined so that the tags are not stripped. You must also format your string resource using CDATA tags, e.g.

<string name="text_with_link"><![CDATA[Visit 
        <a href="https://www.stackoverflow.com/">Stackoverflow</a>
        for the best answers.]]></string>

Failure to use the CDATA tag will not render the string as HTML.

Chantell Osejo
  • 1,456
  • 15
  • 25
2

If your concern is to only open hyperlinks, there's a dynamic approach using a HyperlinkText

Yasser AKBBACH
  • 539
  • 5
  • 7
1

Use this code if you don't have hand on the input text.

 val s = buildAnnotatedString {
    for (link in txt.split(' ')) {
        if (link.matches(".*(#\\w+)|(http(s)?://.+).*".toRegex())) {
            withStyle(SpanStyle(color = Color.Cyan)) {
                append(link + ' ')
            }
        } else {
            append(link + ' ')
        }
    }
}
Text(text = s)

It could be more the # and https:// it's up to you in the regex.

Note: This is not clickable text, If you want one, Check it out the code below (Not recommended for large text).

val uri = LocalUriHandler.current

 FlowRow {
    for (s in txt.split(' ')) {
        if (s.matches(".*(#\\w+)|(http(s)?://.+).*".toRegex())) {
            ClickableText(
                text = AnnotatedString(s + ' '),
                onClick = { runCatching { uri.openUri(s) } },
                style = TextStyle(color = Color.Cyan)
            )
        } else {
            Text(text = s + ' ')
        }
    }
}

And yes you gonna need Flow_Layout accompanist .

enter image description here

Youn Tivoli
  • 230
  • 2
  • 13
0

If you want to make some part of the text clickable, then the following code will work fine.

@Composable
fun SingUpText() {
    val annotatedText = buildAnnotatedString {
        val grayStyle = SpanStyle(color = Color.Gray)
        pushStyle(grayStyle)
        append("Don't have an account? ")
        pop()

        pushStringAnnotation(
            tag = "SignUp",
            annotation = "SignUp"
        )
        val style = SpanStyle(color = AppColor, fontWeight = FontWeight.Bold)

        pushStyle(style)
        append("Sign Up")
        
        pop()
    }

    ClickableText(text = annotatedText, onClick = {
        annotatedText.getStringAnnotations(
            tag = "SingUp",
            start = it,
            end = it
        ).firstOrNull().let { annotatedText ->
            Log.d("Text_Clicked", "SingUpText:text ")
        }
    })

}
Ali Hassan
  • 23
  • 3
0

You can use this code:

@Composable
fun AgreeConditionComponent(
    modifier: Modifier,
    value: Boolean,
    onConditionChecked: (Boolean) -> Unit,
    onConditionTextClicked: () -> Unit,
) {
    val hyperlinkText = "link"
    val annotatedString = buildAnnotatedString {
        append("Click this")
        append(" ")
        val start = length
        val end = length + hyperlinkText.length
        addStringAnnotation(tag = "terms", annotation = "", start = start, end = end)
        withStyle(
            style = SpanStyle(
                textDecoration = TextDecoration.Underline,
                color = Color(0xff64B5F6),
            ),
        ) {
            append(hyperlinkText)
        }
        append(" ")
        append("to go to web site")
    }
    Row(
        modifier = modifier,
        verticalAlignment = Alignment.CenterVertically
    ) {
        Checkbox(
            checked = value,
            onCheckedChange = onConditionChecked,
        )

        Spacer(modifier = Modifier.width(4.dp))

        ClickableText(
            text = annotatedString,
            style = MaterialTheme.typography.subtitle1,
            onClick = { offset ->
                annotatedString.getStringAnnotations(tag = "terms", start = offset, end = offset)
                    .firstOrNull()
                    ?.let { onConditionTextClicked.invoke() }
            }
        )
    }
}

enter image description here

melikaafrakhteh
  • 305
  • 1
  • 2
  • 8