@@ -77,6 +77,7 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
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.platform.LocalInspectionMode
|
||||||
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.text.input.ImeAction
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
@@ -118,17 +119,30 @@ fun TimerSettings(
|
|||||||
) {
|
) {
|
||||||
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
|
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
val inspectionMode = LocalInspectionMode.current
|
||||||
val appName = stringResource(R.string.app_name)
|
val appName = stringResource(R.string.app_name)
|
||||||
val notificationManagerService =
|
val notificationManagerService = remember {
|
||||||
remember { context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager }
|
if (!inspectionMode)
|
||||||
|
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
else
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
val switchItems = remember(
|
val switchItems = remember(
|
||||||
settingsState.dndEnabled,
|
settingsState.dndEnabled,
|
||||||
settingsState.aodEnabled,
|
settingsState.aodEnabled,
|
||||||
|
settingsState.autostartNextSession,
|
||||||
isPlus,
|
isPlus,
|
||||||
serviceRunning
|
serviceRunning
|
||||||
) {
|
) {
|
||||||
listOf(
|
listOf(
|
||||||
|
SettingsSwitchItem(
|
||||||
|
checked = settingsState.autostartNextSession,
|
||||||
|
icon = R.drawable.autoplay,
|
||||||
|
label = R.string.auto_start_next_session,
|
||||||
|
description = R.string.auto_start_next_session_desc,
|
||||||
|
onClick = { onAction(SettingsAction.SaveAutostartNextSession(it)) }
|
||||||
|
),
|
||||||
SettingsSwitchItem(
|
SettingsSwitchItem(
|
||||||
checked = settingsState.dndEnabled,
|
checked = settingsState.dndEnabled,
|
||||||
enabled = !serviceRunning,
|
enabled = !serviceRunning,
|
||||||
@@ -136,7 +150,7 @@ fun TimerSettings(
|
|||||||
label = R.string.dnd,
|
label = R.string.dnd,
|
||||||
description = R.string.dnd_desc,
|
description = R.string.dnd_desc,
|
||||||
onClick = {
|
onClick = {
|
||||||
if (it && !notificationManagerService.isNotificationPolicyAccessGranted()) {
|
if (it && notificationManagerService?.isNotificationPolicyAccessGranted() == false) {
|
||||||
val intent = Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS)
|
val intent = Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS)
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
context,
|
context,
|
||||||
@@ -145,7 +159,7 @@ fun TimerSettings(
|
|||||||
)
|
)
|
||||||
.show()
|
.show()
|
||||||
context.startActivity(intent)
|
context.startActivity(intent)
|
||||||
} else if (!it && notificationManagerService.isNotificationPolicyAccessGranted()) {
|
} else if (!it && notificationManagerService?.isNotificationPolicyAccessGranted() == true) {
|
||||||
notificationManagerService.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL)
|
notificationManagerService.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL)
|
||||||
}
|
}
|
||||||
onAction(SettingsAction.SaveDndEnabled(it))
|
onAction(SettingsAction.SaveDndEnabled(it))
|
||||||
@@ -314,7 +328,7 @@ fun TimerSettings(
|
|||||||
}
|
}
|
||||||
item { Spacer(Modifier.height(12.dp)) }
|
item { Spacer(Modifier.height(12.dp)) }
|
||||||
|
|
||||||
itemsIndexed(if (isPlus) switchItems else switchItems.take(1)) { index, item ->
|
itemsIndexed(if (isPlus) switchItems else switchItems.take(2)) { index, item ->
|
||||||
ListItem(
|
ListItem(
|
||||||
leadingContent = {
|
leadingContent = {
|
||||||
Icon(
|
Icon(
|
||||||
@@ -355,7 +369,11 @@ fun TimerSettings(
|
|||||||
switchItems.size - 1 -> bottomListItemShape
|
switchItems.size - 1 -> bottomListItemShape
|
||||||
else -> middleListItemShape
|
else -> middleListItemShape
|
||||||
}
|
}
|
||||||
else cardShape
|
else when (index) {
|
||||||
|
0 -> topListItemShape
|
||||||
|
switchItems.size - 2 -> bottomListItemShape
|
||||||
|
else -> middleListItemShape
|
||||||
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -501,7 +519,7 @@ private fun TimerSettingsPreview() {
|
|||||||
)
|
)
|
||||||
TimerSettings(
|
TimerSettings(
|
||||||
isPlus = false,
|
isPlus = false,
|
||||||
serviceRunning = true,
|
serviceRunning = false,
|
||||||
settingsState = remember { SettingsState() },
|
settingsState = remember { SettingsState() },
|
||||||
contentPadding = PaddingValues(),
|
contentPadding = PaddingValues(),
|
||||||
focusTimeInputFieldState = focusTimeInputFieldState,
|
focusTimeInputFieldState = focusTimeInputFieldState,
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ sealed interface SettingsAction {
|
|||||||
data class SaveDndEnabled(val enabled: Boolean) : SettingsAction
|
data class SaveDndEnabled(val enabled: Boolean) : SettingsAction
|
||||||
data class SaveMediaVolumeForAlarm(val enabled: Boolean) : SettingsAction
|
data class SaveMediaVolumeForAlarm(val enabled: Boolean) : SettingsAction
|
||||||
data class SaveSingleProgressBar(val enabled: Boolean) : SettingsAction
|
data class SaveSingleProgressBar(val enabled: Boolean) : SettingsAction
|
||||||
|
data class SaveAutostartNextSession(val enabled: Boolean) : SettingsAction
|
||||||
data class SaveAlarmSound(val uri: Uri?) : SettingsAction
|
data class SaveAlarmSound(val uri: Uri?) : SettingsAction
|
||||||
data class SaveTheme(val theme: String) : SettingsAction
|
data class SaveTheme(val theme: String) : SettingsAction
|
||||||
data class SaveColorScheme(val color: Color) : SettingsAction
|
data class SaveColorScheme(val color: Color) : SettingsAction
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ data class SettingsState(
|
|||||||
val dndEnabled: Boolean = false,
|
val dndEnabled: Boolean = false,
|
||||||
val mediaVolumeForAlarm: Boolean = false,
|
val mediaVolumeForAlarm: Boolean = false,
|
||||||
val singleProgressBar: Boolean = false,
|
val singleProgressBar: Boolean = false,
|
||||||
|
val autostartNextSession: Boolean = false,
|
||||||
|
|
||||||
val focusTime: Long = 25 * 60 * 1000L,
|
val focusTime: Long = 25 * 60 * 1000L,
|
||||||
val shortBreakTime: Long = 5 * 60 * 1000L,
|
val shortBreakTime: Long = 5 * 60 * 1000L,
|
||||||
|
|||||||
@@ -114,6 +114,7 @@ class SettingsViewModel(
|
|||||||
is SettingsAction.SaveDndEnabled -> saveDndEnabled(action.enabled)
|
is SettingsAction.SaveDndEnabled -> saveDndEnabled(action.enabled)
|
||||||
is SettingsAction.SaveMediaVolumeForAlarm -> saveMediaVolumeForAlarm(action.enabled)
|
is SettingsAction.SaveMediaVolumeForAlarm -> saveMediaVolumeForAlarm(action.enabled)
|
||||||
is SettingsAction.SaveSingleProgressBar -> saveSingleProgressBar(action.enabled)
|
is SettingsAction.SaveSingleProgressBar -> saveSingleProgressBar(action.enabled)
|
||||||
|
is SettingsAction.SaveAutostartNextSession -> saveAutostartNextSession(action.enabled)
|
||||||
is SettingsAction.SaveColorScheme -> saveColorScheme(action.color)
|
is SettingsAction.SaveColorScheme -> saveColorScheme(action.color)
|
||||||
is SettingsAction.SaveTheme -> saveTheme(action.theme)
|
is SettingsAction.SaveTheme -> saveTheme(action.theme)
|
||||||
is SettingsAction.SaveBlackTheme -> saveBlackTheme(action.enabled)
|
is SettingsAction.SaveBlackTheme -> saveBlackTheme(action.enabled)
|
||||||
@@ -289,6 +290,18 @@ class SettingsViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun saveAutostartNextSession(autostartNextSession: Boolean) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_settingsState.update { currentState ->
|
||||||
|
currentState.copy(autostartNextSession = autostartNextSession)
|
||||||
|
}
|
||||||
|
preferenceRepository.saveBooleanPreference(
|
||||||
|
"autostart_next_session",
|
||||||
|
autostartNextSession
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun reloadSettings() {
|
suspend fun reloadSettings() {
|
||||||
var settingsState = _settingsState.value
|
var settingsState = _settingsState.value
|
||||||
val focusTime =
|
val focusTime =
|
||||||
@@ -356,6 +369,12 @@ class SettingsViewModel(
|
|||||||
"single_progress_bar",
|
"single_progress_bar",
|
||||||
settingsState.singleProgressBar
|
settingsState.singleProgressBar
|
||||||
)
|
)
|
||||||
|
val autostartNextSession =
|
||||||
|
preferenceRepository.getBooleanPreference("autostart_next_session")
|
||||||
|
?: preferenceRepository.saveBooleanPreference(
|
||||||
|
"autostart_next_session",
|
||||||
|
settingsState.autostartNextSession
|
||||||
|
)
|
||||||
|
|
||||||
_settingsState.update { currentState ->
|
_settingsState.update { currentState ->
|
||||||
currentState.copy(
|
currentState.copy(
|
||||||
@@ -372,7 +391,8 @@ class SettingsViewModel(
|
|||||||
vibrateEnabled = vibrateEnabled,
|
vibrateEnabled = vibrateEnabled,
|
||||||
dndEnabled = dndEnabled,
|
dndEnabled = dndEnabled,
|
||||||
mediaVolumeForAlarm = mediaVolumeForAlarm,
|
mediaVolumeForAlarm = mediaVolumeForAlarm,
|
||||||
singleProgressBar = singleProgressBar
|
singleProgressBar = singleProgressBar,
|
||||||
|
autostartNextSession = autostartNextSession
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
26
app/src/main/res/drawable/autoplay.xml
Normal file
26
app/src/main/res/drawable/autoplay.xml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<!--
|
||||||
|
~ 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/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<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="M380,623v-286q0,-12 10.5,-18t20.5,1l223,143q9,6 9,17t-9,17L411,640q-10,7 -20.5,1T380,623ZM120,732v68q0,17 -11.5,28.5T80,840q-17,0 -28.5,-11.5T40,800v-160q0,-17 11.5,-28.5T80,600h160q17,0 28.5,11.5T280,640q0,17 -11.5,28.5T240,680h-58q51,75 129.5,117.5T480,840q104,0 190,-54t132,-145q9,-17 23.5,-27t33.5,-6q18,4 23.5,20.5T879,664q-54,116 -161,186T480,920q-108,0 -202.5,-49.5T120,732ZM83,440q-17,0 -27.5,-12.5T48,398q10,-47 26,-86.5t43,-79.5q10,-15 26,-17t29,11q14,14 14,30.5T175,290q-17,26 -27,52t-18,57q-4,18 -16.5,29.5T83,440ZM440,82q0,19 -11.5,31T398,129q-30,7 -55.5,18T291,175q-16,11 -32.5,10T228,170q-12,-12 -10.5,-27.5T233,116q39,-26 77.5,-42.5T396,48q18,-3 31,7t13,27ZM734,170q-14,14 -31,14.5T670,174q-26,-17 -52,-27t-57,-18q-18,-4 -29.5,-16.5T520,82q0,-17 12.5,-27t29.5,-7q48,9 87,25t79,43q14,10 16,26t-10,28ZM878,440q-19,0 -31,-11.5T831,398q-8,-31 -18.5,-56.5T785,289q-11,-16 -10,-32.5t15,-30.5q12,-12 27.5,-10t26.5,16q27,40 43,79t25,87q3,17 -7,29.5T878,440Z" />
|
||||||
|
</vector>
|
||||||
@@ -107,4 +107,6 @@
|
|||||||
<string name="media_volume_for_alarm_desc">Plays on headphones only. If headphones are disconnected, alarm plays through speaker at media volume.</string>
|
<string name="media_volume_for_alarm_desc">Plays on headphones only. If headphones are disconnected, alarm plays through speaker at media volume.</string>
|
||||||
<string name="session_only_progress">Session-only progress</string>
|
<string name="session_only_progress">Session-only progress</string>
|
||||||
<string name="session_only_progress_desc">Show progress for the current session only in notifications, rather than the full sequence.</string>
|
<string name="session_only_progress_desc">Show progress for the current session only in notifications, rather than the full sequence.</string>
|
||||||
|
<string name="auto_start_next_session_desc">Start next session after stopping an alarm</string>
|
||||||
|
<string name="auto_start_next_session">Auto start next session</string>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user