diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/ComponentCode.kt b/app/src/main/java/com/orange/ouds/app/ui/components/ComponentCode.kt index 47fa0d193e..dc5f836b2e 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/ComponentCode.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/ComponentCode.kt @@ -49,15 +49,17 @@ internal inline fun FunctionCall.Builder.annotatedStringArgument(nam } inline fun FunctionCall.Builder.iconArgument( - name: String, + name: String?, @DrawableRes resId: Int, @StringRes contentDescriptionResId: Int? = null, - tinted: Boolean = true + tinted: Boolean = true, + noinline init: FunctionCall.Builder.() -> Unit = {} ) { constructorCallArgument(name) { painterArgument(if (tinted) resId else R.drawable.ic_untinted_square) contentDescriptionResId?.let { contentDescriptionArgument(it) } if (!tinted) tintedArgument(tinted) + init() } } diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/topappbar/TopAppBarDemoScreen.kt b/app/src/main/java/com/orange/ouds/app/ui/components/topappbar/TopAppBarDemoScreen.kt index a2f4063a39..e383ad6ddb 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/topappbar/TopAppBarDemoScreen.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/topappbar/TopAppBarDemoScreen.kt @@ -24,6 +24,7 @@ import com.orange.ouds.app.R import com.orange.ouds.app.ui.components.Component import com.orange.ouds.app.ui.components.colorArgument import com.orange.ouds.app.ui.components.contentDescriptionArgument +import com.orange.ouds.app.ui.components.iconArgument import com.orange.ouds.app.ui.components.onClickArgument import com.orange.ouds.app.ui.components.painterArgument import com.orange.ouds.app.ui.components.topappbar.TopAppBarDemoState.Companion.ActionIconBadgeCount @@ -37,6 +38,7 @@ import com.orange.ouds.app.ui.utilities.composable.CustomizationSwitchItem import com.orange.ouds.app.ui.utilities.composable.CustomizationTextInput import com.orange.ouds.app.ui.utilities.composable.DemoScreen import com.orange.ouds.app.ui.utilities.nestedName +import com.orange.ouds.app.ui.utilities.rememberUntintedIconPainter import com.orange.ouds.core.component.OudsCenterAlignedTopAppBar import com.orange.ouds.core.component.OudsLargeTopAppBar import com.orange.ouds.core.component.OudsMediumTopAppBar @@ -102,10 +104,17 @@ private fun TopAppBarDemoBottomSheetContent(state: TopAppBarDemoState) { CustomizationFilterChips( applyTopPadding = true, label = stringResource(R.string.app_components_topAppBar_lastActionIconBadge_tech), - chips = TopAppBarDemoState.ActionIconBadge.entries.map { CustomizationFilterChip(it.name, lastActionIconBadgeFilterChipsEnabled) }, + chips = TopAppBarDemoState.ActionIconBadge.entries.map { CustomizationFilterChip(it.name, lastActionIconOptionsEnabled) }, selectedChipIndex = TopAppBarDemoState.ActionIconBadge.entries.indexOf(lastActionIconBadge), onSelectionChange = { index -> lastActionIconBadge = TopAppBarDemoState.ActionIconBadge.entries[index] }, ) + CustomizationFilterChips( + applyTopPadding = true, + label = stringResource(R.string.app_components_topAppBar_lastActionIcon_tech), + chips = TopAppBarDemoState.Icon.entries.map { CustomizationFilterChip(stringResource(it.labelRes), lastActionIconOptionsEnabled) }, + selectedChipIndex = TopAppBarDemoState.Icon.entries.indexOf(lastActionIcon), + onSelectionChange = { index -> lastActionIcon = TopAppBarDemoState.Icon.entries[index] } + ) CustomizationFilterChips( applyTopPadding = true, label = stringResource(R.string.app_components_topAppBar_actionAvatar_tech), @@ -157,10 +166,12 @@ private fun TopAppBarDemoContent(state: TopAppBarDemoState) { val contentDescription = getActionContentDescriptionResId(index)?.let { stringResource(it) }.orEmpty() when (action) { TopAppBarDemoState.Action.Icon -> { + val tinted = lastActionIcon == TopAppBarDemoState.Icon.Tinted || index != lastActionIconIndex OudsTopAppBarAction.Icon( - painter = painterResource(id = LocalThemeDrawableResources.current.tipsAndTricks), + painter = if (tinted) painterResource(id = themeDrawableResources.tipsAndTricks) else rememberUntintedIconPainter(), contentDescription = contentDescription, badge = if (index == lastActionIconIndex) lastTopAppBarActionIconBadge else null, + tinted = tinted, onClick = {} ) } @@ -258,11 +269,12 @@ private fun Code.Builder.topAppBarDemoCodeSnippet(state: TopAppBarDemoState, the val contentDescriptionResId = getActionContentDescriptionResId(index) when (action) { TopAppBarDemoState.Action.Icon -> { - constructorCallArgument(null) { - painterArgument(themeDrawableResources.tipsAndTricks) - if (contentDescriptionResId != null) { - contentDescriptionArgument(contentDescriptionResId) - } + iconArgument( + null, + themeDrawableResources.tipsAndTricks, + contentDescriptionResId, + lastActionIcon == TopAppBarDemoState.Icon.Tinted + ) { val lastActionIconIndex = actions.indexOfLast { it == TopAppBarDemoState.Action.Icon } if (lastActionIconIndex == index && lastActionIconBadge != TopAppBarDemoState.ActionIconBadge.None) { constructorCallArgument("badge") { diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/topappbar/TopAppBarDemoState.kt b/app/src/main/java/com/orange/ouds/app/ui/components/topappbar/TopAppBarDemoState.kt index c4cf398a7d..2aa9fa6e2a 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/topappbar/TopAppBarDemoState.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/topappbar/TopAppBarDemoState.kt @@ -30,6 +30,7 @@ fun rememberTopAppBarDemoState( title: String = stringResource(id = R.string.app_components_topAppBar_title_label), actionCount: Int = 2, lastActionIconBadge: TopAppBarDemoState.ActionIconBadge = TopAppBarDemoState.ActionIconBadge.None, + lastActionIcon: TopAppBarDemoState.Icon = TopAppBarDemoState.Icon.Tinted, actionAvatar: TopAppBarDemoState.ActionAvatar = TopAppBarDemoState.ActionAvatar.Image, actionAvatarMonogram: Char = 'A' ) = rememberSaveable( @@ -39,11 +40,12 @@ fun rememberTopAppBarDemoState( title, actionCount, lastActionIconBadge, + lastActionIcon, actionAvatar, actionAvatarMonogram, saver = TopAppBarDemoState.Saver ) { - TopAppBarDemoState(size, centerAligned, navigationIcon, title, actionCount, lastActionIconBadge, actionAvatar, actionAvatarMonogram) + TopAppBarDemoState(size, centerAligned, navigationIcon, title, actionCount, lastActionIconBadge, lastActionIcon, actionAvatar, actionAvatarMonogram) } class TopAppBarDemoState( @@ -53,6 +55,7 @@ class TopAppBarDemoState( title: String, actionCount: Int, lastActionIconBadge: ActionIconBadge, + lastActionIcon: Icon, actionAvatar: ActionAvatar, actionAvatarMonogram: Char ) { @@ -72,6 +75,7 @@ class TopAppBarDemoState( title, actionCount, lastActionIconBadge, + lastActionIcon, actionAvatar, actionAvatarMonogram ) @@ -85,8 +89,9 @@ class TopAppBarDemoState( list[3] as String, list[4] as Int, list[5] as ActionIconBadge, - list[6] as ActionAvatar, - list[7] as Char + list[6] as Icon, + list[7] as ActionAvatar, + list[8] as Char ) } ) @@ -117,6 +122,8 @@ class TopAppBarDemoState( var lastActionIconBadge: ActionIconBadge by mutableStateOf(lastActionIconBadge) + var lastActionIcon: Icon by mutableStateOf(lastActionIcon) + var actionAvatar: ActionAvatar by mutableStateOf(actionAvatar) var actionAvatarMonogram: Char by mutableStateOf(actionAvatarMonogram) @@ -124,7 +131,7 @@ class TopAppBarDemoState( val centerAlignedSwitchEnabled: Boolean get() = size == Size.Small - val lastActionIconBadgeFilterChipsEnabled: Boolean + val lastActionIconOptionsEnabled: Boolean get() = actions.contains(Action.Icon) val actionAvatarFilterChipsEnabled: Boolean @@ -162,4 +169,9 @@ class TopAppBarDemoState( Icon, Avatar } + + enum class Icon(@StringRes val labelRes: Int) { + Tinted(R.string.app_components_common_tintedIcon_tech), + Untinted(R.string.app_components_common_untintedIcon_tech) + } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a0b764c122..17059d04e1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -409,6 +409,7 @@ Title Action count Last action icon badge + Last action icon Action avatar Monogram Action avatar monogram diff --git a/core-test/src/main/java/com/orange/ouds/core/test/OudsTopAppBarTest.kt b/core-test/src/main/java/com/orange/ouds/core/test/OudsTopAppBarTest.kt index 149cc6489d..d952c7defb 100644 --- a/core-test/src/main/java/com/orange/ouds/core/test/OudsTopAppBarTest.kt +++ b/core-test/src/main/java/com/orange/ouds/core/test/OudsTopAppBarTest.kt @@ -19,7 +19,7 @@ import org.junit.runners.Parameterized @RunWith(Enclosed::class) -internal class OudsTopAppBarTest() { +internal class OudsTopAppBarTest { @RunWith(Parameterized::class) class Default(parameter: Any) : OudsComponentSnapshotTest( @@ -76,4 +76,10 @@ internal class OudsTopAppBarTest() { internal fun data() = OudsPreviewableComponent.TopAppBar.Large.parameters } } + + class WithUntintedIcon : OudsComponentSnapshotTest( + OudsPreviewableComponent.TopAppBar.WithUntintedIcon, + parameter = null, + OudsComponentTestSuite.theme + ) } diff --git a/core/src/main/java/com/orange/ouds/core/component/OudsTopAppBar.kt b/core/src/main/java/com/orange/ouds/core/component/OudsTopAppBar.kt index 2a63411972..80933e53f3 100644 --- a/core/src/main/java/com/orange/ouds/core/component/OudsTopAppBar.kt +++ b/core/src/main/java/com/orange/ouds/core/component/OudsTopAppBar.kt @@ -59,6 +59,7 @@ import com.orange.ouds.core.utilities.CheckerboardPainter import com.orange.ouds.core.utilities.OudsPreview import com.orange.ouds.core.utilities.OudsPreviewLightDark import com.orange.ouds.core.utilities.getPreviewTheme +import com.orange.ouds.core.utilities.rememberRainbowHeartPainter import com.orange.ouds.foundation.utilities.BasicPreviewParameterProvider import com.orange.ouds.theme.OudsThemeContract @@ -104,6 +105,8 @@ import com.orange.ouds.theme.OudsThemeContract * scrolls. See [TopAppBarScrollBehavior.nestedScrollConnection]. * * @sample com.orange.ouds.core.component.samples.OudsTopAppBarSample + * + * @sample com.orange.ouds.core.component.samples.OudsTopAppBarWithUntintedIconSample */ @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -177,6 +180,8 @@ fun OudsTopAppBar( * scrolls. See [TopAppBarScrollBehavior.nestedScrollConnection]. * * @sample com.orange.ouds.core.component.samples.OudsCenterAlignedTopAppBarSample + * + * @sample com.orange.ouds.core.component.samples.OudsTopAppBarWithUntintedIconSample */ @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -260,6 +265,8 @@ fun OudsCenterAlignedTopAppBar( * [collapsedHeight] * * @sample com.orange.ouds.core.component.samples.OudsMediumTopAppBarSample + * + * @sample com.orange.ouds.core.component.samples.OudsTopAppBarWithUntintedIconSample */ @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -345,6 +352,8 @@ fun OudsMediumTopAppBar( * [collapsedHeight] * * @sample com.orange.ouds.core.component.samples.OudsLargeTopAppBarSample + * + * @sample com.orange.ouds.core.component.samples.OudsTopAppBarWithUntintedIconSample */ @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -515,6 +524,7 @@ sealed interface OudsTopAppBarAction : OudsPolymorphicComponentContent { graphicsObject: Any, contentDescription: String, badge: OudsTopAppBarActionBadge?, + override val tinted: Boolean, onClick: () -> Unit ) : OudsTopAppBarAction, OudsComponentIcon(Nothing::class.java, { graphicsObject }, { contentDescription }, onClick) { @@ -535,14 +545,19 @@ sealed interface OudsTopAppBarAction : OudsPolymorphicComponentContent { * @param painter Painter of the icon. * @param contentDescription The content description associated with this [OudsTopAppBarAction.Icon]. * @param badge Optional badge displayed on the icon. + * @param tinted Controls whether the icon should be tinted with the theme color. Defaults to `true`. + * When set to `false`, the icon is displayed with its original colors (e.g., for multi-color icons). + * Note that untinted icons must ensure sufficient contrast with the background for accessibility reasons. * @param onClick Callback invoked when the icon is clicked. */ + @JvmOverloads constructor( painter: Painter, contentDescription: String, badge: OudsTopAppBarActionBadge? = null, + tinted: Boolean = true, onClick: () -> Unit - ) : this(painter as Any, contentDescription, badge, onClick) + ) : this(painter as Any, contentDescription, badge, tinted, onClick) /** * Creates an instance of [OudsTopAppBarAction.Icon]. @@ -550,14 +565,19 @@ sealed interface OudsTopAppBarAction : OudsPolymorphicComponentContent { * @param imageVector Image vector of the icon. * @param contentDescription The content description associated with this [OudsTopAppBarAction.Icon]. * @param badge Optional badge displayed on the icon. + * @param tinted Controls whether the icon should be tinted with the theme color. Defaults to `true`. + * When set to `false`, the icon is displayed with its original colors (e.g., for multi-color icons). + * Note that untinted icons must ensure sufficient contrast with the background for accessibility reasons. * @param onClick Callback invoked when the icon is clicked. */ + @JvmOverloads constructor( imageVector: ImageVector, contentDescription: String, badge: OudsTopAppBarActionBadge? = null, + tinted: Boolean = true, onClick: () -> Unit - ) : this(imageVector as Any, contentDescription, badge, onClick) + ) : this(imageVector as Any, contentDescription, badge, tinted, onClick) /** * Creates an instance of [OudsTopAppBarAction.Icon]. @@ -565,14 +585,19 @@ sealed interface OudsTopAppBarAction : OudsPolymorphicComponentContent { * @param bitmap Image bitmap of the icon. * @param contentDescription The content description associated with this [OudsTopAppBarAction.Icon]. * @param badge Optional badge displayed on the icon. + * @param tinted Controls whether the icon should be tinted with the theme color. Defaults to `true`. + * When set to `false`, the icon is displayed with its original colors (e.g., for multi-color icons). + * Note that untinted icons must ensure sufficient contrast with the background for accessibility reasons. * @param onClick Callback invoked when the icon is clicked. */ + @JvmOverloads constructor( bitmap: ImageBitmap, contentDescription: String, badge: OudsTopAppBarActionBadge? = null, + tinted: Boolean = true, onClick: () -> Unit - ) : this(bitmap as Any, contentDescription, badge, onClick) + ) : this(bitmap as Any, contentDescription, badge, tinted, onClick) } /** @@ -769,13 +794,28 @@ internal fun PreviewOudsLargeTopAppBar( } } +@OudsPreview +@Composable +@Suppress("PreviewShouldNotBeCalledRecursively") +private fun PreviewOudsTopAppBarWithUntintedIcon() = PreviewOudsTopAppBarWithUntintedIcon(getPreviewTheme()) + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +internal fun PreviewOudsTopAppBarWithUntintedIcon(theme: OudsThemeContract) = OudsPreview(theme = theme) { + OudsTopAppBar( + title = "Title", + navigationIcon = OudsTopAppBarNavigationIcon.Back(onClick = {}), + actions = listOf(OudsTopAppBarAction.Icon(painter = rememberRainbowHeartPainter(), contentDescription = "", tinted = false, onClick = {})) + ) +} + internal data class OudsTopAppBarPreviewParameter( val title: String = "Title", val navigationIcon: OudsTopAppBarNavigationIcon? = null, val actions: List = emptyList() ) -internal class OudsTopAppBarPreviewParameterProvider() : BasicPreviewParameterProvider(*previewParameterValues.toTypedArray()) +internal class OudsTopAppBarPreviewParameterProvider : BasicPreviewParameterProvider(*previewParameterValues.toTypedArray()) private val previewParameterValues: List get() = listOf( diff --git a/core/src/main/java/com/orange/ouds/core/component/samples/OudsTopAppBarSamples.kt b/core/src/main/java/com/orange/ouds/core/component/samples/OudsTopAppBarSamples.kt index 8179de9c2f..dd5f96c710 100644 --- a/core/src/main/java/com/orange/ouds/core/component/samples/OudsTopAppBarSamples.kt +++ b/core/src/main/java/com/orange/ouds/core/component/samples/OudsTopAppBarSamples.kt @@ -25,6 +25,7 @@ import com.orange.ouds.core.component.OudsTopAppBar import com.orange.ouds.core.component.OudsTopAppBarAction import com.orange.ouds.core.component.OudsTopAppBarNavigationIcon import com.orange.ouds.core.utilities.OudsPreview +import com.orange.ouds.core.utilities.rememberRainbowHeartPainter @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -78,6 +79,23 @@ internal fun OudsLargeTopAppBarSample() { ) } +@OptIn(ExperimentalMaterial3Api::class) +@Composable +internal fun OudsTopAppBarWithUntintedIconSample() { + OudsTopAppBar( + title = "Title", + navigationIcon = OudsTopAppBarNavigationIcon.Back {}, + actions = listOf( + OudsTopAppBarAction.Icon( + painter = rememberRainbowHeartPainter(), + contentDescription = "", + tinted = false, + onClick = {} + ) + ) + ) +} + @PreviewLightDark @Composable private fun PreviewOudsTopAppBarSample() = OudsPreview { @@ -101,3 +119,9 @@ private fun PreviewOudsMediumTopAppBarSample() = OudsPreview { private fun PreviewOudsLargeTopAppBarSample() = OudsPreview { OudsLargeTopAppBarSample() } + +@PreviewLightDark +@Composable +private fun PreviewOudsTopAppBarWithUntintedIconSample() = OudsPreview { + OudsTopAppBarWithUntintedIconSample() +} diff --git a/core/src/main/java/com/orange/ouds/core/utilities/OudsPreviewableComponent.kt b/core/src/main/java/com/orange/ouds/core/utilities/OudsPreviewableComponent.kt index 661f6c488e..2a07fff1eb 100644 --- a/core/src/main/java/com/orange/ouds/core/utilities/OudsPreviewableComponent.kt +++ b/core/src/main/java/com/orange/ouds/core/utilities/OudsPreviewableComponent.kt @@ -202,6 +202,7 @@ import com.orange.ouds.core.component.PreviewOudsTextInputWithRichText import com.orange.ouds.core.component.PreviewOudsTextInputWithRoundedCorners import com.orange.ouds.core.component.PreviewOudsTextInputWithUntintedLeadingIcon import com.orange.ouds.core.component.PreviewOudsTopAppBar +import com.orange.ouds.core.component.PreviewOudsTopAppBarWithUntintedIcon import com.orange.ouds.core.theme.WindowWidthSizeClass import com.orange.ouds.foundation.InternalOudsApi import com.orange.ouds.theme.OudsThemeContract @@ -254,6 +255,7 @@ interface OudsPreviewableComponent { } object WithUntintedIcon : OudsPreviewableComponent { + override val parameters: List = emptyList() @Composable @@ -984,6 +986,7 @@ interface OudsPreviewableComponent { } object WithUntintedIcon : OudsPreviewableComponent { + override val parameters: List = emptyList() @Composable @@ -1855,5 +1858,17 @@ interface OudsPreviewableComponent { ) } } + + object WithUntintedIcon : OudsPreviewableComponent { + + override val parameters: List = emptyList() + + @Composable + override fun Preview(theme: OudsThemeContract, darkThemeEnabled: Boolean, highContrastModeEnabled: Boolean, parameter: Any?) { + PreviewOudsTopAppBarWithUntintedIcon(theme = theme) + } + + override fun isPreviewAvailable(darkThemeEnabled: Boolean, highContrastModeEnabled: Boolean) = !darkThemeEnabled && !highContrastModeEnabled + } } } diff --git a/theme-orange-compact/src/test/snapshots/images/com.orange.ouds.core.test_OudsTopAppBarTest$WithUntintedIcon_takeLightThemeSnapshot.png b/theme-orange-compact/src/test/snapshots/images/com.orange.ouds.core.test_OudsTopAppBarTest$WithUntintedIcon_takeLightThemeSnapshot.png new file mode 100644 index 0000000000..45216f8396 Binary files /dev/null and b/theme-orange-compact/src/test/snapshots/images/com.orange.ouds.core.test_OudsTopAppBarTest$WithUntintedIcon_takeLightThemeSnapshot.png differ diff --git a/theme-orange/src/test/snapshots/images/com.orange.ouds.core.test_OudsTopAppBarTest$WithUntintedIcon_takeLightThemeSnapshot.png b/theme-orange/src/test/snapshots/images/com.orange.ouds.core.test_OudsTopAppBarTest$WithUntintedIcon_takeLightThemeSnapshot.png new file mode 100644 index 0000000000..c70235c9b9 Binary files /dev/null and b/theme-orange/src/test/snapshots/images/com.orange.ouds.core.test_OudsTopAppBarTest$WithUntintedIcon_takeLightThemeSnapshot.png differ diff --git a/theme-sosh/src/test/snapshots/images/com.orange.ouds.core.test_OudsTopAppBarTest$WithUntintedIcon_takeLightThemeSnapshot.png b/theme-sosh/src/test/snapshots/images/com.orange.ouds.core.test_OudsTopAppBarTest$WithUntintedIcon_takeLightThemeSnapshot.png new file mode 100644 index 0000000000..e3393442ce Binary files /dev/null and b/theme-sosh/src/test/snapshots/images/com.orange.ouds.core.test_OudsTopAppBarTest$WithUntintedIcon_takeLightThemeSnapshot.png differ diff --git a/theme-wireframe/src/test/snapshots/images/com.orange.ouds.core.test_OudsTopAppBarTest$WithUntintedIcon_takeLightThemeSnapshot.png b/theme-wireframe/src/test/snapshots/images/com.orange.ouds.core.test_OudsTopAppBarTest$WithUntintedIcon_takeLightThemeSnapshot.png new file mode 100644 index 0000000000..fc79a15ce5 Binary files /dev/null and b/theme-wireframe/src/test/snapshots/images/com.orange.ouds.core.test_OudsTopAppBarTest$WithUntintedIcon_takeLightThemeSnapshot.png differ