diff --git a/app/src/androidTest/kotlin/com/wire/android/ui/WireTestTheme.kt b/app/src/androidTest/kotlin/com/wire/android/ui/WireTestTheme.kt index eeb5ca1fb16..448a4ee3983 100644 --- a/app/src/androidTest/kotlin/com/wire/android/ui/WireTestTheme.kt +++ b/app/src/androidTest/kotlin/com/wire/android/ui/WireTestTheme.kt @@ -22,6 +22,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.remember import com.wire.android.ui.common.snackbar.LocalSnackbarHostState +import com.wire.android.ui.theme.Accent import com.wire.android.ui.theme.DefaultWireFixedColorScheme import com.wire.android.ui.theme.WireColorScheme import com.wire.android.ui.theme.WireColorSchemeTypes @@ -47,6 +48,7 @@ fun WireTestTheme( wireFixedColorScheme, wireTypography, wireDimensions, + Accent.Unknown, content ) } diff --git a/app/src/main/kotlin/com/wire/android/di/ObserveSelfUserUseCaseProvider.kt b/app/src/main/kotlin/com/wire/android/di/ObserveSelfUserUseCaseProvider.kt new file mode 100644 index 00000000000..d4fc399cf0e --- /dev/null +++ b/app/src/main/kotlin/com/wire/android/di/ObserveSelfUserUseCaseProvider.kt @@ -0,0 +1,39 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +package com.wire.android.di + +import com.wire.kalium.logic.CoreLogic +import com.wire.kalium.logic.data.user.UserId +import com.wire.kalium.logic.feature.user.ObserveSelfUserUseCase +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject + +class ObserveSelfUserUseCaseProvider @AssistedInject constructor( + @KaliumCoreLogic private val coreLogic: CoreLogic, + @Assisted private val userId: UserId +) { + val observeSelfUser: ObserveSelfUserUseCase + get() = coreLogic.getSessionScope(userId).users.observeSelfUser + + @AssistedFactory + interface Factory { + fun create(userId: UserId): ObserveSelfUserUseCaseProvider + } +} diff --git a/app/src/main/kotlin/com/wire/android/ui/WireActivity.kt b/app/src/main/kotlin/com/wire/android/ui/WireActivity.kt index 06d0d89cd27..9a49d5064f5 100644 --- a/app/src/main/kotlin/com/wire/android/ui/WireActivity.kt +++ b/app/src/main/kotlin/com/wire/android/ui/WireActivity.kt @@ -258,7 +258,7 @@ class WireActivity : AppCompatActivity() { LocalSnackbarHostState provides snackbarHostState, LocalActivity provides this ) { - WireTheme { + WireTheme(accent = viewModel.globalAppState.userAccent) { val navigator = rememberNavigator( finish = this@WireActivity::finish, isAllowedToNavigate = { navigationCommand -> diff --git a/app/src/main/kotlin/com/wire/android/ui/WireActivityViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/WireActivityViewModel.kt index 858f687ff5d..fedd2fad1d6 100644 --- a/app/src/main/kotlin/com/wire/android/ui/WireActivityViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/WireActivityViewModel.kt @@ -33,6 +33,7 @@ import com.wire.android.di.IsProfileQRCodeEnabledUseCaseProvider import com.wire.android.di.KaliumCoreLogic import com.wire.android.di.ObserveIfE2EIRequiredDuringLoginUseCaseProvider import com.wire.android.di.ObserveScreenshotCensoringConfigUseCaseProvider +import com.wire.android.di.ObserveSelfUserUseCaseProvider import com.wire.android.di.ObserveSyncStateUseCaseProvider import com.wire.android.feature.AccountSwitchUseCase import com.wire.android.feature.SwitchAccountActions @@ -45,6 +46,7 @@ import com.wire.android.ui.common.dialogs.CustomServerDetailsDialogState import com.wire.android.ui.common.dialogs.CustomServerDialogState import com.wire.android.ui.common.dialogs.CustomServerNoNetworkDialogState import com.wire.android.ui.joinConversation.JoinConversationViaCodeState +import com.wire.android.ui.theme.Accent import com.wire.android.ui.theme.ThemeOption import com.wire.android.util.CurrentScreen import com.wire.android.util.CurrentScreenManager @@ -87,6 +89,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flatMapLatest @@ -124,6 +127,7 @@ class WireActivityViewModel @Inject constructor( private val observeIfE2EIRequiredDuringLoginUseCaseProviderFactory: ObserveIfE2EIRequiredDuringLoginUseCaseProvider.Factory, private val workManager: Lazy, private val isProfileQRCodeEnabledFactory: IsProfileQRCodeEnabledUseCaseProvider.Factory, + private val observeSelfUserFactory: ObserveSelfUserUseCaseProvider.Factory, ) : ActionsViewModel() { var globalAppState: GlobalAppState by mutableStateOf(GlobalAppState()) @@ -154,6 +158,7 @@ class WireActivityViewModel @Inject constructor( observeNewClientState() observeScreenshotCensoringConfigState() observeAppThemeState() + observeSelectedAccent() observeLogoutState() resetNewRegistrationAnalyticsState() } @@ -173,6 +178,22 @@ class WireActivityViewModel @Inject constructor( } } + private fun observeSelectedAccent() { + viewModelScope.launch(dispatchers.io()) { + observeCurrentValidUserId.flatMapLatest { + it?.let { + observeSelfUserFactory.create(it).observeSelfUser().map { user -> + Accent.fromAccentId(user.accentId) + } + } ?: flowOf(Accent.Unknown) + } + .distinctUntilChanged() + .collectLatest { + globalAppState = globalAppState.copy(userAccent = it) + } + } + } + private fun observeSyncState() { viewModelScope.launch(dispatchers.io()) { observeCurrentValidUserId @@ -606,7 +627,8 @@ data class GlobalAppState( val conversationJoinedDialog: JoinConversationViaCodeState? = null, val newClientDialog: NewClientsData? = null, val screenshotCensoringEnabled: Boolean = true, - val themeOption: ThemeOption = ThemeOption.SYSTEM + val themeOption: ThemeOption = ThemeOption.SYSTEM, + val userAccent: Accent = Accent.Unknown ) enum class InitialAppState { diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/ConversationScreen.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/ConversationScreen.kt index dd9dcdd9eef..b535467be04 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/ConversationScreen.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/ConversationScreen.kt @@ -917,6 +917,11 @@ private fun ConversationScreen( Box(modifier = Modifier) { // only here we will use normal Scaffold because of specific behaviour of message composer Scaffold( + contentColor = if (conversationInfoViewState.isBubbleUiEnabled) { + colorsScheme().primary + } else { + colorsScheme().background + }, topBar = { Column { ConversationScreenTopAppBar( @@ -1260,7 +1265,13 @@ fun MessageList( contentAlignment = Alignment.BottomEnd, modifier = modifier .fillMaxSize() - .background(color = colorsScheme().surfaceContainerLow), + .background( + color = if (isBubbleUiEnabled) { + colorsScheme().bubblesBackground + } else { + colorsScheme().surfaceContainerLow + } + ), content = { LazyColumn( state = lazyListState, diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/ConversationTopAppBar.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/ConversationTopAppBar.kt index dcd0084e577..03d0eb38ba0 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/ConversationTopAppBar.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/ConversationTopAppBar.kt @@ -37,6 +37,7 @@ import androidx.compose.runtime.Composable 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.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow @@ -82,7 +83,8 @@ fun ConversationScreenTopAppBar( onJoinCallButtonClick: () -> Unit, onAudioPermissionPermanentlyDenied: () -> Unit, isInteractionEnabled: Boolean, - isDropDownEnabled: Boolean = false + isDropDownEnabled: Boolean = false, + containerColor: Color? = null ) { val featureVisibilityFlags = LocalFeatureVisibilityFlags.current ConversationScreenTopAppBarContent( @@ -96,7 +98,8 @@ fun ConversationScreenTopAppBar( onJoinCallButtonClick = onJoinCallButtonClick, onAudioPermissionPermanentlyDenied = onAudioPermissionPermanentlyDenied, isInteractionEnabled = isInteractionEnabled, - isSearchEnabled = featureVisibilityFlags.ConversationSearchIcon + isSearchEnabled = featureVisibilityFlags.ConversationSearchIcon, + containerColor = containerColor ) } @@ -113,7 +116,8 @@ private fun ConversationScreenTopAppBarContent( onAudioPermissionPermanentlyDenied: () -> Unit, isInteractionEnabled: Boolean, isSearchEnabled: Boolean, - isDropDownEnabled: Boolean = false + isDropDownEnabled: Boolean = false, + containerColor: Color? = null ) { TopAppBar( title = { @@ -191,7 +195,7 @@ private fun ConversationScreenTopAppBarContent( } }, colors = TopAppBarDefaults.centerAlignedTopAppBarColors( - containerColor = MaterialTheme.colorScheme.background, + containerColor = containerColor ?: MaterialTheme.colorScheme.background, titleContentColor = MaterialTheme.colorScheme.onBackground, actionIconContentColor = MaterialTheme.colorScheme.onBackground, navigationIconContentColor = MaterialTheme.colorScheme.onBackground diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/QuotedMessage.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/QuotedMessage.kt index d3bd86a9987..ac9e421bfe2 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/QuotedMessage.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/QuotedMessage.kt @@ -117,7 +117,6 @@ internal fun QuotedMessage( senderName = messageData.senderName, originalDateTimeText = messageData.originalMessageDateDescription, assetName = quotedContent.assetName, - accent = messageData.senderAccent, modifier = modifier, style = style, startContent = startContent, @@ -128,7 +127,6 @@ internal fun QuotedMessage( senderName = messageData.senderName, asset = quotedContent.displayable, originalDateTimeText = messageData.originalMessageDateDescription, - accent = messageData.senderAccent, modifier = modifier, style = style, startContent = startContent, @@ -138,7 +136,6 @@ internal fun QuotedMessage( UIQuotedMessage.UIQuotedData.Deleted -> QuotedDeleted( senderName = messageData.senderName, originalDateDescription = messageData.originalMessageDateDescription, - accent = messageData.senderAccent, modifier = modifier, style = style, clickable = clickable @@ -149,7 +146,6 @@ internal fun QuotedMessage( editedTimeDescription = messageData.editedTimeDescription, originalDateTimeDescription = messageData.originalMessageDateDescription, senderName = messageData.senderName, - accent = messageData.senderAccent, modifier = modifier, style = style, startContent = startContent, @@ -159,7 +155,6 @@ internal fun QuotedMessage( is UIQuotedMessage.UIQuotedData.AudioMessage -> QuotedAudioMessage( senderName = messageData.senderName, originalDateTimeText = messageData.originalMessageDateDescription, - accent = messageData.senderAccent, modifier = modifier, style = style, startContent = startContent, @@ -170,7 +165,6 @@ internal fun QuotedMessage( senderName = messageData.senderName, originalDateTimeText = messageData.originalMessageDateDescription, locationName = quotedContent.locationName, - accent = messageData.senderAccent, modifier = modifier, style = style, startContent = startContent, @@ -219,7 +213,6 @@ fun QuotedMessagePreview( private fun QuotedMessageContent( senderName: String?, style: QuotedMessageStyle, - accent: Accent, modifier: Modifier = Modifier, endContent: @Composable () -> Unit = {}, startContent: @Composable () -> Unit = {}, @@ -229,12 +222,8 @@ private fun QuotedMessageContent( ) { val quoteOutlineShape = RoundedCornerShape(dimensions().messageAssetBorderRadius) val background = when (style.messageStyle) { - MessageStyle.BUBBLE_SELF -> MaterialTheme.wireColorScheme.accentVariantColors.getOrDefault( - style.selfAccent, - colorsScheme().primaryVariant - ) - - MessageStyle.BUBBLE_OTHER -> MaterialTheme.wireColorScheme.surface + MessageStyle.BUBBLE_SELF -> colorsScheme().selfBubble.secondary + MessageStyle.BUBBLE_OTHER -> colorsScheme().otherBubble.secondary MessageStyle.NORMAL -> MaterialTheme.wireColorScheme.surfaceVariant } @@ -274,8 +263,7 @@ private fun QuotedMessageContent( QuotedMessageTopRow( senderName, displayReplyArrow = style.quotedStyle == QuotedStyle.COMPLETE, - messageStyle = style.messageStyle, - accent = accent + messageStyle = style.messageStyle ) Row(horizontalArrangement = Arrangement.spacedBy(dimensions().spacing4x)) { Column( @@ -308,16 +296,21 @@ private fun QuotedMessageContent( private fun QuotedMessageTopRow( senderName: String?, displayReplyArrow: Boolean, - messageStyle: MessageStyle, - accent: Accent + messageStyle: MessageStyle ) { val authorColor = when (messageStyle) { - MessageStyle.BUBBLE_SELF -> colorsScheme().wireAccentColors.getOrDefault(accent, colorsScheme().onSurfaceVariant) - MessageStyle.BUBBLE_OTHER -> colorsScheme().wireAccentColors.getOrDefault(accent, colorsScheme().onSurfaceVariant) + MessageStyle.BUBBLE_SELF -> colorsScheme().selfBubble.primaryOnSecondary + MessageStyle.BUBBLE_OTHER -> colorsScheme().otherBubble.primaryOnSecondary MessageStyle.NORMAL -> colorsScheme().onSurfaceVariant } + val replyIconColor = when (messageStyle) { + MessageStyle.BUBBLE_SELF -> colorsScheme().selfBubble.onSecondary + MessageStyle.BUBBLE_OTHER -> colorsScheme().otherBubble.onSecondary + MessageStyle.NORMAL -> colorsScheme().secondaryText + } + Row( horizontalArrangement = Arrangement.spacedBy(dimensions().spacing2x), verticalAlignment = Alignment.CenterVertically @@ -325,7 +318,7 @@ private fun QuotedMessageTopRow( if (displayReplyArrow) { Icon( painter = painterResource(id = R.drawable.ic_reply), - tint = colorsScheme().secondaryText, + tint = replyIconColor, contentDescription = null, modifier = Modifier.size(dimensions().messageQuoteIconSize), ) @@ -341,7 +334,6 @@ fun QuotedUnavailable(style: QuotedMessageStyle) { QuotedMessageContent( stringResource(id = R.string.username_unavailable_label), style = style, - accent = Accent.Unknown, centerContent = { MainContentText(stringResource(R.string.label_quote_invalid_or_not_found), fontStyle = FontStyle.Italic) } @@ -353,7 +345,6 @@ fun QuotedInvalid(style: QuotedMessageStyle) { QuotedMessageContent( senderName = null, style = style, - accent = Accent.Unknown, centerContent = { StatusBox(stringResource(R.string.label_quote_invalid_or_not_found)) } @@ -365,7 +356,6 @@ private fun QuotedDeleted( senderName: UIText, originalDateDescription: UIText, style: QuotedMessageStyle, - accent: Accent, clickable: Clickable?, modifier: Modifier = Modifier, startContent: @Composable () -> Unit = {} @@ -373,7 +363,6 @@ private fun QuotedDeleted( QuotedMessageContent( senderName.asString(), style = style, - accent = accent, modifier = modifier, startContent = { startContent() @@ -382,7 +371,7 @@ private fun QuotedDeleted( StatusBox(stringResource(R.string.deleted_message_text)) }, footerContent = { - QuotedMessageOriginalDate(originalDateDescription) + QuotedMessageOriginalDate(originalDateDescription, style) }, clickable = clickable ) @@ -395,7 +384,6 @@ private fun QuotedText( originalDateTimeDescription: UIText, senderName: UIText, style: QuotedMessageStyle, - accent: Accent, clickable: Clickable?, modifier: Modifier = Modifier, startContent: @Composable () -> Unit = {} @@ -403,7 +391,6 @@ private fun QuotedText( QuotedMessageContent( senderName = senderName.asString(), style = style, - accent = accent, modifier = modifier, startContent = { startContent() @@ -414,10 +401,10 @@ private fun QuotedText( StatusBox(it.asString()) } } - MainMarkdownText(text, messageStyle = style.messageStyle) + MainMarkdownText(text, messageStyle = style.messageStyle, accent = style.selfAccent) }, footerContent = { - QuotedMessageOriginalDate(originalDateTimeDescription) + QuotedMessageOriginalDate(originalDateTimeDescription, style) }, clickable = clickable ) @@ -425,12 +412,18 @@ private fun QuotedText( @Composable private fun QuotedMessageOriginalDate( - originalDateTimeText: UIText + originalDateTimeText: UIText, + style: QuotedMessageStyle ) { + val color = when (style.messageStyle) { + MessageStyle.BUBBLE_SELF -> colorsScheme().selfBubble.onSecondary + MessageStyle.BUBBLE_OTHER -> colorsScheme().otherBubble.onSecondary + MessageStyle.NORMAL -> colorsScheme().secondaryText + } Text( originalDateTimeText.asString(), style = typography().subline01, - color = colorsScheme().secondaryText, + color = color, ) } @@ -440,7 +433,6 @@ private fun QuotedImage( asset: ImageAsset.PrivateAsset, originalDateTimeText: UIText, style: QuotedMessageStyle, - accent: Accent, clickable: Clickable?, modifier: Modifier = Modifier, startContent: @Composable () -> Unit = {} @@ -453,7 +445,6 @@ private fun QuotedImage( QuotedMessageContent( senderName = senderName.asString(), style = style, - accent = accent, modifier = modifier, endContent = { Image( @@ -466,12 +457,15 @@ private fun QuotedImage( alignment = Alignment.Center, contentScale = ContentScale.Crop ) - }, startContent = { + }, + startContent = { startContent() - }, centerContent = { + }, + centerContent = { MainContentText(stringResource(R.string.notification_shared_picture)) - }, footerContent = { - QuotedMessageOriginalDate(originalDateTimeText) + }, + footerContent = { + QuotedMessageOriginalDate(originalDateTimeText, style) }, clickable = clickable ) @@ -504,11 +498,10 @@ private fun QuotedImage( QuotedMessageTopRow( senderName = senderName.asString(), displayReplyArrow = true, - messageStyle = style.messageStyle, - accent = accent + messageStyle = style.messageStyle ) MainContentText(stringResource(R.string.notification_shared_picture)) - QuotedMessageOriginalDate(originalDateTimeText) + QuotedMessageOriginalDate(originalDateTimeText, style) } } } @@ -579,7 +572,6 @@ fun QuotedAudioMessage( senderName: UIText, originalDateTimeText: UIText, style: QuotedMessageStyle, - accent: Accent, startContent: @Composable () -> Unit, clickable: Clickable?, modifier: Modifier = Modifier @@ -587,7 +579,6 @@ fun QuotedAudioMessage( QuotedMessageContent( senderName = senderName.asString(), style = style, - accent = accent, modifier = modifier, centerContent = { MainContentText(stringResource(R.string.attachment_voice_message)) @@ -605,15 +596,22 @@ fun QuotedAudioMessage( tint = colorsScheme().secondaryText ) }, - footerContent = { QuotedMessageOriginalDate(originalDateTimeText) }, + footerContent = { QuotedMessageOriginalDate(originalDateTimeText, style) }, clickable = clickable ) } @Composable -private fun MainMarkdownText(text: String, messageStyle: MessageStyle, fontStyle: FontStyle = FontStyle.Normal) { +private fun MainMarkdownText(text: String, messageStyle: MessageStyle, accent: Accent, fontStyle: FontStyle = FontStyle.Normal) { + + val color = when (messageStyle) { + MessageStyle.BUBBLE_SELF -> colorsScheme().selfBubble.onSecondary + MessageStyle.BUBBLE_OTHER -> colorsScheme().otherBubble.onSecondary + MessageStyle.NORMAL -> colorsScheme().onSurfaceVariant + } + val nodeData = NodeData( - color = colorsScheme().onSurfaceVariant, + color = color, style = MaterialTheme.wireTypography.subline01.copy(fontStyle = fontStyle), colorScheme = MaterialTheme.wireColorScheme, typography = MaterialTheme.wireTypography, @@ -621,7 +619,8 @@ private fun MainMarkdownText(text: String, messageStyle: MessageStyle, fontStyle mentions = listOf(), disableLinks = true, messageStyle = messageStyle, - messageColors = MessageColors(highlighted = messageStyle.highlighted()) + messageColors = MessageColors(highlighted = messageStyle.highlighted()), + accent = accent ) val markdownPreview = remember(text) { @@ -657,7 +656,6 @@ private fun QuotedGenericAsset( originalDateTimeText: UIText, assetName: String?, style: QuotedMessageStyle, - accent: Accent, clickable: Clickable?, modifier: Modifier = Modifier, startContent: @Composable () -> Unit = {} @@ -665,7 +663,6 @@ private fun QuotedGenericAsset( QuotedMessageContent( senderName = senderName.asString(), style = style, - accent = accent, modifier = modifier, centerContent = { assetName?.let { @@ -684,7 +681,7 @@ private fun QuotedGenericAsset( tint = colorsScheme().secondaryText ) }, - footerContent = { QuotedMessageOriginalDate(originalDateTimeText) }, + footerContent = { QuotedMessageOriginalDate(originalDateTimeText, style) }, clickable = clickable ) } @@ -695,7 +692,6 @@ private fun QuotedLocation( originalDateTimeText: UIText, locationName: String, style: QuotedMessageStyle, - accent: Accent, clickable: Clickable?, modifier: Modifier = Modifier, startContent: @Composable () -> Unit = {} @@ -703,7 +699,6 @@ private fun QuotedLocation( QuotedMessageContent( senderName = senderName.asString(), style = style, - accent = accent, modifier = modifier, centerContent = { MainContentText(locationName) @@ -720,7 +715,7 @@ private fun QuotedLocation( tint = colorsScheme().secondaryText ) }, - footerContent = { QuotedMessageOriginalDate(originalDateTimeText) }, + footerContent = { QuotedMessageOriginalDate(originalDateTimeText, style) }, clickable = clickable ) } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/ReactionPill.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/ReactionPill.kt index b6bcebd0e83..9b6e3703081 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/ReactionPill.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/ReactionPill.kt @@ -40,6 +40,7 @@ import com.wire.android.ui.common.button.WireSecondaryButton import com.wire.android.ui.common.button.wireSecondaryButtonColors import com.wire.android.ui.common.dimensions import com.wire.android.ui.home.conversations.messages.item.interceptCombinedClickable +import com.wire.android.ui.theme.Accent import com.wire.android.ui.theme.WireTheme import com.wire.android.ui.theme.wireColorScheme import com.wire.android.ui.theme.wireTypography @@ -115,7 +116,7 @@ fun ReactionPill( @PreviewMultipleThemes @Composable -fun ReactionPillPreview() = WireTheme { +fun ReactionPillPreview() = WireTheme(accent = Accent.Unknown) { ReactionPill( emoji = "👍", count = 5, @@ -124,6 +125,17 @@ fun ReactionPillPreview() = WireTheme { ) } +@PreviewMultipleThemes +@Composable +fun ReactionOwnPillPreview() = WireTheme(accent = Accent.Amber) { + ReactionPill( + emoji = "👍", + count = 5, + isOwn = true, + onTap = {} + ) +} + private val minDimension = 1.dp private val borderRadius = 12.dp private val borderStrokeWidth = 1.dp diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/MessageBubbleItem.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/MessageBubbleItem.kt index 873fae7757a..51c814abb57 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/MessageBubbleItem.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/MessageBubbleItem.kt @@ -51,6 +51,7 @@ import com.wire.android.ui.home.conversations.model.UIMessage import com.wire.android.ui.home.conversations.model.UIMessageContent import com.wire.android.ui.home.conversations.model.messagetypes.image.MaxBounds import com.wire.android.ui.theme.Accent +import com.wire.android.ui.theme.color import com.wire.android.ui.theme.wireColorScheme @Suppress("CyclomaticComplexMethod") @@ -120,21 +121,16 @@ fun MessageBubbleItem( val shape = RoundedCornerShape(dimensions().corner16x) val bubbleColorOther: Color = if (messageStatus.isDeleted) { - MaterialTheme.colorScheme.surface + colorsScheme().surface } else { - MaterialTheme.colorScheme.background + colorsScheme().otherBubble.primary } val bubbleColor = when { messageStatus.isDeleted || message.decryptionFailed -> { - MaterialTheme.colorScheme.surface + colorsScheme().surface } - isSelfMessage -> { - MaterialTheme.wireColorScheme.wireAccentColors.getOrDefault( - accent, - MaterialTheme.wireColorScheme.primary - ) - } + isSelfMessage -> colorsScheme().selfBubble.primary else -> { bubbleColorOther @@ -144,10 +140,7 @@ fun MessageBubbleItem( val borderColor = when { message.decryptionFailed -> MaterialTheme.wireColorScheme.outline messageStatus.isDeleted -> if (isSelfMessage) { - MaterialTheme.wireColorScheme.wireAccentColors.getOrDefault( - accent, - MaterialTheme.wireColorScheme.primary - ) + accent.color(MaterialTheme.wireColorScheme.primary) } else { colorsScheme().outline } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/MessageContentAndStatus.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/MessageContentAndStatus.kt index a4eee178969..3a962829b57 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/MessageContentAndStatus.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/MessageContentAndStatus.kt @@ -193,7 +193,8 @@ private fun MessageContent( buttonList = null, messageId = message.header.messageId, onLinkClick = onLinkClick, - messageStyle = messageStyle + messageStyle = messageStyle, + accent = accent ) } } @@ -230,7 +231,8 @@ private fun MessageContent( buttonList = messageContent.buttonList, messageId = message.header.messageId, onLinkClick = onLinkClick, - messageStyle = messageStyle + messageStyle = messageStyle, + accent = accent ) } } @@ -319,7 +321,8 @@ private fun MessageContent( buttonList = null, messageId = message.header.messageId, onLinkClick = onLinkClick, - messageStyle = messageStyle + messageStyle = messageStyle, + accent = accent ) Spacer(modifier = Modifier.height(dimensions().spacing8x)) } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/MessageReactionsItem.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/MessageReactionsItem.kt index e177d25f81f..de458637468 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/MessageReactionsItem.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/MessageReactionsItem.kt @@ -29,6 +29,7 @@ import androidx.compose.ui.unit.dp import com.wire.android.ui.common.dimensions import com.wire.android.ui.home.conversations.messages.ReactionPill import com.wire.android.ui.home.conversations.model.MessageFooter +import com.wire.android.ui.theme.Accent import com.wire.android.ui.theme.WireTheme import com.wire.android.util.ui.PreviewMultipleThemes @@ -73,7 +74,7 @@ fun MessageReactionsItem( @PreviewMultipleThemes @Composable -fun LongMessageReactionsItemPreview() = WireTheme { +fun LongMessageReactionsItemPreview() = WireTheme(accent = Accent.Green) { Box(modifier = Modifier.width(300.dp)) { MessageReactionsItem( messageStyle = MessageStyle.NORMAL, @@ -97,7 +98,7 @@ fun LongMessageReactionsItemPreview() = WireTheme { @PreviewMultipleThemes @Composable -fun LongMessageReactionsBubbleItemPreview() = WireTheme { +fun LongMessageReactionsBubbleItemPreview() = WireTheme(accent = Accent.Petrol) { Box(modifier = Modifier.width(300.dp)) { MessageReactionsItem( messageStyle = MessageStyle.BUBBLE_OTHER, diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/MessageStatusIndicator.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/MessageStatusIndicator.kt index 839aedc4336..39578be81ce 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/MessageStatusIndicator.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/MessageStatusIndicator.kt @@ -27,6 +27,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import com.wire.android.R +import com.wire.android.ui.common.colorsScheme import com.wire.android.ui.common.spacers.HorizontalSpace import com.wire.android.ui.home.conversations.model.MessageFlowStatus import com.wire.android.ui.theme.WireTheme @@ -42,14 +43,14 @@ fun MessageStatusIndicator( isGroupConversation: Boolean = false ) { val defaultTint = when (messageStyle) { - MessageStyle.BUBBLE_SELF -> MaterialTheme.wireColorScheme.onPrimary - MessageStyle.BUBBLE_OTHER -> MaterialTheme.wireColorScheme.secondaryText + MessageStyle.BUBBLE_SELF -> colorsScheme().selfBubble.onPrimary + MessageStyle.BUBBLE_OTHER -> colorsScheme().otherBubble.onPrimary MessageStyle.NORMAL -> MaterialTheme.wireColorScheme.onTertiaryButtonDisabled } val errorTint = when (messageStyle) { - MessageStyle.BUBBLE_SELF -> MaterialTheme.wireColorScheme.onPrimary - MessageStyle.BUBBLE_OTHER -> MaterialTheme.wireColorScheme.error + MessageStyle.BUBBLE_SELF -> colorsScheme().selfBubble.onPrimary + MessageStyle.BUBBLE_OTHER -> colorsScheme().otherBubble.onPrimary MessageStyle.NORMAL -> MaterialTheme.wireColorScheme.error } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/MessageStyle.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/MessageStyle.kt index 4e20a433ae8..1d8521bafc0 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/MessageStyle.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/MessageStyle.kt @@ -22,6 +22,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextAlign +import com.wire.android.ui.common.colorsScheme import com.wire.android.ui.theme.wireColorScheme enum class MessageStyle { @@ -41,8 +42,8 @@ fun MessageStyle.alpha() = when (this) { @Composable fun MessageStyle.textColor(): Color { return when (this) { - MessageStyle.BUBBLE_SELF -> MaterialTheme.wireColorScheme.onPrimary - MessageStyle.BUBBLE_OTHER -> MaterialTheme.wireColorScheme.secondaryText + MessageStyle.BUBBLE_SELF -> colorsScheme().selfBubble.onPrimary + MessageStyle.BUBBLE_OTHER -> colorsScheme().otherBubble.onPrimary MessageStyle.NORMAL -> MaterialTheme.wireColorScheme.secondaryText } } @@ -50,39 +51,39 @@ fun MessageStyle.textColor(): Color { @Composable fun MessageStyle.onBackground(): Color { return when (this) { - MessageStyle.BUBBLE_SELF -> MaterialTheme.wireColorScheme.onPrimary - MessageStyle.BUBBLE_OTHER -> MaterialTheme.wireColorScheme.onBackground + MessageStyle.BUBBLE_SELF -> colorsScheme().selfBubble.onPrimary + MessageStyle.BUBBLE_OTHER -> colorsScheme().otherBubble.onPrimary MessageStyle.NORMAL -> MaterialTheme.wireColorScheme.onBackground } } @Composable fun MessageStyle.surface(): Color = when (this) { - MessageStyle.BUBBLE_SELF -> MaterialTheme.wireColorScheme.scrim - MessageStyle.BUBBLE_OTHER -> MaterialTheme.wireColorScheme.surfaceVariant + MessageStyle.BUBBLE_SELF -> colorsScheme().selfBubble.secondary + MessageStyle.BUBBLE_OTHER -> colorsScheme().otherBubble.secondary MessageStyle.NORMAL -> MaterialTheme.wireColorScheme.outline } @Composable fun MessageStyle.onSurface(): Color { return when (this) { - MessageStyle.BUBBLE_SELF -> MaterialTheme.wireColorScheme.onPrimary - MessageStyle.BUBBLE_OTHER -> MaterialTheme.wireColorScheme.onSurface + MessageStyle.BUBBLE_SELF -> colorsScheme().selfBubble.onSecondary + MessageStyle.BUBBLE_OTHER -> colorsScheme().otherBubble.onSecondary MessageStyle.NORMAL -> MaterialTheme.wireColorScheme.onSurface } } @Composable fun MessageStyle.highlighted(): Color = when (this) { - MessageStyle.BUBBLE_SELF -> MaterialTheme.wireColorScheme.onPrimary - MessageStyle.BUBBLE_OTHER -> MaterialTheme.wireColorScheme.primary + MessageStyle.BUBBLE_SELF -> colorsScheme().selfBubble.onPrimary + MessageStyle.BUBBLE_OTHER -> colorsScheme().otherBubble.onPrimary MessageStyle.NORMAL -> MaterialTheme.wireColorScheme.primary } @Composable fun MessageStyle.onNodeBackground(): Color = when (this) { - MessageStyle.BUBBLE_SELF -> MaterialTheme.wireColorScheme.onScrim - MessageStyle.BUBBLE_OTHER -> MaterialTheme.wireColorScheme.onBackground + MessageStyle.BUBBLE_SELF -> colorsScheme().selfBubble.onSecondary + MessageStyle.BUBBLE_OTHER -> colorsScheme().otherBubble.onSecondary MessageStyle.NORMAL -> MaterialTheme.wireColorScheme.onBackground } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/model/MessageTypes.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/model/MessageTypes.kt index 6c51df93b0b..060d13bb8b0 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/model/MessageTypes.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/model/MessageTypes.kt @@ -70,6 +70,7 @@ import com.wire.android.ui.markdown.MessageColors import com.wire.android.ui.markdown.NodeActions import com.wire.android.ui.markdown.NodeData import com.wire.android.ui.markdown.toMarkdownDocument +import com.wire.android.ui.theme.Accent import com.wire.android.ui.theme.wireColorScheme import com.wire.android.ui.theme.wireDimensions import com.wire.android.ui.theme.wireTypography @@ -91,6 +92,7 @@ internal fun MessageBody( messageId: String, messageBody: MessageBody?, isAvailable: Boolean, + accent: Accent, onOpenProfile: (String) -> Unit, buttonList: PersistentList?, onLinkClick: (String) -> Unit, @@ -103,9 +105,9 @@ internal fun MessageBody( } ?: Pair(emptyList(), null) val color = when (messageStyle) { - MessageStyle.BUBBLE_SELF -> MaterialTheme.wireColorScheme.onPrimary + MessageStyle.BUBBLE_SELF -> colorsScheme().selfBubble.onPrimary MessageStyle.BUBBLE_OTHER -> when { - isAvailable -> MaterialTheme.colorScheme.onBackground + isAvailable -> colorsScheme().otherBubble.onPrimary else -> MaterialTheme.wireColorScheme.secondaryText } @@ -130,7 +132,8 @@ internal fun MessageBody( onLinkClick = onLinkClick ), messageStyle = messageStyle, - messageColors = MessageColors(highlighted = messageStyle.highlighted()) + messageColors = MessageColors(highlighted = messageStyle.highlighted()), + accent = accent ) val markdownDocument = remember(text) { diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/model/messagetypes/audio/AudioMessageType.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/model/messagetypes/audio/AudioMessageType.kt index d8c8e1bc2a2..bf8fe214943 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/model/messagetypes/audio/AudioMessageType.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/model/messagetypes/audio/AudioMessageType.kt @@ -285,9 +285,9 @@ fun SuccessfulAudioMessageContent( ) val currentTimeColor = when (messageStyle) { - MessageStyle.BUBBLE_SELF -> MaterialTheme.wireColorScheme.onPrimary - MessageStyle.BUBBLE_OTHER -> MaterialTheme.wireColorScheme.primary - MessageStyle.NORMAL -> MaterialTheme.wireColorScheme.primary + MessageStyle.BUBBLE_SELF -> colorsScheme().selfBubble.onPrimary + MessageStyle.BUBBLE_OTHER -> colorsScheme().otherBubble.onPrimary + MessageStyle.NORMAL -> colorsScheme().primary } Row { @@ -394,8 +394,8 @@ private fun AudioMessageSlider( val activatedColor = messageStyle.highlighted() val disabledColor = when (messageStyle) { - MessageStyle.BUBBLE_SELF -> colorsScheme().onPrimary.copy(alpha = 0.7F) - MessageStyle.BUBBLE_OTHER -> colorsScheme().onTertiaryButtonDisabled + MessageStyle.BUBBLE_SELF -> colorsScheme().selfBubble.onPrimary.copy(alpha = 0.7F) + MessageStyle.BUBBLE_OTHER -> colorsScheme().otherBubble.onPrimary.copy(alpha = 0.7F) MessageStyle.NORMAL -> colorsScheme().onTertiaryButtonDisabled } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/model/messagetypes/video/VideoMessage.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/model/messagetypes/video/VideoMessage.kt index 46189382465..b2910f3e19b 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/model/messagetypes/video/VideoMessage.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/model/messagetypes/video/VideoMessage.kt @@ -109,8 +109,8 @@ fun VideoMessage( ) val textColor = when (messageStyle) { - MessageStyle.BUBBLE_SELF -> colorsScheme().onPrimary - MessageStyle.BUBBLE_OTHER -> colorsScheme().onBackground + MessageStyle.BUBBLE_SELF -> colorsScheme().selfBubble.onPrimary + MessageStyle.BUBBLE_OTHER -> colorsScheme().otherBubble.onPrimary MessageStyle.NORMAL -> colorsScheme().onSurfaceVariant } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/common/LastMessageSubtitle.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/common/LastMessageSubtitle.kt index 4a23fc764fb..1f1ae7a8c0e 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/common/LastMessageSubtitle.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/common/LastMessageSubtitle.kt @@ -29,6 +29,7 @@ import com.wire.android.ui.markdown.MessageColors import com.wire.android.ui.markdown.NodeData import com.wire.android.ui.markdown.getFirstInlines import com.wire.android.ui.markdown.toMarkdownDocument +import com.wire.android.ui.theme.Accent import com.wire.android.ui.theme.wireColorScheme import com.wire.android.ui.theme.wireTypography import com.wire.android.util.ui.UIText @@ -59,7 +60,8 @@ private fun LastMessageMarkdown(text: String, leadingText: String = "") { searchQuery = "", mentions = listOf(), disableLinks = true, - messageColors = MessageColors(highlighted = MaterialTheme.wireColorScheme.primary,) + messageColors = MessageColors(highlighted = MaterialTheme.wireColorScheme.primary,), + accent = Accent.Unknown ) val markdownPreview = remember(text) { diff --git a/app/src/main/kotlin/com/wire/android/ui/home/settings/account/color/ChangeUserColorScreen.kt b/app/src/main/kotlin/com/wire/android/ui/home/settings/account/color/ChangeUserColorScreen.kt index 6ebe9c36159..1a6ca216139 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/settings/account/color/ChangeUserColorScreen.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/settings/account/color/ChangeUserColorScreen.kt @@ -117,123 +117,125 @@ fun ChangeUserColorContent( ) { val scrollState = rememberScrollState() with(state) { - WireScaffold( - modifier = modifier, - topBar = { - WireCenterAlignedTopAppBar( - elevation = scrollState.rememberTopBarElevationState().value, - onNavigationPressed = onBackPressed, - title = stringResource(id = R.string.settings_myaccount_user_color_title) - ) - }, - bottomBar = { - Box(modifier = Modifier.padding(MaterialTheme.wireDimensions.spacing16x)) { - WirePrimaryButton( - text = stringResource(R.string.label_save), - onClick = onSavePressed, - fillMaxWidth = true, - trailingIcon = Icons.Filled.ChevronRight.Icon(), - state = if (state.accent != null) Default else Disabled, - loading = state.isPerformingAction, - modifier = Modifier.fillMaxWidth() + WireTheme(accent = state.accent ?: Accent.Unknown) { + WireScaffold( + modifier = modifier, + topBar = { + WireCenterAlignedTopAppBar( + elevation = scrollState.rememberTopBarElevationState().value, + onNavigationPressed = onBackPressed, + title = stringResource(id = R.string.settings_myaccount_user_color_title) ) + }, + bottomBar = { + Box(modifier = Modifier.padding(MaterialTheme.wireDimensions.spacing16x)) { + WirePrimaryButton( + text = stringResource(R.string.label_save), + onClick = onSavePressed, + fillMaxWidth = true, + trailingIcon = Icons.Filled.ChevronRight.Icon(), + state = if (state.accent != null) Default else Disabled, + loading = state.isPerformingAction, + modifier = Modifier.fillMaxWidth() + ) + } } - } - ) { internalPadding -> - Column( - modifier = Modifier - .padding(internalPadding) - .fillMaxSize() - ) { - Text( - text = stringResource(id = R.string.settings_myaccount_user_color_description), - style = MaterialTheme.wireTypography.body01, + ) { internalPadding -> + Column( modifier = Modifier - .fillMaxWidth() - .padding( - horizontal = MaterialTheme.wireDimensions.spacing16x, - vertical = MaterialTheme.wireDimensions.spacing16x - ) - ) + .padding(internalPadding) + .fillMaxSize() + ) { + Text( + text = stringResource(id = R.string.settings_myaccount_user_color_description), + style = MaterialTheme.wireTypography.body01, + modifier = Modifier + .fillMaxWidth() + .padding( + horizontal = MaterialTheme.wireDimensions.spacing16x, + vertical = MaterialTheme.wireDimensions.spacing16x + ) + ) - VerticalSpace.x24() + VerticalSpace.x24() - state.accent?.let { accentColor -> - Column(modifier = Modifier.fillMaxWidth()) { - SectionHeader(stringResource(R.string.settings_myaccount_user_color)) + state.accent?.let { accentColor -> + Column(modifier = Modifier.fillMaxWidth()) { + SectionHeader(stringResource(R.string.settings_myaccount_user_color)) - val items = Accent.entries.filter { accent -> accent != Accent.Unknown } + val items = Accent.entries.filter { accent -> accent != Accent.Unknown } - WireDropDown( - items = items.map { stringResource(it.resourceId()) }, - defaultItemIndex = if (accentColor == Accent.Unknown) -1 else items.indexOf(accentColor), - label = null, - modifier = Modifier.padding(horizontal = MaterialTheme.wireDimensions.spacing16x), - autoUpdateSelection = false, - showDefaultTextIndicator = false, - leadingCompose = { index -> if (index >= 0) AccentCircle(Accent.entries[index]) else null }, - onChangeClickDescription = stringResource(R.string.content_description_settings_myaccount_user_color) - ) { selectedIndex -> - onChangePressed(items[selectedIndex]) + WireDropDown( + items = items.map { stringResource(it.resourceId()) }, + defaultItemIndex = if (accentColor == Accent.Unknown) -1 else items.indexOf(accentColor), + label = null, + modifier = Modifier.padding(horizontal = MaterialTheme.wireDimensions.spacing16x), + autoUpdateSelection = false, + showDefaultTextIndicator = false, + leadingCompose = { index -> if (index >= 0) AccentCircle(Accent.entries[index]) else null }, + onChangeClickDescription = stringResource(R.string.content_description_settings_myaccount_user_color) + ) { selectedIndex -> + onChangePressed(items[selectedIndex]) + } } } - } - VerticalSpace.x24() - if (state.isMessageBubbleEnabled) { - SectionHeader(stringResource(R.string.settings_myaccount_user_color_example)) - VerticalSpace.x4() + VerticalSpace.x24() + if (state.isMessageBubbleEnabled) { + SectionHeader(stringResource(R.string.settings_myaccount_user_color_example)) + VerticalSpace.x4() - Column( - modifier = Modifier - .weight(weight = 1f, fill = true) - .fillMaxWidth() - .verticalScroll(scrollState) - ) { + Column( + modifier = Modifier + .weight(weight = 1f, fill = true) + .fillMaxWidth() + .verticalScroll(scrollState) + ) { - Column(modifier = Modifier.background(colorsScheme().surface)) { + Column(modifier = Modifier.background(colorsScheme().surface)) { - VerticalSpace.x12() + VerticalSpace.x12() - RegularMessageItem( - message = mockMessageWithTextContent(SELF_USER_MESSAGE_TEXT).copy( - header = mockMessageWithText.header.copy( - username = UIText.DynamicString( - "Paul Nagel" + RegularMessageItem( + message = mockMessageWithTextContent(SELF_USER_MESSAGE_TEXT).copy( + header = mockMessageWithText.header.copy( + username = UIText.DynamicString( + "Paul Nagel" + ), + accent = state.accent ?: Accent.Blue ), - accent = state.accent ?: Accent.Blue + messageFooter = mockEmptyFooter, ), - messageFooter = mockEmptyFooter, - ), - conversationDetailsData = ConversationDetailsData.Group(null, QualifiedID("value", "domain")), - clickActions = MessageClickActions.Content(), - isBubbleUiEnabled = true, - ) + conversationDetailsData = ConversationDetailsData.Group(null, QualifiedID("value", "domain")), + clickActions = MessageClickActions.Content(), + isBubbleUiEnabled = true, + ) - RegularMessageItem( - message = mockMessageWithTextContent("Have a look here: www.wire.com").copy( - source = MessageSource.OtherUser, - header = mockHeader.copy( - username = UIText.DynamicString( - "Paul Nagel" + RegularMessageItem( + message = mockMessageWithTextContent("Have a look here: www.wire.com").copy( + source = MessageSource.OtherUser, + header = mockHeader.copy( + username = UIText.DynamicString( + "Paul Nagel" + ), + membership = Membership.Standard ), - membership = Membership.Standard - ), - messageFooter = MessageFooter( - messageId = "messageId", - reactions = mapOf( - "👍" to 16, - "❤️" to 12, + messageFooter = MessageFooter( + messageId = "messageId", + reactions = mapOf( + "👍" to 16, + "❤️" to 12, + ), + ownReactions = setOf("👍"), ), - ownReactions = setOf("👍"), ), - ), - conversationDetailsData = ConversationDetailsData.Group(null, QualifiedID("value", "domain")), - clickActions = MessageClickActions.Content(), - isBubbleUiEnabled = true, - ) + conversationDetailsData = ConversationDetailsData.Group(null, QualifiedID("value", "domain")), + clickActions = MessageClickActions.Content(), + isBubbleUiEnabled = true, + ) - VerticalSpace.x24() + VerticalSpace.x24() + } } } } @@ -266,5 +268,5 @@ fun PreviewChangeUserColor() = WireTheme { @PreviewMultipleThemes @Composable fun PreviewChangeUserColorWithExample() = WireTheme { - ChangeUserColorContent(AccentActionState(Accent.Blue, isMessageBubbleEnabled = true), {}, {}, {}) + ChangeUserColorContent(AccentActionState(Accent.Green, isMessageBubbleEnabled = true), {}, {}, {}) } diff --git a/app/src/main/kotlin/com/wire/android/ui/markdown/MarkdownComposer.kt b/app/src/main/kotlin/com/wire/android/ui/markdown/MarkdownComposer.kt index d6971bac147..687ed85d98a 100644 --- a/app/src/main/kotlin/com/wire/android/ui/markdown/MarkdownComposer.kt +++ b/app/src/main/kotlin/com/wire/android/ui/markdown/MarkdownComposer.kt @@ -266,13 +266,8 @@ fun appendLinksAndMentions( val linkInfos = LinkSpannableString.getLinkInfos(stringBuilder.toString(), Linkify.WEB_URLS or Linkify.EMAIL_ADDRESSES) val linkAndMentionColor = when (nodeData.messageStyle) { - MessageStyle.BUBBLE_SELF -> if (nodeData.isAccentBackground) { - nodeData.colorScheme.onScrim - } else { - nodeData.colorScheme.onPrimary - } - - MessageStyle.BUBBLE_OTHER -> nodeData.colorScheme.primary + MessageStyle.BUBBLE_SELF -> nodeData.colorScheme.selfBubble.onPrimary + MessageStyle.BUBBLE_OTHER -> nodeData.colorScheme.otherBubble.onPrimary MessageStyle.NORMAL -> nodeData.colorScheme.primary } diff --git a/app/src/main/kotlin/com/wire/android/ui/markdown/MarkdownTable.kt b/app/src/main/kotlin/com/wire/android/ui/markdown/MarkdownTable.kt index ed976e973f5..9a990cad57c 100644 --- a/app/src/main/kotlin/com/wire/android/ui/markdown/MarkdownTable.kt +++ b/app/src/main/kotlin/com/wire/android/ui/markdown/MarkdownTable.kt @@ -32,7 +32,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.buildAnnotatedString import com.wire.android.ui.common.dimensions -import com.wire.android.ui.home.conversations.messages.item.MessageStyle import com.wire.android.ui.home.conversations.messages.item.onNodeBackground import com.wire.android.ui.home.conversations.messages.item.surface @@ -113,7 +112,7 @@ private fun parseRowCells( inlineNodeChildren( child.children, this, - nodeData.copy(isAccentBackground = nodeData.messageStyle == MessageStyle.BUBBLE_SELF) + nodeData ) ) } diff --git a/app/src/main/kotlin/com/wire/android/ui/markdown/NodeData.kt b/app/src/main/kotlin/com/wire/android/ui/markdown/NodeData.kt index 0716a19efe7..803639920c8 100644 --- a/app/src/main/kotlin/com/wire/android/ui/markdown/NodeData.kt +++ b/app/src/main/kotlin/com/wire/android/ui/markdown/NodeData.kt @@ -21,6 +21,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.TextStyle import com.wire.android.ui.home.conversations.messages.item.MessageStyle +import com.wire.android.ui.theme.Accent import com.wire.android.ui.theme.WireColorScheme import com.wire.android.ui.theme.WireTypography import com.wire.kalium.logic.data.user.UserId @@ -28,7 +29,6 @@ import com.wire.kalium.logic.data.user.UserId data class NodeData( val modifier: Modifier = Modifier, val color: Color = Color.Unspecified, - val isAccentBackground: Boolean = false, val style: TextStyle, val colorScheme: WireColorScheme, val typography: WireTypography, @@ -37,7 +37,8 @@ data class NodeData( val disableLinks: Boolean = false, val actions: NodeActions? = null, val messageStyle: MessageStyle = MessageStyle.NORMAL, - val messageColors: MessageColors + val messageColors: MessageColors, + val accent: Accent ) data class MessageColors(val highlighted: Color) diff --git a/app/src/test/kotlin/com/wire/android/ui/WireActivityViewModelTest.kt b/app/src/test/kotlin/com/wire/android/ui/WireActivityViewModelTest.kt index 78a646d89e6..af13875ce0b 100644 --- a/app/src/test/kotlin/com/wire/android/ui/WireActivityViewModelTest.kt +++ b/app/src/test/kotlin/com/wire/android/ui/WireActivityViewModelTest.kt @@ -33,10 +33,12 @@ import com.wire.android.datastore.GlobalDataStore import com.wire.android.di.IsProfileQRCodeEnabledUseCaseProvider import com.wire.android.di.ObserveIfE2EIRequiredDuringLoginUseCaseProvider import com.wire.android.di.ObserveScreenshotCensoringConfigUseCaseProvider +import com.wire.android.di.ObserveSelfUserUseCaseProvider import com.wire.android.di.ObserveSyncStateUseCaseProvider import com.wire.android.feature.AccountSwitchUseCase import com.wire.android.framework.TestClient import com.wire.android.framework.TestUser +import com.wire.android.framework.TestUser.SELF_USER import com.wire.android.services.ServicesManager import com.wire.android.ui.common.dialogs.CustomServerDetailsDialogState import com.wire.android.ui.common.dialogs.CustomServerNoNetworkDialogState @@ -76,6 +78,7 @@ import com.wire.kalium.logic.feature.session.DoesValidSessionExistResult import com.wire.kalium.logic.feature.session.DoesValidSessionExistUseCase import com.wire.kalium.logic.feature.session.GetAllSessionsResult import com.wire.kalium.logic.feature.session.ObserveSessionsUseCase +import com.wire.kalium.logic.feature.user.ObserveSelfUserUseCase import com.wire.kalium.logic.feature.user.screenshotCensoring.ObserveScreenshotCensoringConfigResult import com.wire.kalium.logic.feature.user.screenshotCensoring.ObserveScreenshotCensoringConfigUseCase import com.wire.kalium.logic.feature.user.webSocketStatus.ObservePersistentWebSocketConnectionStatusUseCase @@ -784,6 +787,9 @@ class WireActivityViewModelTest { flowOf(false) every { workManager.cancelAllWorkByTag(any()) } returns OperationImpl() every { workManager.enqueueUniquePeriodicWork(any(), any(), any()) } returns OperationImpl() + val observeSelfUserUseCase = mockk() + every { observeSelfUserFactory.create(any()).observeSelfUser } returns observeSelfUserUseCase + coEvery { observeSelfUserUseCase() } returns flowOf(SELF_USER) } @MockK @@ -849,6 +855,9 @@ class WireActivityViewModelTest { @MockK lateinit var isProfileQRCodeEnabledFactory: IsProfileQRCodeEnabledUseCaseProvider.Factory + @MockK + lateinit var observeSelfUserFactory: ObserveSelfUserUseCaseProvider.Factory + private val viewModel by lazy { WireActivityViewModel( coreLogic = { coreLogic }, @@ -870,6 +879,7 @@ class WireActivityViewModelTest { observeIfE2EIRequiredDuringLoginUseCaseProviderFactory = observeIfE2EIRequiredDuringLoginUseCaseProviderFactory, workManager = { workManager }, isProfileQRCodeEnabledFactory = isProfileQRCodeEnabledFactory, + observeSelfUserFactory = observeSelfUserFactory ) } diff --git a/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/Accent.kt b/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/Accent.kt index b548d2be01b..aa65127ee16 100644 --- a/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/Accent.kt +++ b/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/Accent.kt @@ -18,6 +18,8 @@ package com.wire.android.ui.theme import androidx.annotation.StringRes +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color import com.wire.android.ui.common.R @@ -38,11 +40,16 @@ enum class Accent(val accentId: Int) { } } +@Composable +fun Accent.color(fallback: Color): Color = MaterialTheme.wireColorScheme.wireAccentColors.getOrDefault(this, fallback) + class WireAccentColors(private val association: (Accent) -> Color) { fun getOrDefault(accent: Accent, default: Color): Color = when (accent) { Accent.Unknown -> default else -> association(accent) } + + fun get(accent: Accent): Color = association(accent) } @StringRes diff --git a/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/AccentSwatch.kt b/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/AccentSwatch.kt new file mode 100644 index 00000000000..2e5a1653892 --- /dev/null +++ b/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/AccentSwatch.kt @@ -0,0 +1,297 @@ +/* + * Wire + * Copyright (C) 2025 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +@file:Suppress("ParameterListWrapping", "Wrapping") + +package com.wire.android.ui.theme + +import androidx.compose.runtime.Stable +import androidx.compose.ui.graphics.Color + +enum class Tone { + T50, T100, T200, T300, T400, T500, T600, T700, T800, T900, + Black, White, + G10, G20, G30, G40, G50, G60, G70, G80, G90, G95, G100 +} + +@Stable +data class Shades( + val t50: Color, val t100: Color, val t200: Color, val t300: Color, val t400: Color, + val t500: Color, val t600: Color, val t700: Color, val t800: Color, val t900: Color, + val g10: Color = WireColorPalette.Gray10, val g20: Color = WireColorPalette.Gray20, + val g30: Color = WireColorPalette.Gray30, val g40: Color = WireColorPalette.Gray40, + val g50: Color = WireColorPalette.Gray50, val g60: Color = WireColorPalette.Gray60, + val g70: Color = WireColorPalette.Gray70, val g80: Color = WireColorPalette.Gray80, + val g90: Color = WireColorPalette.Gray90, val g95: Color = WireColorPalette.Gray95, + val g100: Color = WireColorPalette.Gray100, + val black: Color = Color.Black, val white: Color = Color.White, +) { + @Suppress("CyclomaticComplexMethod") + operator fun get(t: Tone) = when (t) { + Tone.T50 -> t50; Tone.T100 -> t100; Tone.T200 -> t200; Tone.T300 -> t300; Tone.T400 -> t400 + Tone.T500 -> t500; Tone.T600 -> t600; Tone.T700 -> t700; Tone.T800 -> t800; Tone.T900 -> t900 + Tone.G10 -> g10; Tone.G20 -> g20; Tone.G30 -> g30; Tone.G40 -> g40; Tone.G50 -> g50 + Tone.G60 -> g60; Tone.G70 -> g70; Tone.G80 -> g80; Tone.G90 -> g90; Tone.G95 -> g95; Tone.G100 -> g100 + Tone.Black -> black; Tone.White -> white + } +} + +@Stable +data class AccentSwatch(val light: Shades, val dark: Shades) +object AccentSwatches { + val Blue = AccentSwatch( + light = Shades( + WireColorPalette.LightBlue50, WireColorPalette.LightBlue100, WireColorPalette.LightBlue200, + WireColorPalette.LightBlue300, WireColorPalette.LightBlue400, WireColorPalette.LightBlue500, + WireColorPalette.LightBlue600, WireColorPalette.LightBlue700, WireColorPalette.LightBlue800, + WireColorPalette.LightBlue900 + ), + dark = Shades( + WireColorPalette.DarkBlue50, WireColorPalette.DarkBlue100, WireColorPalette.DarkBlue200, + WireColorPalette.DarkBlue300, WireColorPalette.DarkBlue400, WireColorPalette.DarkBlue500, + WireColorPalette.DarkBlue600, WireColorPalette.DarkBlue700, WireColorPalette.DarkBlue800, + WireColorPalette.DarkBlue900 + ) + ) + val Green = AccentSwatch( + light = Shades( + WireColorPalette.LightGreen50, WireColorPalette.LightGreen100, WireColorPalette.LightGreen200, + WireColorPalette.LightGreen300, WireColorPalette.LightGreen400, WireColorPalette.LightGreen500, + WireColorPalette.LightGreen600, WireColorPalette.LightGreen700, WireColorPalette.LightGreen800, + WireColorPalette.LightGreen900 + ), + dark = Shades( + WireColorPalette.DarkGreen50, WireColorPalette.DarkGreen100, WireColorPalette.DarkGreen200, + WireColorPalette.DarkGreen300, WireColorPalette.DarkGreen400, WireColorPalette.DarkGreen500, + WireColorPalette.DarkGreen600, WireColorPalette.DarkGreen700, WireColorPalette.DarkGreen800, + WireColorPalette.DarkGreen900 + ) + ) + val Petrol = AccentSwatch( + light = Shades( + WireColorPalette.LightPetrol50, WireColorPalette.LightPetrol100, WireColorPalette.LightPetrol200, + WireColorPalette.LightPetrol300, WireColorPalette.LightPetrol400, WireColorPalette.LightPetrol500, + WireColorPalette.LightPetrol600, WireColorPalette.LightPetrol700, WireColorPalette.LightPetrol800, + WireColorPalette.LightPetrol900 + ), + dark = Shades( + WireColorPalette.DarkPetrol50, WireColorPalette.DarkPetrol100, WireColorPalette.DarkPetrol200, + WireColorPalette.DarkPetrol300, WireColorPalette.DarkPetrol400, WireColorPalette.DarkPetrol500, + WireColorPalette.DarkPetrol600, WireColorPalette.DarkPetrol700, WireColorPalette.DarkPetrol800, + WireColorPalette.DarkPetrol900 + ) + ) + val Purple = AccentSwatch( + light = Shades( + WireColorPalette.LightPurple50, WireColorPalette.LightPurple100, WireColorPalette.LightPurple200, + WireColorPalette.LightPurple300, WireColorPalette.LightPurple400, WireColorPalette.LightPurple500, + WireColorPalette.LightPurple600, WireColorPalette.LightPurple700, WireColorPalette.LightPurple800, + WireColorPalette.LightPurple900 + ), + dark = Shades( + WireColorPalette.DarkPurple50, WireColorPalette.DarkPurple100, WireColorPalette.DarkPurple200, + WireColorPalette.DarkPurple300, WireColorPalette.DarkPurple400, WireColorPalette.DarkPurple500, + WireColorPalette.DarkPurple600, WireColorPalette.DarkPurple700, WireColorPalette.DarkPurple800, + WireColorPalette.DarkPurple900 + ) + ) + val Red = AccentSwatch( + light = Shades( + WireColorPalette.LightRed50, WireColorPalette.LightRed100, WireColorPalette.LightRed200, + WireColorPalette.LightRed300, WireColorPalette.LightRed400, WireColorPalette.LightRed500, + WireColorPalette.LightRed600, WireColorPalette.LightRed700, WireColorPalette.LightRed800, + WireColorPalette.LightRed900 + ), + dark = Shades( + WireColorPalette.DarkRed50, WireColorPalette.DarkRed100, WireColorPalette.DarkRed200, + WireColorPalette.DarkRed300, WireColorPalette.DarkRed400, WireColorPalette.DarkRed500, + WireColorPalette.DarkRed600, WireColorPalette.DarkRed700, WireColorPalette.DarkRed800, + WireColorPalette.DarkRed900 + ) + ) + val Amber = AccentSwatch( + light = Shades( + WireColorPalette.LightAmber50, WireColorPalette.LightAmber100, WireColorPalette.LightAmber200, + WireColorPalette.LightAmber300, WireColorPalette.LightAmber400, WireColorPalette.LightAmber500, + WireColorPalette.LightAmber600, WireColorPalette.LightAmber700, WireColorPalette.LightAmber800, + WireColorPalette.LightAmber900 + ), + dark = Shades( + WireColorPalette.DarkAmber50, WireColorPalette.DarkAmber100, WireColorPalette.DarkAmber200, + WireColorPalette.DarkAmber300, WireColorPalette.DarkAmber400, WireColorPalette.DarkAmber500, + WireColorPalette.DarkAmber600, WireColorPalette.DarkAmber700, WireColorPalette.DarkAmber800, + WireColorPalette.DarkAmber900 + ) + ) +} + +private fun Accent.asSwatch(): AccentSwatch = when (this) { + Accent.Blue -> AccentSwatches.Blue + Accent.Green -> AccentSwatches.Green + Accent.Petrol -> AccentSwatches.Petrol + Accent.Purple -> AccentSwatches.Purple + Accent.Red -> AccentSwatches.Red + Accent.Amber -> AccentSwatches.Amber + Accent.Unknown -> AccentSwatches.Blue +} + +data class AccentRoleMap( + // Base + val primary: Tone, + val primaryFocus: Tone, + val primaryVariant: Tone, + val onPrimaryVariant: Tone, + val focus: Tone, + + // Backgrounds + val primaryContainer: Tone, + + // Primary buttons + val primaryButtonEnabled: Tone, + val primaryButtonFocus: Tone, + val primaryButtonSelected: Tone, + + // Secondary buttons + val secondaryButtonFocus: Tone, + val secondaryButtonFocusOutline: Tone, + val secondaryButtonSelected: Tone, + val secondaryButtonSelectedOutline: Tone, + val onSecondaryButtonSelected: Tone, + + // Tertiary buttons + val tertiaryButtonFocusOutline: Tone, + val tertiaryButtonSelected: Tone, + val tertiaryButtonSelectedOutline: Tone, + val onTertiaryButtonSelected: Tone, + + // Avatars + val groupAvatar: Tone, + val channelAvatarOutline: Tone, + val channelAvatarBackground: Tone, + val channelOnAvatarBackground: Tone, + + // Chat Bubbles + val bubbleSelfPrimary: Tone, + val bubbleSelfSecondary: Tone, + val bubbleSelfPrimaryOnSecondary: Tone, + val bubbleOtherPrimaryOnSecondary: Tone +) + +private val LightRoles = AccentRoleMap( + primary = Tone.T500, + primaryFocus = Tone.T700, + primaryVariant = Tone.T50, + onPrimaryVariant = Tone.T500, + focus = Tone.T300, + + primaryContainer = Tone.T400, + + primaryButtonEnabled = Tone.T500, + primaryButtonFocus = Tone.T700, + primaryButtonSelected = Tone.T700, + + secondaryButtonFocus = Tone.G30, + secondaryButtonFocusOutline = Tone.T500, + secondaryButtonSelected = Tone.T50, + secondaryButtonSelectedOutline = Tone.T300, + onSecondaryButtonSelected = Tone.T500, + + tertiaryButtonFocusOutline = Tone.T500, + tertiaryButtonSelected = Tone.T50, + tertiaryButtonSelectedOutline = Tone.T300, + onTertiaryButtonSelected = Tone.T500, + + groupAvatar = Tone.T500, + channelAvatarOutline = Tone.T50, + channelAvatarBackground = Tone.T100, + channelOnAvatarBackground = Tone.T500, + + bubbleSelfPrimary = Tone.T500, + bubbleSelfSecondary = Tone.T600, + bubbleSelfPrimaryOnSecondary = Tone.T200, + bubbleOtherPrimaryOnSecondary = Tone.T500, +) +private val DarkRoles = AccentRoleMap( + primary = Tone.T500, + primaryFocus = Tone.T300, + primaryVariant = Tone.T800, + onPrimaryVariant = Tone.T300, + focus = Tone.T100, + + primaryContainer = Tone.T400, // TODO for bubbles should be 800 or 900 + + primaryButtonEnabled = Tone.T500, + primaryButtonFocus = Tone.T400, // TODO should be darker + primaryButtonSelected = Tone.T400, + + secondaryButtonFocus = Tone.T800, + secondaryButtonFocusOutline = Tone.T500, + secondaryButtonSelected = Tone.T800, + secondaryButtonSelectedOutline = Tone.T500, + onSecondaryButtonSelected = Tone.White, + + tertiaryButtonFocusOutline = Tone.T500, + tertiaryButtonSelected = Tone.G95, + tertiaryButtonSelectedOutline = Tone.G90, + onTertiaryButtonSelected = Tone.T500, + + groupAvatar = Tone.T500, + channelAvatarOutline = Tone.T50, + channelAvatarBackground = Tone.T100, + channelOnAvatarBackground = Tone.T500, + + bubbleSelfPrimary = Tone.T800, + bubbleSelfSecondary = Tone.T900, + bubbleSelfPrimaryOnSecondary = Tone.T400, + bubbleOtherPrimaryOnSecondary = Tone.T500, +) + +fun WireColorScheme.withAccent(accent: Accent, isDark: Boolean): WireColorScheme { + val swatch = accent.asSwatch().let { if (isDark) it.dark else it.light } + val roles = if (isDark) DarkRoles else LightRoles + + return copy( + primary = swatch[roles.primary], + + primaryVariant = swatch[roles.primaryVariant], + onPrimaryVariant = swatch[roles.onPrimaryVariant], + primaryButtonSelected = swatch[roles.primaryButtonSelected], + primaryButtonRipple = swatch[roles.primaryButtonFocus], + + primaryButtonEnabled = swatch[roles.primaryButtonEnabled], + onPrimaryButtonSelected = swatch[roles.primaryButtonEnabled], + + secondaryButtonSelected = swatch[roles.secondaryButtonSelected], + onSecondaryButtonSelected = swatch[roles.onSecondaryButtonSelected], + secondaryButtonSelectedOutline = swatch[roles.secondaryButtonSelectedOutline], + secondaryButtonRipple = swatch[roles.secondaryButtonFocus], + + tertiaryButtonSelected = swatch[roles.tertiaryButtonSelected], + onTertiaryButtonSelected = swatch[roles.onTertiaryButtonSelected], + tertiaryButtonSelectedOutline = swatch[roles.tertiaryButtonSelectedOutline], + tertiaryButtonRipple = swatch[roles.tertiaryButtonFocusOutline], + selfBubble = selfBubble.copy( + primary = swatch[roles.bubbleSelfPrimary], + secondary = swatch[roles.bubbleSelfSecondary], + primaryOnSecondary = swatch[roles.bubbleSelfPrimaryOnSecondary] + ), + otherBubble = otherBubble.copy( + primaryOnSecondary = swatch[roles.bubbleOtherPrimaryOnSecondary] + ) + ) +} diff --git a/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/LocalWireAccent.kt b/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/LocalWireAccent.kt new file mode 100644 index 00000000000..504cfd592af --- /dev/null +++ b/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/LocalWireAccent.kt @@ -0,0 +1,22 @@ +/* + * Wire + * Copyright (C) 2025 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.wire.android.ui.theme + +import androidx.compose.runtime.staticCompositionLocalOf + +val LocalWireAccent = staticCompositionLocalOf { Accent.Unknown } diff --git a/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/Theme.kt b/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/Theme.kt index 0b9dc3c67f4..1fa15d7cfc1 100644 --- a/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/Theme.kt +++ b/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/Theme.kt @@ -19,6 +19,7 @@ package com.wire.android.ui.theme //import com.wire.android.navigation.rememberNavigator +import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SnackbarHostState @@ -31,19 +32,23 @@ import com.wire.android.ui.common.snackbar.LocalSnackbarHostState @Composable fun WireTheme( - wireColorScheme: WireColorScheme = WireColorSchemeTypes.currentTheme, + baseColorScheme: WireColorScheme = WireColorSchemeTypes.currentTheme, wireFixedColorScheme: WireFixedColorScheme = DefaultWireFixedColorScheme, wireTypography: WireTypography = WireTypographyTypes.currentScreenSize, wireDimensions: WireDimensions = WireDimensionsTypes.currentScreenSize.currentOrientation, + accent: Accent = Accent.Unknown, content: @Composable () -> Unit ) { val isPreview = LocalInspectionMode.current + val isDarkTheme = isSystemInDarkTheme() + val wireColorScheme = remember(baseColorScheme, accent) { baseColorScheme.withAccent(accent, isDarkTheme) } @Suppress("SpreadOperator") CompositionLocalProvider( LocalWireColors provides wireColorScheme, LocalWireFixedColors provides wireFixedColorScheme, LocalWireTypography provides wireTypography, LocalWireDimensions provides wireDimensions, + LocalWireAccent provides accent, // we need to provide our default content color dependent on the current colorScheme, otherwise it's Color.Black LocalContentColor provides wireColorScheme.onBackground, *if (isPreview) { diff --git a/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/WireColorScheme.kt b/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/WireColorScheme.kt index fa0022a7f81..dc3db1d5e37 100644 --- a/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/WireColorScheme.kt +++ b/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/WireColorScheme.kt @@ -35,7 +35,7 @@ import io.github.esentsov.PackagePrivate @Suppress("LongParameterList") @Immutable -class WireColorScheme( +data class WireColorScheme( val useDarkSystemBarIcons: Boolean, val connectivityBarShouldUseDarkIcons: Boolean, @@ -62,9 +62,6 @@ class WireColorScheme( val surfaceContainer: Color, val surfaceContainerHigh: Color, val surfaceContainerHighest: Color, - val defaultBubbleContainerBackgroundColor: Color, - val bubbleContainerAccentBackgroundColor: WireAccentColors, - val accentVariantColors: WireAccentColors, // buttons val primaryButtonEnabled: Color, val onPrimaryButtonEnabled: Color, @@ -97,9 +94,16 @@ class WireColorScheme( // custom val emojiBackgroundColor: Color, val defaultSelectedItemInLoadingState: Color, - val onScrim: Color + val onScrim: Color, + + // message bubbles + val bubblesBackground: Color, + val defaultBubbleContainerBackgroundColor: Color, + val bubbleContainerAccentBackgroundColor: WireAccentColors, + val selfBubble: BubbleColors, + val otherBubble: BubbleColors - ) { +) { fun toColorScheme(): ColorScheme = ColorScheme( primary = primary, onPrimary = onPrimary, primaryContainer = primaryVariant, onPrimaryContainer = onPrimaryVariant, @@ -167,33 +171,22 @@ private val LightWireColorScheme = WireColorScheme( Accent.Unknown -> WireColorPalette.LightBlue400 } }, - accentVariantColors = WireAccentColors { - when (it) { - Accent.Amber -> WireColorPalette.LightAmber50 - Accent.Blue -> WireColorPalette.LightBlue50 - Accent.Green -> WireColorPalette.LightGreen50 - Accent.Purple -> WireColorPalette.LightPurple50 - Accent.Red -> WireColorPalette.LightRed50 - Accent.Petrol -> WireColorPalette.LightPetrol50 - Accent.Unknown -> WireColorPalette.LightBlue50 - } - }, // buttons primaryButtonEnabled = WireColorPalette.LightBlue500, onPrimaryButtonEnabled = Color.White, - primaryButtonDisabled = WireColorPalette.Gray50, onPrimaryButtonDisabled = WireColorPalette.Gray80, + primaryButtonDisabled = WireColorPalette.Gray50, onPrimaryButtonDisabled = Gray80, primaryButtonSelected = WireColorPalette.LightBlue700, onPrimaryButtonSelected = Color.White, primaryButtonRipple = Color.Black, secondaryButtonEnabled = Color.White, onSecondaryButtonEnabled = Color.Black, secondaryButtonEnabledOutline = WireColorPalette.Gray40, secondaryButtonDisabled = WireColorPalette.Gray20, onSecondaryButtonDisabled = WireColorPalette.Gray70, secondaryButtonDisabledOutline = WireColorPalette.Gray40, - secondaryButtonSelected = WireColorPalette.LightBlue50, onSecondaryButtonSelected = WireColorPalette.LightBlue500, + secondaryButtonSelected = LightBlue50, onSecondaryButtonSelected = WireColorPalette.LightBlue500, secondaryButtonSelectedOutline = WireColorPalette.LightBlue300, secondaryButtonRipple = Color.Black, tertiaryButtonEnabled = Color.Transparent, onTertiaryButtonEnabled = Color.Black, tertiaryButtonDisabled = Color.Transparent, onTertiaryButtonDisabled = WireColorPalette.Gray60, - tertiaryButtonSelected = WireColorPalette.LightBlue50, onTertiaryButtonSelected = WireColorPalette.LightBlue500, + tertiaryButtonSelected = LightBlue50, onTertiaryButtonSelected = WireColorPalette.LightBlue500, tertiaryButtonSelectedOutline = WireColorPalette.LightBlue300, tertiaryButtonRipple = Color.Black, @@ -243,7 +236,22 @@ private val LightWireColorScheme = WireColorScheme( // custom emojiBackgroundColor = Color.White, defaultSelectedItemInLoadingState = WireColorPalette.LightBlue100, - onScrim = Color.White + onScrim = Color.White, + bubblesBackground = Color.White, + selfBubble = BubbleColors( + primary = WireColorPalette.LightBlue500, + secondary = WireColorPalette.LightBlue600, + onPrimary = Color.White, + onSecondary = Color.White, + primaryOnSecondary = WireColorPalette.LightBlue200 + ), + otherBubble = BubbleColors( + primary = WireColorPalette.Gray20, + secondary = Color.White, + onPrimary = Color.Black, + onSecondary = Color.Black, + primaryOnSecondary = WireColorPalette.LightBlue500 + ), ) // Dark WireColorScheme @@ -272,7 +280,7 @@ private val DarkWireColorScheme = WireColorScheme( surfaceContainerLowest = WireColorPalette.Gray100, surfaceContainerLow = WireColorPalette.Gray95, surfaceContainer = WireColorPalette.Gray90, - surfaceContainerHigh = WireColorPalette.Gray80, + surfaceContainerHigh = Gray80, surfaceContainerHighest = WireColorPalette.Gray70, defaultBubbleContainerBackgroundColor = WireColorPalette.DarkBlue400, bubbleContainerAccentBackgroundColor = WireAccentColors { @@ -286,21 +294,10 @@ private val DarkWireColorScheme = WireColorScheme( Accent.Unknown -> WireColorPalette.DarkBlue400 } }, - accentVariantColors = WireAccentColors { - when (it) { - Accent.Amber -> WireColorPalette.DarkAmber800 - Accent.Blue -> WireColorPalette.DarkBlue800 - Accent.Green -> WireColorPalette.DarkGreen800 - Accent.Purple -> WireColorPalette.DarkPurple800 - Accent.Red -> WireColorPalette.DarkRed800 - Accent.Petrol -> WireColorPalette.DarkPetrol800 - Accent.Unknown -> WireColorPalette.DarkBlue800 - } - }, // buttons primaryButtonEnabled = WireColorPalette.DarkBlue500, onPrimaryButtonEnabled = Color.Black, - primaryButtonDisabled = WireColorPalette.Gray80, onPrimaryButtonDisabled = WireColorPalette.Gray50, + primaryButtonDisabled = Gray80, onPrimaryButtonDisabled = WireColorPalette.Gray50, primaryButtonSelected = WireColorPalette.DarkBlue400, onPrimaryButtonSelected = Color.Black, primaryButtonRipple = Color.White, secondaryButtonEnabled = WireColorPalette.Gray90, onSecondaryButtonEnabled = Color.White, @@ -361,7 +358,22 @@ private val DarkWireColorScheme = WireColorScheme( }, emojiBackgroundColor = Color.Black, defaultSelectedItemInLoadingState = WireColorPalette.DarkBlue800, - onScrim = Color.White + onScrim = Color.White, + bubblesBackground = WireColorPalette.Gray100, + selfBubble = BubbleColors( + primary = WireColorPalette.LightBlue800, + secondary = WireColorPalette.LightBlue900, + onPrimary = Color.White, + onSecondary = Color.White, + primaryOnSecondary = WireColorPalette.DarkBlue400 + ), + otherBubble = BubbleColors( + primary = WireColorPalette.Gray90, + secondary = WireColorPalette.Gray95, + onPrimary = Color.White, + onSecondary = Color.White, + primaryOnSecondary = WireColorPalette.DarkBlue500 + ), ) @PackagePrivate @@ -369,3 +381,11 @@ val WireColorSchemeTypes: ThemeDependent = ThemeDependent( light = LightWireColorScheme, dark = DarkWireColorScheme ) + +data class BubbleColors( + val primary: Color, + val secondary: Color, + val onPrimary: Color, + val onSecondary: Color, + val primaryOnSecondary: Color +) diff --git a/kalium b/kalium index 67c823879b5..d96675d2f1c 160000 --- a/kalium +++ b/kalium @@ -1 +1 @@ -Subproject commit 67c823879b595c4337255e7d0f9b3df5007c4785 +Subproject commit d96675d2f1ca1f1f09eb3eb2f4fbdcc0b3fe4e8b