-
-
Save venator85/e19857c7cfdddeae3210253b40f19108 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.cmttranslations.listener.ui.util | |
import androidx.compose.material3.MaterialTheme | |
import androidx.compose.runtime.Composable | |
import androidx.compose.runtime.Immutable | |
import androidx.compose.runtime.getValue | |
import androidx.compose.runtime.remember | |
import androidx.compose.runtime.rememberUpdatedState | |
import androidx.compose.ui.text.AnnotatedString | |
import androidx.compose.ui.text.LinkAnnotation | |
import androidx.compose.ui.text.SpanStyle | |
import androidx.compose.ui.text.TextLinkStyles | |
import androidx.compose.ui.text.buildAnnotatedString | |
import androidx.compose.ui.text.style.TextDecoration | |
sealed interface ClickableStyleString | |
/** | |
* A sealed interface representing different types of styled strings that can be used in a | |
* [rememberStyledClickableString] function. It includes clickable email links | |
* and clickable URL links. | |
* | |
* Each type contains the highlighted text, its style, and additional properties | |
* for clickable types (email and URL). | |
*/ | |
@Immutable | |
sealed interface StyledString { | |
val highlightedText: String | |
val style: SpanStyle | |
/** | |
* Represents a clickable email link with a specific style. | |
* | |
* @param highlightedText The text to be highlighted. | |
* @param email The email address that the link points to. | |
* @param style The style to be applied to the highlighted text. | |
*/ | |
@Immutable | |
data class ClickableEmail( | |
override val highlightedText: String, | |
val email: String, | |
override val style: SpanStyle, | |
) : StyledString, ClickableStyleString | |
/** | |
* Represents a clickable URL link with a specific style. | |
* | |
* @param highlightedText The text to be highlighted. | |
* @param url The URL that the link points to. | |
* @param style The style to be applied to the highlighted text. | |
*/ | |
@Immutable | |
data class ClickableUrl( | |
override val highlightedText: String, | |
val url: String, | |
override val style: SpanStyle | |
) : StyledString, ClickableStyleString | |
} | |
/** | |
* Creates an [AnnotatedString] from the provided full text, automatically detecting | |
* email addresses and URLs using regex patterns. | |
* | |
* This function builds an annotated string that applies clickable links to detected | |
* email addresses and URLs in the text. | |
* | |
* @param fullText The complete text to be processed. | |
* @param emailStyle The style to apply to detected email addresses. Defaults to blue color. | |
* @param urlStyle The style to apply to detected URLs. Defaults to blue color. | |
* @param onClick A callback function that is invoked when a clickable link is clicked. | |
* @return An [AnnotatedString] with automatically detected clickable email and URL links. | |
*/ | |
@Composable | |
fun rememberStyledClickableString( | |
fullText: String, | |
emailStyle: SpanStyle = SpanStyle(color = MaterialTheme.colorScheme.primary, textDecoration = TextDecoration.Underline), | |
urlStyle: SpanStyle = SpanStyle(color = MaterialTheme.colorScheme.primary, textDecoration = TextDecoration.Underline), | |
onClick: (ClickableStyleString) -> Unit | |
): AnnotatedString { | |
val currentOnClick by rememberUpdatedState(onClick) | |
return remember(fullText, emailStyle, urlStyle) { | |
buildAnnotatedString { | |
append(fullText) | |
// Apply email styles | |
val emailMatches = EMAIL_REGEX.findAll(fullText) | |
emailMatches.forEach { match -> | |
val emailString = StyledString.ClickableEmail( | |
highlightedText = match.value, | |
email = match.value, | |
style = emailStyle | |
) | |
applyStyle( | |
styledString = emailString, | |
startIndex = match.range.first, | |
endIndex = match.range.last + 1, | |
onClick = currentOnClick | |
) | |
} | |
// Apply URL styles | |
val urlMatches = URL_REGEX.findAll(fullText) | |
urlMatches.forEach { match -> | |
val urlString = StyledString.ClickableUrl( | |
highlightedText = match.value, | |
url = match.value, | |
style = urlStyle | |
) | |
applyStyle( | |
styledString = urlString, | |
startIndex = match.range.first, | |
endIndex = match.range.last + 1, | |
onClick = currentOnClick | |
) | |
} | |
} | |
} | |
} | |
private fun AnnotatedString.Builder.applyStyle( | |
styledString: StyledString, | |
startIndex: Int, | |
endIndex: Int, | |
onClick: (ClickableStyleString) -> Unit | |
) { | |
when (styledString) { | |
is StyledString.ClickableUrl -> { | |
val linkAnnotation = LinkAnnotation.Url( | |
url = styledString.url, | |
styles = TextLinkStyles(style = styledString.style), | |
linkInteractionListener = { onClick(styledString) } | |
) | |
addLink(linkAnnotation, startIndex, endIndex) | |
} | |
is StyledString.ClickableEmail -> { | |
val linkAnnotation = LinkAnnotation.Clickable( | |
tag = styledString.highlightedText, | |
styles = TextLinkStyles(style = styledString.style), | |
linkInteractionListener = { onClick(styledString) } | |
) | |
addLink(linkAnnotation, startIndex, endIndex) | |
} | |
} | |
} | |
// Regex patterns for email and URL detection | |
private val EMAIL_REGEX = Regex( | |
pattern = "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}", | |
option = RegexOption.IGNORE_CASE | |
) | |
private val URL_REGEX = Regex( | |
pattern = "https?://[a-zA-Z0-9.-]+(?:\\.[a-zA-Z]{2,})+(?:/[^\\s]*)?", | |
option = RegexOption.IGNORE_CASE | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment