diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/components/SharpRippleButton.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/components/SharpRippleButton.kt index 9e15dd863..37cd66c8f 100644 --- a/app/src/main/java/com/infomaniak/swisstransfer/ui/components/SharpRippleButton.kt +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/components/SharpRippleButton.kt @@ -20,6 +20,7 @@ package com.infomaniak.swisstransfer.ui.components import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.selection.selectable import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.runtime.Composable @@ -29,10 +30,26 @@ import com.infomaniak.swisstransfer.ui.theme.CustomShapes import com.infomaniak.swisstransfer.ui.theme.SwissTransferTheme @Composable -fun SharpRippleButton(modifier: Modifier = Modifier, onClick: () -> Unit, content: @Composable RowScope.() -> Unit) { +fun SharpRippleButton( + modifier: Modifier = Modifier, + isSelected: () -> Boolean = { false }, + onClick: () -> Unit, + content: @Composable RowScope.() -> Unit, +) { + val colors = if (isSelected()) { + ButtonDefaults.textButtonColors( + contentColor = SwissTransferTheme.colors.primaryTextColor, + containerColor = SwissTransferTheme.colors.selectedSettingItem, + ) + } else { + ButtonDefaults.textButtonColors(contentColor = SwissTransferTheme.colors.primaryTextColor) + } Button( - modifier = modifier, - colors = ButtonDefaults.textButtonColors(contentColor = SwissTransferTheme.colors.primaryTextColor), + modifier = modifier.selectable( + selected = isSelected(), + onClick = onClick, + ), + colors = colors, shape = CustomShapes.None, onClick = onClick, content = content, diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/icons/app/Checkmark.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/icons/app/Checkmark.kt new file mode 100644 index 000000000..182d7eca8 --- /dev/null +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/icons/app/Checkmark.kt @@ -0,0 +1,61 @@ +package com.infomaniak.swisstransfer.ui.icons.app + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.size +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.PathFillType.Companion.NonZero +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.ImageVector.Builder +import androidx.compose.ui.graphics.vector.path +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.infomaniak.swisstransfer.ui.icons.AppIcons +import androidx.compose.ui.graphics.StrokeCap.Companion.Round as strokeCapRound +import androidx.compose.ui.graphics.StrokeJoin.Companion.Round as strokeJoinRound + +val AppIcons.Checkmark: ImageVector + get() { + if (_checkmark != null) { + return _checkmark!! + } + _checkmark = Builder( + name = "Checkmark", + defaultWidth = 16.0.dp, + defaultHeight = 16.0.dp, + viewportWidth = 16.0f, + viewportHeight = 16.0f + ).apply { + path( + fill = null, + stroke = SolidColor(Color(0xFF9F9F9F)), + strokeLineWidth = 2.0f, + strokeLineCap = strokeCapRound, + strokeLineJoin = strokeJoinRound, + strokeLineMiter = 4.0f, + pathFillType = NonZero + ) { + moveTo(15.0f, 3.0f) + lineTo(5.593f, 13.419f) + lineTo(1.0f, 9.085f) + } + }.build() + return _checkmark!! + } + +private var _checkmark: ImageVector? = null + +@Preview +@Composable +private fun Preview() { + Box { + Image( + imageVector = AppIcons.Checkmark, + contentDescription = null, + modifier = Modifier.size(AppIcons.previewSize) + ) + } +} diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/icons/app/Clock.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/icons/app/Clock.kt new file mode 100644 index 000000000..c0243b6cb --- /dev/null +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/icons/app/Clock.kt @@ -0,0 +1,64 @@ +package com.infomaniak.swisstransfer.ui.icons.app + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.size +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.PathFillType.Companion.NonZero +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.ImageVector.Builder +import androidx.compose.ui.graphics.vector.group +import androidx.compose.ui.graphics.vector.path +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.infomaniak.swisstransfer.ui.icons.AppIcons +import androidx.compose.ui.graphics.StrokeCap.Companion.Round as strokeCapRound +import androidx.compose.ui.graphics.StrokeJoin.Companion.Round as strokeJoinRound + +val AppIcons.Clock: ImageVector + get() { + if (_clock != null) { + return _clock!! + } + _clock = Builder( + name = "Clock", defaultWidth = 24.0.dp, defaultHeight = 24.0.dp, viewportWidth = 24.0f, viewportHeight = 24.0f + ).apply { + group { + path( + fill = null, + stroke = SolidColor(Color(0xFF9f9f9f)), + strokeLineWidth = 1.5f, + strokeLineCap = strokeCapRound, + strokeLineJoin = strokeJoinRound, + strokeLineMiter = 4.0f, + pathFillType = NonZero + ) { + moveTo(1.5f, 12.0f) + arcToRelative(10.5f, 10.5f, 0.0f, true, false, 21.0f, 0.0f) + arcToRelative(10.5f, 10.5f, 0.0f, false, false, -21.0f, 0.0f) + moveTo(12.0f, 12.0f) + verticalLineTo(8.25f) + moveTo(12.0f, 12.0f) + lineToRelative(4.687f, 4.688f) + } + } + }.build() + return _clock!! + } + +private var _clock: ImageVector? = null + +@Preview +@Composable +private fun Preview() { + Box { + Image( + imageVector = AppIcons.Clock, + contentDescription = null, + modifier = Modifier.size(AppIcons.previewSize) + ) + } +} diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/icons/app/FileBadgeArrowDown.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/icons/app/FileBadgeArrowDown.kt new file mode 100644 index 000000000..085b43d47 --- /dev/null +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/icons/app/FileBadgeArrowDown.kt @@ -0,0 +1,91 @@ +package com.infomaniak.swisstransfer.ui.icons.app + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.PathFillType.Companion.NonZero +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.ImageVector.Builder +import androidx.compose.ui.graphics.vector.group +import androidx.compose.ui.graphics.vector.path +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.infomaniak.swisstransfer.ui.icons.AppIcons +import androidx.compose.ui.graphics.StrokeCap.Companion.Round as strokeCapRound +import androidx.compose.ui.graphics.StrokeJoin.Companion.Round as strokeJoinRound + +val AppIcons.FileBadgeArrowDown: ImageVector + get() { + if (_fileBadgeArrowDown != null) { + return _fileBadgeArrowDown!! + } + _fileBadgeArrowDown = Builder( + name = "FileBadgeArrowDown", + defaultWidth = 24.0.dp, + defaultHeight = 24.0.dp, + viewportWidth = 24.0f, + viewportHeight = 24.0f + ).apply { + group { + path( + fill = null, + stroke = SolidColor(Color(0xFF9f9f9f)), + strokeLineWidth = 1.5f, + strokeLineCap = strokeCapRound, + strokeLineJoin = strokeJoinRound, + strokeLineMiter = 4.0f, + pathFillType = NonZero + ) { + moveTo(11.25f, 17.25f) + arcToRelative(6.0f, 6.0f, 0.0f, true, false, 12.0f, 0.0f) + arcToRelative(6.0f, 6.0f, 0.0f, false, false, -12.0f, 0.0f) + moveToRelative(6.0f, -3.0f) + verticalLineToRelative(6.0f) + moveToRelative(0.0f, 0.0f) + lineTo(15.0f, 18.0f) + moveToRelative(2.25f, 2.25f) + lineTo(19.5f, 18.0f) + } + path( + fill = null, + stroke = SolidColor(Color(0xFF9f9f9f)), + strokeLineWidth = 1.5f, + strokeLineCap = strokeCapRound, + strokeLineJoin = strokeJoinRound, + strokeLineMiter = 4.0f, + pathFillType = NonZero + ) { + moveTo(8.25f, 20.25f) + horizontalLineToRelative(-6.0f) + arcToRelative(1.5f, 1.5f, 0.0f, false, true, -1.5f, -1.5f) + verticalLineTo(2.25f) + arcToRelative(1.5f, 1.5f, 0.0f, false, true, 1.5f, -1.5f) + horizontalLineToRelative(10.629f) + arcToRelative(1.5f, 1.5f, 0.0f, false, true, 1.06f, 0.439f) + lineToRelative(2.872f, 2.872f) + arcToRelative(1.5f, 1.5f, 0.0f, false, true, 0.439f, 1.06f) + verticalLineTo(8.25f) + } + } + }.build() + return _fileBadgeArrowDown!! + } + +private var _fileBadgeArrowDown: ImageVector? = null + +@Preview +@Composable +private fun Preview() { + Box { + Image( + imageVector = AppIcons.FileBadgeArrowDown, + contentDescription = null, + modifier = Modifier.size(AppIcons.previewSize) + ) + } +} diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/icons/app/PaintbrushPalette.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/icons/app/PaintbrushPalette.kt new file mode 100644 index 000000000..bade550ec --- /dev/null +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/icons/app/PaintbrushPalette.kt @@ -0,0 +1,97 @@ +package com.infomaniak.swisstransfer.ui.icons.app + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.size +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.PathFillType.Companion.NonZero +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.ImageVector.Builder +import androidx.compose.ui.graphics.vector.group +import androidx.compose.ui.graphics.vector.path +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.infomaniak.swisstransfer.ui.icons.AppIcons +import androidx.compose.ui.graphics.StrokeCap.Companion.Round as strokeCapRound +import androidx.compose.ui.graphics.StrokeJoin.Companion.Round as strokeJoinRound + +val AppIcons.PaintbrushPalette: ImageVector + get() { + if (_paintbrushPalette != null) { + return _paintbrushPalette!! + } + _paintbrushPalette = Builder( + name = "PaintbrushPalette", + defaultWidth = 24.0.dp, + defaultHeight = 24.0.dp, + viewportWidth = 24.0f, + viewportHeight = 24.0f + ).apply { + group { + path( + fill = null, + stroke = SolidColor(Color(0xFF9f9f9f)), + strokeLineWidth = 1.5f, + strokeLineCap = strokeCapRound, + strokeLineJoin = strokeJoinRound, + strokeLineMiter = 4.0f, + pathFillType = NonZero + ) { + moveTo(10.644f, 17.865f) + curveToRelative(1.589f, -1.9f, 0.1f, -4.338f, 1.513f, -5.981f) + arcToRelative(2.759f, 2.759f, 0.0f, true, true, 4.18f, 3.6f) + arcToRelative(6.5f, 6.5f, 0.0f, false, true, -5.693f, 2.38f) + } + path( + fill = null, + stroke = SolidColor(Color(0xFF9f9f9f)), + strokeLineWidth = 1.5f, + strokeLineCap = strokeCapRound, + strokeLineJoin = strokeJoinRound, + strokeLineMiter = 4.0f, + pathFillType = NonZero + ) { + moveTo(16.62f, 12.277f) + lineToRelative(6.232f, -8.253f) + arcTo(2.037f, 2.037f, 0.0f, true, false, 19.4f, 1.88f) + lineTo(14.871f, 11.0f) + } + path( + fill = null, + stroke = SolidColor(Color(0xFF9f9f9f)), + strokeLineWidth = 1.5f, + strokeLineCap = strokeCapRound, + strokeLineJoin = strokeJoinRound, + strokeLineMiter = 4.0f, + pathFillType = NonZero + ) { + moveTo(13.5f, 5.225f) + horizontalLineTo(3.75f) + arcToRelative(3.0f, 3.0f, 0.0f, false, false, -3.0f, 3.0f) + verticalLineToRelative(12.0f) + arcToRelative(3.0f, 3.0f, 0.0f, false, false, 3.0f, 3.0f) + horizontalLineToRelative(12.0f) + arcToRelative(3.0f, 3.0f, 0.0f, false, false, 3.0f, -3.0f) + verticalLineToRelative(-1.5f) + } + } + }.build() + return _paintbrushPalette!! + } + +private var _paintbrushPalette: ImageVector? = null + +@Preview +@Composable +private fun Preview() { + Box { + Image( + imageVector = AppIcons.PaintbrushPalette, + contentDescription = null, + modifier = Modifier.size(AppIcons.previewSize) + ) + } +} diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/SettingsScreen.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/SettingsScreen.kt index 1833ca783..f8fefb3be 100644 --- a/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/SettingsScreen.kt +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/SettingsScreen.kt @@ -23,6 +23,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.selection.selectableGroup import androidx.compose.foundation.verticalScroll import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface @@ -32,16 +33,20 @@ import androidx.compose.material3.adaptive.WindowAdaptiveInfo import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import com.infomaniak.swisstransfer.R -import com.infomaniak.swisstransfer.ui.components.* +import com.infomaniak.swisstransfer.ui.components.TwoPaneScaffold import com.infomaniak.swisstransfer.ui.icons.AppIcons -import com.infomaniak.swisstransfer.ui.icons.app.Add -import com.infomaniak.swisstransfer.ui.icons.app.Bell -import com.infomaniak.swisstransfer.ui.icons.app.SpeechBubble -import com.infomaniak.swisstransfer.ui.screen.main.settings.components.EndIconType +import com.infomaniak.swisstransfer.ui.icons.app.* +import com.infomaniak.swisstransfer.ui.screen.main.settings.SettingsOptionScreens.* +import com.infomaniak.swisstransfer.ui.screen.main.settings.components.EndIconType.CHEVRON +import com.infomaniak.swisstransfer.ui.screen.main.settings.components.EndIconType.OPEN_OUTSIDE import com.infomaniak.swisstransfer.ui.screen.main.settings.components.SettingDivider import com.infomaniak.swisstransfer.ui.screen.main.settings.components.SettingItem import com.infomaniak.swisstransfer.ui.screen.main.settings.components.SettingTitle @@ -55,7 +60,7 @@ import com.infomaniak.swisstransfer.ui.utils.PreviewTablet fun SettingsScreenWrapper( windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo(), ) { - TwoPaneScaffold( // TODO: Replace Any with item type + TwoPaneScaffold( windowAdaptiveInfo, listPane = { SettingsScreen( @@ -63,24 +68,39 @@ fun SettingsScreenWrapper( // Navigate to the detail pane with the passed item navigateTo(ListDetailPaneScaffoldRole.Detail, item) }, + getSelectedSetting = { currentDestination?.content }, ) }, detailPane = { - // Show the detail pane content if selected item is available - if (currentDestination?.content == null) { - Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { - Text("Select a setting item", color = SwissTransferTheme.colors.secondaryTextColor) - } - } else { - Text("Show selected item") + var lastSelectedScreen by rememberSaveable { mutableStateOf(null) } + + val destination = currentDestination?.content ?: lastSelectedScreen + currentDestination?.content?.let { lastSelectedScreen = it } + + when (destination) { + THEME -> SettingsThemeScreen() + NOTIFICATIONS -> {} + VALIDITY_PERIOD -> {} + DOWNLOAD_LIMIT -> {} + EMAIL_LANGUAGE -> {} + DISCOVER_INFOMANIAK -> {} + SHARE_IDEAS -> {} + GIVE_FEEDBACK -> {} + null -> NoSelectionEmptyState() } } ) } @Composable -private fun SettingsScreen(onItemClick: (Any) -> Unit) { - Column(modifier = Modifier.verticalScroll(rememberScrollState())) { +private fun SettingsScreen(onItemClick: (SettingsOptionScreens) -> Unit, getSelectedSetting: () -> SettingsOptionScreens?) { + val selectedSetting = getSelectedSetting() + + Column( + modifier = Modifier + .verticalScroll(rememberScrollState()) + .selectableGroup(), + ) { Text( modifier = Modifier.padding(horizontal = Margin.Medium, vertical = Margin.Large), text = stringResource(R.string.settingsTitle), @@ -88,29 +108,86 @@ private fun SettingsScreen(onItemClick: (Any) -> Unit) { ) SettingTitle(R.string.settingsCategoryGeneral) - // TODO: Use correct icon - SettingItem(R.string.settingsOptionTheme, AppIcons.Add, "TODO", EndIconType.CHEVRON) {} - SettingItem(R.string.settingsOptionNotifications, AppIcons.Bell, "TODO", endIcon = EndIconType.OPEN_OUTSIDE) {} + SettingItem( + titleRes = R.string.settingsOptionTheme, + isSelected = { selectedSetting == THEME }, + icon = AppIcons.PaintbrushPalette, + description = "TODO", + CHEVRON + ) { + onItemClick(THEME) + } + SettingItem( + titleRes = R.string.settingsOptionNotifications, + isSelected = { selectedSetting == NOTIFICATIONS }, + icon = AppIcons.Bell, + description = "TODO", + endIcon = OPEN_OUTSIDE, + ) { + onItemClick(NOTIFICATIONS) + } SettingDivider() SettingTitle(R.string.settingsCategoryDefaultSettings) - // TODO: Use correct icon - SettingItem(R.string.settingsOptionValidityPeriod, AppIcons.Add, "TODO", endIcon = EndIconType.CHEVRON) {} - // TODO: Use correct icon - SettingItem(R.string.settingsOptionDownloadLimit, AppIcons.Add, "TODO", endIcon = EndIconType.CHEVRON) {} - SettingItem(R.string.settingsOptionEmailLanguage, AppIcons.SpeechBubble, "TODO", endIcon = EndIconType.CHEVRON) {} + SettingItem( + titleRes = R.string.settingsOptionValidityPeriod, + isSelected = { selectedSetting == VALIDITY_PERIOD }, + icon = AppIcons.FileBadgeArrowDown, + description = "TODO", + endIcon = CHEVRON, + ) { + onItemClick(VALIDITY_PERIOD) + } + SettingItem( + titleRes = R.string.settingsOptionDownloadLimit, + isSelected = { selectedSetting == DOWNLOAD_LIMIT }, + icon = AppIcons.Clock, + description = "TODO", + endIcon = CHEVRON, + ) { + onItemClick(DOWNLOAD_LIMIT) + } + SettingItem( + titleRes = R.string.settingsOptionEmailLanguage, + isSelected = { selectedSetting == EMAIL_LANGUAGE }, + icon = AppIcons.SpeechBubble, + description = "TODO", + endIcon = CHEVRON, + ) { + onItemClick(EMAIL_LANGUAGE) + } SettingDivider() SettingTitle(R.string.settingsCategoryAbout) - SettingItem(R.string.settingsOptionDiscoverInfomaniak, endIcon = EndIconType.OPEN_OUTSIDE) {} - SettingItem(R.string.settingsOptionShareIdeas, endIcon = EndIconType.OPEN_OUTSIDE) {} - SettingItem(R.string.settingsOptionGiveFeedback, endIcon = EndIconType.OPEN_OUTSIDE) {} - SettingItem(R.string.version, description = "0.0.1", onClick = null) + SettingItem(R.string.settingsOptionDiscoverInfomaniak, { selectedSetting == DISCOVER_INFOMANIAK }, endIcon = OPEN_OUTSIDE) { + onItemClick(DISCOVER_INFOMANIAK) + } + SettingItem(R.string.settingsOptionShareIdeas, { selectedSetting == SHARE_IDEAS }, endIcon = OPEN_OUTSIDE) { + onItemClick(SHARE_IDEAS) + } + SettingItem(R.string.settingsOptionGiveFeedback, { selectedSetting == GIVE_FEEDBACK }, endIcon = OPEN_OUTSIDE) { + onItemClick(GIVE_FEEDBACK) + } + SettingItem(R.string.version, isSelected = { false }, description = "0.0.1", onClick = null) + } +} + +// Show the detail pane content if selected item is available +@Composable +private fun NoSelectionEmptyState() { + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + Text("Select a setting item", color = SwissTransferTheme.colors.secondaryTextColor) } } +enum class SettingsOptionScreens { + THEME, NOTIFICATIONS, + VALIDITY_PERIOD, DOWNLOAD_LIMIT, EMAIL_LANGUAGE, + DISCOVER_INFOMANIAK, SHARE_IDEAS, GIVE_FEEDBACK, +} + @PreviewMobile @PreviewTablet @Composable diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/SettingsThemeScreen.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/SettingsThemeScreen.kt new file mode 100644 index 000000000..c994f2082 --- /dev/null +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/SettingsThemeScreen.kt @@ -0,0 +1,67 @@ +/* + * Infomaniak SwissTransfer - Android + * Copyright (C) 2024 Infomaniak Network SA + * + * 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 . + */ + +package com.infomaniak.swisstransfer.ui.screen.main.settings + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import com.infomaniak.swisstransfer.R +import com.infomaniak.swisstransfer.ui.icons.AppIcons +import com.infomaniak.swisstransfer.ui.icons.app.Add +import com.infomaniak.swisstransfer.ui.screen.main.settings.components.SettingOption +import com.infomaniak.swisstransfer.ui.screen.main.settings.components.SettingTitle +import com.infomaniak.swisstransfer.ui.screen.main.settings.components.SingleSelectOptions +import com.infomaniak.swisstransfer.ui.theme.SwissTransferTheme +import com.infomaniak.swisstransfer.ui.utils.PreviewMobile +import com.infomaniak.swisstransfer.ui.utils.PreviewTablet + +@Composable +fun SettingsThemeScreen() { + Column(modifier = Modifier.verticalScroll(rememberScrollState())) { + SettingTitle(titleRes = R.string.appName) + + var selectedItem by rememberSaveable { mutableIntStateOf(0) } // TODO: Use DataStore or Realm + SingleSelectOptions(ThemeOption.entries, { selectedItem }, { selectedItem = it }) + } +} + +enum class ThemeOption(override val title: Int, override val icon: ImageVector) : SettingOption { + SYSTEM(R.string.appName, AppIcons.Add), + LIGHT(R.string.appName, AppIcons.Add), + DARK(R.string.appName, AppIcons.Add), +} + +@PreviewMobile +@PreviewTablet +@Composable +private fun SettingsThemeScreenPreview() { + SwissTransferTheme { + Surface { + SettingsThemeScreen() + } + } +} diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/components/SettingItem.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/components/SettingItem.kt index 3865cffec..80ecb6bb1 100644 --- a/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/components/SettingItem.kt +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/components/SettingItem.kt @@ -18,8 +18,10 @@ package com.infomaniak.swisstransfer.ui.screen.main.settings.components +import android.content.res.Configuration import androidx.annotation.StringRes import androidx.compose.foundation.layout.* +import androidx.compose.foundation.selection.selectableGroup import androidx.compose.material3.Icon import androidx.compose.material3.Surface import androidx.compose.material3.Text @@ -46,6 +48,7 @@ private val ITEM_MIN_HEIGHT = 56.dp @Composable fun SettingItem( @StringRes titleRes: Int, + isSelected: () -> Boolean, icon: ImageVector? = null, description: String? = null, endIcon: EndIconType? = null, @@ -58,6 +61,7 @@ fun SettingItem( onClick?.let { SharpRippleButton( modifier = modifier, + isSelected = isSelected, onClick = it, ) { SettingItemContent(icon, titleRes, description, endIcon) @@ -107,22 +111,23 @@ private fun SettingItemContent( } } -@Preview +@Preview(name = "Light") +@Preview(name = "Dark", uiMode = Configuration.UI_MODE_NIGHT_YES or Configuration.UI_MODE_TYPE_NORMAL) @Composable private fun SettingItemPreview() { SwissTransferTheme { Surface { - Column { + Column(modifier = Modifier.selectableGroup()) { SettingTitle(R.string.appName) - SettingItem(R.string.appName, AppIcons.Add, "Clair", EndIconType.CHEVRON) {} - SettingItem(R.string.appName, AppIcons.Folder, endIcon = EndIconType.OPEN_OUTSIDE) {} - SettingItem(R.string.appName, description = "1.1.2") {} - SettingItem(R.string.appName) {} + SettingItem(R.string.appName, { true }, AppIcons.Add, "Clair", EndIconType.CHEVRON) {} + SettingItem(R.string.appName, { false }, AppIcons.Folder, endIcon = EndIconType.OPEN_OUTSIDE) {} + SettingItem(R.string.appName, { false }, description = "1.1.2") {} + SettingItem(R.string.appName, { false }) {} SettingDivider() SettingTitle(R.string.appName) - SettingItem(R.string.appName, endIcon = EndIconType.OPEN_OUTSIDE) {} - SettingItem(R.string.appName, endIcon = EndIconType.OPEN_OUTSIDE) {} - SettingItem(R.string.appName, description = "0.0.1", onClick = null) + SettingItem(R.string.appName, { false }, endIcon = EndIconType.OPEN_OUTSIDE) {} + SettingItem(R.string.appName, { false }, endIcon = EndIconType.OPEN_OUTSIDE) {} + SettingItem(R.string.appName, { false }, description = "0.0.1", onClick = null) } } } diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/components/SingleSelectOptions.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/components/SingleSelectOptions.kt new file mode 100644 index 000000000..0b6f0005e --- /dev/null +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/components/SingleSelectOptions.kt @@ -0,0 +1,114 @@ +/* + * Infomaniak SwissTransfer - Android + * Copyright (C) 2024 Infomaniak Network SA + * + * 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 . + */ + +package com.infomaniak.swisstransfer.ui.screen.main.settings.components + +import android.content.res.Configuration +import androidx.annotation.StringRes +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.scaleIn +import androidx.compose.animation.scaleOut +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.selection.selectable +import androidx.compose.foundation.selection.selectableGroup +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import com.infomaniak.swisstransfer.R +import com.infomaniak.swisstransfer.ui.components.SharpRippleButton +import com.infomaniak.swisstransfer.ui.icons.AppIcons +import com.infomaniak.swisstransfer.ui.icons.app.Add +import com.infomaniak.swisstransfer.ui.icons.app.Checkmark +import com.infomaniak.swisstransfer.ui.theme.Margin +import com.infomaniak.swisstransfer.ui.theme.SwissTransferTheme + +@Composable +fun SingleSelectOptions(items: List, selectedItem: () -> Int, setSelectedItem: (Int) -> Unit) { + Column(Modifier.selectableGroup()) { + items.forEachIndexed { index, item -> + if (index > 0) HorizontalDivider(Modifier.padding(horizontal = Margin.Medium)) + SettingOptionItem(item, isSelected = selectedItem() == index) { setSelectedItem(index) } + } + } +} + +@Composable +private fun SettingOptionItem(item: SettingOption, isSelected: Boolean, onClick: () -> Unit) { + SharpRippleButton( + modifier = Modifier.selectable(selected = isSelected, onClick = onClick), + onClick = onClick + ) { + Row( + Modifier + .fillMaxWidth() + .padding(horizontal = Margin.Medium, vertical = Margin.Medium), + verticalAlignment = Alignment.CenterVertically, + ) { + item.icon?.let { + Icon(imageVector = it, contentDescription = null) + Spacer(modifier = Modifier.width(Margin.Medium)) + } + + Text(text = stringResource(id = item.title), Modifier.weight(1f)) + + if (isSelected) Spacer(modifier = Modifier.width(Margin.Medium)) + AnimatedVisibility( + visible = isSelected, + enter = scaleIn(), + exit = scaleOut(), + ) { + Icon( + imageVector = AppIcons.Checkmark, + contentDescription = null, + tint = SwissTransferTheme.materialColors.primary, + ) + } + } + } +} + +interface SettingOption { + @get:StringRes + val title: Int + val icon: ImageVector? +} + +@Preview(name = "Light") +@Preview(name = "Dark", uiMode = Configuration.UI_MODE_NIGHT_YES or Configuration.UI_MODE_TYPE_NORMAL) +@Composable +private fun SettingOptionItemPreview() { + SwissTransferTheme { + Surface { + Column { + val item = object : SettingOption { + override val title: Int = R.string.appName + override val icon: ImageVector = AppIcons.Add + } + SettingOptionItem(item, true) {} + SettingOptionItem(item, false) {} + } + } + } +} diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/theme/ColorDark.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/theme/ColorDark.kt index 685d1e85a..342a7c3e4 100644 --- a/app/src/main/java/com/infomaniak/swisstransfer/ui/theme/ColorDark.kt +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/theme/ColorDark.kt @@ -69,4 +69,5 @@ val CustomDarkColorScheme = CustomColorScheme( iconColor = Color(shark), navigationItemBackground = Color(dark2), tertiaryButtonBackground = Color(dark2), + selectedSettingItem = Color(dark2), ) diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/theme/ColorLight.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/theme/ColorLight.kt index 51f77945f..80c2bef8c 100644 --- a/app/src/main/java/com/infomaniak/swisstransfer/ui/theme/ColorLight.kt +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/theme/ColorLight.kt @@ -73,4 +73,5 @@ val CustomLightColorScheme = CustomColorScheme( iconColor = Color(elephant), navigationItemBackground = LightColorScheme.background, tertiaryButtonBackground = Color(rabbit), + selectedSettingItem = Color(rabbit), ) diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/theme/Theme.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/theme/Theme.kt index 832c5ec04..def44f51c 100644 --- a/app/src/main/java/com/infomaniak/swisstransfer/ui/theme/Theme.kt +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/theme/Theme.kt @@ -67,4 +67,5 @@ data class CustomColorScheme( val iconColor: Color = Color.Unspecified, val navigationItemBackground: Color = Color.Unspecified, val tertiaryButtonBackground: Color = Color.Unspecified, + val selectedSettingItem: Color = Color.Unspecified, )