@@ -40,7 +40,6 @@ 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.items
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
@@ -132,46 +131,59 @@ fun TimerSettings(
|
||||
settingsState.dndEnabled,
|
||||
settingsState.aodEnabled,
|
||||
settingsState.autostartNextSession,
|
||||
settingsState.lockScreenInAod,
|
||||
isPlus,
|
||||
serviceRunning
|
||||
) {
|
||||
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(
|
||||
checked = settingsState.dndEnabled,
|
||||
enabled = !serviceRunning,
|
||||
icon = R.drawable.dnd,
|
||||
label = R.string.dnd,
|
||||
description = R.string.dnd_desc,
|
||||
onClick = {
|
||||
if (it && notificationManagerService?.isNotificationPolicyAccessGranted() == false) {
|
||||
val intent = Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS)
|
||||
Toast.makeText(
|
||||
context,
|
||||
"Enable permission for \"$appName\"",
|
||||
Toast.LENGTH_LONG
|
||||
)
|
||||
.show()
|
||||
context.startActivity(intent)
|
||||
} else if (!it && notificationManagerService?.isNotificationPolicyAccessGranted() == true) {
|
||||
notificationManagerService.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL)
|
||||
listOf(
|
||||
SettingsSwitchItem(
|
||||
checked = settingsState.autostartNextSession,
|
||||
icon = R.drawable.autoplay,
|
||||
label = R.string.auto_start_next_timer,
|
||||
description = R.string.auto_start_next_timer_desc,
|
||||
onClick = { onAction(SettingsAction.SaveAutostartNextSession(it)) }
|
||||
),
|
||||
SettingsSwitchItem(
|
||||
checked = settingsState.dndEnabled,
|
||||
enabled = !serviceRunning,
|
||||
icon = R.drawable.dnd,
|
||||
label = R.string.dnd,
|
||||
description = R.string.dnd_desc,
|
||||
onClick = {
|
||||
if (it && notificationManagerService?.isNotificationPolicyAccessGranted() == false) {
|
||||
val intent = Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS)
|
||||
Toast.makeText(
|
||||
context,
|
||||
"Enable permission for \"$appName\"",
|
||||
Toast.LENGTH_LONG
|
||||
)
|
||||
.show()
|
||||
context.startActivity(intent)
|
||||
} else if (!it && notificationManagerService?.isNotificationPolicyAccessGranted() == true) {
|
||||
notificationManagerService.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL)
|
||||
}
|
||||
onAction(SettingsAction.SaveDndEnabled(it))
|
||||
}
|
||||
onAction(SettingsAction.SaveDndEnabled(it))
|
||||
}
|
||||
)
|
||||
),
|
||||
SettingsSwitchItem(
|
||||
checked = settingsState.aodEnabled,
|
||||
enabled = isPlus,
|
||||
icon = R.drawable.aod,
|
||||
label = R.string.always_on_display,
|
||||
description = R.string.always_on_display_desc,
|
||||
onClick = { onAction(SettingsAction.SaveAodEnabled(it)) }
|
||||
listOf(
|
||||
SettingsSwitchItem(
|
||||
checked = settingsState.aodEnabled,
|
||||
enabled = isPlus,
|
||||
icon = R.drawable.aod,
|
||||
label = R.string.always_on_display,
|
||||
description = R.string.always_on_display_desc,
|
||||
onClick = { onAction(SettingsAction.SaveAodEnabled(it)) }
|
||||
),
|
||||
SettingsSwitchItem(
|
||||
checked = settingsState.lockScreenInAod && isPlus,
|
||||
enabled = isPlus,
|
||||
icon = R.drawable.mobile_lock_portrait,
|
||||
label = R.string.secure_aod,
|
||||
description = R.string.secure_aod_desc,
|
||||
onClick = { onAction(SettingsAction.SaveLockScreenInAod(it)) }
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -328,7 +340,7 @@ fun TimerSettings(
|
||||
}
|
||||
item { Spacer(Modifier.height(12.dp)) }
|
||||
|
||||
itemsIndexed(if (isPlus) switchItems else switchItems.take(2)) { index, item ->
|
||||
itemsIndexed(switchItems[0]) { index, item ->
|
||||
ListItem(
|
||||
leadingContent = {
|
||||
Icon(
|
||||
@@ -364,20 +376,63 @@ fun TimerSettings(
|
||||
},
|
||||
colors = listItemColors,
|
||||
modifier = Modifier.clip(
|
||||
if (isPlus) when (index) {
|
||||
when (index) {
|
||||
0 -> topListItemShape
|
||||
switchItems.size - 1 -> bottomListItemShape
|
||||
else -> middleListItemShape
|
||||
}
|
||||
else when (index) {
|
||||
0 -> topListItemShape
|
||||
switchItems.size - 2 -> bottomListItemShape
|
||||
switchItems[0].size - 1 -> bottomListItemShape
|
||||
else -> middleListItemShape
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (isPlus) {
|
||||
item { Spacer(Modifier.height(12.dp)) }
|
||||
itemsIndexed(switchItems[1]) { index, item ->
|
||||
ListItem(
|
||||
leadingContent = {
|
||||
Icon(
|
||||
painterResource(item.icon),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.padding(top = 4.dp)
|
||||
)
|
||||
},
|
||||
headlineContent = { Text(stringResource(item.label)) },
|
||||
supportingContent = { Text(stringResource(item.description)) },
|
||||
trailingContent = {
|
||||
Switch(
|
||||
checked = item.checked,
|
||||
onCheckedChange = { item.onClick(it) },
|
||||
enabled = item.enabled,
|
||||
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[1].size - 1 -> bottomListItemShape
|
||||
else -> middleListItemShape
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA) {
|
||||
item { Spacer(Modifier.height(12.dp)) }
|
||||
item {
|
||||
@@ -430,7 +485,7 @@ fun TimerSettings(
|
||||
item {
|
||||
PlusDivider(setShowPaywall)
|
||||
}
|
||||
items(switchItems.drop(1)) { item ->
|
||||
itemsIndexed(switchItems[1]) { index, item ->
|
||||
ListItem(
|
||||
leadingContent = {
|
||||
Icon(
|
||||
@@ -465,7 +520,13 @@ fun TimerSettings(
|
||||
)
|
||||
},
|
||||
colors = listItemColors,
|
||||
modifier = Modifier.clip(cardShape)
|
||||
modifier = Modifier.clip(
|
||||
when (index) {
|
||||
0 -> topListItemShape
|
||||
switchItems[1].size - 1 -> bottomListItemShape
|
||||
else -> middleListItemShape
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ sealed interface SettingsAction {
|
||||
data class SaveMediaVolumeForAlarm(val enabled: Boolean) : SettingsAction
|
||||
data class SaveSingleProgressBar(val enabled: Boolean) : SettingsAction
|
||||
data class SaveAutostartNextSession(val enabled: Boolean) : SettingsAction
|
||||
data class SaveLockScreenInAod(val enabled: Boolean) : SettingsAction
|
||||
data class SaveAlarmSound(val uri: Uri?) : SettingsAction
|
||||
data class SaveTheme(val theme: String) : SettingsAction
|
||||
data class SaveColorScheme(val color: Color) : SettingsAction
|
||||
|
||||
@@ -34,6 +34,7 @@ data class SettingsState(
|
||||
val mediaVolumeForAlarm: Boolean = false,
|
||||
val singleProgressBar: Boolean = false,
|
||||
val autostartNextSession: Boolean = false,
|
||||
val lockScreenInAod: Boolean = true,
|
||||
|
||||
val focusTime: Long = 25 * 60 * 1000L,
|
||||
val shortBreakTime: Long = 5 * 60 * 1000L,
|
||||
|
||||
@@ -115,6 +115,7 @@ class SettingsViewModel(
|
||||
is SettingsAction.SaveMediaVolumeForAlarm -> saveMediaVolumeForAlarm(action.enabled)
|
||||
is SettingsAction.SaveSingleProgressBar -> saveSingleProgressBar(action.enabled)
|
||||
is SettingsAction.SaveAutostartNextSession -> saveAutostartNextSession(action.enabled)
|
||||
is SettingsAction.SaveLockScreenInAod -> saveLockScreenInAod(action.enabled)
|
||||
is SettingsAction.SaveColorScheme -> saveColorScheme(action.color)
|
||||
is SettingsAction.SaveTheme -> saveTheme(action.theme)
|
||||
is SettingsAction.SaveBlackTheme -> saveBlackTheme(action.enabled)
|
||||
@@ -302,6 +303,18 @@ class SettingsViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveLockScreenInAod(lockScreenInAod: Boolean) {
|
||||
viewModelScope.launch {
|
||||
_settingsState.update { currentState ->
|
||||
currentState.copy(lockScreenInAod = lockScreenInAod)
|
||||
}
|
||||
preferenceRepository.saveBooleanPreference(
|
||||
"lock_screen_in_aod",
|
||||
lockScreenInAod
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun reloadSettings() {
|
||||
var settingsState = _settingsState.value
|
||||
val focusTime =
|
||||
@@ -375,6 +388,8 @@ class SettingsViewModel(
|
||||
"autostart_next_session",
|
||||
settingsState.autostartNextSession
|
||||
)
|
||||
val lockScreenInAod = preferenceRepository.getBooleanPreference("lock_screen_in_aod")
|
||||
?: preferenceRepository.saveBooleanPreference("lock_screen_in_aod", true)
|
||||
|
||||
_settingsState.update { currentState ->
|
||||
currentState.copy(
|
||||
@@ -392,7 +407,8 @@ class SettingsViewModel(
|
||||
dndEnabled = dndEnabled,
|
||||
mediaVolumeForAlarm = mediaVolumeForAlarm,
|
||||
singleProgressBar = singleProgressBar,
|
||||
autostartNextSession = autostartNextSession
|
||||
autostartNextSession = autostartNextSession,
|
||||
lockScreenInAod = lockScreenInAod
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
26
app/src/main/res/drawable/mobile_lock_portrait.xml
Normal file
26
app/src/main/res/drawable/mobile_lock_portrait.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="M280,920q-33,0 -56.5,-23.5T200,840v-720q0,-33 23.5,-56.5T280,40h400q33,0 56.5,23.5T760,120v124q18,7 29,22t11,34v80q0,19 -11,34t-29,22v404q0,33 -23.5,56.5T680,920L280,920ZM394,640h172q14,0 24,-10t10,-24v-132q0,-14 -10,-24t-24,-10h-6v-40q0,-33 -23.5,-56.5T480,320q-33,0 -56.5,23.5T400,400v40h-6q-14,0 -24,10t-10,24v132q0,14 10,24t24,10ZM440,440v-40q0,-17 11.5,-28.5T480,360q17,0 28.5,11.5T520,400v40h-80Z" />
|
||||
</vector>
|
||||
@@ -15,7 +15,7 @@
|
||||
~ If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<string name="alarm">Alarm</string>
|
||||
<string name="alarm_desc">Ring alarm when a timer completes</string>
|
||||
<string name="alarm_sound">Alarm sound</string>
|
||||
@@ -68,7 +68,7 @@
|
||||
<string name="productivity_analysis_desc">Focus durations at different times of the day</string>
|
||||
<string name="rate_on_google_play">Rate on Google Play</string>
|
||||
<string name="restart">Restart</string>
|
||||
<string name="selected">Selected</string>
|
||||
<string name="selected" tools:override="true">Selected</string>
|
||||
<string name="session_length">Session length</string>
|
||||
<string name="session_length_desc">Focus intervals in one session: %1$d</string>
|
||||
<string name="settings">Settings</string>
|
||||
@@ -107,6 +107,8 @@
|
||||
<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_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>
|
||||
<string name="auto_start_next_timer_desc">Start next timer after stopping an alarm</string>
|
||||
<string name="auto_start_next_timer">Auto start next timer</string>
|
||||
<string name="secure_aod">Secure AOD</string>
|
||||
<string name="secure_aod_desc">Automatically lock your device after a timeout, while keeping the AOD visible</string>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user