diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt index 97436c2bc0c..109133cdd06 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt @@ -177,6 +177,7 @@ fun aCustomReactionState( ) = CustomReactionState( target = target, selectedEmoji = persistentSetOf(), + skinTone = null, eventSink = eventSink, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index f1f8b844ef0..6c0add5b6de 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -257,8 +257,8 @@ fun MessagesView( CustomReactionBottomSheet( state = state.customReactionState, onSelectEmoji = { eventId, emoji -> - state.eventSink(MessagesEvents.ToggleReaction(emoji.unicode, eventId)) - } + state.eventSink(MessagesEvents.ToggleReaction(emoji, eventId)) + }, ) ReactionSummaryView(state = state.reactionSummaryState) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionBottomSheet.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionBottomSheet.kt index face810bc78..c5981eb145a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionBottomSheet.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionBottomSheet.kt @@ -22,7 +22,6 @@ import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier -import io.element.android.emojibasebindings.Emoji import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet import io.element.android.libraries.designsystem.theme.components.hide import io.element.android.libraries.matrix.api.core.EventId @@ -31,7 +30,7 @@ import io.element.android.libraries.matrix.api.core.EventId @Composable fun CustomReactionBottomSheet( state: CustomReactionState, - onSelectEmoji: (EventId, Emoji) -> Unit, + onSelectEmoji: (EventId, String) -> Unit, modifier: Modifier = Modifier, ) { val sheetState = rememberModalBottomSheetState() @@ -42,7 +41,7 @@ fun CustomReactionBottomSheet( state.eventSink(CustomReactionEvents.DismissCustomReactionSheet) } - fun onEmojiSelectedDismiss(emoji: Emoji) { + fun onEmojiSelectedDismiss(emoji: String) { if (target?.event?.eventId == null) return sheetState.hide(coroutineScope) { state.eventSink(CustomReactionEvents.DismissCustomReactionSheet) @@ -60,6 +59,7 @@ fun CustomReactionBottomSheet( onSelectEmoji = ::onEmojiSelectedDismiss, emojibaseStore = target.emojibaseStore, selectedEmojis = state.selectedEmoji, + skinTone = state.skinTone, modifier = Modifier.fillMaxSize(), ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionPresenter.kt index a75e084abf5..207b7aa7115 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionPresenter.kt @@ -18,23 +18,28 @@ package io.element.android.features.messages.impl.timeline.components.customreac import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.preferences.api.store.SessionPreferencesStore import kotlinx.collections.immutable.toImmutableSet import kotlinx.coroutines.launch import javax.inject.Inject class CustomReactionPresenter @Inject constructor( - private val emojibaseProvider: EmojibaseProvider + private val emojibaseProvider: EmojibaseProvider, + private val sessionPreferencesStore: SessionPreferencesStore, ) : Presenter { @Composable override fun present(): CustomReactionState { val target: MutableState = remember { mutableStateOf(CustomReactionState.Target.None) } + val skinTone by sessionPreferencesStore.getSkinTone().collectAsState(initial = null) val localCoroutineScope = rememberCoroutineScope() fun handleShowCustomReactionSheet(event: TimelineItem.Event) { @@ -67,7 +72,8 @@ class CustomReactionPresenter @Inject constructor( return CustomReactionState( target = target.value, selectedEmoji = selectedEmoji, - eventSink = { handleEvents(it) } + skinTone = skinTone, + eventSink = ::handleEvents, ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionState.kt index 5474068df06..36f045cf97b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionState.kt @@ -23,6 +23,7 @@ import kotlinx.collections.immutable.ImmutableSet data class CustomReactionState( val target: Target, val selectedEmoji: ImmutableSet, + val skinTone: String?, val eventSink: (CustomReactionEvents) -> Unit, ) { sealed interface Target { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/EmojiItem.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/EmojiItem.kt index 25bc605e73a..b1373be39d7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/EmojiItem.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/EmojiItem.kt @@ -16,40 +16,63 @@ package io.element.android.features.messages.impl.timeline.components.customreaction +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background -import androidx.compose.foundation.clickable +import androidx.compose.foundation.border +import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.sizeIn +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.ripple.rememberRipple +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.LocalTextStyle +import androidx.compose.material3.TooltipBox +import androidx.compose.material3.TooltipDefaults +import androidx.compose.material3.rememberTooltipState import androidx.compose.runtime.Composable import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import io.element.android.compound.theme.ElementTheme import io.element.android.emojibasebindings.Emoji +import io.element.android.emojibasebindings.EmojiSkin import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.text.toDp import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.ui.strings.CommonStrings +import kotlinx.collections.immutable.ImmutableSet +import kotlinx.collections.immutable.persistentSetOf +import kotlinx.coroutines.launch +val SKIN_MODIFIERS = setOf("🏻", "🏼", "🏽", "🏾", "🏿") + +@OptIn(ExperimentalFoundationApi::class) @Composable fun EmojiItem( - item: Emoji, + emoji: String, isSelected: Boolean, - onSelectEmoji: (Emoji) -> Unit, + onSelectEmoji: (String) -> Unit, + onLongPress: () -> Unit, modifier: Modifier = Modifier, emojiSize: TextUnit = 20.sp, ) { @@ -59,17 +82,18 @@ fun EmojiItem( Color.Transparent } val description = if (isSelected) { - stringResource(id = CommonStrings.a11y_remove_reaction_with, item.unicode) + stringResource(id = CommonStrings.a11y_remove_reaction_with, emoji) } else { - stringResource(id = CommonStrings.a11y_react_with, item.unicode) + stringResource(id = CommonStrings.a11y_react_with, emoji) } Box( modifier = modifier .sizeIn(minWidth = 40.dp, minHeight = 40.dp) .background(backgroundColor, CircleShape) - .clickable( + .combinedClickable( enabled = true, - onClick = { onSelectEmoji(item) }, + onClick = { onSelectEmoji(emoji) }, + onLongClick = onLongPress, indication = rememberRipple(bounded = false, radius = emojiSize.toDp() / 2 + 10.dp), interactionSource = remember { MutableInteractionSource() } ) @@ -79,7 +103,7 @@ fun EmojiItem( contentAlignment = Alignment.Center ) { Text( - text = item.unicode, + text = emoji, style = LocalTextStyle.current.copy(fontSize = emojiSize), ) } @@ -87,23 +111,21 @@ fun EmojiItem( @PreviewsDayNight @Composable -internal fun EmojiItemPreview() = ElementPreview { +internal fun EmojiItemPreview(@PreviewParameter(EmojiItemVariant::class) variant: String) = ElementPreview { Row( horizontalArrangement = Arrangement.spacedBy(8.dp), ) { for (isSelected in listOf(true, false)) { EmojiItem( - item = Emoji( - hexcode = "", - label = "", - tags = null, - shortcodes = emptyList(), - unicode = "πŸ‘", - skins = null - ), + emoji = "πŸ‘$variant", isSelected = isSelected, onSelectEmoji = {}, + onLongPress = {}, ) } } } + +internal class EmojiItemVariant : PreviewParameterProvider { + override val values = sequenceOf("") + SKIN_MODIFIERS.asSequence() +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/EmojiPicker.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/EmojiPicker.kt index 3af55a6e93e..897f04cfd07 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/EmojiPicker.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/EmojiPicker.kt @@ -17,28 +17,46 @@ package io.element.android.features.messages.impl.timeline.components.customreaction import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.SecondaryTabRow import androidx.compose.material3.Tab +import androidx.compose.material3.TooltipBox +import androidx.compose.material3.TooltipDefaults +import androidx.compose.material3.rememberTooltipState import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import io.element.android.compound.theme.ElementTheme import io.element.android.emojibasebindings.Emoji +import io.element.android.emojibasebindings.EmojiSkin import io.element.android.emojibasebindings.EmojibaseCategory import io.element.android.emojibasebindings.EmojibaseDatasource import io.element.android.emojibasebindings.EmojibaseStore @@ -53,10 +71,11 @@ import kotlinx.coroutines.launch @OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class) @Composable fun EmojiPicker( - onSelectEmoji: (Emoji) -> Unit, + onSelectEmoji: (String) -> Unit, emojibaseStore: EmojibaseStore, selectedEmojis: ImmutableSet, modifier: Modifier = Modifier, + skinTone: String? = null, ) { val coroutineScope = rememberCoroutineScope() val categories = remember { emojibaseStore.categories } @@ -87,27 +106,107 @@ fun EmojiPicker( ) { index -> val category = EmojibaseCategory.entries[index] val emojis = categories[category] ?: listOf() + + val emojiSize = 32.dp.toSp() + val contentPadding = PaddingValues(vertical = 12.dp, horizontal = 16.dp) + LazyVerticalGrid( modifier = Modifier.fillMaxSize(), columns = GridCells.Adaptive(minSize = 48.dp), - contentPadding = PaddingValues(vertical = 10.dp, horizontal = 16.dp), + contentPadding = contentPadding, horizontalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.spacedBy(2.dp) ) { items(emojis, key = { it.unicode }) { item -> - EmojiItem( - modifier = Modifier.aspectRatio(1f), - item = item, - isSelected = selectedEmojis.contains(item.unicode), - onSelectEmoji = onSelectEmoji, - emojiSize = 32.dp.toSp(), - ) + val content = @Composable { + val emoji = if (skinTone != null) item.withSkinTone(skinTone)?.unicode ?: item.unicode else item.unicode + EmojiItem( + modifier = Modifier.aspectRatio(1f), + emoji = emoji, + emojiSize = emojiSize, + isSelected = selectedEmojis.contains(emoji), + onSelectEmoji = onSelectEmoji, + onLongPress = {}, + ) + } + + if (item.skins != null) { + val variants = listOf(item.unicode) + item.skins!!.map(EmojiSkin::unicode) + EmojiPickerTooltip( + emojis = variants, + emojiSize = emojiSize, + contentPadding = contentPadding, + arrangement = Arrangement.spacedBy(16.dp), + selectedEmojis = selectedEmojis, + onSelectEmoji = onSelectEmoji, + content = content, + ) + } else content() } } } } } +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun EmojiPickerTooltip( + emojis: List, + contentPadding: PaddingValues, + arrangement: Arrangement.HorizontalOrVertical, + selectedEmojis: ImmutableSet, + onSelectEmoji: (String) -> Unit, + modifier: Modifier = Modifier, + emojiSize: TextUnit = 20.sp, + tooltipVisible: Boolean = false, + content: @Composable () -> Unit, +) { + val scope = rememberCoroutineScope() + val scrollState = rememberScrollState() + val tooltipState = rememberTooltipState(initialIsVisible = tooltipVisible, isPersistent = true) + + // Note that this renders wrongly (with a spurious background box) in + // the preview, but it's fine when running the app for realsies + // https://issuetracker.google.com/issues/308808808 + TooltipBox( + positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(), + tooltip = { + Box( + modifier = Modifier + .clip(CircleShape) + .background(ElementTheme.colors.bgSubtleSecondary) + .border(1.dp, ElementTheme.colors.borderInteractiveSecondary, CircleShape) + .horizontalScroll(scrollState), + ) { + Row( + modifier = modifier.padding(contentPadding), + horizontalArrangement = arrangement, + ) { + emojis.forEach { variant -> + EmojiItem( + emoji = variant, + emojiSize = emojiSize, + isSelected = selectedEmojis.contains(variant), + onSelectEmoji = { + scope.launch { tooltipState.dismiss() } + onSelectEmoji(it) + }, + onLongPress = {} + ) + } + } + } + }, + state = tooltipState, + content = content, + ) +} + +private fun Emoji.withSkinTone(tone: String): EmojiSkin? { + if (tone !in SKIN_MODIFIERS) return null + return skins?.firstOrNull { skin -> tone in skin.unicode } +} + @PreviewsDayNight @Composable internal fun EmojiPickerPreview() = ElementPreview { @@ -118,3 +217,38 @@ internal fun EmojiPickerPreview() = ElementPreview { modifier = Modifier.fillMaxWidth(), ) } + +@PreviewsDayNight +@Composable +internal fun EmojiPickerTooltipPreview() { + val emojiSize = 32.dp.toSp() + val contentPadding = PaddingValues(14.dp) + val arrangement = Arrangement.spacedBy(12.dp) + + ElementPreview { + Box( + modifier = Modifier.size(500.dp, 300.dp), + contentAlignment = Alignment.Center, + ) { + EmojiPickerTooltip( + modifier = Modifier.padding(4.dp), + emojis = (setOf("") + SKIN_MODIFIERS).map { "🀌$it" }, + emojiSize = emojiSize, + contentPadding = contentPadding, + arrangement = arrangement, + selectedEmojis = persistentSetOf("🀌🏽"), + onSelectEmoji = {}, + tooltipVisible = true, + ) { + EmojiItem( + emoji = "🀌", + emojiSize = emojiSize, + isSelected = true, + onSelectEmoji = {}, + onLongPress = {}, + ) + } + } + } +} + diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsEvents.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsEvents.kt index ab4987f9d90..b604498825f 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsEvents.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsEvents.kt @@ -21,6 +21,9 @@ import io.element.android.compound.theme.Theme sealed interface AdvancedSettingsEvents { data class SetDeveloperModeEnabled(val enabled: Boolean) : AdvancedSettingsEvents data class SetSharePresenceEnabled(val enabled: Boolean) : AdvancedSettingsEvents + data object ChangeSkinTone : AdvancedSettingsEvents + data object CancelChangeSkinTone : AdvancedSettingsEvents + data class SetSkinTone(val modifier: String?) : AdvancedSettingsEvents data object ChangeTheme : AdvancedSettingsEvents data object CancelChangeTheme : AdvancedSettingsEvents data class SetTheme(val theme: Theme) : AdvancedSettingsEvents diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt index 54d40926bca..925ba5d1bbf 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt @@ -44,10 +44,14 @@ class AdvancedSettingsPresenter @Inject constructor( val isSharePresenceEnabled by sessionPreferencesStore .isSharePresenceEnabled() .collectAsState(initial = true) + val skinTone by sessionPreferencesStore + .getSkinTone() + .collectAsState(initial = null) val theme by remember { appPreferencesStore.getThemeFlow().mapToTheme() } .collectAsState(initial = Theme.System) + var showChangeSkinToneDialog by remember { mutableStateOf(false) } var showChangeThemeDialog by remember { mutableStateOf(false) } fun handleEvents(event: AdvancedSettingsEvents) { @@ -64,6 +68,12 @@ class AdvancedSettingsPresenter @Inject constructor( appPreferencesStore.setTheme(event.theme.name) showChangeThemeDialog = false } + AdvancedSettingsEvents.CancelChangeSkinTone -> showChangeSkinToneDialog = false + AdvancedSettingsEvents.ChangeSkinTone -> showChangeSkinToneDialog = true + is AdvancedSettingsEvents.SetSkinTone -> localCoroutineScope.launch { + sessionPreferencesStore.setSkinTone(event.modifier) + showChangeSkinToneDialog = false + } } } @@ -72,6 +82,8 @@ class AdvancedSettingsPresenter @Inject constructor( isSharePresenceEnabled = isSharePresenceEnabled, theme = theme, showChangeThemeDialog = showChangeThemeDialog, + skinTone = skinTone, + showChangeSkinToneDialog = showChangeSkinToneDialog, eventSink = { handleEvents(it) } ) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsState.kt index 527515d867c..f0cd4353bbe 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsState.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsState.kt @@ -23,5 +23,7 @@ data class AdvancedSettingsState( val isSharePresenceEnabled: Boolean, val theme: Theme, val showChangeThemeDialog: Boolean, + val skinTone: String?, + val showChangeSkinToneDialog: Boolean, val eventSink: (AdvancedSettingsEvents) -> Unit ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStateProvider.kt index 21ccec52e42..241bfa9c222 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStateProvider.kt @@ -26,6 +26,8 @@ open class AdvancedSettingsStateProvider : PreviewParameterProvider Unit = {}, ) = AdvancedSettingsState( isDeveloperModeEnabled = isDeveloperModeEnabled, isSharePresenceEnabled = isSendPublicReadReceiptsEnabled, theme = Theme.System, showChangeThemeDialog = showChangeThemeDialog, + skinTone = skinTone, + showChangeSkinToneDialog = showChangeSkinToneDialog, eventSink = eventSink ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt index 38311ee6ef5..6beacb3a0bf 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt @@ -16,10 +16,12 @@ package io.element.android.features.preferences.impl.advanced +import androidx.compose.material3.LocalTextStyle import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.sp import io.element.android.compound.theme.Theme import io.element.android.compound.theme.themes import io.element.android.features.preferences.impl.R @@ -33,6 +35,8 @@ import io.element.android.libraries.designsystem.theme.components.ListItem import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.immutableListOf +import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList @Composable @@ -81,11 +85,28 @@ fun AdvancedSettingsView( ), onClick = { state.eventSink(AdvancedSettingsEvents.SetSharePresenceEnabled(!state.isSharePresenceEnabled)) } ) + ListItem( + headlineContent = { + Text(text = stringResource(id = R.string.screen_advanced_settings_skin_tone)) + }, + supportingContent = { + Text(text = stringResource(id = R.string.screen_advanced_settings_skin_tone_description)) + }, + trailingContent = ListItemContent.Custom { + Text( + text = ("πŸ‘‹" + state.skinTone.orEmpty()), + style = LocalTextStyle.current.copy(fontSize = 32.sp), + ) + }, + onClick = { + state.eventSink(AdvancedSettingsEvents.ChangeSkinTone) + } + ) } if (state.showChangeThemeDialog) { SingleSelectionDialog( - options = getOptions(), + options = getThemeOptions(), initialSelection = themes.indexOf(state.theme), onSelectOption = { state.eventSink( @@ -97,10 +118,28 @@ fun AdvancedSettingsView( onDismissRequest = { state.eventSink(AdvancedSettingsEvents.CancelChangeTheme) }, ) } + + if (state.showChangeSkinToneDialog) { + SingleSelectionDialog( + options = getSkinToneOptions(), + initialSelection = when (state.skinTone) { + null, "" -> 0 + else -> state.skinTone.codePointAt(0) - 0x1f3fa + }, + onSelectOption = { + val tone = when (it) { + 0 -> null + else -> String(intArrayOf(it + 0x1f3fa), 0, 1) + } + state.eventSink(AdvancedSettingsEvents.SetSkinTone(tone)) + }, + onDismissRequest = { state.eventSink(AdvancedSettingsEvents.CancelChangeSkinTone) }, + ) + } } @Composable -private fun getOptions(): ImmutableList { +private fun getThemeOptions(): ImmutableList { return themes.map { ListOption(title = it.toHumanReadable()) }.toImmutableList() @@ -117,6 +156,23 @@ private fun Theme.toHumanReadable(): String { ) } +@Composable +private fun getSkinToneOptions(): ImmutableList { + val emojis = setOf("πŸ‘‹", "πŸ§‘", "πŸƒ") + val modifiers = sortedMapOf( + "" to "No modifier", + "🏻" to "Light Skin Tone", + "🏼" to "Medium-Light Skin Tone", + "🏽" to "Medium Skin Tone", + "🏾" to "Medium-Dark Skin Tone", + "🏿" to "Dark Skin Tone", + ) + + return modifiers.map { e -> + ListOption(title = emojis.joinToString(" ") { it + e.key }, subtitle = e.value) + }.toImmutableList() +} + @PreviewsDayNight @Composable internal fun AdvancedSettingsViewPreview(@PreviewParameter(AdvancedSettingsStateProvider::class) state: AdvancedSettingsState) = diff --git a/features/preferences/impl/src/main/res/values/localazy.xml b/features/preferences/impl/src/main/res/values/localazy.xml index da71324b9ec..88e821ff521 100644 --- a/features/preferences/impl/src/main/res/values/localazy.xml +++ b/features/preferences/impl/src/main/res/values/localazy.xml @@ -15,6 +15,8 @@ "Share presence" "If turned off, you won’t be able to send or receive read receipts or typing notifications." "Enable option to view message source in the timeline." + Emoji Skin Tone + Default skin tone to use when sending emoji reactions. Long hold on an emoji that supports multiple skin tone variants to choose another at any time. "You have no blocked users" "Unblock" "You\'ll be able to see all messages from them again." diff --git a/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/SessionPreferencesStore.kt b/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/SessionPreferencesStore.kt index 455a4da9ca2..22d25a10384 100644 --- a/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/SessionPreferencesStore.kt +++ b/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/SessionPreferencesStore.kt @@ -37,5 +37,8 @@ interface SessionPreferencesStore { suspend fun setSkipSessionVerification(skip: Boolean) fun isSessionVerificationSkipped(): Flow + suspend fun setSkinTone(modifier: String?) + fun getSkinTone(): Flow + suspend fun clear() } diff --git a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultSessionPreferencesStore.kt b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultSessionPreferencesStore.kt index a7b542a4369..848bc2bf1c0 100644 --- a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultSessionPreferencesStore.kt +++ b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultSessionPreferencesStore.kt @@ -21,6 +21,7 @@ import androidx.datastore.preferences.core.PreferenceDataStoreFactory import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.booleanPreferencesKey import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.preferencesDataStoreFile import io.element.android.libraries.androidutils.file.safeDelete import io.element.android.libraries.androidutils.hash.hash @@ -50,6 +51,7 @@ class DefaultSessionPreferencesStore( private val sendTypingNotificationsKey = booleanPreferencesKey("sendTypingNotifications") private val renderTypingNotificationsKey = booleanPreferencesKey("renderTypingNotifications") private val skipSessionVerification = booleanPreferencesKey("skipSessionVerification") + private val skinToneKey = stringPreferencesKey("skinTone") private val dataStoreFile = storeFile(context, sessionId) private val store = PreferenceDataStoreFactory.create( @@ -90,6 +92,14 @@ class DefaultSessionPreferencesStore( override suspend fun setSkipSessionVerification(skip: Boolean) = update(skipSessionVerification, skip) override fun isSessionVerificationSkipped(): Flow = get(skipSessionVerification) { false } + override suspend fun setSkinTone(modifier: String?) { + update(skinToneKey, modifier.orEmpty()) + } + + override fun getSkinTone(): Flow { + return get(skinToneKey) { "" }.map { it.ifEmpty { null } } + } + override suspend fun clear() { dataStoreFile.safeDelete() } diff --git a/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/InMemorySessionPreferencesStore.kt b/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/InMemorySessionPreferencesStore.kt index b1c9ba7c200..701efb55fbb 100644 --- a/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/InMemorySessionPreferencesStore.kt +++ b/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/InMemorySessionPreferencesStore.kt @@ -27,6 +27,7 @@ class InMemorySessionPreferencesStore( isSendTypingNotificationsEnabled: Boolean = true, isRenderTypingNotificationsEnabled: Boolean = true, isSessionVerificationSkipped: Boolean = false, + skinTone: String? = null, ) : SessionPreferencesStore { private val isSharePresenceEnabled = MutableStateFlow(isSharePresenceEnabled) private val isSendPublicReadReceiptsEnabled = MutableStateFlow(isSendPublicReadReceiptsEnabled) @@ -34,6 +35,7 @@ class InMemorySessionPreferencesStore( private val isSendTypingNotificationsEnabled = MutableStateFlow(isSendTypingNotificationsEnabled) private val isRenderTypingNotificationsEnabled = MutableStateFlow(isRenderTypingNotificationsEnabled) private val isSessionVerificationSkipped = MutableStateFlow(isSessionVerificationSkipped) + private val skinTone = MutableStateFlow(skinTone) var clearCallCount = 0 private set @@ -75,6 +77,14 @@ class InMemorySessionPreferencesStore( return isSessionVerificationSkipped } + override suspend fun setSkinTone(modifier: String?) { + skinTone.tryEmit(modifier) + } + + override fun getSkinTone(): Flow { + return skinTone + } + override suspend fun clear() { clearCallCount++ isSendPublicReadReceiptsEnabled.tryEmit(true) diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiItem_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiItem_Day_1_en.png new file mode 100644 index 00000000000..70a2d22fdc9 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiItem_Day_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:604680749ac222a1403132f07785c3c996204ad1e2b415219e11301046006fed +size 7046 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiItem_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiItem_Day_2_en.png new file mode 100644 index 00000000000..500b01bd432 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiItem_Day_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fcf35ff841a4bff5fc6c07c0549c83d8d64fd5f9e007e754a0e81d57d7d70738 +size 7116 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiItem_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiItem_Day_3_en.png new file mode 100644 index 00000000000..8ffe3399c89 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiItem_Day_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b5ac504d79fe2391a3ff06ae359616c6886010516622c3f635f7c9247aeaddbc +size 7081 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiItem_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiItem_Day_4_en.png new file mode 100644 index 00000000000..f0f7169cf78 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiItem_Day_4_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6f871c70c6c4b0d5d8e23799ff6e66602ba5f482d3f648c019a55d545e4a9703 +size 6996 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiItem_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiItem_Day_5_en.png new file mode 100644 index 00000000000..e4748df9e0f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiItem_Day_5_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5405791ac4ccc66f0118a060951264c36b4a3c79b8bc1396b97e756d5e3a521f +size 6950 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiItem_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiItem_Night_1_en.png new file mode 100644 index 00000000000..ce1ccdc1f5f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiItem_Night_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:10bbcfea45a5b9b1d8db09e7cd0a5dbb97a0440c75c28bd78aacc5cee4619065 +size 7088 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiItem_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiItem_Night_2_en.png new file mode 100644 index 00000000000..266044f40b2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiItem_Night_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:29f90388c42a105cd8a1c951956c610eb6e362937a6f6aa75172b714fd00bb4e +size 7114 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiItem_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiItem_Night_3_en.png new file mode 100644 index 00000000000..1cbb266370d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiItem_Night_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:df89a2880d74ba142c1c003d61a2c1b43a153df84f234d86bce815d45892b0fe +size 7064 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiItem_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiItem_Night_4_en.png new file mode 100644 index 00000000000..f5970790da4 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiItem_Night_4_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c848ee9e6cea06616ef2dd323bfd511e5a2cf8110d8f0ca3334d6b31d791dc00 +size 6997 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiItem_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiItem_Night_5_en.png new file mode 100644 index 00000000000..ae22b78648d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiItem_Night_5_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d1a0268f68d58db313ce5e582486991d95051dbf4633bc74a9573b0aa32f0a24 +size 6955 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiPickerTooltip_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiPickerTooltip_Day_0_en.png new file mode 100644 index 00000000000..7cc8195145e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiPickerTooltip_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2ba721ada2d327246827ab867702a95bd28ad10959930856311d3f85d70cff20 +size 8703 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiPickerTooltip_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiPickerTooltip_Night_0_en.png new file mode 100644 index 00000000000..5b5c1923215 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiPickerTooltip_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7346560d38c1942f1eb72752dd5dd949a3bd0aa005db7a2bb62911d6af6cf005 +size 7500 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiPicker_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiPicker_Day_0_en.png index c876ecf0e4e..1f6775c0542 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiPicker_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiPicker_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9bc9d7caab8a956190b9b893c8c563f464edc6a4a2fd337d919cce7ae4f1b6e8 -size 231301 +oid sha256:f6195f474096bd60540e04cdff5dc116e9d064a143c5846ec1a030b3558e2593 +size 230142 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiPicker_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiPicker_Night_0_en.png index 43fb60a147c..c4a874fc91b 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiPicker_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiPicker_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:57506329b66b64b6c118c9b04fe76ad343b5181b7d4640fe8b86adf24ef8687a -size 234070 +oid sha256:7abe2242aeef49a95d7ad326ba535003da98db2a0a9b8f5c3177ad1b19ddbd16 +size 233063 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_0_en.png index 10c7213abf6..e44444646ff 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2d4b39aa22007f8e10f5b64ba96b335223bdb8b9e0ef1d60bb1a2ff3190290f3 -size 42023 +oid sha256:765c82679c44546d48beb6f6adca27707fd42abadccfafbee84cf75f68957964 +size 66109 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_1_en.png index c9564c8a50d..8e6a3d6fdef 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6bb51fa5e738393685af840444d5b3aca3413b1f8dca5020fd9f3cce7f38219e -size 41435 +oid sha256:6c7d8c2bc999b3568d9e645747c7125e055b84fee23c1b1f791daf01204b1a41 +size 65553 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_2_en.png index 22f43dc2858..2af65454b76 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:29a4ce49d2191395d93f50e489137d1a939b23231ef9865217564d87eda86482 -size 30952 +oid sha256:4a51f990d95c08c154ea92ce338e79c4cbf4409535bf782ff617f180845ff438 +size 31533 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_3_en.png index 17fa444b6e5..fe89895e300 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cf4034873a1ea243d39767f7d0b6d24d365dc9a260cffaa0e20d43cdc8d43411 -size 41463 +oid sha256:8b1754cd4fe28f05002862d090996f409d76de514f2e431345051bcd08897488 +size 65591 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_4_en.png new file mode 100644 index 00000000000..3e29924953e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_4_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:13f595992d8541834b852b754c7aad0f297b66114f70a1d949996153192c90e6 +size 55202 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_5_en.png new file mode 100644 index 00000000000..8666304517f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_5_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:79f1854a967cee5bfa81746622647e857ccfae199e5555a0c1ea24f3b045455e +size 66169 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_0_en.png index da0f0160606..4216d578278 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:46ddcc5be668477289909de2667759a3ddf1698790f8f610eecbd94eaf14f81e -size 40714 +oid sha256:3bc84ab62990240de0de408d716be8c497011bf4dec67957d6698f613f908816 +size 64612 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_1_en.png index 6e5354f4f9e..ef338d2427c 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:839c064ea5d6c37e6dc49c1e2970a4dd358d45dbbd7887467d802547032aca1f -size 40272 +oid sha256:503e0c1ffb6b47ac9cc21aa414773dd4cd1ac0ef26965a7a61bdf02b1cc1617d +size 64192 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_2_en.png index 2e7cc350f6a..216ebf5db61 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b671e3da7af1357e6e9f3d2837a676762f50384b786e9bcc3e208f4423dc8967 -size 28785 +oid sha256:936409af933d93dc50287fd9aa9e5636da2e21f3f4b192660d2cda8fae90a829 +size 29343 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_3_en.png index 294c96fe732..4d3c8402b43 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3a3bf2dba2a903b36cc2e6e2f0fc6a3830e1d864cc779a20a3faaec1ee71b500 -size 40392 +oid sha256:7f25eaf5d3ecc80bef40fe1e144369e9ef4865310124de7061477e61789ed3f3 +size 64260 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_4_en.png new file mode 100644 index 00000000000..54405aeec3e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_4_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2fe2d67af2a3c0438bf362077455955d77938f4899502b5890c898d11284e20f +size 52701 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_5_en.png new file mode 100644 index 00000000000..2b42cad87bb --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_5_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:00221c7f8393025838f7e59552626575bc5366fd3aefc757bc7c98b64d2b4af6 +size 64355