diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/ClickableListItem.kt b/app/src/main/java/org/nsh07/pomodoro/ui/ClickableListItem.kt
new file mode 100644
index 0000000..2d05a68
--- /dev/null
+++ b/app/src/main/java/org/nsh07/pomodoro/ui/ClickableListItem.kt
@@ -0,0 +1,80 @@
+package org.nsh07.pomodoro.ui
+
+import androidx.compose.animation.core.animateDpAsState
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.collectIsPressedAsState
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
+import androidx.compose.material3.ListItem
+import androidx.compose.material3.ListItemColors
+import androidx.compose.material3.ListItemDefaults
+import androidx.compose.material3.MaterialTheme.motionScheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Composable
+fun ClickableListItem(
+ headlineContent: @Composable (() -> Unit),
+ modifier: Modifier = Modifier,
+ overlineContent: @Composable (() -> Unit)? = null,
+ supportingContent: @Composable (() -> Unit)? = null,
+ leadingContent: @Composable (() -> Unit)? = null,
+ trailingContent: @Composable (() -> Unit)? = null,
+ colors: ListItemColors = ListItemDefaults.colors(),
+ tonalElevation: Dp = ListItemDefaults.Elevation,
+ shadowElevation: Dp = ListItemDefaults.Elevation,
+ items: Int,
+ index: Int,
+ onClick: () -> Unit
+) {
+ val interactionSource = remember { MutableInteractionSource() }
+ val isPressed by interactionSource.collectIsPressedAsState()
+
+ val top by animateDpAsState(
+ if (isPressed) 40.dp
+ else {
+ if (items == 1 || index == 0) 20.dp
+ else 4.dp
+ },
+ motionScheme.fastSpatialSpec()
+ )
+ val bottom by animateDpAsState(
+ if (isPressed) 40.dp
+ else {
+ if (items == 1 || index == items - 1) 20.dp
+ else 4.dp
+ },
+ motionScheme.fastSpatialSpec()
+ )
+
+ ListItem(
+ headlineContent = headlineContent,
+ modifier = modifier
+ .clip(
+ RoundedCornerShape(
+ topStart = top,
+ topEnd = top,
+ bottomStart = bottom,
+ bottomEnd = bottom
+ )
+ )
+ .clickable(
+ onClick = onClick,
+ interactionSource = interactionSource,
+ ),
+ overlineContent = overlineContent,
+ supportingContent = supportingContent,
+ leadingContent = leadingContent,
+ trailingContent = trailingContent,
+ colors = colors,
+ tonalElevation = tonalElevation,
+ shadowElevation = shadowElevation
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/ColorSchemePickerDialog.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/ColorSchemePickerDialog.kt
new file mode 100644
index 0000000..d963ee5
--- /dev/null
+++ b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/ColorSchemePickerDialog.kt
@@ -0,0 +1,157 @@
+package org.nsh07.pomodoro.ui.settingsScreen
+
+import androidx.compose.animation.AnimatedContent
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.foundation.layout.wrapContentWidth
+import androidx.compose.material3.AlertDialogDefaults
+import androidx.compose.material3.BasicAlertDialog
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.IconButtonDefaults
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.MaterialTheme.colorScheme
+import androidx.compose.material3.MaterialTheme.shapes
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+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.graphics.Color
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.util.fastForEach
+import org.nsh07.pomodoro.R
+import org.nsh07.pomodoro.ui.theme.TomatoTheme
+
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Composable
+fun ColorPickerButton(
+ color: Color,
+ isSelected: Boolean,
+ modifier: Modifier = Modifier,
+ onClick: () -> Unit
+) {
+ IconButton(
+ shapes = IconButtonDefaults.shapes(),
+ colors = IconButtonDefaults.iconButtonColors(containerColor = color),
+ modifier = modifier.size(48.dp),
+ onClick = onClick
+ ) {
+ AnimatedContent(isSelected) { isSelected ->
+ when (isSelected) {
+ true -> Icon(
+ painterResource(R.drawable.check),
+ tint = Color.Black,
+ contentDescription = null
+ )
+
+ else ->
+ if (color == Color.White) Icon(
+ painterResource(R.drawable.colors),
+ tint = Color.Black,
+ contentDescription = null
+ )
+ }
+ }
+ }
+}
+
+@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
+@Composable
+fun ColorSchemePickerDialog(
+ currentColor: Color,
+ modifier: Modifier = Modifier,
+ setShowDialog: (Boolean) -> Unit,
+ onColorChange: (Color) -> Unit,
+) {
+ val colorSchemes = listOf(
+ Color(0xfffeb4a7), Color(0xffffb3c0), Color(0xfffcaaff), Color(0xffb9c3ff),
+ Color(0xff62d3ff), Color(0xff44d9f1), Color(0xff52dbc9), Color(0xff78dd77),
+ Color(0xff9fd75c), Color(0xffc1d02d), Color(0xfffabd00), Color(0xffffb86e),
+ Color.White
+ )
+
+ BasicAlertDialog(
+ onDismissRequest = { setShowDialog(false) },
+ modifier = modifier
+ ) {
+ Surface(
+ modifier = Modifier
+ .wrapContentWidth()
+ .wrapContentHeight(),
+ color = colorScheme.surfaceContainer,
+ shape = shapes.extraLarge,
+ tonalElevation = AlertDialogDefaults.TonalElevation
+ ) {
+ Column(modifier = Modifier.padding(24.dp)) {
+ Text(
+ text = "Choose color scheme",
+ style = MaterialTheme.typography.headlineSmall
+ )
+
+ Spacer(Modifier.height(16.dp))
+
+ Column(Modifier.align(Alignment.CenterHorizontally)) {
+ (0..11 step 4).forEach {
+ Row {
+ colorSchemes.slice(it..it + 3).fastForEach { color ->
+ ColorPickerButton(
+ color,
+ color == currentColor,
+ modifier = Modifier.padding(4.dp)
+ ) {
+ onColorChange(color)
+ }
+ }
+ }
+ }
+ ColorPickerButton(
+ colorSchemes.last(),
+ colorSchemes.last() == currentColor,
+ modifier = Modifier.padding(4.dp)
+ ) {
+ onColorChange(colorSchemes.last())
+ }
+ }
+
+ Spacer(Modifier.height(24.dp))
+
+ TextButton(
+ shapes = ButtonDefaults.shapes(),
+ onClick = { setShowDialog(false) },
+ modifier = Modifier.align(Alignment.End)
+ ) {
+ Text("Ok")
+ }
+ }
+ }
+ }
+}
+
+@Preview
+@Composable
+fun ColorPickerDialogPreview() {
+ var currentColor by remember { mutableStateOf(Color(0xfffeb4a7)) }
+ TomatoTheme(darkTheme = true) {
+ ColorSchemePickerDialog(
+ currentColor,
+ setShowDialog = {},
+ onColorChange = { currentColor = it }
+ )
+ }
+}
diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/ColorSchemePickerListItem.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/ColorSchemePickerListItem.kt
new file mode 100644
index 0000000..bc09f9e
--- /dev/null
+++ b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/ColorSchemePickerListItem.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2025 Nishant Mishra
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nsh07.pomodoro.ui.settingsScreen
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme.colorScheme
+import androidx.compose.material3.Text
+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.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.painterResource
+import org.nsh07.pomodoro.R
+import org.nsh07.pomodoro.ui.ClickableListItem
+import org.nsh07.pomodoro.ui.theme.CustomColors.listItemColors
+
+@Composable
+fun ColorSchemePickerListItem(
+ color: Color,
+ items: Int,
+ index: Int,
+ onColorChange: (Color) -> Unit,
+ modifier: Modifier = Modifier
+) {
+ var showDialog by rememberSaveable { mutableStateOf(false) }
+
+ if (showDialog) {
+ ColorSchemePickerDialog(
+ currentColor = color,
+ setShowDialog = { showDialog = it },
+ onColorChange = onColorChange
+ )
+ }
+
+ ClickableListItem(
+ leadingContent = {
+ Icon(
+ painter = painterResource(R.drawable.palette),
+ contentDescription = null,
+ tint = colorScheme.primary
+ )
+ },
+ headlineContent = { Text("Color scheme") },
+ supportingContent = {
+ Text(
+ if (color == Color.White) "Dynamic"
+ else "Color"
+ )
+ },
+ colors = listItemColors,
+ items = items,
+ index = index,
+ modifier = modifier.fillMaxWidth()
+ ) { showDialog = true }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsScreen.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsScreen.kt
index 580fa90..12bcdac 100644
--- a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsScreen.kt
+++ b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsScreen.kt
@@ -53,6 +53,7 @@ import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberSliderState
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
@@ -139,6 +140,7 @@ fun SettingsScreenRoot(
}
},
onThemeChange = viewModel::saveTheme,
+ onColorSchemeChange = viewModel::saveColorScheme,
modifier = modifier
)
}
@@ -159,6 +161,7 @@ private fun SettingsScreen(
onBlackThemeChange: (Boolean) -> Unit,
onAlarmSoundChanged: (Uri?) -> Unit,
onThemeChange: (String) -> Unit,
+ onColorSchemeChange: (Color) -> Unit,
modifier: Modifier = Modifier
) {
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
@@ -185,6 +188,12 @@ private fun SettingsScreen(
}
val context = LocalContext.current
+ var alarmName by remember { mutableStateOf("") }
+
+ LaunchedEffect(Unit) {
+ alarmName = RingtoneManager.getRingtone(context, alarmSound.toUri())
+ ?.getTitle(context) ?: ""
+ }
val ringtonePickerLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult()
@@ -358,25 +367,11 @@ private fun SettingsScreen(
item { Spacer(Modifier.height(12.dp)) }
item {
- ListItem(
- leadingContent = {
- Icon(
- painter = painterResource(R.drawable.palette),
- contentDescription = null,
- tint = colorScheme.primary
- )
- },
- headlineContent = { Text("Color scheme") },
- supportingContent = {
- Text(
- if (preferencesState.colorScheme.toColor() == Color.White) "Dynamic"
- else "Color"
- )
- },
- colors = listItemColors,
- modifier = Modifier
- .clip(topListItemShape)
- .clickable(onClick = {})
+ ColorSchemePickerListItem(
+ color = preferencesState.colorScheme.toColor(),
+ items = 3,
+ index = 0,
+ onColorChange = onColorSchemeChange
)
}
item {
@@ -385,6 +380,8 @@ private fun SettingsScreen(
themeMap = themeMap,
reverseThemeMap = reverseThemeMap,
onThemeChange = onThemeChange,
+ items = 3,
+ index = 1,
modifier = Modifier
.clip(middleListItemShape)
)
@@ -432,14 +429,7 @@ private fun SettingsScreen(
Icon(painterResource(R.drawable.alarm), null)
},
headlineContent = { Text("Alarm sound") },
- supportingContent = {
- Text(
- remember(alarmSound) {
- RingtoneManager.getRingtone(context, alarmSound.toUri())
- ?.getTitle(context) ?: ""
- }
- )
- },
+ supportingContent = { Text(alarmName) },
colors = listItemColors,
modifier = Modifier
.clip(topListItemShape)
@@ -542,6 +532,7 @@ fun SettingsScreenPreview() {
onBlackThemeChange = {},
onAlarmSoundChanged = {},
onThemeChange = {},
+ onColorSchemeChange = {},
modifier = Modifier.fillMaxSize()
)
}
diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/ThemePickerListItem.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/ThemePickerListItem.kt
index 4c43549..9fda303 100644
--- a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/ThemePickerListItem.kt
+++ b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/ThemePickerListItem.kt
@@ -7,10 +7,8 @@
package org.nsh07.pomodoro.ui.settingsScreen
-import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.Icon
-import androidx.compose.material3.ListItem
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@@ -19,6 +17,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
+import org.nsh07.pomodoro.ui.ClickableListItem
import org.nsh07.pomodoro.ui.theme.CustomColors.listItemColors
@Composable
@@ -26,6 +25,8 @@ fun ThemePickerListItem(
theme: String,
themeMap: Map>,
reverseThemeMap: Map,
+ items: Int,
+ index: Int,
onThemeChange: (String) -> Unit,
modifier: Modifier = Modifier
) {
@@ -41,7 +42,7 @@ fun ThemePickerListItem(
)
}
- ListItem(
+ ClickableListItem(
leadingContent = {
Icon(
painter = painterResource(themeMap[theme]!!.first),
@@ -53,8 +54,8 @@ fun ThemePickerListItem(
Text(themeMap[theme]!!.second)
},
colors = listItemColors,
- modifier = modifier
- .fillMaxWidth()
- .clickable { showDialog = true }
- )
+ items = items,
+ index = index,
+ modifier = modifier.fillMaxWidth()
+ ) { showDialog = true }
}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/colors.xml b/app/src/main/res/drawable/colors.xml
new file mode 100644
index 0000000..7449d11
--- /dev/null
+++ b/app/src/main/res/drawable/colors.xml
@@ -0,0 +1,16 @@
+
+
+
+
+