diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/AboutCard.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/AboutCard.kt
index 96da61c..499b503 100644
--- a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/AboutCard.kt
+++ b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/AboutCard.kt
@@ -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 .
+ * 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 .
*/
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))
diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/AlarmSettings.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/AlarmSettings.kt
new file mode 100644
index 0000000..659d75f
--- /dev/null
+++ b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/AlarmSettings.kt
@@ -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 .
+ */
+
+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)) }
+ }
+ }
+}
diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/AppearanceSettings.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/AppearanceSettings.kt
new file mode 100644
index 0000000..ca27045
--- /dev/null
+++ b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/AppearanceSettings.kt
@@ -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 .
+ */
+
+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> = 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 = 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 = {}
+ )
+}
diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/TimerSettings.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/TimerSettings.kt
new file mode 100644
index 0000000..46354cd
--- /dev/null
+++ b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/TimerSettings.kt
@@ -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 .
+ */
+
+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 = {}
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/theme/Color.kt b/app/src/main/java/org/nsh07/pomodoro/ui/theme/Color.kt
index 738f989..064d7d8 100644
--- a/app/src/main/java/org/nsh07/pomodoro/ui/theme/Color.kt
+++ b/app/src/main/java/org/nsh07/pomodoro/ui/theme/Color.kt
@@ -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 .
+ */
+
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,
+ )
}
\ No newline at end of file