diff --git a/app/src/main/java/org/nsh07/pomodoro/service/TimerService.kt b/app/src/main/java/org/nsh07/pomodoro/service/TimerService.kt
index bffa6cd..3948133 100644
--- a/app/src/main/java/org/nsh07/pomodoro/service/TimerService.kt
+++ b/app/src/main/java/org/nsh07/pomodoro/service/TimerService.kt
@@ -450,15 +450,19 @@ class TimerService : Service() {
}
private fun initializeMediaPlayer(): MediaPlayer? {
+ val settingsState = _settingsState.value
return try {
MediaPlayer().apply {
setAudioAttributes(
AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
- .setUsage(AudioAttributes.USAGE_ALARM)
+ .setUsage(
+ if (settingsState.mediaVolumeForAlarm) AudioAttributes.USAGE_MEDIA
+ else AudioAttributes.USAGE_ALARM
+ )
.build()
)
- _settingsState.value.alarmSoundUri?.let {
+ settingsState.alarmSoundUri?.let {
setDataSource(applicationContext, it)
prepare()
}
diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsSwitchItem.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsSwitchItem.kt
index f16c2f4..e202910 100644
--- a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsSwitchItem.kt
+++ b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsSwitchItem.kt
@@ -23,6 +23,7 @@ import androidx.annotation.StringRes
data class SettingsSwitchItem(
val checked: Boolean,
val enabled: Boolean = true,
+ val collapsible: Boolean = false,
@param:DrawableRes val icon: Int,
@param:StringRes val label: Int,
@param:StringRes val description: Int,
diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/screens/AlarmSettings.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/screens/AlarmSettings.kt
index e0e2fd9..fdd7ea4 100644
--- a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/screens/AlarmSettings.kt
+++ b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/screens/AlarmSettings.kt
@@ -25,6 +25,7 @@ import android.net.Uri
import android.os.Build
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
+import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
@@ -43,6 +44,7 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.LargeFlexibleTopAppBar
import androidx.compose.material3.ListItem
+import androidx.compose.material3.MaterialTheme.motionScheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Switch
import androidx.compose.material3.SwitchDefaults
@@ -60,8 +62,10 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
+import androidx.compose.ui.util.fastForEach
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.nsh07.pomodoro.R
@@ -74,6 +78,7 @@ import org.nsh07.pomodoro.ui.theme.CustomColors.listItemColors
import org.nsh07.pomodoro.ui.theme.CustomColors.switchColors
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
@@ -127,25 +132,36 @@ fun AlarmSettings(
}
val switchItems = remember(
- settingsState.blackTheme,
- settingsState.aodEnabled,
settingsState.alarmEnabled,
- settingsState.vibrateEnabled
+ settingsState.vibrateEnabled,
+ settingsState.mediaVolumeForAlarm
) {
listOf(
- SettingsSwitchItem(
- checked = settingsState.alarmEnabled,
- icon = R.drawable.alarm_on,
- label = R.string.sound,
- description = R.string.alarm_desc,
- onClick = { onAction(SettingsAction.SaveAlarmEnabled(it)) }
+ listOf(
+ SettingsSwitchItem(
+ checked = settingsState.alarmEnabled,
+ icon = R.drawable.alarm_on,
+ label = R.string.sound,
+ description = R.string.alarm_desc,
+ onClick = { onAction(SettingsAction.SaveAlarmEnabled(it)) }
+ ),
+ SettingsSwitchItem(
+ checked = settingsState.vibrateEnabled,
+ icon = R.drawable.mobile_vibrate,
+ label = R.string.vibrate,
+ description = R.string.vibrate_desc,
+ onClick = { onAction(SettingsAction.SaveVibrateEnabled(it)) }
+ )
),
- SettingsSwitchItem(
- checked = settingsState.vibrateEnabled,
- icon = R.drawable.mobile_vibrate,
- label = R.string.vibrate,
- description = R.string.vibrate_desc,
- onClick = { onAction(SettingsAction.SaveVibrateEnabled(it)) }
+ listOf(
+ SettingsSwitchItem(
+ checked = settingsState.mediaVolumeForAlarm,
+ collapsible = true,
+ icon = R.drawable.music_note,
+ label = R.string.media_volume_for_alarm,
+ description = R.string.media_volume_for_alarm_desc,
+ onClick = { onAction(SettingsAction.SaveMediaVolumeForAlarm(it)) }
+ )
)
)
}
@@ -203,44 +219,65 @@ fun AlarmSettings(
.clickable(onClick = { ringtonePickerLauncher.launch(ringtonePickerIntent) })
)
}
- itemsIndexed(switchItems) { 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 -> bottomListItemShape
- else -> middleListItemShape
+ switchItems.fastForEach { items ->
+ itemsIndexed(items) { index, item ->
+ ListItem(
+ leadingContent = {
+ Icon(painterResource(item.icon), contentDescription = null)
+ },
+ headlineContent = { Text(stringResource(item.label)) },
+ supportingContent = {
+ if (item.collapsible) {
+ var expanded by remember { mutableStateOf(false) }
+ Text(
+ stringResource(item.description),
+ maxLines = if (expanded) Int.MAX_VALUE else 2,
+ overflow = TextOverflow.Ellipsis,
+ modifier = Modifier
+ .clickable { expanded = !expanded }
+ .animateContentSize(motionScheme.defaultSpatialSpec())
+ )
+ } else {
+ 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 {
+ items.size == 1 -> cardShape
+ index == items.lastIndex -> bottomListItemShape
+ else -> middleListItemShape
+ }
+ )
+ )
+ }
+
+ item {
+ Spacer(Modifier.height(12.dp))
+ }
}
item { Spacer(Modifier.height(12.dp)) }
diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/SettingsAction.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/SettingsAction.kt
index ae5541a..56e9549 100644
--- a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/SettingsAction.kt
+++ b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/SettingsAction.kt
@@ -26,6 +26,7 @@ sealed interface SettingsAction {
data class SaveBlackTheme(val enabled: Boolean) : SettingsAction
data class SaveAodEnabled(val enabled: Boolean) : SettingsAction
data class SaveDndEnabled(val enabled: Boolean) : SettingsAction
+ data class SaveMediaVolumeForAlarm(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
diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/SettingsState.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/SettingsState.kt
index 31ddc7c..8b74711 100644
--- a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/SettingsState.kt
+++ b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/SettingsState.kt
@@ -31,6 +31,7 @@ data class SettingsState(
val alarmEnabled: Boolean = true,
val vibrateEnabled: Boolean = true,
val dndEnabled: Boolean = false,
+ val mediaVolumeForAlarm: Boolean = false,
val focusTime: Long = 25 * 60 * 1000L,
val shortBreakTime: Long = 5 * 60 * 1000L,
diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/SettingsViewModel.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/SettingsViewModel.kt
index a637111..d9a3fbe 100644
--- a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/SettingsViewModel.kt
+++ b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/SettingsViewModel.kt
@@ -109,6 +109,7 @@ class SettingsViewModel(
is SettingsAction.SaveAlarmEnabled -> saveAlarmEnabled(action.enabled)
is SettingsAction.SaveVibrateEnabled -> saveVibrateEnabled(action.enabled)
is SettingsAction.SaveDndEnabled -> saveDndEnabled(action.enabled)
+ is SettingsAction.SaveMediaVolumeForAlarm -> saveMediaVolumeForAlarm(action.enabled)
is SettingsAction.SaveColorScheme -> saveColorScheme(action.color)
is SettingsAction.SaveTheme -> saveTheme(action.theme)
is SettingsAction.SaveBlackTheme -> saveBlackTheme(action.enabled)
@@ -260,6 +261,18 @@ class SettingsViewModel(
}
}
+ private fun saveMediaVolumeForAlarm(mediaVolumeForAlarm: Boolean) {
+ viewModelScope.launch {
+ _settingsState.update { currentState ->
+ currentState.copy(mediaVolumeForAlarm = mediaVolumeForAlarm)
+ }
+ preferenceRepository.saveBooleanPreference(
+ "media_volume_for_alarm",
+ mediaVolumeForAlarm
+ )
+ }
+ }
+
suspend fun reloadSettings() {
var settingsState = _settingsState.value
val focusTime =
@@ -316,6 +329,12 @@ class SettingsViewModel(
)
val dndEnabled = preferenceRepository.getBooleanPreference("dnd_enabled")
?: preferenceRepository.saveBooleanPreference("dnd_enabled", settingsState.dndEnabled)
+ val mediaVolumeForAlarm =
+ preferenceRepository.getBooleanPreference("media_volume_for_alarm")
+ ?: preferenceRepository.saveBooleanPreference(
+ "media_volume_for_alarm",
+ settingsState.mediaVolumeForAlarm
+ )
_settingsState.update { currentState ->
currentState.copy(
@@ -330,7 +349,8 @@ class SettingsViewModel(
aodEnabled = aodEnabled,
alarmEnabled = alarmEnabled,
vibrateEnabled = vibrateEnabled,
- dndEnabled = dndEnabled
+ dndEnabled = dndEnabled,
+ mediaVolumeForAlarm = mediaVolumeForAlarm
)
}
diff --git a/app/src/main/res/drawable/music_note.xml b/app/src/main/res/drawable/music_note.xml
new file mode 100644
index 0000000..84eb1e9
--- /dev/null
+++ b/app/src/main/res/drawable/music_note.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 112488e..441470e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -103,4 +103,6 @@
Support me with a small donation
Customize further with Tomato+
License
+ Headphone mode
+ Plays on headphones only. If headphones are disconnected, alarm plays through speaker at media volume.
\ No newline at end of file