diff --git a/app/src/main/java/org/nsh07/pomodoro/MainActivity.kt b/app/src/main/java/org/nsh07/pomodoro/MainActivity.kt
index a98932a..9404106 100644
--- a/app/src/main/java/org/nsh07/pomodoro/MainActivity.kt
+++ b/app/src/main/java/org/nsh07/pomodoro/MainActivity.kt
@@ -1,3 +1,20 @@
+/*
+ * 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
import android.os.Bundle
@@ -11,8 +28,6 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import org.nsh07.pomodoro.ui.AppScreen
-import org.nsh07.pomodoro.ui.NavItem
-import org.nsh07.pomodoro.ui.Screen
import org.nsh07.pomodoro.ui.settingsScreen.viewModel.SettingsViewModel
import org.nsh07.pomodoro.ui.theme.TomatoTheme
import org.nsh07.pomodoro.ui.timerScreen.viewModel.TimerViewModel
@@ -70,27 +85,4 @@ class MainActivity : ComponentActivity() {
// Increase the timer loop frequency again when visible to make the progress smoother
appContainer.appTimerRepository.timerFrequency = 10f
}
-
- companion object {
- val screens = listOf(
- NavItem(
- Screen.Timer,
- R.drawable.timer_outlined,
- R.drawable.timer_filled,
- R.string.timer
- ),
- NavItem(
- Screen.Stats,
- R.drawable.monitoring,
- R.drawable.monitoring_filled,
- R.string.stats
- ),
- NavItem(
- Screen.Settings,
- R.drawable.settings,
- R.drawable.settings_filled,
- R.string.settings
- )
- )
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/AppScreen.kt b/app/src/main/java/org/nsh07/pomodoro/ui/AppScreen.kt
index b26212e..6d1123e 100644
--- a/app/src/main/java/org/nsh07/pomodoro/ui/AppScreen.kt
+++ b/app/src/main/java/org/nsh07/pomodoro/ui/AppScreen.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
@@ -45,7 +55,6 @@ import androidx.navigation3.runtime.entryProvider
import androidx.navigation3.runtime.rememberNavBackStack
import androidx.navigation3.ui.NavDisplay
import androidx.window.core.layout.WindowSizeClass
-import org.nsh07.pomodoro.MainActivity.Companion.screens
import org.nsh07.pomodoro.service.TimerService
import org.nsh07.pomodoro.ui.settingsScreen.SettingsScreenRoot
import org.nsh07.pomodoro.ui.statsScreen.StatsScreenRoot
@@ -100,7 +109,7 @@ fun AppScreen(
if (wide) ShortNavigationBarArrangement.Centered
else ShortNavigationBarArrangement.EqualWeight
) {
- screens.forEach {
+ mainScreens.forEach {
val selected = backStack.last() == it.route
ShortNavigationBarItem(
selected = selected,
@@ -131,7 +140,7 @@ fun AppScreen(
SharedTransitionLayout {
NavDisplay(
backStack = backStack,
- onBack = { backStack.removeLastOrNull() },
+ onBack = backStack::removeLastOrNull,
transitionSpec = {
ContentTransform(
fadeIn(motionScheme.defaultEffectsSpec()),
@@ -213,7 +222,7 @@ fun AppScreen(
)
}
- entry {
+ entry {
SettingsScreenRoot(
modifier = modifier.padding(
start = contentPadding.calculateStartPadding(layoutDirection),
diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/Navigation.kt b/app/src/main/java/org/nsh07/pomodoro/ui/Navigation.kt
new file mode 100644
index 0000000..2641e7a
--- /dev/null
+++ b/app/src/main/java/org/nsh07/pomodoro/ui/Navigation.kt
@@ -0,0 +1,62 @@
+/*
+ * 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
+
+import org.nsh07.pomodoro.R
+
+val mainScreens = listOf(
+ NavItem(
+ Screen.Timer,
+ R.drawable.timer_outlined,
+ R.drawable.timer_filled,
+ R.string.timer
+ ),
+ NavItem(
+ Screen.Stats,
+ R.drawable.monitoring,
+ R.drawable.monitoring_filled,
+ R.string.stats
+ ),
+ NavItem(
+ Screen.Settings.Main,
+ R.drawable.settings,
+ R.drawable.settings_filled,
+ R.string.settings
+ )
+)
+
+val settingsScreens = listOf(
+ SettingsNavItem(
+ Screen.Settings.Timer,
+ R.drawable.timer_filled,
+ R.string.timer,
+ listOf(R.string.durations, R.string.session_length, R.string.always_on_display)
+ ),
+ SettingsNavItem(
+ Screen.Settings.Alarm,
+ R.drawable.alarm,
+ R.string.alarm,
+ listOf(R.string.alarm_sound, R.string.alarm, R.string.vibrate)
+ ),
+ SettingsNavItem(
+ Screen.Settings.Appearance,
+ R.drawable.palette,
+ R.string.appearance,
+ listOf(R.string.color_scheme, R.string.theme, R.string.black_theme)
+ )
+)
diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/Screen.kt b/app/src/main/java/org/nsh07/pomodoro/ui/Screen.kt
index 2a43655..a37fe21 100644
--- a/app/src/main/java/org/nsh07/pomodoro/ui/Screen.kt
+++ b/app/src/main/java/org/nsh07/pomodoro/ui/Screen.kt
@@ -1,3 +1,20 @@
+/*
+ * 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
import androidx.annotation.DrawableRes
@@ -13,7 +30,19 @@ sealed class Screen : NavKey {
object AOD : Screen()
@Serializable
- object Settings : Screen()
+ sealed class Settings : Screen() {
+ @Serializable
+ object Main : Settings()
+
+ @Serializable
+ object Alarm : Settings()
+
+ @Serializable
+ object Appearance : Settings()
+
+ @Serializable
+ object Timer : Settings()
+ }
@Serializable
object Stats : Screen()
@@ -24,4 +53,11 @@ data class NavItem(
@param:DrawableRes val unselectedIcon: Int,
@param:DrawableRes val selectedIcon: Int,
@param:StringRes val label: Int
-)
\ No newline at end of file
+)
+
+data class SettingsNavItem(
+ val route: Screen.Settings,
+ @param:DrawableRes val icon: Int,
+ @param:StringRes val label: Int,
+ val innerSettings: List
+)
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 9bc992a..ebc7c3f 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
@@ -19,6 +19,11 @@ package org.nsh07.pomodoro.ui.settingsScreen
import android.content.Intent
import android.net.Uri
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.animation.slideInHorizontally
+import androidx.compose.animation.slideOutHorizontally
+import androidx.compose.animation.togetherWith
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -27,10 +32,12 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
+import androidx.compose.material3.Icon
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.SliderState
import androidx.compose.material3.Text
@@ -40,24 +47,36 @@ import androidx.compose.material3.rememberSliderState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
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.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Devices
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
+import androidx.navigation3.runtime.entryProvider
+import androidx.navigation3.runtime.rememberNavBackStack
+import androidx.navigation3.ui.NavDisplay
import org.nsh07.pomodoro.R
import org.nsh07.pomodoro.service.TimerService
+import org.nsh07.pomodoro.ui.ClickableListItem
+import org.nsh07.pomodoro.ui.Screen
import org.nsh07.pomodoro.ui.settingsScreen.components.AboutCard
+import org.nsh07.pomodoro.ui.settingsScreen.screens.AlarmSettings
+import org.nsh07.pomodoro.ui.settingsScreen.screens.AppearanceSettings
+import org.nsh07.pomodoro.ui.settingsScreen.screens.TimerSettings
import org.nsh07.pomodoro.ui.settingsScreen.viewModel.PreferencesState
import org.nsh07.pomodoro.ui.settingsScreen.viewModel.SettingsViewModel
+import org.nsh07.pomodoro.ui.settingsScreens
import org.nsh07.pomodoro.ui.theme.AppFonts.robotoFlexTopBar
import org.nsh07.pomodoro.ui.theme.CustomColors.topBarColors
import org.nsh07.pomodoro.ui.theme.TomatoTheme
@@ -147,44 +166,122 @@ private fun SettingsScreen(
onColorSchemeChange: (Color) -> Unit,
modifier: Modifier = Modifier
) {
+ val context = LocalContext.current
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
+ val backStack = rememberNavBackStack(Screen.Settings.Main)
- Column(modifier.nestedScroll(scrollBehavior.nestedScrollConnection)) {
- TopAppBar(
- title = {
- Text(
- stringResource(R.string.settings),
- style = LocalTextStyle.current.copy(
- fontFamily = robotoFlexTopBar,
- fontSize = 32.sp,
- lineHeight = 32.sp
+ NavDisplay(
+ backStack = backStack,
+ onBack = backStack::removeLastOrNull,
+ transitionSpec = {
+ (slideInHorizontally(initialOffsetX = { it }))
+ .togetherWith(slideOutHorizontally(targetOffsetX = { -it / 4 }) + fadeOut())
+ },
+ popTransitionSpec = {
+ (slideInHorizontally(initialOffsetX = { -it / 4 }) + fadeIn())
+ .togetherWith(slideOutHorizontally(targetOffsetX = { it }))
+ },
+ predictivePopTransitionSpec = {
+ (slideInHorizontally(initialOffsetX = { -it / 4 }) + fadeIn())
+ .togetherWith(slideOutHorizontally(targetOffsetX = { it }))
+ },
+ entryProvider = entryProvider {
+ entry {
+ Column(modifier.nestedScroll(scrollBehavior.nestedScrollConnection)) {
+ TopAppBar(
+ title = {
+ Text(
+ stringResource(R.string.settings),
+ style = LocalTextStyle.current.copy(
+ fontFamily = robotoFlexTopBar,
+ fontSize = 32.sp,
+ lineHeight = 32.sp
+ )
+ )
+ },
+ subtitle = {},
+ colors = topBarColors,
+ titleHorizontalAlignment = Alignment.CenterHorizontally,
+ scrollBehavior = scrollBehavior
)
+
+ LazyColumn(
+ verticalArrangement = Arrangement.spacedBy(2.dp),
+ modifier = Modifier
+ .background(topBarColors.containerColor)
+ .fillMaxSize()
+ .padding(horizontal = 16.dp)
+ ) {
+ item { Spacer(Modifier.height(12.dp)) }
+
+ item { AboutCard() }
+
+ item { Spacer(Modifier.height(12.dp)) }
+
+ itemsIndexed(settingsScreens) { index, item ->
+ ClickableListItem(
+ leadingContent = {
+ Icon(painterResource(item.icon), null)
+ },
+ headlineContent = { Text(stringResource(item.label)) },
+ supportingContent = {
+ Text(
+ remember {
+ item.innerSettings.joinToString(", ") {
+ context.getString(it)
+ }
+ },
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis
+ )
+ },
+ trailingContent = {
+ Icon(painterResource(R.drawable.arrow_forward_big), null)
+ },
+ items = settingsScreens.size,
+ index = index
+ ) { backStack.add(item.route) }
+ }
+
+ item { Spacer(Modifier.height(12.dp)) }
+ }
+ }
+ }
+
+ entry {
+ AlarmSettings(
+ preferencesState = preferencesState,
+ alarmEnabled = alarmEnabled,
+ vibrateEnabled = vibrateEnabled,
+ alarmSound = alarmSound,
+ onAlarmEnabledChange = onAlarmEnabledChange,
+ onVibrateEnabledChange = onVibrateEnabledChange,
+ onAlarmSoundChanged = onAlarmSoundChanged,
+ onBack = backStack::removeLastOrNull
)
- },
- subtitle = {},
- colors = topBarColors,
- titleHorizontalAlignment = Alignment.CenterHorizontally,
- scrollBehavior = scrollBehavior
- )
-
- LazyColumn(
- verticalArrangement = Arrangement.spacedBy(2.dp),
- modifier = Modifier
- .background(topBarColors.containerColor)
- .fillMaxSize()
- .padding(horizontal = 16.dp)
- ) {
- item { Spacer(Modifier.height(12.dp)) }
-
- item { AboutCard() }
-
- item { Spacer(Modifier.height(12.dp)) }
-
- item {}
-
- item { Spacer(Modifier.height(12.dp)) }
+ }
+ entry {
+ AppearanceSettings(
+ preferencesState = preferencesState,
+ onBlackThemeChange = onBlackThemeChange,
+ onThemeChange = onThemeChange,
+ onColorSchemeChange = onColorSchemeChange,
+ onBack = backStack::removeLastOrNull
+ )
+ }
+ entry {
+ TimerSettings(
+ aodEnabled = preferencesState.aodEnabled,
+ focusTimeInputFieldState = focusTimeInputFieldState,
+ shortBreakTimeInputFieldState = shortBreakTimeInputFieldState,
+ longBreakTimeInputFieldState = longBreakTimeInputFieldState,
+ sessionsSliderState = sessionsSliderState,
+ onAodEnabledChange = onAodEnabledChange,
+ onBack = backStack::removeLastOrNull
+ )
+ }
}
- }
+ )
}
@OptIn(ExperimentalMaterial3Api::class)
diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/screens/AlarmSettings.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/screens/AlarmSettings.kt
index f971356..3b6b48f 100644
--- a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/screens/AlarmSettings.kt
+++ b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/screens/AlarmSettings.kt
@@ -151,10 +151,10 @@ fun AlarmSettings(
Column(modifier.nestedScroll(scrollBehavior.nestedScrollConnection)) {
LargeFlexibleTopAppBar(
title = {
- Text("Alarm", fontFamily = robotoFlexTopBar)
+ Text(stringResource(R.string.alarm), fontFamily = robotoFlexTopBar)
},
subtitle = {
- Text("Settings")
+ Text(stringResource(R.string.settings))
},
navigationIcon = {
IconButton(onBack) {
@@ -241,12 +241,7 @@ fun AlarmSettings(
@Preview
@Composable
fun AlarmSettingsPreview() {
- val preferencesState = PreferencesState(
- theme = "auto",
- colorScheme = "White",
- blackTheme = false,
- aodEnabled = false
- )
+ val preferencesState = PreferencesState()
AlarmSettings(
preferencesState = preferencesState,
alarmEnabled = true,
diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/screens/AppearanceSettings.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/screens/AppearanceSettings.kt
index c9379f3..81175af 100644
--- a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/screens/AppearanceSettings.kt
+++ b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/screens/AppearanceSettings.kt
@@ -42,7 +42,6 @@ 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
@@ -70,8 +69,6 @@ fun AppearanceSettings(
onBack: () -> Unit,
modifier: Modifier = Modifier
) {
- val context = LocalContext.current
-
val themeMap: Map> = remember {
mapOf(
"auto" to Pair(
@@ -82,23 +79,21 @@ fun AppearanceSettings(
"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 reverseThemeMap: Map = mapOf(
+ stringResource(R.string.system_default) to "auto",
+ stringResource(R.string.light) to "light",
+ stringResource(R.string.dark) to "dark"
+ )
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
Column(modifier.nestedScroll(scrollBehavior.nestedScrollConnection)) {
LargeFlexibleTopAppBar(
title = {
- Text("Appearance", fontFamily = robotoFlexTopBar)
+ Text(stringResource(R.string.appearance), fontFamily = robotoFlexTopBar)
},
subtitle = {
- Text("Settings")
+ Text(stringResource(R.string.settings))
},
navigationIcon = {
IconButton(onBack) {
diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/screens/TimerSettings.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/screens/TimerSettings.kt
index c10d0ab..01dddba 100644
--- a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/screens/TimerSettings.kt
+++ b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/screens/TimerSettings.kt
@@ -93,10 +93,10 @@ fun TimerSettings(
Column(modifier.nestedScroll(scrollBehavior.nestedScrollConnection)) {
LargeFlexibleTopAppBar(
title = {
- Text("Timer", fontFamily = robotoFlexTopBar)
+ Text(stringResource(R.string.timer), fontFamily = robotoFlexTopBar)
},
subtitle = {
- Text("Settings")
+ Text(stringResource(R.string.settings))
},
navigationIcon = {
IconButton(onBack) {
diff --git a/app/src/main/res/drawable/arrow_forward_big.xml b/app/src/main/res/drawable/arrow_forward_big.xml
new file mode 100644
index 0000000..eac1368
--- /dev/null
+++ b/app/src/main/res/drawable/arrow_forward_big.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index c185a08..2697a07 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,3 +1,20 @@
+
+
Alarm
Ring alarm when a timer completes
@@ -59,4 +76,6 @@
Vibrate
Vibrate when a timer completes
Weekly productivity analysis
+ Appearance
+ Durations
\ No newline at end of file