Skip to content

Commit

Permalink
Allows fixed size emoji
Browse files Browse the repository at this point in the history
  • Loading branch information
SalomonBrys committed May 22, 2024
1 parent 9eddcb3 commit 470336d
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,13 @@ public fun TextWithPlatformEmoji(
maxLines: Int = Int.MAX_VALUE,
minLines: Int = 1,
onTextLayout: (TextLayoutResult) -> Unit = {},
style: TextStyle = LocalTextStyle.current
style: TextStyle = LocalTextStyle.current,
fixedEmojiSize: Boolean = false,
) {
WithPlatformEmoji(text) { emojiAnnotatedString, emojiInlineContent ->
WithPlatformEmoji(
text = text,
fixedImageSize = fixedEmojiSize,
) { emojiAnnotatedString, emojiInlineContent ->
Text(
text = emojiAnnotatedString,
modifier = modifier,
Expand Down Expand Up @@ -102,9 +106,13 @@ public fun TextWithPlatformEmoji(
minLines: Int = 1,
inlineContent: Map<String, InlineTextContent> = mapOf(),
onTextLayout: (TextLayoutResult) -> Unit = {},
style: TextStyle = LocalTextStyle.current
style: TextStyle = LocalTextStyle.current,
fixedEmojiSize: Boolean = false,
) {
WithPlatformEmoji(text) { emojiAnnotatedString, emojiInlineContent ->
WithPlatformEmoji(
text = text,
fixedImageSize = fixedEmojiSize,
) { emojiAnnotatedString, emojiInlineContent ->
Text(
text = emojiAnnotatedString,
modifier = modifier,
Expand Down Expand Up @@ -152,9 +160,13 @@ public fun TextWithNotoImageEmoji(
maxLines: Int = Int.MAX_VALUE,
minLines: Int = 1,
onTextLayout: (TextLayoutResult) -> Unit = {},
style: TextStyle = LocalTextStyle.current
style: TextStyle = LocalTextStyle.current,
fixedEmojiSize: Boolean = false,
) {
WithNotoImageEmoji(text) { emojiAnnotatedString, emojiInlineContent ->
WithNotoImageEmoji(
text = text,
fixedSize = fixedEmojiSize,
) { emojiAnnotatedString, emojiInlineContent ->
Text(
text = emojiAnnotatedString,
modifier = modifier,
Expand Down Expand Up @@ -203,9 +215,13 @@ public fun TextWithNotoImageEmoji(
minLines: Int = 1,
inlineContent: Map<String, InlineTextContent> = mapOf(),
onTextLayout: (TextLayoutResult) -> Unit = {},
style: TextStyle = LocalTextStyle.current
style: TextStyle = LocalTextStyle.current,
fixedEmojiSize: Boolean = false,
) {
WithNotoImageEmoji(text) { emojiAnnotatedString, emojiInlineContent ->
WithNotoImageEmoji(
text = text,
fixedSize = fixedEmojiSize,
) { emojiAnnotatedString, emojiInlineContent ->
Text(
text = emojiAnnotatedString,
modifier = modifier,
Expand Down Expand Up @@ -241,8 +257,6 @@ public fun TextWithNotoImageEmoji(
public fun TextWithNotoAnimatedEmoji(
text: String,
modifier: Modifier = Modifier,
iterations: Int = Int.MAX_VALUE,
speed: Float = 1f,
color: Color = Color.Unspecified,
fontSize: TextUnit = TextUnit.Unspecified,
fontStyle: FontStyle? = null,
Expand All @@ -257,12 +271,16 @@ public fun TextWithNotoAnimatedEmoji(
maxLines: Int = Int.MAX_VALUE,
minLines: Int = 1,
onTextLayout: (TextLayoutResult) -> Unit = {},
style: TextStyle = LocalTextStyle.current
style: TextStyle = LocalTextStyle.current,
emojiAnimationIterations: Int = Int.MAX_VALUE,
emojiAnimationSpeed: Float = 1f,
fixedEmojiSize: Boolean = false,
) {
WithNotoAnimatedEmoji(
text = text,
iterations = iterations,
speed = speed
iterations = emojiAnimationIterations,
speed = emojiAnimationSpeed,
fixedSize = fixedEmojiSize,
) { emojiAnnotatedString, emojiInlineContent ->
Text(
text = emojiAnnotatedString,
Expand Down Expand Up @@ -299,8 +317,6 @@ public fun TextWithNotoAnimatedEmoji(
public fun TextWithNotoAnimatedEmoji(
text: AnnotatedString,
modifier: Modifier = Modifier,
iterations: Int = Int.MAX_VALUE,
speed: Float = 1f,
color: Color = Color.Unspecified,
fontSize: TextUnit = TextUnit.Unspecified,
fontStyle: FontStyle? = null,
Expand All @@ -316,12 +332,16 @@ public fun TextWithNotoAnimatedEmoji(
minLines: Int = 1,
inlineContent: Map<String, InlineTextContent> = mapOf(),
onTextLayout: (TextLayoutResult) -> Unit = {},
style: TextStyle = LocalTextStyle.current
style: TextStyle = LocalTextStyle.current,
fixedEmojiSize: Boolean = false,
emojiAnimationIterations: Int = Int.MAX_VALUE,
emojiAnimationSpeed: Float = 1f,
) {
WithNotoAnimatedEmoji(
text = text,
iterations = iterations,
speed = speed
iterations = emojiAnimationIterations,
speed = emojiAnimationSpeed,
fixedSize = fixedEmojiSize,
) { emojiAnnotatedString, emojiInlineContent ->
Text(
text = emojiAnnotatedString,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,13 @@ public fun TextWithPlatformEmoji(
maxLines: Int = Int.MAX_VALUE,
minLines: Int = 1,
onTextLayout: (TextLayoutResult) -> Unit = {},
style: TextStyle = LocalTextStyle.current
style: TextStyle = LocalTextStyle.current,
fixedEmojiSize: Boolean = false,
) {
WithPlatformEmoji(text) { emojiAnnotatedString, emojiInlineContent ->
WithPlatformEmoji(
text = text,
fixedImageSize = fixedEmojiSize,
) { emojiAnnotatedString, emojiInlineContent ->
Text(
text = emojiAnnotatedString,
modifier = modifier,
Expand Down Expand Up @@ -98,9 +102,13 @@ public fun TextWithPlatformEmoji(
minLines: Int = 1,
inlineContent: Map<String, InlineTextContent> = mapOf(),
onTextLayout: (TextLayoutResult) -> Unit = {},
style: TextStyle = LocalTextStyle.current
style: TextStyle = LocalTextStyle.current,
fixedEmojiSize: Boolean = false,
) {
WithPlatformEmoji(text) { emojiAnnotatedString, emojiInlineContent ->
WithPlatformEmoji(
text = text,
fixedImageSize = fixedEmojiSize,
) { emojiAnnotatedString, emojiInlineContent ->
Text(
text = emojiAnnotatedString,
modifier = modifier,
Expand Down Expand Up @@ -148,9 +156,13 @@ public fun TextWithNotoImageEmoji(
maxLines: Int = Int.MAX_VALUE,
minLines: Int = 1,
onTextLayout: (TextLayoutResult) -> Unit = {},
style: TextStyle = LocalTextStyle.current
style: TextStyle = LocalTextStyle.current,
fixedEmojiSize: Boolean = false,
) {
WithNotoImageEmoji(text) { emojiAnnotatedString, emojiInlineContent ->
WithNotoImageEmoji(
text = text,
fixedSize = fixedEmojiSize,
) { emojiAnnotatedString, emojiInlineContent ->
Text(
text = emojiAnnotatedString,
modifier = modifier,
Expand Down Expand Up @@ -199,9 +211,13 @@ public fun TextWithNotoImageEmoji(
minLines: Int = 1,
inlineContent: Map<String, InlineTextContent> = mapOf(),
onTextLayout: (TextLayoutResult) -> Unit = {},
style: TextStyle = LocalTextStyle.current
style: TextStyle = LocalTextStyle.current,
fixedEmojiSize: Boolean = false,
) {
WithNotoImageEmoji(text) { emojiAnnotatedString, emojiInlineContent ->
WithNotoImageEmoji(
text = text,
fixedSize = fixedEmojiSize,
) { emojiAnnotatedString, emojiInlineContent ->
Text(
text = emojiAnnotatedString,
modifier = modifier,
Expand Down Expand Up @@ -251,9 +267,17 @@ public fun TextWithNotoAnimatedEmoji(
maxLines: Int = Int.MAX_VALUE,
minLines: Int = 1,
onTextLayout: (TextLayoutResult) -> Unit = {},
style: TextStyle = LocalTextStyle.current
style: TextStyle = LocalTextStyle.current,
emojiAnimationIterations: Int = Int.MAX_VALUE,
emojiAnimationSpeed: Float = 1f,
fixedEmojiSize: Boolean = false,
) {
WithNotoAnimatedEmoji(text) { emojiAnnotatedString, emojiInlineContent ->
WithNotoAnimatedEmoji(
text = text,
iterations = emojiAnimationIterations,
speed = emojiAnimationSpeed,
fixedSize = fixedEmojiSize,
) { emojiAnnotatedString, emojiInlineContent ->
Text(
text = emojiAnnotatedString,
modifier = modifier,
Expand Down Expand Up @@ -304,9 +328,17 @@ public fun TextWithNotoAnimatedEmoji(
minLines: Int = 1,
inlineContent: Map<String, InlineTextContent> = mapOf(),
onTextLayout: (TextLayoutResult) -> Unit = {},
style: TextStyle = LocalTextStyle.current
style: TextStyle = LocalTextStyle.current,
emojiAnimationIterations: Int = Int.MAX_VALUE,
emojiAnimationSpeed: Float = 1f,
fixedEmojiSize: Boolean = false,
) {
WithNotoAnimatedEmoji(text) { emojiAnnotatedString, emojiInlineContent ->
WithNotoAnimatedEmoji(
text = text,
iterations = emojiAnimationIterations,
speed = emojiAnimationSpeed,
fixedSize = fixedEmojiSize,
) { emojiAnnotatedString, emojiInlineContent ->
Text(
text = emojiAnnotatedString,
modifier = modifier,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.text.InlineTextContent
import androidx.compose.foundation.text.appendInlineContent
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.Placeholder
Expand All @@ -33,7 +30,7 @@ public fun String.withEmoji(): String {
}

@Composable
private fun WithNotoEmoji(
private fun WithDynamicSizedNotoEmoji(
text: CharSequence,
content: @Composable (AnnotatedString, Map<String, InlineTextContent>) -> Unit,
createInlineTextContent: suspend (FoundEmoji) -> InlineTextContent?
Expand Down Expand Up @@ -83,14 +80,67 @@ private fun WithNotoEmoji(
content(annotatedString, inlineContent)
}

@Composable
private fun WithFixedSizedNotoEmoji(
text: CharSequence,
content: @Composable (AnnotatedString, Map<String, InlineTextContent>) -> Unit,
createInlineTextContent: suspend (FoundEmoji) -> InlineTextContent?
) {
val service = EmojiService.get() ?: return

val all = remember(text) { service.finder.findEmoji(text).toList() }

val inlineContent = HashMap<String, InlineTextContent>()
val annotatedString = buildAnnotatedString {
var start = 0
all.forEach { found ->
if (text is AnnotatedString)
append(text.subSequence(start, found.start))
else
append(text.substring(start, found.start))
val inlineContentID = "emoji:${found.emoji}"
inlineContent[inlineContentID] = InlineTextContent(Placeholder(1.em, 1.em, PlaceholderVerticalAlign.Center)) {
var itc: InlineTextContent? by remember { mutableStateOf(null) }
LaunchedEffect(null) {
itc = createInlineTextContent(found)
}
if (itc == null) {
content(AnnotatedString(found.emoji.details.string), emptyMap())
} else {
itc!!.children("")
}
}
appendInlineContent(inlineContentID)
start = found.end
}
if (text is AnnotatedString)
append(text.subSequence(start, text.length))
else
append(text.substring(start, text.length))
}

content(annotatedString, inlineContent)
}

@Composable
private fun WithNotoEmoji(
text: CharSequence,
content: @Composable (AnnotatedString, Map<String, InlineTextContent>) -> Unit,
createInlineTextContent: suspend (FoundEmoji) -> InlineTextContent?,
fixedSize: Boolean,
) {
if (fixedSize) WithFixedSizedNotoEmoji(text, content, createInlineTextContent)
else WithDynamicSizedNotoEmoji(text, content, createInlineTextContent)
}

private suspend fun createNotoSvgInlineContent(emoji: Emoji, download: suspend (EmojiUrl) -> ByteArray): InlineTextContent? {
try {
val bytes = download(EmojiUrl.from(emoji, EmojiUrl.Type.SVG))
val svg = SVGImage.create(bytes)
return InlineTextContent(
placeholder = Placeholder(1.em, 1.em / svg.sizeRatio(), PlaceholderVerticalAlign.Center),
children = {
SVGImage(svg, "${emoji.details.description} emoji", Modifier.size(20.dp))
SVGImage(svg, "${emoji.details.description} emoji", Modifier.fillMaxSize())
}
)
} catch (t: Throwable) {
Expand All @@ -104,19 +154,22 @@ private suspend fun createNotoSvgInlineContent(emoji: Emoji, download: suspend (
* Replaces all emojis with [NotoImageEmoji].
*
* @param text The text to with Emoji UTF characters.
* @param fixedSize If true, then the emoji will not be resized once downloaded.
* @param content A lambda that receives the `AnnotatedString` and its corresponding `InlineTextContent` map
* These should be used to display: `{ astr, map -> Text(astr, inlineContent = map) }`.
*/
@Composable
public fun WithNotoImageEmoji(
text: CharSequence,
fixedSize: Boolean = false,
content: @Composable (AnnotatedString, Map<String, InlineTextContent>) -> Unit
) {
val download = LocalEmojiDownloader.current
WithNotoEmoji(
text = text,
content = content,
createInlineTextContent = { found -> createNotoSvgInlineContent(found.emoji, download) }
createInlineTextContent = { found -> createNotoSvgInlineContent(found.emoji, download) },
fixedSize = fixedSize,
)
}

Expand Down Expand Up @@ -149,6 +202,7 @@ private suspend fun createNotoLottieInlineContent(
* @param text The text to with Emoji UTF characters.
* @param iterations The number of times that the animations will be played (default is infinite).
* @param speed Speed at which the animations will be rendered.
* @param fixedSize If true, then the emoji will not be resized once downloaded.
* @param content A lambda that receives the `AnnotatedString` and its corresponding `InlineTextContent` map
* These should be used to display: `{ astr, map -> Text(astr, inlineContent = map) }`.
*/
Expand All @@ -157,13 +211,15 @@ public fun WithNotoAnimatedEmoji(
text: CharSequence,
iterations: Int = Int.MAX_VALUE,
speed: Float = 1f,
fixedSize: Boolean = false,
content: @Composable (AnnotatedString, Map<String, InlineTextContent>) -> Unit
) {
val download = LocalEmojiDownloader.current
WithNotoEmoji(
text = text,
content = content,
createInlineTextContent = { found -> createNotoLottieInlineContent(found.emoji, iterations, speed, download) }
createInlineTextContent = { found -> createNotoLottieInlineContent(found.emoji, iterations, speed, download) },
fixedSize = fixedSize
)
}

Expand All @@ -174,11 +230,13 @@ public fun WithNotoAnimatedEmoji(
* - On all other platforms: does not modify the text at all (map will be empty).
*
* @param text The text to with Emoji UTF characters.
* @param fixedImageSize If true, then the emoji will not be resized once downloaded.
* @param content A lambda that receives the `AnnotatedString` and its corresponding `InlineTextContent` map
* These should be used to display: `{ astr, map -> Text(astr, inlineContent = map) }`.
*/
@Composable
public expect fun WithPlatformEmoji(
text: CharSequence,
fixedImageSize: Boolean = false,
content: @Composable (AnnotatedString, Map<String, InlineTextContent>) -> Unit
)
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ internal actual suspend fun platformDownloadBytes(url: String): ByteArray {
@Composable
public actual fun WithPlatformEmoji(
text: CharSequence,
fixedImageSize: Boolean,
content: @Composable (AnnotatedString, Map<String, InlineTextContent>) -> Unit
) {
val annotatedString = remember(text) { AnnotatedString.Builder().append(text).toAnnotatedString() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ internal actual suspend fun platformDownloadBytes(url: String): ByteArray =
@Composable
public actual fun WithPlatformEmoji(
text: CharSequence,
fixedImageSize: Boolean,
content: @Composable (AnnotatedString, Map<String, InlineTextContent>) -> Unit
) {
val annotatedString = remember(text) { AnnotatedString.Builder().append(text).toAnnotatedString() }
Expand Down
Loading

0 comments on commit 470336d

Please sign in to comment.