refactor: reorganize folder structure of settingsScreen

This commit is contained in:
Nishant Mishra
2025-10-22 18:24:26 +05:30
parent 5849a2cde4
commit 612bc27859
13 changed files with 199 additions and 424 deletions

View File

@@ -1,25 +0,0 @@
package org.nsh07.pomodoro.ui.settingsScreen
import androidx.compose.foundation.text.input.InputTransformation
import androidx.compose.foundation.text.input.OutputTransformation
import androidx.compose.foundation.text.input.TextFieldBuffer
import androidx.compose.foundation.text.input.insert
import androidx.core.text.isDigitsOnly
object MinutesInputTransformation : InputTransformation {
override fun TextFieldBuffer.transformInput() {
if (!this.asCharSequence().isDigitsOnly() || this.length > 2) {
revertAllChanges()
}
}
}
object MinutesOutputTransformation : OutputTransformation {
override fun TextFieldBuffer.transformOutput() {
if (this.length == 0) {
insert(0, "00")
} else if (this.toString().toInt() < 10) {
insert(0, "0")
}
}
}

View File

@@ -1,97 +1,66 @@
/* /*
* Copyright (c) 2025 Nishant Mishra * Copyright (c) 2025 Nishant Mishra
* *
* You should have received a copy of the GNU General Public License * This file is part of Tomato - a minimalist pomodoro timer for Android.
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* 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 package org.nsh07.pomodoro.ui.settingsScreen
import android.app.Activity
import android.content.Intent import android.content.Intent
import android.media.RingtoneManager
import android.net.Uri import android.net.Uri
import android.os.Build
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding 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.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.input.TextFieldState import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.foundation.text.input.rememberTextFieldState import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.FilledTonalIconToggleButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.ListItem
import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.LocalTextStyle
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.SliderState
import androidx.compose.material3.Switch
import androidx.compose.material3.SwitchDefaults
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberSliderState import androidx.compose.material3.rememberSliderState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.tooling.preview.Devices import androidx.compose.ui.tooling.preview.Devices
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.core.net.toUri
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.nsh07.pomodoro.R import org.nsh07.pomodoro.R
import org.nsh07.pomodoro.service.TimerService import org.nsh07.pomodoro.service.TimerService
import org.nsh07.pomodoro.ui.settingsScreen.components.AboutCard
import org.nsh07.pomodoro.ui.settingsScreen.viewModel.PreferencesState import org.nsh07.pomodoro.ui.settingsScreen.viewModel.PreferencesState
import org.nsh07.pomodoro.ui.settingsScreen.viewModel.SettingsViewModel import org.nsh07.pomodoro.ui.settingsScreen.viewModel.SettingsViewModel
import org.nsh07.pomodoro.ui.theme.AppFonts.robotoFlexTopBar import org.nsh07.pomodoro.ui.theme.AppFonts.robotoFlexTopBar
import org.nsh07.pomodoro.ui.theme.CustomColors.listItemColors
import org.nsh07.pomodoro.ui.theme.CustomColors.topBarColors 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
import org.nsh07.pomodoro.ui.theme.TomatoTheme import org.nsh07.pomodoro.ui.theme.TomatoTheme
import org.nsh07.pomodoro.utils.toColor
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@@ -178,100 +147,7 @@ private fun SettingsScreen(
onColorSchemeChange: (Color) -> Unit, onColorSchemeChange: (Color) -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val context = LocalContext.current val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
val switchColors = SwitchDefaults.colors(
checkedIconColor = colorScheme.primary,
)
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"
)
}
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 intent = Intent(RingtoneManager.ACTION_RINGTONE_PICKER).apply {
putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_ALARM)
putExtra(RingtoneManager.EXTRA_RINGTONE_TITLE, stringResource(R.string.alarm_sound))
putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, alarmSound.toUri())
}
val switchItems = remember(
preferencesState.blackTheme,
preferencesState.aodEnabled,
alarmEnabled,
vibrateEnabled
) {
listOf(
SettingsSwitchItem(
checked = preferencesState.blackTheme,
icon = R.drawable.contrast,
label = R.string.black_theme,
description = R.string.black_theme_desc,
onClick = onBlackThemeChange
),
SettingsSwitchItem(
checked = preferencesState.aodEnabled,
icon = R.drawable.aod,
label = R.string.always_on_display,
description = R.string.always_on_display_desc,
onClick = onAodEnabledChange
),
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)) { Column(modifier.nestedScroll(scrollBehavior.nestedScrollConnection)) {
TopAppBar( TopAppBar(
@@ -298,246 +174,15 @@ private fun SettingsScreen(
.fillMaxSize() .fillMaxSize()
.padding(horizontal = 16.dp) .padding(horizontal = 16.dp)
) { ) {
item { item { Spacer(Modifier.height(12.dp)) }
Spacer(Modifier.height(12.dp))
} item { AboutCard() }
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 { Spacer(Modifier.height(12.dp)) }
item { 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)
)
}
itemsIndexed(switchItems.take(2)) { 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
.padding(top = if (index != 0) 16.dp else 0.dp)
.clip(if (index == 0) bottomListItemShape else cardShape)
)
}
item { Spacer(Modifier.height(12.dp)) } item { Spacer(Modifier.height(12.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(intent) })
)
}
itemsIndexed(switchItems.drop(2)) { 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 - 2 -> bottomListItemShape
else -> middleListItemShape
}
)
)
}
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)
)
}
}
}
} }
} }
} }
@@ -570,11 +215,3 @@ fun SettingsScreenPreview() {
) )
} }
} }
data class SettingsSwitchItem(
val checked: Boolean,
@param:DrawableRes val icon: Int,
@param:StringRes val label: Int,
@param:StringRes val description: Int,
val onClick: (Boolean) -> Unit
)

View File

@@ -0,0 +1,29 @@
/*
* 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.annotation.DrawableRes
import androidx.annotation.StringRes
data class SettingsSwitchItem(
val checked: Boolean,
@param:DrawableRes val icon: Int,
@param:StringRes val label: Int,
@param:StringRes val description: Int,
val onClick: (Boolean) -> Unit
)

View File

@@ -15,7 +15,7 @@
* If not, see <https://www.gnu.org/licenses/>. * If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.nsh07.pomodoro.ui.settingsScreen package org.nsh07.pomodoro.ui.settingsScreen.components
import android.widget.Toast import android.widget.Toast
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement

View File

@@ -1,4 +1,21 @@
package org.nsh07.pomodoro.ui.settingsScreen /*
* 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.components
import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column

View File

@@ -1,11 +1,21 @@
/* /*
* Copyright (c) 2025 Nishant Mishra * Copyright (c) 2025 Nishant Mishra
* *
* You should have received a copy of the GNU General Public License * This file is part of Tomato - a minimalist pomodoro timer for Android.
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* 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 package org.nsh07.pomodoro.ui.settingsScreen.components
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.Icon import androidx.compose.material3.Icon

View File

@@ -1,4 +1,21 @@
package org.nsh07.pomodoro.ui.settingsScreen /*
* 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.components
import androidx.compose.animation.animateColorAsState import androidx.compose.animation.animateColorAsState
import androidx.compose.foundation.background import androidx.compose.foundation.background

View File

@@ -0,0 +1,42 @@
/*
* 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.components
import androidx.compose.foundation.text.input.InputTransformation
import androidx.compose.foundation.text.input.OutputTransformation
import androidx.compose.foundation.text.input.TextFieldBuffer
import androidx.compose.foundation.text.input.insert
import androidx.core.text.isDigitsOnly
object MinutesInputTransformation : InputTransformation {
override fun TextFieldBuffer.transformInput() {
if (!this.asCharSequence().isDigitsOnly() || this.length > 2) {
revertAllChanges()
}
}
}
object MinutesOutputTransformation : OutputTransformation {
override fun TextFieldBuffer.transformOutput() {
if (this.length == 0) {
insert(0, "00")
} else if (this.toString().toInt() < 10) {
insert(0, "0")
}
}
}

View File

@@ -1,11 +1,21 @@
/* /*
* Copyright (c) 2025 Nishant Mishra * Copyright (c) 2025 Nishant Mishra
* *
* You should have received a copy of the GNU General Public License * This file is part of Tomato - a minimalist pomodoro timer for Android.
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* 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 package org.nsh07.pomodoro.ui.settingsScreen.components
import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@@ -85,7 +95,7 @@ fun ThemeDialog(
) { ) {
themeMap.entries.forEachIndexed { index: Int, pair: Map.Entry<String, Pair<Int, Int>> -> themeMap.entries.forEachIndexed { index: Int, pair: Map.Entry<String, Pair<Int, Int>> ->
val text = pair.value.second val text = pair.value.second
val selected = text == selectedOption.value val selected = text == selectedOption.intValue
ListItem( ListItem(
leadingContent = { leadingContent = {
@@ -113,9 +123,9 @@ fun ThemeDialog(
} }
) )
.selectable( .selectable(
selected = (text == selectedOption.value), selected = (text == selectedOption.intValue),
onClick = { onClick = {
selectedOption.value = text selectedOption.intValue = text
onThemeChange( onThemeChange(
reverseThemeMap[context.getString( reverseThemeMap[context.getString(
selectedOption.intValue selectedOption.intValue

View File

@@ -1,11 +1,21 @@
/* /*
* Copyright (c) 2025 Nishant Mishra * Copyright (c) 2025 Nishant Mishra
* *
* You should have received a copy of the GNU General Public License * This file is part of Tomato - a minimalist pomodoro timer for Android.
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* 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 package org.nsh07.pomodoro.ui.settingsScreen.components
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.Icon import androidx.compose.material3.Icon

View File

@@ -15,7 +15,7 @@
* If not, see <https://www.gnu.org/licenses/>. * If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.nsh07.pomodoro.ui.settingsScreen package org.nsh07.pomodoro.ui.settingsScreen.screens
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent
@@ -57,11 +57,13 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.net.toUri import androidx.core.net.toUri
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.nsh07.pomodoro.R import org.nsh07.pomodoro.R
import org.nsh07.pomodoro.ui.settingsScreen.SettingsSwitchItem
import org.nsh07.pomodoro.ui.settingsScreen.viewModel.PreferencesState import org.nsh07.pomodoro.ui.settingsScreen.viewModel.PreferencesState
import org.nsh07.pomodoro.ui.theme.AppFonts.robotoFlexTopBar import org.nsh07.pomodoro.ui.theme.AppFonts.robotoFlexTopBar
import org.nsh07.pomodoro.ui.theme.CustomColors.listItemColors import org.nsh07.pomodoro.ui.theme.CustomColors.listItemColors
@@ -234,3 +236,24 @@ fun AlarmSettings(
} }
} }
} }
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Preview
@Composable
fun AlarmSettingsPreview() {
val preferencesState = PreferencesState(
theme = "auto",
colorScheme = "White",
blackTheme = false,
aodEnabled = false
)
AlarmSettings(
preferencesState = preferencesState,
alarmEnabled = true,
vibrateEnabled = false,
alarmSound = "",
onAlarmEnabledChange = {},
onVibrateEnabledChange = {},
onAlarmSoundChanged = {},
onBack = {})
}

View File

@@ -15,7 +15,7 @@
* If not, see <https://www.gnu.org/licenses/>. * If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.nsh07.pomodoro.ui.settingsScreen package org.nsh07.pomodoro.ui.settingsScreen.screens
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@@ -48,6 +48,9 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import org.nsh07.pomodoro.R import org.nsh07.pomodoro.R
import org.nsh07.pomodoro.ui.settingsScreen.SettingsSwitchItem
import org.nsh07.pomodoro.ui.settingsScreen.components.ColorSchemePickerListItem
import org.nsh07.pomodoro.ui.settingsScreen.components.ThemePickerListItem
import org.nsh07.pomodoro.ui.settingsScreen.viewModel.PreferencesState import org.nsh07.pomodoro.ui.settingsScreen.viewModel.PreferencesState
import org.nsh07.pomodoro.ui.theme.AppFonts.robotoFlexTopBar import org.nsh07.pomodoro.ui.theme.AppFonts.robotoFlexTopBar
import org.nsh07.pomodoro.ui.theme.CustomColors.listItemColors import org.nsh07.pomodoro.ui.theme.CustomColors.listItemColors

View File

@@ -15,7 +15,7 @@
* If not, see <https://www.gnu.org/licenses/>. * If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.nsh07.pomodoro.ui.settingsScreen package org.nsh07.pomodoro.ui.settingsScreen.screens
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.background import androidx.compose.foundation.background
@@ -65,6 +65,8 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import org.nsh07.pomodoro.R import org.nsh07.pomodoro.R
import org.nsh07.pomodoro.ui.settingsScreen.SettingsSwitchItem
import org.nsh07.pomodoro.ui.settingsScreen.components.MinuteInputField
import org.nsh07.pomodoro.ui.theme.AppFonts.robotoFlexTopBar import org.nsh07.pomodoro.ui.theme.AppFonts.robotoFlexTopBar
import org.nsh07.pomodoro.ui.theme.CustomColors.listItemColors import org.nsh07.pomodoro.ui.theme.CustomColors.listItemColors
import org.nsh07.pomodoro.ui.theme.CustomColors.switchColors import org.nsh07.pomodoro.ui.theme.CustomColors.switchColors