feat(ui): implement separate settings pages

This commit is contained in:
Nishant Mishra
2025-10-22 13:10:00 +05:30
parent 3818567af0
commit 5849a2cde4
5 changed files with 787 additions and 7 deletions

View File

@@ -1,8 +1,18 @@
/*
* Copyright (c) 2025 Nishant Mishra
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
* This file is part of Tomato - a minimalist pomodoro timer for Android.
*
* Tomato 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.
*
* Tomato 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 Tomato.
* If not, see <https://www.gnu.org/licenses/>.
*/
package org.nsh07.pomodoro.ui.settingsScreen
@@ -36,7 +46,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import org.nsh07.pomodoro.BuildConfig
import org.nsh07.pomodoro.R
import org.nsh07.pomodoro.ui.theme.AppFonts.robotoFlexHeadline
import org.nsh07.pomodoro.ui.theme.AppFonts.robotoFlexTopBar
// Taken from https://github.com/shub39/Grit/blob/master/app/src/main/java/com/shub39/grit/core/presentation/settings/ui/component/AboutApp.kt
@@ -71,10 +80,7 @@ fun AboutCard(modifier: Modifier = Modifier) {
style = MaterialTheme.typography.titleLarge,
fontFamily = robotoFlexTopBar
)
Text(
text = "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})",
fontFamily = robotoFlexHeadline
)
Text(text = "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})")
}
Spacer(modifier = Modifier.weight(1f))

View File

@@ -0,0 +1,236 @@
/*
* Copyright (c) 2025 Nishant Mishra
*
* This file is part of Tomato - a minimalist pomodoro timer for Android.
*
* Tomato 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.
*
* Tomato 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 Tomato.
* If not, see <https://www.gnu.org/licenses/>.
*/
package org.nsh07.pomodoro.ui.settingsScreen
import android.app.Activity
import android.content.Intent
import android.media.RingtoneManager
import android.net.Uri
import android.os.Build
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LargeFlexibleTopAppBar
import androidx.compose.material3.ListItem
import androidx.compose.material3.Switch
import androidx.compose.material3.SwitchDefaults
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.nsh07.pomodoro.R
import org.nsh07.pomodoro.ui.settingsScreen.viewModel.PreferencesState
import org.nsh07.pomodoro.ui.theme.AppFonts.robotoFlexTopBar
import org.nsh07.pomodoro.ui.theme.CustomColors.listItemColors
import org.nsh07.pomodoro.ui.theme.CustomColors.switchColors
import org.nsh07.pomodoro.ui.theme.CustomColors.topBarColors
import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.bottomListItemShape
import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.middleListItemShape
import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.topListItemShape
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun AlarmSettings(
preferencesState: PreferencesState,
alarmEnabled: Boolean,
vibrateEnabled: Boolean,
alarmSound: String,
onAlarmEnabledChange: (Boolean) -> Unit,
onVibrateEnabledChange: (Boolean) -> Unit,
onAlarmSoundChanged: (Uri?) -> Unit,
onBack: () -> Unit,
modifier: Modifier = Modifier
) {
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
val context = LocalContext.current
var alarmName by remember { mutableStateOf("...") }
LaunchedEffect(alarmSound) {
withContext(Dispatchers.IO) {
alarmName =
RingtoneManager.getRingtone(context, alarmSound.toUri())?.getTitle(context) ?: ""
}
}
val ringtonePickerLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK) {
val uri =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
result.data?.getParcelableExtra(
RingtoneManager.EXTRA_RINGTONE_PICKED_URI,
Uri::class.java
)
} else {
@Suppress("DEPRECATION")
result.data?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI)
}
onAlarmSoundChanged(uri)
}
}
val ringtonePickerIntent = remember(alarmSound) {
Intent(RingtoneManager.ACTION_RINGTONE_PICKER).apply {
putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_ALARM)
putExtra(RingtoneManager.EXTRA_RINGTONE_TITLE, context.getString(R.string.alarm_sound))
putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, alarmSound.toUri())
}
}
val switchItems = remember(
preferencesState.blackTheme,
preferencesState.aodEnabled,
alarmEnabled,
vibrateEnabled
) {
listOf(
SettingsSwitchItem(
checked = alarmEnabled,
icon = R.drawable.alarm_on,
label = R.string.alarm,
description = R.string.alarm_desc,
onClick = onAlarmEnabledChange
),
SettingsSwitchItem(
checked = vibrateEnabled,
icon = R.drawable.mobile_vibrate,
label = R.string.vibrate,
description = R.string.vibrate_desc,
onClick = onVibrateEnabledChange
)
)
}
Column(modifier.nestedScroll(scrollBehavior.nestedScrollConnection)) {
LargeFlexibleTopAppBar(
title = {
Text("Alarm", fontFamily = robotoFlexTopBar)
},
subtitle = {
Text("Settings")
},
navigationIcon = {
IconButton(onBack) {
Icon(
painterResource(R.drawable.arrow_back),
null
)
}
},
colors = topBarColors,
scrollBehavior = scrollBehavior
)
LazyColumn(
verticalArrangement = Arrangement.spacedBy(2.dp),
modifier = Modifier
.background(topBarColors.containerColor)
.fillMaxSize()
.padding(horizontal = 16.dp)
) {
item {
Spacer(Modifier.height(14.dp))
}
item {
ListItem(
leadingContent = {
Icon(painterResource(R.drawable.alarm), null)
},
headlineContent = { Text(stringResource(R.string.alarm_sound)) },
supportingContent = { Text(alarmName) },
colors = listItemColors,
modifier = Modifier
.clip(topListItemShape)
.clickable(onClick = { ringtonePickerLauncher.launch(ringtonePickerIntent) })
)
}
itemsIndexed(switchItems) { index, item ->
ListItem(
leadingContent = {
Icon(painterResource(item.icon), contentDescription = null)
},
headlineContent = { Text(stringResource(item.label)) },
supportingContent = { Text(stringResource(item.description)) },
trailingContent = {
Switch(
checked = item.checked,
onCheckedChange = { item.onClick(it) },
thumbContent = {
if (item.checked) {
Icon(
painter = painterResource(R.drawable.check),
contentDescription = null,
modifier = Modifier.size(SwitchDefaults.IconSize),
)
} else {
Icon(
painter = painterResource(R.drawable.clear),
contentDescription = null,
modifier = Modifier.size(SwitchDefaults.IconSize),
)
}
},
colors = switchColors
)
},
colors = listItemColors,
modifier = Modifier
.clip(
when (index) {
switchItems.lastIndex -> bottomListItemShape
else -> middleListItemShape
}
)
)
}
item { Spacer(Modifier.height(12.dp)) }
}
}
}

View File

@@ -0,0 +1,199 @@
/*
* Copyright (c) 2025 Nishant Mishra
*
* This file is part of Tomato - a minimalist pomodoro timer for Android.
*
* Tomato 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.
*
* Tomato 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 Tomato.
* If not, see <https://www.gnu.org/licenses/>.
*/
package org.nsh07.pomodoro.ui.settingsScreen
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LargeFlexibleTopAppBar
import androidx.compose.material3.ListItem
import androidx.compose.material3.Switch
import androidx.compose.material3.SwitchDefaults
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.nsh07.pomodoro.R
import org.nsh07.pomodoro.ui.settingsScreen.viewModel.PreferencesState
import org.nsh07.pomodoro.ui.theme.AppFonts.robotoFlexTopBar
import org.nsh07.pomodoro.ui.theme.CustomColors.listItemColors
import org.nsh07.pomodoro.ui.theme.CustomColors.switchColors
import org.nsh07.pomodoro.ui.theme.CustomColors.topBarColors
import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.bottomListItemShape
import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.middleListItemShape
import org.nsh07.pomodoro.utils.toColor
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun AppearanceSettings(
preferencesState: PreferencesState,
onBlackThemeChange: (Boolean) -> Unit,
onThemeChange: (String) -> Unit,
onColorSchemeChange: (Color) -> Unit,
onBack: () -> Unit,
modifier: Modifier = Modifier
) {
val context = LocalContext.current
val themeMap: Map<String, Pair<Int, Int>> = remember {
mapOf(
"auto" to Pair(
R.drawable.brightness_auto,
R.string.system_default
),
"light" to Pair(R.drawable.light_mode, R.string.light),
"dark" to Pair(R.drawable.dark_mode, R.string.dark)
)
}
val reverseThemeMap: Map<String, String> = remember {
mapOf(
context.getString(R.string.system_default) to "auto",
context.getString(R.string.light) to "light",
context.getString(R.string.dark) to "dark"
)
}
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
Column(modifier.nestedScroll(scrollBehavior.nestedScrollConnection)) {
LargeFlexibleTopAppBar(
title = {
Text("Appearance", fontFamily = robotoFlexTopBar)
},
subtitle = {
Text("Settings")
},
navigationIcon = {
IconButton(onBack) {
Icon(
painterResource(R.drawable.arrow_back),
null
)
}
},
colors = topBarColors,
scrollBehavior = scrollBehavior
)
LazyColumn(
verticalArrangement = Arrangement.spacedBy(2.dp),
modifier = Modifier
.background(topBarColors.containerColor)
.fillMaxSize()
.padding(horizontal = 16.dp)
) {
item {
Spacer(Modifier.height(14.dp))
}
item {
ColorSchemePickerListItem(
color = preferencesState.colorScheme.toColor(),
items = 3,
index = 0,
onColorChange = onColorSchemeChange
)
}
item {
ThemePickerListItem(
theme = preferencesState.theme,
themeMap = themeMap,
reverseThemeMap = reverseThemeMap,
onThemeChange = onThemeChange,
items = 3,
index = 1,
modifier = Modifier
.clip(middleListItemShape)
)
}
item {
val item = SettingsSwitchItem(
checked = preferencesState.blackTheme,
icon = R.drawable.contrast,
label = R.string.black_theme,
description = R.string.black_theme_desc,
onClick = onBlackThemeChange
)
ListItem(
leadingContent = {
Icon(painterResource(item.icon), contentDescription = null)
},
headlineContent = { Text(stringResource(item.label)) },
supportingContent = { Text(stringResource(item.description)) },
trailingContent = {
Switch(
checked = item.checked,
onCheckedChange = { item.onClick(it) },
thumbContent = {
if (item.checked) {
Icon(
painter = painterResource(R.drawable.check),
contentDescription = null,
modifier = Modifier.size(SwitchDefaults.IconSize),
)
} else {
Icon(
painter = painterResource(R.drawable.clear),
contentDescription = null,
modifier = Modifier.size(SwitchDefaults.IconSize),
)
}
},
colors = switchColors
)
},
colors = listItemColors,
modifier = Modifier.clip(bottomListItemShape)
)
}
item { Spacer(Modifier.height(12.dp)) }
}
}
}
@Preview
@Composable
fun AppearanceSettingsPreview() {
val preferencesState = PreferencesState()
AppearanceSettings(
preferencesState = preferencesState,
onBlackThemeChange = {},
onThemeChange = {},
onColorSchemeChange = {},
onBack = {}
)
}

View File

@@ -0,0 +1,315 @@
/*
* Copyright (c) 2025 Nishant Mishra
*
* This file is part of Tomato - a minimalist pomodoro timer for Android.
*
* Tomato 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.
*
* Tomato 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 Tomato.
* If not, see <https://www.gnu.org/licenses/>.
*/
package org.nsh07.pomodoro.ui.settingsScreen
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.background
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.FilledTonalIconToggleButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.LargeFlexibleTopAppBar
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.Slider
import androidx.compose.material3.SliderState
import androidx.compose.material3.Switch
import androidx.compose.material3.SwitchDefaults
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.nsh07.pomodoro.R
import org.nsh07.pomodoro.ui.theme.AppFonts.robotoFlexTopBar
import org.nsh07.pomodoro.ui.theme.CustomColors.listItemColors
import org.nsh07.pomodoro.ui.theme.CustomColors.switchColors
import org.nsh07.pomodoro.ui.theme.CustomColors.topBarColors
import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.bottomListItemShape
import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.cardShape
import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.middleListItemShape
import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.topListItemShape
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun TimerSettings(
aodEnabled: Boolean,
focusTimeInputFieldState: TextFieldState,
shortBreakTimeInputFieldState: TextFieldState,
longBreakTimeInputFieldState: TextFieldState,
sessionsSliderState: SliderState,
onAodEnabledChange: (Boolean) -> Unit,
onBack: () -> Unit,
modifier: Modifier = Modifier
) {
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
Column(modifier.nestedScroll(scrollBehavior.nestedScrollConnection)) {
LargeFlexibleTopAppBar(
title = {
Text("Timer", fontFamily = robotoFlexTopBar)
},
subtitle = {
Text("Settings")
},
navigationIcon = {
IconButton(onBack) {
Icon(
painterResource(R.drawable.arrow_back),
null
)
}
},
colors = topBarColors,
scrollBehavior = scrollBehavior
)
LazyColumn(
verticalArrangement = Arrangement.spacedBy(2.dp),
modifier = Modifier
.background(topBarColors.containerColor)
.fillMaxSize()
.padding(horizontal = 16.dp)
) {
item {
Spacer(Modifier.height(14.dp))
}
item {
Row(
horizontalArrangement = Arrangement.Center,
modifier = Modifier
.fillMaxWidth()
.horizontalScroll(rememberScrollState())
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(2.dp)
) {
Text(
stringResource(R.string.focus),
style = typography.titleSmallEmphasized
)
MinuteInputField(
state = focusTimeInputFieldState,
shape = RoundedCornerShape(
topStart = topListItemShape.topStart,
bottomStart = topListItemShape.topStart,
topEnd = topListItemShape.bottomStart,
bottomEnd = topListItemShape.bottomStart
),
imeAction = ImeAction.Next
)
}
Spacer(Modifier.width(2.dp))
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(2.dp)
) {
Text(
stringResource(R.string.short_break),
style = typography.titleSmallEmphasized
)
MinuteInputField(
state = shortBreakTimeInputFieldState,
shape = RoundedCornerShape(middleListItemShape.topStart),
imeAction = ImeAction.Next
)
}
Spacer(Modifier.width(2.dp))
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(2.dp)
) {
Text(
stringResource(R.string.long_break),
style = typography.titleSmallEmphasized
)
MinuteInputField(
state = longBreakTimeInputFieldState,
shape = RoundedCornerShape(
topStart = bottomListItemShape.topStart,
bottomStart = bottomListItemShape.topStart,
topEnd = bottomListItemShape.bottomStart,
bottomEnd = bottomListItemShape.bottomStart
),
imeAction = ImeAction.Done
)
}
}
}
item {
Spacer(Modifier.height(12.dp))
}
item {
ListItem(
leadingContent = {
Icon(painterResource(R.drawable.clocks), null)
},
headlineContent = {
Text(stringResource(R.string.session_length))
},
supportingContent = {
Column {
Text(
stringResource(
R.string.session_length_desc,
sessionsSliderState.value.toInt()
)
)
Slider(
state = sessionsSliderState,
modifier = Modifier.padding(vertical = 4.dp)
)
}
},
colors = listItemColors,
modifier = Modifier.clip(cardShape)
)
}
item { Spacer(Modifier.height(12.dp)) }
item {
val item = SettingsSwitchItem(
checked = aodEnabled,
icon = R.drawable.aod,
label = R.string.always_on_display,
description = R.string.always_on_display_desc,
onClick = onAodEnabledChange
)
ListItem(
leadingContent = {
Icon(
painterResource(item.icon),
contentDescription = null,
modifier = Modifier.padding(top = 4.dp)
)
},
headlineContent = { Text(stringResource(item.label)) },
supportingContent = { Text(stringResource(item.description)) },
trailingContent = {
Switch(
checked = item.checked,
onCheckedChange = { item.onClick(it) },
thumbContent = {
if (item.checked) {
Icon(
painter = painterResource(R.drawable.check),
contentDescription = null,
modifier = Modifier.size(SwitchDefaults.IconSize),
)
} else {
Icon(
painter = painterResource(R.drawable.clear),
contentDescription = null,
modifier = Modifier.size(SwitchDefaults.IconSize),
)
}
},
colors = switchColors
)
},
colors = listItemColors,
modifier = Modifier.clip(cardShape)
)
}
item {
var expanded by remember { mutableStateOf(false) }
Column(
horizontalAlignment = Alignment.End,
modifier = Modifier
.padding(vertical = 6.dp)
.fillMaxWidth()
) {
FilledTonalIconToggleButton(
checked = expanded,
onCheckedChange = { expanded = it },
shapes = IconButtonDefaults.toggleableShapes(),
modifier = Modifier.width(52.dp)
) {
Icon(
painterResource(R.drawable.info),
null
)
}
AnimatedVisibility(expanded) {
Text(
stringResource(R.string.pomodoro_info),
style = typography.bodyMedium,
color = colorScheme.onSurfaceVariant,
modifier = Modifier.padding(8.dp)
)
}
}
}
item { Spacer(Modifier.height(12.dp)) }
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Preview
@Composable
private fun TimerSettingsPreview() {
val focusTimeInputFieldState = TextFieldState("25")
val shortBreakTimeInputFieldState = TextFieldState("5")
val longBreakTimeInputFieldState = TextFieldState("15")
val sessionsSliderState = SliderState(
value = 4f,
valueRange = 1f..8f,
steps = 6
)
TimerSettings(
focusTimeInputFieldState = focusTimeInputFieldState,
shortBreakTimeInputFieldState = shortBreakTimeInputFieldState,
longBreakTimeInputFieldState = longBreakTimeInputFieldState,
sessionsSliderState = sessionsSliderState,
aodEnabled = true,
onBack = {},
onAodEnabledChange = {}
)
}

View File

@@ -1,9 +1,28 @@
/*
* Copyright (c) 2025 Nishant Mishra
*
* This file is part of Tomato - a minimalist pomodoro timer for Android.
*
* Tomato 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.
*
* Tomato 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 Tomato.
* If not, see <https://www.gnu.org/licenses/>.
*/
package org.nsh07.pomodoro.ui.theme
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ListItemColors
import androidx.compose.material3.ListItemDefaults
import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.SwitchColors
import androidx.compose.material3.SwitchDefaults
import androidx.compose.material3.TopAppBarColors
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
@@ -41,4 +60,9 @@ object CustomColors {
supportingColor = colorScheme.onSecondaryFixedVariant,
trailingIconColor = colorScheme.onSecondaryFixedVariant
)
val switchColors: SwitchColors
@Composable get() = SwitchDefaults.colors(
checkedIconColor = colorScheme.primary,
)
}