mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-17 23:43:34 +01:00
Fix member label emoji ignoring use system emoji preference.
This commit is contained in:
committed by
Cody Henthorne
parent
fa2b0aedb0
commit
7e605fb6de
@@ -5,11 +5,14 @@
|
|||||||
|
|
||||||
package org.thoughtcrime.securesms.components.emoji
|
package org.thoughtcrime.securesms.components.emoji
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.text.InlineTextContent
|
import androidx.compose.foundation.text.InlineTextContent
|
||||||
|
import androidx.compose.foundation.text.appendInlineContent
|
||||||
|
import androidx.compose.material3.LocalTextStyle
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
@@ -21,10 +24,12 @@ import androidx.compose.ui.text.AnnotatedString
|
|||||||
import androidx.compose.ui.text.Placeholder
|
import androidx.compose.ui.text.Placeholder
|
||||||
import androidx.compose.ui.text.PlaceholderVerticalAlign
|
import androidx.compose.ui.text.PlaceholderVerticalAlign
|
||||||
import androidx.compose.ui.text.buildAnnotatedString
|
import androidx.compose.ui.text.buildAnnotatedString
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.TextUnit
|
||||||
import com.google.accompanist.drawablepainter.rememberDrawablePainter
|
import com.google.accompanist.drawablepainter.rememberDrawablePainter
|
||||||
import org.signal.core.ui.compose.DayNightPreviews
|
import org.signal.core.ui.compose.DayNightPreviews
|
||||||
import org.signal.core.ui.compose.Previews
|
import org.signal.core.ui.compose.Previews
|
||||||
|
import org.thoughtcrime.securesms.components.emoji.parsing.EmojiParser
|
||||||
|
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies Signal or System emoji to the given content based off user settings.
|
* Applies Signal or System emoji to the given content based off user settings.
|
||||||
@@ -34,6 +39,7 @@ import org.signal.core.ui.compose.Previews
|
|||||||
@Composable
|
@Composable
|
||||||
fun Emojifier(
|
fun Emojifier(
|
||||||
text: String,
|
text: String,
|
||||||
|
useSystemEmoji: Boolean = !LocalInspectionMode.current && SignalStore.settings.isPreferSystemEmoji,
|
||||||
content: @Composable (AnnotatedString, Map<String, InlineTextContent>) -> Unit = { annotatedText, inlineContent ->
|
content: @Composable (AnnotatedString, Map<String, InlineTextContent>) -> Unit = { annotatedText, inlineContent ->
|
||||||
Text(
|
Text(
|
||||||
text = annotatedText,
|
text = annotatedText,
|
||||||
@@ -41,38 +47,56 @@ fun Emojifier(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
if (LocalInspectionMode.current) {
|
if (useSystemEmoji) {
|
||||||
content(buildAnnotatedString { append(text) }, emptyMap())
|
content(buildAnnotatedString { append(text) }, emptyMap())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val candidates = remember(text) { EmojiProvider.getCandidates(text) }
|
val fontSize = LocalTextStyle.current.fontSize
|
||||||
val candidateMap: Map<String, InlineTextContent> = remember(text) {
|
|
||||||
candidates?.associate { candidate ->
|
val foundEmojis: List<EmojiParser.Candidate> = remember(text) {
|
||||||
candidate.drawInfo.emoji to InlineTextContent(placeholder = Placeholder(20.sp, 20.sp, PlaceholderVerticalAlign.TextCenter)) {
|
EmojiProvider.getCandidates(text)?.list.orEmpty()
|
||||||
Image(
|
}
|
||||||
painter = rememberDrawablePainter(EmojiProvider.getEmojiDrawable(context, candidate.drawInfo.emoji)),
|
val inlineContentByEmoji: Map<String, InlineTextContent> = remember(text, fontSize) {
|
||||||
contentDescription = null
|
foundEmojis.associate { it.drawInfo.emoji to createInlineContent(context, it.drawInfo.emoji, fontSize) }
|
||||||
)
|
|
||||||
}
|
|
||||||
} ?: emptyMap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val annotatedString = buildAnnotatedString {
|
val annotatedString = remember(text) { buildAnnotatedString(text, foundEmojis) }
|
||||||
append(text)
|
content(annotatedString, inlineContentByEmoji)
|
||||||
|
}
|
||||||
|
|
||||||
candidates?.forEach {
|
private fun createInlineContent(context: Context, emoji: String, fontSize: TextUnit): InlineTextContent {
|
||||||
addStringAnnotation(
|
return InlineTextContent(
|
||||||
tag = "EMOJI",
|
placeholder = Placeholder(width = fontSize, height = fontSize, PlaceholderVerticalAlign.TextCenter)
|
||||||
annotation = it.drawInfo.emoji,
|
) {
|
||||||
start = it.startIndex,
|
Image(
|
||||||
end = it.endIndex
|
painter = rememberDrawablePainter(EmojiProvider.getEmojiDrawable(context, emoji)),
|
||||||
)
|
contentDescription = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an [AnnotatedString] from [text], substituting each emoji in [foundEmojis] with an inline content placeholder.
|
||||||
|
*/
|
||||||
|
private fun buildAnnotatedString(
|
||||||
|
text: String,
|
||||||
|
foundEmojis: List<EmojiParser.Candidate>
|
||||||
|
): AnnotatedString = buildAnnotatedString {
|
||||||
|
var nextSegmentStartIndex = 0
|
||||||
|
|
||||||
|
foundEmojis.forEach { emoji ->
|
||||||
|
if (emoji.startIndex > nextSegmentStartIndex) {
|
||||||
|
append(text, start = nextSegmentStartIndex, end = emoji.startIndex)
|
||||||
}
|
}
|
||||||
|
appendInlineContent(emoji.drawInfo.emoji)
|
||||||
|
nextSegmentStartIndex = emoji.endIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
content(annotatedString, candidateMap)
|
if (nextSegmentStartIndex < text.length) {
|
||||||
|
append(text, start = nextSegmentStartIndex, end = text.length)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
@@ -41,6 +41,9 @@ public class EmojiParser {
|
|||||||
this.emojiTree = emojiTree;
|
this.emojiTree = emojiTree;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an ordered list of every emoji occurrence found in the given text.
|
||||||
|
*/
|
||||||
public @NonNull CandidateList findCandidates(@Nullable CharSequence text) {
|
public @NonNull CandidateList findCandidates(@Nullable CharSequence text) {
|
||||||
List<Candidate> results = new LinkedList<>();
|
List<Candidate> results = new LinkedList<>();
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.ProvideTextStyle
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -25,6 +26,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import org.signal.core.ui.compose.DayNightPreviews
|
import org.signal.core.ui.compose.DayNightPreviews
|
||||||
import org.signal.core.ui.compose.Previews
|
import org.signal.core.ui.compose.Previews
|
||||||
|
import org.thoughtcrime.securesms.components.emoji.Emojifier
|
||||||
|
|
||||||
private val defaultModifier = Modifier.padding(horizontal = 12.dp, vertical = 2.dp)
|
private val defaultModifier = Modifier.padding(horizontal = 12.dp, vertical = 2.dp)
|
||||||
private val defaultTextStyle: @Composable () -> TextStyle = { MaterialTheme.typography.bodyLarge }
|
private val defaultTextStyle: @Composable () -> TextStyle = { MaterialTheme.typography.bodyLarge }
|
||||||
@@ -83,22 +85,28 @@ fun MemberLabelPill(
|
|||||||
.then(modifier),
|
.then(modifier),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
if (!emoji.isNullOrEmpty()) {
|
ProvideTextStyle(textStyle) {
|
||||||
Text(
|
if (!emoji.isNullOrEmpty()) {
|
||||||
text = emoji,
|
Emojifier(text = emoji) { annotatedText, inlineContent ->
|
||||||
style = textStyle,
|
Text(
|
||||||
modifier = Modifier.padding(end = 5.dp)
|
text = annotatedText,
|
||||||
)
|
inlineContent = inlineContent,
|
||||||
}
|
modifier = Modifier.padding(end = 5.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (text.isNotEmpty()) {
|
if (text.isNotEmpty()) {
|
||||||
Text(
|
Emojifier(text = text) { annotatedText, inlineContent ->
|
||||||
text = text,
|
Text(
|
||||||
color = textColor,
|
text = annotatedText,
|
||||||
style = textStyle,
|
inlineContent = inlineContent,
|
||||||
maxLines = 1,
|
color = textColor,
|
||||||
overflow = TextOverflow.Ellipsis
|
maxLines = 1,
|
||||||
)
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.FlowRow
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.ProvideTextStyle
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -23,6 +24,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import org.signal.core.ui.compose.DayNightPreviews
|
import org.signal.core.ui.compose.DayNightPreviews
|
||||||
import org.signal.core.ui.compose.Previews
|
import org.signal.core.ui.compose.Previews
|
||||||
|
import org.thoughtcrime.securesms.components.emoji.Emojifier
|
||||||
|
|
||||||
private val defaultLabelModifier = Modifier.padding(horizontal = 6.dp, vertical = 2.dp)
|
private val defaultLabelModifier = Modifier.padding(horizontal = 6.dp, vertical = 2.dp)
|
||||||
private val defaultLabelTextStyle: @Composable () -> TextStyle = { MaterialTheme.typography.bodySmall }
|
private val defaultLabelTextStyle: @Composable () -> TextStyle = { MaterialTheme.typography.bodySmall }
|
||||||
@@ -101,14 +103,17 @@ private fun SenderNameWithLabel(
|
|||||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||||
itemVerticalAlignment = Alignment.CenterVertically
|
itemVerticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
Text(
|
ProvideTextStyle(MaterialTheme.typography.labelMedium.copy(fontWeight = FontWeight.Bold)) {
|
||||||
text = senderName,
|
Emojifier(text = senderName) { annotatedText, inlineContent ->
|
||||||
color = senderColor,
|
Text(
|
||||||
style = MaterialTheme.typography.labelMedium,
|
text = annotatedText,
|
||||||
fontWeight = FontWeight.Bold,
|
inlineContent = inlineContent,
|
||||||
maxLines = 1,
|
color = senderColor,
|
||||||
overflow = TextOverflow.Ellipsis
|
maxLines = 1,
|
||||||
)
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (memberLabel != null) {
|
if (memberLabel != null) {
|
||||||
labelSlot(memberLabel)
|
labelSlot(memberLabel)
|
||||||
|
|||||||
@@ -7,10 +7,13 @@ package org.thoughtcrime.securesms.video.videoconverter.exceptions
|
|||||||
class EncodingException : Exception {
|
class EncodingException : Exception {
|
||||||
/** Whether the input video was HDR content. */
|
/** Whether the input video was HDR content. */
|
||||||
@JvmField var isHdrInput: Boolean = false
|
@JvmField var isHdrInput: Boolean = false
|
||||||
|
|
||||||
/** Whether HDR-to-SDR tone-mapping was successfully applied to the decoder. */
|
/** Whether HDR-to-SDR tone-mapping was successfully applied to the decoder. */
|
||||||
@JvmField var toneMapApplied: Boolean = false
|
@JvmField var toneMapApplied: Boolean = false
|
||||||
|
|
||||||
/** The name of the video decoder codec that was selected, or null if decoder creation failed. */
|
/** The name of the video decoder codec that was selected, or null if decoder creation failed. */
|
||||||
@JvmField var decoderName: String? = null
|
@JvmField var decoderName: String? = null
|
||||||
|
|
||||||
/** The name of the video encoder codec that was selected, or null if encoder creation failed. */
|
/** The name of the video encoder codec that was selected, or null if encoder creation failed. */
|
||||||
@JvmField var encoderName: String? = null
|
@JvmField var encoderName: String? = null
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user