Merge pull request #4378 from element-hq/feature/bma/customSuperButton

Be able to correctly render the UI with other colors.
This commit is contained in:
Benoit Marty 2025-03-11 09:26:54 +01:00 committed by GitHub
commit a31af8dcd6
20 changed files with 131 additions and 37 deletions

View file

@ -64,6 +64,7 @@ class MainActivity : NodeActivity() {
ElementThemeApp( ElementThemeApp(
appPreferencesStore = appBindings.preferencesStore(), appPreferencesStore = appBindings.preferencesStore(),
enterpriseService = appBindings.enterpriseService(), enterpriseService = appBindings.enterpriseService(),
buildMeta = appBindings.buildMeta()
) { ) {
CompositionLocalProvider( CompositionLocalProvider(
LocalSnackbarDispatcher provides appBindings.snackbarDispatcher(), LocalSnackbarDispatcher provides appBindings.snackbarDispatcher(),

View file

@ -13,6 +13,7 @@ import io.element.android.features.enterprise.api.EnterpriseService
import io.element.android.features.lockscreen.api.LockScreenEntryPoint import io.element.android.features.lockscreen.api.LockScreenEntryPoint
import io.element.android.features.lockscreen.api.LockScreenService import io.element.android.features.lockscreen.api.LockScreenService
import io.element.android.features.rageshake.api.reporter.BugReporter import io.element.android.features.rageshake.api.reporter.BugReporter
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.matrix.api.tracing.TracingService import io.element.android.libraries.matrix.api.tracing.TracingService
@ -38,4 +39,6 @@ interface AppBindings {
fun analyticsService(): AnalyticsService fun analyticsService(): AnalyticsService
fun enterpriseService(): EnterpriseService fun enterpriseService(): EnterpriseService
fun buildMeta(): BuildMeta
} }

View file

@ -48,6 +48,7 @@ import io.element.android.features.enterprise.api.EnterpriseService
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.bindings import io.element.android.libraries.architecture.bindings
import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.core.log.logger.LoggerTag
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.designsystem.theme.ElementThemeApp import io.element.android.libraries.designsystem.theme.ElementThemeApp
import io.element.android.libraries.preferences.api.store.AppPreferencesStore import io.element.android.libraries.preferences.api.store.AppPreferencesStore
import timber.log.Timber import timber.log.Timber
@ -64,6 +65,7 @@ class ElementCallActivity :
@Inject lateinit var appPreferencesStore: AppPreferencesStore @Inject lateinit var appPreferencesStore: AppPreferencesStore
@Inject lateinit var enterpriseService: EnterpriseService @Inject lateinit var enterpriseService: EnterpriseService
@Inject lateinit var pictureInPicturePresenter: PictureInPicturePresenter @Inject lateinit var pictureInPicturePresenter: PictureInPicturePresenter
@Inject lateinit var buildMeta: BuildMeta
private lateinit var presenter: Presenter<CallScreenState> private lateinit var presenter: Presenter<CallScreenState>
@ -114,6 +116,7 @@ class ElementCallActivity :
ElementThemeApp( ElementThemeApp(
appPreferencesStore = appPreferencesStore, appPreferencesStore = appPreferencesStore,
enterpriseService = enterpriseService, enterpriseService = enterpriseService,
buildMeta = buildMeta,
) { ) {
val state = presenter.present() val state = presenter.present()
eventSink = state.eventSink eventSink = state.eventSink

View file

@ -21,6 +21,7 @@ import io.element.android.features.call.impl.utils.ActiveCallManager
import io.element.android.features.call.impl.utils.CallState import io.element.android.features.call.impl.utils.CallState
import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.api.EnterpriseService
import io.element.android.libraries.architecture.bindings import io.element.android.libraries.architecture.bindings
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.designsystem.theme.ElementThemeApp import io.element.android.libraries.designsystem.theme.ElementThemeApp
import io.element.android.libraries.preferences.api.store.AppPreferencesStore import io.element.android.libraries.preferences.api.store.AppPreferencesStore
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
@ -51,6 +52,9 @@ class IncomingCallActivity : AppCompatActivity() {
@Inject @Inject
lateinit var enterpriseService: EnterpriseService lateinit var enterpriseService: EnterpriseService
@Inject
lateinit var buildMeta: BuildMeta
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -71,6 +75,7 @@ class IncomingCallActivity : AppCompatActivity() {
ElementThemeApp( ElementThemeApp(
appPreferencesStore = appPreferencesStore, appPreferencesStore = appPreferencesStore,
enterpriseService = enterpriseService, enterpriseService = enterpriseService,
buildMeta = buildMeta,
) { ) {
IncomingCallScreen( IncomingCallScreen(
notificationData = notificationData, notificationData = notificationData,

View file

@ -22,6 +22,7 @@ import io.element.android.features.lockscreen.impl.unlock.PinUnlockPresenter
import io.element.android.features.lockscreen.impl.unlock.PinUnlockView import io.element.android.features.lockscreen.impl.unlock.PinUnlockView
import io.element.android.features.lockscreen.impl.unlock.di.PinUnlockBindings import io.element.android.features.lockscreen.impl.unlock.di.PinUnlockBindings
import io.element.android.libraries.architecture.bindings import io.element.android.libraries.architecture.bindings
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.designsystem.theme.ElementThemeApp import io.element.android.libraries.designsystem.theme.ElementThemeApp
import io.element.android.libraries.preferences.api.store.AppPreferencesStore import io.element.android.libraries.preferences.api.store.AppPreferencesStore
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -38,6 +39,7 @@ class PinUnlockActivity : AppCompatActivity() {
@Inject lateinit var lockScreenService: LockScreenService @Inject lateinit var lockScreenService: LockScreenService
@Inject lateinit var appPreferencesStore: AppPreferencesStore @Inject lateinit var appPreferencesStore: AppPreferencesStore
@Inject lateinit var enterpriseService: EnterpriseService @Inject lateinit var enterpriseService: EnterpriseService
@Inject lateinit var buildMeta: BuildMeta
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge() enableEdgeToEdge()
@ -47,6 +49,7 @@ class PinUnlockActivity : AppCompatActivity() {
ElementThemeApp( ElementThemeApp(
appPreferencesStore = appPreferencesStore, appPreferencesStore = appPreferencesStore,
enterpriseService = enterpriseService, enterpriseService = enterpriseService,
buildMeta = buildMeta,
) { ) {
val state = presenter.present() val state = presenter.present()
PinUnlockView( PinUnlockView(

View file

@ -8,6 +8,8 @@
package io.element.android.features.messages.impl.timeline.components package io.element.android.features.messages.impl.timeline.components
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -28,7 +30,10 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent
import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionEvent import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionEvent
import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionState import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionState
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.text.toPx import io.element.android.libraries.designsystem.text.toPx
import io.element.android.libraries.designsystem.theme.LocalBuildMeta
import io.element.android.libraries.designsystem.theme.highlightedMessageBackgroundColor import io.element.android.libraries.designsystem.theme.highlightedMessageBackgroundColor
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.UserId
@ -171,8 +176,13 @@ private fun Modifier.focusedEvent(
focusedEventOffset: Dp focusedEventOffset: Dp
): Modifier { ): Modifier {
val highlightedLineColor = ElementTheme.colors.textActionAccent val highlightedLineColor = ElementTheme.colors.textActionAccent
val gradientFirstColor = if (LocalBuildMeta.current.isEnterpriseBuild) {
ElementTheme.colors.textActionAccent.copy(alpha = 0.125f)
} else {
ElementTheme.colors.highlightedMessageBackgroundColor
}
val gradientColors = listOf( val gradientColors = listOf(
ElementTheme.colors.highlightedMessageBackgroundColor, gradientFirstColor,
ElementTheme.colors.bgCanvasDefault, ElementTheme.colors.bgCanvasDefault,
) )
val verticalOffset = focusedEventOffset.toPx() val verticalOffset = focusedEventOffset.toPx()
@ -196,3 +206,15 @@ private fun Modifier.focusedEvent(
} }
}.padding(top = 4.dp) }.padding(top = 4.dp)
} }
@PreviewsDayNight
@Composable
internal fun FocusedEventPreview() = ElementPreview {
Box(
modifier = Modifier
.padding(16.dp)
.fillMaxWidth()
.height(160.dp)
.focusedEvent(0.dp),
)
}

View file

@ -37,13 +37,13 @@ internal fun TimelineItemReadMarkerView(
Text( Text(
text = stringResource(id = R.string.screen_room_timeline_read_marker_title).uppercase(), text = stringResource(id = R.string.screen_room_timeline_read_marker_title).uppercase(),
style = ElementTheme.typography.fontBodySmMedium, style = ElementTheme.typography.fontBodySmMedium,
color = ElementTheme.colors.textSecondary, color = ElementTheme.colors.textActionAccent,
) )
HorizontalDivider( HorizontalDivider(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 2.dp), .padding(horizontal = 2.dp),
color = ElementTheme.colors.borderInteractivePrimary, color = ElementTheme.colors.textActionAccent,
) )
} }
} }

View file

@ -27,6 +27,7 @@ import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size import androidx.compose.ui.geometry.Size
import androidx.compose.ui.geometry.center import androidx.compose.ui.geometry.center
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.LinearGradientShader import androidx.compose.ui.graphics.LinearGradientShader
import androidx.compose.ui.graphics.RadialGradientShader import androidx.compose.ui.graphics.RadialGradientShader
@ -36,10 +37,12 @@ import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import io.element.android.compound.annotations.CoreColorToken import io.element.android.compound.annotations.CoreColorToken
import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.compound.tokens.generated.internal.LightColorTokens import io.element.android.compound.tokens.generated.internal.LightColorTokens
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.LocalBuildMeta
import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Icon
@OptIn(CoreColorToken::class) @OptIn(CoreColorToken::class)
@ -50,6 +53,16 @@ fun GradientFloatingActionButton(
shape: Shape = RoundedCornerShape(25), shape: Shape = RoundedCornerShape(25),
content: @Composable () -> Unit, content: @Composable () -> Unit,
) { ) {
val color1 = if (LocalBuildMeta.current.isEnterpriseBuild) {
ElementTheme.colors.textActionAccent
} else {
LightColorTokens.colorGreen700
}
val color2 = if (LocalBuildMeta.current.isEnterpriseBuild) {
ElementTheme.colors.textActionAccent
} else {
LightColorTokens.colorBlue900
}
val linearShaderBrush = remember { val linearShaderBrush = remember {
object : ShaderBrush() { object : ShaderBrush() {
override fun createShader(size: Size): Shader { override fun createShader(size: Size): Shader {
@ -57,8 +70,8 @@ fun GradientFloatingActionButton(
from = Offset(size.width, size.height), from = Offset(size.width, size.height),
to = Offset(size.width, 0f), to = Offset(size.width, 0f),
colors = listOf( colors = listOf(
LightColorTokens.colorBlue900, color2,
LightColorTokens.colorGreen700, color1,
), ),
) )
} }
@ -71,8 +84,8 @@ fun GradientFloatingActionButton(
center = size.center, center = size.center,
radius = size.width / 2, radius = size.width / 2,
colors = listOf( colors = listOf(
LightColorTokens.colorGreen700, color1,
LightColorTokens.colorBlue900, color2,
) )
) )
} }
@ -85,8 +98,8 @@ fun GradientFloatingActionButton(
.graphicsLayer(shape = shape, clip = false) .graphicsLayer(shape = shape, clip = false)
.clip(shape) .clip(shape)
.drawBehind { .drawBehind {
drawRect(brush = radialShaderBrush, alpha = 0.4f)
drawRect(brush = linearShaderBrush) drawRect(brush = linearShaderBrush)
drawRect(brush = radialShaderBrush, alpha = 0.4f, blendMode = BlendMode.Overlay)
} }
.clickable( .clickable(
enabled = true, enabled = true,

View file

@ -42,6 +42,7 @@ import io.element.android.compound.tokens.generated.internal.DarkColorTokens
import io.element.android.compound.tokens.generated.internal.LightColorTokens import io.element.android.compound.tokens.generated.internal.LightColorTokens
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.LocalBuildMeta
import io.element.android.libraries.designsystem.theme.components.ButtonSize import io.element.android.libraries.designsystem.theme.components.ButtonSize
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
import io.element.android.libraries.designsystem.theme.components.lowHorizontalPaddingValue import io.element.android.libraries.designsystem.theme.components.lowHorizontalPaddingValue
@ -66,17 +67,24 @@ fun SuperButton(
} }
} }
val isLightTheme = ElementTheme.isLightTheme val isLightTheme = ElementTheme.isLightTheme
val colors = remember(isLightTheme) { val colors = if (LocalBuildMeta.current.isEnterpriseBuild) {
if (isLightTheme) { listOf(
listOf( ElementTheme.colors.textActionAccent,
LightColorTokens.colorBlue900, ElementTheme.colors.textActionAccent,
LightColorTokens.colorGreen1100, )
) } else {
} else { remember(isLightTheme) {
listOf( if (isLightTheme) {
DarkColorTokens.colorBlue900, listOf(
DarkColorTokens.colorGreen1100, LightColorTokens.colorBlue900,
) LightColorTokens.colorGreen1100,
)
} else {
listOf(
DarkColorTokens.colorBlue900,
DarkColorTokens.colorGreen1100,
)
}
} }
} }

View file

@ -9,17 +9,40 @@ package io.element.android.libraries.designsystem.theme
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.staticCompositionLocalOf
import io.element.android.compound.theme.ElementTheme import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.theme.Theme import io.element.android.compound.theme.Theme
import io.element.android.compound.theme.isDark import io.element.android.compound.theme.isDark
import io.element.android.compound.theme.mapToTheme import io.element.android.compound.theme.mapToTheme
import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.api.EnterpriseService
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.core.meta.BuildType
import io.element.android.libraries.preferences.api.store.AppPreferencesStore import io.element.android.libraries.preferences.api.store.AppPreferencesStore
val LocalBuildMeta = staticCompositionLocalOf {
BuildMeta(
isDebuggable = true,
buildType = BuildType.DEBUG,
applicationName = "MyApp",
productionApplicationName = "MyAppProd",
desktopApplicationName = "MyAppDesktop",
applicationId = "AppId",
isEnterpriseBuild = false,
lowPrivacyLoggingEnabled = false,
versionName = "aVersion",
versionCode = 123,
gitRevision = "aRevision",
gitBranchName = "aBranch",
flavorDescription = "aFlavor",
flavorShortDescription = "aFlavorShort",
)
}
/** /**
* Theme to use for all the regular screens of the application. * Theme to use for all the regular screens of the application.
* Will manage the light / dark theme based on the user preference. * Will manage the light / dark theme based on the user preference.
@ -31,6 +54,7 @@ import io.element.android.libraries.preferences.api.store.AppPreferencesStore
fun ElementThemeApp( fun ElementThemeApp(
appPreferencesStore: AppPreferencesStore, appPreferencesStore: AppPreferencesStore,
enterpriseService: EnterpriseService, enterpriseService: EnterpriseService,
buildMeta: BuildMeta,
content: @Composable () -> Unit, content: @Composable () -> Unit,
) { ) {
val theme by remember { val theme by remember {
@ -48,10 +72,14 @@ fun ElementThemeApp(
} }
val compoundLight = remember { enterpriseService.semanticColorsLight() } val compoundLight = remember { enterpriseService.semanticColorsLight() }
val compoundDark = remember { enterpriseService.semanticColorsDark() } val compoundDark = remember { enterpriseService.semanticColorsDark() }
ElementTheme( CompositionLocalProvider(
darkTheme = theme.isDark(), LocalBuildMeta provides buildMeta,
content = content, ) {
compoundLight = compoundLight, ElementTheme(
compoundDark = compoundDark, darkTheme = theme.isDark(),
) content = content,
compoundLight = compoundLight,
compoundDark = compoundDark,
)
}
} }

View file

@ -66,6 +66,7 @@ class KonsistPreviewTest {
"CallScreenPipViewPreview", "CallScreenPipViewPreview",
"ColorAliasesPreview", "ColorAliasesPreview",
"DefaultRoomListTopBarWithIndicatorPreview", "DefaultRoomListTopBarWithIndicatorPreview",
"FocusedEventPreview",
"GradientFloatingActionButtonCircleShapePreview", "GradientFloatingActionButtonCircleShapePreview",
"IconsCompoundPreview", "IconsCompoundPreview",
"IconsOtherPreview", "IconsOtherPreview",

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:2becc435316b7adb15745bd0afbc2634cbc643b3919b2e7405b8a2b9b310acd0 oid sha256:c899d8ca8e98ae73c8acd90cef3967834901929833c4d8287356a320f2bf4c0e
size 4451 size 4523

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:eb901534fbf1e05988710ea799d94daf9fa6a881edd84463ca0e065282ffbdb2 oid sha256:f7579ab37a0ebe9671a46e591037fbd8991d3265e203d6ad0dcec6a6b7ceb588
size 4439 size 4386

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a2ea87ef947697fc3ea21c8953149db1c729be5391a4fd65f63b7b04aaf0e8cf
size 10465

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8ce07ba72b4542584273ee4004365fe50da4747e4aa06c46ea0d6b192647a1ac
size 10204

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:e51f7c0e57fbdbeea27962cf3ebf58ead3a5689251eced57f2a37cb4373c9858 oid sha256:867a27b40625fe6d0c214c3a4bea0a09baf298aebb53d3ff77e19b3dfe250414
size 7057 size 10393

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:fd6fd55c333b87c619189f4b0e1b1b0616f65982b322e03e271a59eaaf1a649b oid sha256:df3a081fb057accffb4179c86f46cb05b033962c0339a96767b68610b9df9ecc
size 6967 size 10313

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:6eb030d6d2f1c80cb7dea618c3a47db2f7db7b26ad19b3ba9313c0535ab7a761 oid sha256:c43a92d9ca48521da56819a405585a96255b302a517832b7375870752489dacb
size 6533 size 10516

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:b955337a438e1c29cd75961dadc489d14f726f7524281f4368fac1c1cfcb58d6 oid sha256:82e2d283d97af1db75bacef222976be6e4f646d5606e9f4aa95fa15b81bb0898
size 6512 size 10414

View file

@ -229,6 +229,7 @@ Compose:
- LocalRoomMemberProfilesCache - LocalRoomMemberProfilesCache
- LocalMentionSpanTheme - LocalMentionSpanTheme
- LocalAnalyticsService - LocalAnalyticsService
- LocalBuildMeta
CompositionLocalNaming: CompositionLocalNaming:
active: true active: true
ContentEmitterReturningValues: ContentEmitterReturningValues: