feat: Add options in the settings menu to disable alarm and vibration
#36
This commit is contained in:
@@ -13,8 +13,8 @@ import kotlinx.coroutines.withContext
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for reading/writing app preferences to the app's database. This style of storage aims
|
* Interface for reading/writing app preferences to the app's database. This style of storage aims
|
||||||
* to mimic the Preferences DataStore library, preventing the requirement of migration if the
|
* to mimic the Preferences DataStore library, preventing the requirement of migration if new
|
||||||
* database schema changes.
|
* preferences are added
|
||||||
*/
|
*/
|
||||||
interface PreferenceRepository {
|
interface PreferenceRepository {
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ interface TimerRepository {
|
|||||||
var longBreakTime: Long
|
var longBreakTime: Long
|
||||||
var sessionLength: Int
|
var sessionLength: Int
|
||||||
var timerFrequency: Float
|
var timerFrequency: Float
|
||||||
|
var alarmEnabled: Boolean
|
||||||
|
var vibrateEnabled: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -28,4 +30,6 @@ class AppTimerRepository : TimerRepository {
|
|||||||
override var longBreakTime = 15 * 60 * 1000L
|
override var longBreakTime = 15 * 60 * 1000L
|
||||||
override var sessionLength = 4
|
override var sessionLength = 4
|
||||||
override var timerFrequency: Float = 10f
|
override var timerFrequency: Float = 10f
|
||||||
|
override var alarmEnabled = true
|
||||||
|
override var vibrateEnabled = true
|
||||||
}
|
}
|
||||||
@@ -344,23 +344,28 @@ class TimerService : Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun startAlarm() {
|
fun startAlarm() {
|
||||||
alarm.start()
|
if (timerRepository.alarmEnabled) alarm.start()
|
||||||
|
|
||||||
if (!vibrator.hasVibrator()) {
|
if (timerRepository.vibrateEnabled) {
|
||||||
return
|
if (!vibrator.hasVibrator()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val vibrationPattern = longArrayOf(0, 1000, 1000, 1000)
|
||||||
|
val repeat = 2
|
||||||
|
val effect = VibrationEffect.createWaveform(vibrationPattern, repeat)
|
||||||
|
vibrator.vibrate(effect)
|
||||||
}
|
}
|
||||||
|
|
||||||
val vibrationPattern = longArrayOf(0, 1000, 1000, 1000)
|
|
||||||
val repeat = 2
|
|
||||||
|
|
||||||
val effect = VibrationEffect.createWaveform(vibrationPattern, repeat)
|
|
||||||
vibrator.vibrate(effect)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stopAlarm() {
|
fun stopAlarm() {
|
||||||
alarm.pause()
|
if (timerRepository.alarmEnabled) {
|
||||||
alarm.seekTo(0)
|
alarm.pause()
|
||||||
vibrator.cancel()
|
alarm.seekTo(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timerRepository.vibrateEnabled) {
|
||||||
|
vibrator.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
_timerState.update { currentState ->
|
_timerState.update { currentState ->
|
||||||
currentState.copy(alarmRinging = false)
|
currentState.copy(alarmRinging = false)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
package org.nsh07.pomodoro.ui.settingsScreen
|
package org.nsh07.pomodoro.ui.settingsScreen
|
||||||
|
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.horizontalScroll
|
import androidx.compose.foundation.horizontalScroll
|
||||||
@@ -18,8 +19,10 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
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.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.rememberScrollState
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.text.input.TextFieldState
|
import androidx.compose.foundation.text.input.TextFieldState
|
||||||
@@ -32,10 +35,11 @@ import androidx.compose.material3.IconButtonDefaults
|
|||||||
import androidx.compose.material3.ListItem
|
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.colorScheme
|
||||||
import androidx.compose.material3.MaterialTheme.shapes
|
|
||||||
import androidx.compose.material3.MaterialTheme.typography
|
import androidx.compose.material3.MaterialTheme.typography
|
||||||
import androidx.compose.material3.Slider
|
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
|
||||||
@@ -56,12 +60,17 @@ 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.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import org.nsh07.pomodoro.R
|
import org.nsh07.pomodoro.R
|
||||||
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.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
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@@ -80,6 +89,9 @@ fun SettingsScreenRoot(
|
|||||||
viewModel.longBreakTimeTextFieldState
|
viewModel.longBreakTimeTextFieldState
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val alarmEnabled = viewModel.alarmEnabled.collectAsStateWithLifecycle()
|
||||||
|
val vibrateEnabled = viewModel.vibrateEnabled.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
val sessionsSliderState = rememberSaveable(
|
val sessionsSliderState = rememberSaveable(
|
||||||
saver = SliderState.Saver(
|
saver = SliderState.Saver(
|
||||||
viewModel.sessionsSliderState.onValueChangeFinished,
|
viewModel.sessionsSliderState.onValueChangeFinished,
|
||||||
@@ -94,6 +106,10 @@ fun SettingsScreenRoot(
|
|||||||
shortBreakTimeInputFieldState = shortBreakTimeInputFieldState,
|
shortBreakTimeInputFieldState = shortBreakTimeInputFieldState,
|
||||||
longBreakTimeInputFieldState = longBreakTimeInputFieldState,
|
longBreakTimeInputFieldState = longBreakTimeInputFieldState,
|
||||||
sessionsSliderState = sessionsSliderState,
|
sessionsSliderState = sessionsSliderState,
|
||||||
|
alarmEnabled = alarmEnabled.value,
|
||||||
|
vibrateEnabled = vibrateEnabled.value,
|
||||||
|
onAlarmEnabledChange = viewModel::saveAlarmEnabled,
|
||||||
|
onVibrateEnabledChange = viewModel::saveVibrateEnabled,
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -105,9 +121,35 @@ private fun SettingsScreen(
|
|||||||
shortBreakTimeInputFieldState: TextFieldState,
|
shortBreakTimeInputFieldState: TextFieldState,
|
||||||
longBreakTimeInputFieldState: TextFieldState,
|
longBreakTimeInputFieldState: TextFieldState,
|
||||||
sessionsSliderState: SliderState,
|
sessionsSliderState: SliderState,
|
||||||
|
alarmEnabled: Boolean,
|
||||||
|
vibrateEnabled: Boolean,
|
||||||
|
onAlarmEnabledChange: (Boolean) -> Unit,
|
||||||
|
onVibrateEnabledChange: (Boolean) -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
|
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
|
||||||
|
val switchColors = SwitchDefaults.colors(
|
||||||
|
checkedIconColor = colorScheme.primary,
|
||||||
|
)
|
||||||
|
|
||||||
|
val switchItems = remember(alarmEnabled, vibrateEnabled) {
|
||||||
|
listOf(
|
||||||
|
SettingsSwitchItem(
|
||||||
|
checked = alarmEnabled,
|
||||||
|
icon = R.drawable.alarm_on,
|
||||||
|
label = "Alarm",
|
||||||
|
description = "Ring your system alarm sound when a timer completes",
|
||||||
|
onClick = onAlarmEnabledChange
|
||||||
|
),
|
||||||
|
SettingsSwitchItem(
|
||||||
|
checked = vibrateEnabled,
|
||||||
|
icon = R.drawable.mobile_vibrate,
|
||||||
|
label = "Vibrate",
|
||||||
|
description = "Vibrate in a repeating pattern when a timer completes",
|
||||||
|
onClick = onVibrateEnabledChange
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Column(modifier.nestedScroll(scrollBehavior.nestedScrollConnection)) {
|
Column(modifier.nestedScroll(scrollBehavior.nestedScrollConnection)) {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
@@ -155,10 +197,10 @@ private fun SettingsScreen(
|
|||||||
MinuteInputField(
|
MinuteInputField(
|
||||||
state = focusTimeInputFieldState,
|
state = focusTimeInputFieldState,
|
||||||
shape = RoundedCornerShape(
|
shape = RoundedCornerShape(
|
||||||
topStart = 16.dp,
|
topStart = topListItemShape.topStart,
|
||||||
bottomStart = 16.dp,
|
bottomStart = topListItemShape.topStart,
|
||||||
topEnd = 4.dp,
|
topEnd = topListItemShape.bottomStart,
|
||||||
bottomEnd = 4.dp
|
bottomEnd = topListItemShape.bottomStart
|
||||||
),
|
),
|
||||||
imeAction = ImeAction.Next
|
imeAction = ImeAction.Next
|
||||||
)
|
)
|
||||||
@@ -174,7 +216,7 @@ private fun SettingsScreen(
|
|||||||
)
|
)
|
||||||
MinuteInputField(
|
MinuteInputField(
|
||||||
state = shortBreakTimeInputFieldState,
|
state = shortBreakTimeInputFieldState,
|
||||||
shape = RoundedCornerShape(4.dp),
|
shape = RoundedCornerShape(middleListItemShape.topStart),
|
||||||
imeAction = ImeAction.Next
|
imeAction = ImeAction.Next
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -190,10 +232,10 @@ private fun SettingsScreen(
|
|||||||
MinuteInputField(
|
MinuteInputField(
|
||||||
state = longBreakTimeInputFieldState,
|
state = longBreakTimeInputFieldState,
|
||||||
shape = RoundedCornerShape(
|
shape = RoundedCornerShape(
|
||||||
topStart = 4.dp,
|
topStart = bottomListItemShape.topStart,
|
||||||
bottomStart = 4.dp,
|
bottomStart = bottomListItemShape.topStart,
|
||||||
topEnd = 16.dp,
|
topEnd = bottomListItemShape.bottomStart,
|
||||||
bottomEnd = 16.dp
|
bottomEnd = bottomListItemShape.bottomStart
|
||||||
),
|
),
|
||||||
imeAction = ImeAction.Done
|
imeAction = ImeAction.Done
|
||||||
)
|
)
|
||||||
@@ -224,7 +266,48 @@ private fun SettingsScreen(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
colors = listItemColors,
|
colors = listItemColors,
|
||||||
modifier = Modifier.clip(shapes.large)
|
modifier = Modifier.clip(cardShape)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
item { Spacer(Modifier.height(12.dp)) }
|
||||||
|
itemsIndexed(switchItems) { index, item ->
|
||||||
|
ListItem(
|
||||||
|
leadingContent = {
|
||||||
|
Icon(painterResource(item.icon), contentDescription = null)
|
||||||
|
},
|
||||||
|
headlineContent = { Text(item.label) },
|
||||||
|
supportingContent = { Text(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) {
|
||||||
|
0 -> topListItemShape
|
||||||
|
switchItems.lastIndex -> bottomListItemShape
|
||||||
|
else -> middleListItemShape
|
||||||
|
}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
item {
|
item {
|
||||||
@@ -275,7 +358,19 @@ fun SettingsScreenPreview() {
|
|||||||
shortBreakTimeInputFieldState = rememberTextFieldState((5 * 60 * 1000).toString()),
|
shortBreakTimeInputFieldState = rememberTextFieldState((5 * 60 * 1000).toString()),
|
||||||
longBreakTimeInputFieldState = rememberTextFieldState((15 * 60 * 1000).toString()),
|
longBreakTimeInputFieldState = rememberTextFieldState((15 * 60 * 1000).toString()),
|
||||||
sessionsSliderState = rememberSliderState(value = 3f, steps = 3, valueRange = 1f..5f),
|
sessionsSliderState = rememberSliderState(value = 3f, steps = 3, valueRange = 1f..5f),
|
||||||
|
alarmEnabled = true,
|
||||||
|
vibrateEnabled = true,
|
||||||
|
onAlarmEnabledChange = {},
|
||||||
|
onVibrateEnabledChange = {},
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class SettingsSwitchItem(
|
||||||
|
val checked: Boolean,
|
||||||
|
@DrawableRes val icon: Int,
|
||||||
|
val label: String,
|
||||||
|
val description: String,
|
||||||
|
val onClick: (Boolean) -> Unit
|
||||||
|
)
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ import androidx.lifecycle.viewmodel.initializer
|
|||||||
import androidx.lifecycle.viewmodel.viewModelFactory
|
import androidx.lifecycle.viewmodel.viewModelFactory
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.FlowPreview
|
import kotlinx.coroutines.FlowPreview
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.debounce
|
import kotlinx.coroutines.flow.debounce
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.nsh07.pomodoro.TomatoApplication
|
import org.nsh07.pomodoro.TomatoApplication
|
||||||
@@ -44,6 +47,14 @@ class SettingsViewModel(
|
|||||||
onValueChangeFinished = ::updateSessionLength
|
onValueChangeFinished = ::updateSessionLength
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private val _alarmEnabled: MutableStateFlow<Boolean> =
|
||||||
|
MutableStateFlow(timerRepository.alarmEnabled)
|
||||||
|
val alarmEnabled: StateFlow<Boolean> = _alarmEnabled.asStateFlow()
|
||||||
|
|
||||||
|
private val _vibrateEnabled: MutableStateFlow<Boolean> =
|
||||||
|
MutableStateFlow(timerRepository.alarmEnabled)
|
||||||
|
val vibrateEnabled: StateFlow<Boolean> = _vibrateEnabled.asStateFlow()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
snapshotFlow { focusTimeTextFieldState.text }
|
snapshotFlow { focusTimeTextFieldState.text }
|
||||||
@@ -92,6 +103,22 @@ class SettingsViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun saveAlarmEnabled(enabled: Boolean) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
timerRepository.alarmEnabled = preferenceRepository
|
||||||
|
.saveIntPreference("alarm_enabled", if (enabled) 1 else 0) == 1
|
||||||
|
_alarmEnabled.value = enabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveVibrateEnabled(enabled: Boolean) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
timerRepository.vibrateEnabled = preferenceRepository
|
||||||
|
.saveIntPreference("vibrate_enabled", if (enabled) 1 else 0) == 1
|
||||||
|
_vibrateEnabled.value = enabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val Factory: ViewModelProvider.Factory = viewModelFactory {
|
val Factory: ViewModelProvider.Factory = viewModelFactory {
|
||||||
initializer {
|
initializer {
|
||||||
|
|||||||
43
app/src/main/java/org/nsh07/pomodoro/ui/theme/Shape.kt
Normal file
43
app/src/main/java/org/nsh07/pomodoro/ui/theme/Shape.kt
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Nishant Mishra
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.nsh07.pomodoro.ui.theme
|
||||||
|
|
||||||
|
import androidx.compose.foundation.shape.CornerBasedShape
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
|
import androidx.compose.material3.MaterialTheme.shapes
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
|
||||||
|
object TomatoShapeDefaults {
|
||||||
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||||
|
val topListItemShape: RoundedCornerShape
|
||||||
|
@Composable get() =
|
||||||
|
RoundedCornerShape(
|
||||||
|
topStart = shapes.largeIncreased.topStart,
|
||||||
|
topEnd = shapes.largeIncreased.topEnd,
|
||||||
|
bottomStart = shapes.extraSmall.bottomStart,
|
||||||
|
bottomEnd = shapes.extraSmall.bottomStart
|
||||||
|
)
|
||||||
|
|
||||||
|
val middleListItemShape: RoundedCornerShape
|
||||||
|
@Composable get() = RoundedCornerShape(shapes.extraSmall.topStart)
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||||
|
val bottomListItemShape: RoundedCornerShape
|
||||||
|
@Composable get() =
|
||||||
|
RoundedCornerShape(
|
||||||
|
topStart = shapes.extraSmall.topStart,
|
||||||
|
topEnd = shapes.extraSmall.topEnd,
|
||||||
|
bottomStart = shapes.largeIncreased.bottomStart,
|
||||||
|
bottomEnd = shapes.largeIncreased.bottomEnd
|
||||||
|
)
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||||
|
val cardShape: CornerBasedShape
|
||||||
|
@Composable get() = shapes.largeIncreased
|
||||||
|
}
|
||||||
@@ -77,6 +77,18 @@ class TimerViewModel(
|
|||||||
timerRepository.sessionLength
|
timerRepository.sessionLength
|
||||||
)
|
)
|
||||||
|
|
||||||
|
timerRepository.alarmEnabled = (preferenceRepository.getIntPreference("alarm_enabled")
|
||||||
|
?: preferenceRepository.saveIntPreference(
|
||||||
|
"alarm_enabled",
|
||||||
|
1
|
||||||
|
)) == 1
|
||||||
|
timerRepository.vibrateEnabled =
|
||||||
|
(preferenceRepository.getIntPreference("vibrate_enabled")
|
||||||
|
?: preferenceRepository.saveIntPreference(
|
||||||
|
"vibrate_enabled",
|
||||||
|
1
|
||||||
|
)) == 1
|
||||||
|
|
||||||
resetTimer()
|
resetTimer()
|
||||||
|
|
||||||
var lastDate = statRepository.getLastDate()
|
var lastDate = statRepository.getLastDate()
|
||||||
|
|||||||
16
app/src/main/res/drawable/alarm_on.xml
Normal file
16
app/src/main/res/drawable/alarm_on.xml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<!--
|
||||||
|
~ Copyright (c) 2025 Nishant Mishra
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU General Public License
|
||||||
|
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:fillColor="#e3e3e3"
|
||||||
|
android:pathData="m438,548 l-57,-57q-12,-12 -28,-12t-28,12q-12,12 -12,28.5t12,28.5l85,86q12,12 28,12t28,-12l170,-170q12,-12 12,-28.5T636,407q-12,-12 -28.5,-12T579,407L438,548ZM480,880q-75,0 -140.5,-28.5t-114,-77q-48.5,-48.5 -77,-114T120,520q0,-75 28.5,-140.5t77,-114q48.5,-48.5 114,-77T480,160q75,0 140.5,28.5t114,77q48.5,48.5 77,114T840,520q0,75 -28.5,140.5t-77,114q-48.5,48.5 -114,77T480,880ZM82,292q-11,-11 -11,-28t11,-28l114,-114q11,-11 28,-11t28,11q11,11 11,28t-11,28L138,292q-11,11 -28,11t-28,-11ZM878,292q-11,11 -28,11t-28,-11L708,178q-11,-11 -11,-28t11,-28q11,-11 28,-11t28,11l114,114q11,11 11,28t-11,28Z" />
|
||||||
|
</vector>
|
||||||
16
app/src/main/res/drawable/check.xml
Normal file
16
app/src/main/res/drawable/check.xml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<!--
|
||||||
|
~ Copyright (c) 2025 Nishant Mishra
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU General Public License
|
||||||
|
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:fillColor="#e3e3e3"
|
||||||
|
android:pathData="m382,606 l339,-339q12,-12 28,-12t28,12q12,12 12,28.5T777,324L410,692q-12,12 -28,12t-28,-12L182,520q-12,-12 -11.5,-28.5T183,463q12,-12 28.5,-12t28.5,12l142,143Z" />
|
||||||
|
</vector>
|
||||||
16
app/src/main/res/drawable/clear.xml
Normal file
16
app/src/main/res/drawable/clear.xml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<!--
|
||||||
|
~ Copyright (c) 2025 Nishant Mishra
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU General Public License
|
||||||
|
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:fillColor="#e3e3e3"
|
||||||
|
android:pathData="M480,536 L284,732q-11,11 -28,11t-28,-11q-11,-11 -11,-28t11,-28l196,-196 -196,-196q-11,-11 -11,-28t11,-28q11,-11 28,-11t28,11l196,196 196,-196q11,-11 28,-11t28,11q11,11 11,28t-11,28L536,480l196,196q11,11 11,28t-11,28q-11,11 -28,11t-28,-11L480,536Z" />
|
||||||
|
</vector>
|
||||||
16
app/src/main/res/drawable/mobile_vibrate.xml
Normal file
16
app/src/main/res/drawable/mobile_vibrate.xml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<!--
|
||||||
|
~ Copyright (c) 2025 Nishant Mishra
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU General Public License
|
||||||
|
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:fillColor="#e3e3e3"
|
||||||
|
android:pathData="M320,840q-33,0 -56.5,-23.5T240,760v-560q0,-33 23.5,-56.5T320,120h320q33,0 56.5,23.5T720,200v560q0,33 -23.5,56.5T640,840L320,840ZM480,320q17,0 28.5,-11.5T520,280q0,-17 -11.5,-28.5T480,240q-17,0 -28.5,11.5T440,280q0,17 11.5,28.5T480,320ZM0,560v-160q0,-17 11.5,-28.5T40,360q17,0 28.5,11.5T80,400v160q0,17 -11.5,28.5T40,600q-17,0 -28.5,-11.5T0,560ZM120,640v-320q0,-17 11.5,-28.5T160,280q17,0 28.5,11.5T200,320v320q0,17 -11.5,28.5T160,680q-17,0 -28.5,-11.5T120,640ZM880,560v-160q0,-17 11.5,-28.5T920,360q17,0 28.5,11.5T960,400v160q0,17 -11.5,28.5T920,600q-17,0 -28.5,-11.5T880,560ZM760,640v-320q0,-17 11.5,-28.5T800,280q17,0 28.5,11.5T840,320v320q0,17 -11.5,28.5T800,680q-17,0 -28.5,-11.5T760,640Z" />
|
||||||
|
</vector>
|
||||||
Reference in New Issue
Block a user