refactor: reorganize folder structure of settingsScreen
This commit is contained in:
@@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
)
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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 = {})
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
Reference in New Issue
Block a user