Merge pull request #37 from nsh07/alarm-tone-picker

feat: Implement an alarm sound picker
This commit is contained in:
Nishant Mishra
2025-09-16 08:03:51 +05:30
committed by GitHub
15 changed files with 405 additions and 68 deletions

View File

@@ -0,0 +1,133 @@
{
"formatVersion": 1,
"database": {
"version": 2,
"identityHash": "4691d636a1c16c8cd33dc1bf0602190c",
"entities": [
{
"tableName": "int_preference",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `value` INTEGER NOT NULL, PRIMARY KEY(`key`))",
"fields": [
{
"fieldPath": "key",
"columnName": "key",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "value",
"columnName": "value",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"key"
]
}
},
{
"tableName": "boolean_preference",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `value` INTEGER NOT NULL, PRIMARY KEY(`key`))",
"fields": [
{
"fieldPath": "key",
"columnName": "key",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "value",
"columnName": "value",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"key"
]
}
},
{
"tableName": "string_preference",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `value` TEXT NOT NULL, PRIMARY KEY(`key`))",
"fields": [
{
"fieldPath": "key",
"columnName": "key",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "value",
"columnName": "value",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"key"
]
}
},
{
"tableName": "stat",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`date` TEXT NOT NULL, `focusTimeQ1` INTEGER NOT NULL, `focusTimeQ2` INTEGER NOT NULL, `focusTimeQ3` INTEGER NOT NULL, `focusTimeQ4` INTEGER NOT NULL, `breakTime` INTEGER NOT NULL, PRIMARY KEY(`date`))",
"fields": [
{
"fieldPath": "date",
"columnName": "date",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "focusTimeQ1",
"columnName": "focusTimeQ1",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "focusTimeQ2",
"columnName": "focusTimeQ2",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "focusTimeQ3",
"columnName": "focusTimeQ3",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "focusTimeQ4",
"columnName": "focusTimeQ4",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "breakTime",
"columnName": "breakTime",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"date"
]
}
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '4691d636a1c16c8cd33dc1bf0602190c')"
]
}
}

View File

@@ -55,8 +55,8 @@ class MainActivity : ComponentActivity() {
val screens = listOf(
NavItem(
Screen.Timer,
R.drawable.hourglass,
R.drawable.hourglass_filled,
R.drawable.timer_outlined,
R.drawable.timer_filled,
"Timer"
),
NavItem(

View File

@@ -8,14 +8,18 @@
package org.nsh07.pomodoro.data
import android.content.Context
import androidx.room.AutoMigration
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
@Database(
entities = [IntPreference::class, Stat::class],
version = 1
entities = [IntPreference::class, BooleanPreference::class, StringPreference::class, Stat::class],
version = 2,
autoMigrations = [
AutoMigration(from = 1, to = 2)
]
)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {

View File

@@ -11,7 +11,17 @@ import androidx.room.Entity
import androidx.room.PrimaryKey
/**
* Class for storing app preferences (settings) in the app's database
* Class for storing boolean preferences in the app's database
*/
@Entity(tableName = "boolean_preference")
data class BooleanPreference(
@PrimaryKey
val key: String,
val value: Boolean
)
/**
* Class for storing integer preferences in the app's database
*/
@Entity(tableName = "int_preference")
data class IntPreference(
@@ -19,3 +29,13 @@ data class IntPreference(
val key: String,
val value: Int
)
/**
* Class for storing string preferences in the app's database
*/
@Entity(tableName = "string_preference")
data class StringPreference(
@PrimaryKey
val key: String,
val value: String
)

View File

@@ -11,15 +11,40 @@ import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy.Companion.REPLACE
import androidx.room.Query
import kotlinx.coroutines.flow.Flow
@Dao
interface PreferenceDao {
@Insert(onConflict = REPLACE)
suspend fun insertIntPreference(preference: IntPreference)
@Insert(onConflict = REPLACE)
suspend fun insertBooleanPreference(preference: BooleanPreference)
@Insert(onConflict = REPLACE)
suspend fun insertStringPreference(preference: StringPreference)
@Query("DELETE FROM int_preference")
suspend fun resetIntPreferences()
@Query("DELETE FROM boolean_preference")
suspend fun resetBooleanPreferences()
@Query("DELETE FROM string_preference")
suspend fun resetStringPreferences()
@Query("SELECT value FROM int_preference WHERE `key` = :key")
suspend fun getIntPreference(key: String): Int?
@Query("SELECT value FROM boolean_preference WHERE `key` = :key")
suspend fun getBooleanPreference(key: String): Boolean?
@Query("SELECT value FROM boolean_preference WHERE `key` = :key")
fun getBooleanPreferenceFlow(key: String): Flow<Boolean>
@Query("SELECT value FROM string_preference WHERE `key` = :key")
suspend fun getStringPreference(key: String): String?
@Query("SELECT value FROM string_preference WHERE `key` = :key")
fun getStringPreferenceFlow(key: String): Flow<String>
}

View File

@@ -9,6 +9,7 @@ package org.nsh07.pomodoro.data
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.withContext
/**
@@ -22,11 +23,41 @@ interface PreferenceRepository {
*/
suspend fun saveIntPreference(key: String, value: Int): Int
/**
* Saves a boolean preference key-value pair to the database.
*/
suspend fun saveBooleanPreference(key: String, value: Boolean): Boolean
/**
* Saves a string preference key-value pair to the database.
*/
suspend fun saveStringPreference(key: String, value: String): String
/**
* Retrieves an integer preference key-value pair from the database.
*/
suspend fun getIntPreference(key: String): Int?
/**
* Retrieves a boolean preference key-value pair from the database.
*/
suspend fun getBooleanPreference(key: String): Boolean?
/**
* Retrieves a boolean preference key-value pair as a flow from the database.
*/
fun getBooleanPreferenceFlow(key: String): Flow<Boolean>
/**
* Retrieves a string preference key-value pair from the database.
*/
suspend fun getStringPreference(key: String): String?
/**
* Retrieves a string preference key-value pair as a flow from the database.
*/
fun getStringPreferenceFlow(key: String): Flow<String>
/**
* Erases all integer preference key-value pairs in the database. Do note that the default values
* will need to be rewritten manually
@@ -47,11 +78,39 @@ class AppPreferenceRepository(
value
}
override suspend fun saveBooleanPreference(key: String, value: Boolean): Boolean =
withContext(ioDispatcher) {
preferenceDao.insertBooleanPreference(BooleanPreference(key, value))
value
}
override suspend fun saveStringPreference(key: String, value: String): String =
withContext(ioDispatcher) {
preferenceDao.insertStringPreference(StringPreference(key, value))
value
}
override suspend fun getIntPreference(key: String): Int? = withContext(ioDispatcher) {
preferenceDao.getIntPreference(key)
}
override suspend fun getBooleanPreference(key: String): Boolean? = withContext(ioDispatcher) {
preferenceDao.getBooleanPreference(key)
}
override fun getBooleanPreferenceFlow(key: String): Flow<Boolean> =
preferenceDao.getBooleanPreferenceFlow(key)
override suspend fun getStringPreference(key: String): String? = withContext(ioDispatcher) {
preferenceDao.getStringPreference(key)
}
override fun getStringPreferenceFlow(key: String): Flow<String> =
preferenceDao.getStringPreferenceFlow(key)
override suspend fun resetSettings() = withContext(ioDispatcher) {
preferenceDao.resetIntPreferences()
preferenceDao.resetBooleanPreferences()
preferenceDao.resetStringPreferences()
}
}

View File

@@ -7,6 +7,8 @@
package org.nsh07.pomodoro.data
import android.net.Uri
import android.provider.Settings
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.lightColorScheme
@@ -18,12 +20,17 @@ interface TimerRepository {
var focusTime: Long
var shortBreakTime: Long
var longBreakTime: Long
var sessionLength: Int
var timerFrequency: Float
var alarmEnabled: Boolean
var vibrateEnabled: Boolean
var colorScheme: ColorScheme
var alarmSoundUri: Uri?
}
/**
@@ -38,4 +45,6 @@ class AppTimerRepository : TimerRepository {
override var alarmEnabled = true
override var vibrateEnabled = true
override var colorScheme = lightColorScheme()
override var alarmSoundUri: Uri? =
Settings.System.DEFAULT_ALARM_ALERT_URI ?: Settings.System.DEFAULT_RINGTONE_URI
}

View File

@@ -10,7 +10,6 @@ import android.os.SystemClock
import android.os.VibrationEffect
import android.os.Vibrator
import android.os.VibratorManager
import android.provider.Settings
import androidx.compose.ui.graphics.toArgb
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
@@ -57,11 +56,7 @@ class TimerService : Service() {
private val scope = CoroutineScope(Dispatchers.IO + job)
private val skipScope = CoroutineScope(Dispatchers.IO + job)
private val alarm by lazy {
MediaPlayer.create(
this, Settings.System.DEFAULT_ALARM_ALERT_URI ?: Settings.System.DEFAULT_RINGTONE_URI
)
}
private var alarm: MediaPlayer? = null
private val vibrator by lazy {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
@@ -78,6 +73,11 @@ class TimerService : Service() {
return null
}
override fun onCreate() {
super.onCreate()
alarm = MediaPlayer.create(this, timerRepository.alarmSoundUri)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
when (intent?.action) {
Actions.TOGGLE.toString() -> {
@@ -94,6 +94,8 @@ class TimerService : Service() {
Actions.SKIP.toString() -> skipTimer(true)
Actions.STOP_ALARM.toString() -> stopAlarm()
Actions.UPDATE_ALARM_TONE.toString() -> updateAlarmTone()
}
return super.onStartCommand(intent, flags, startId)
}
@@ -315,7 +317,7 @@ class TimerService : Service() {
}
fun startAlarm() {
if (timerRepository.alarmEnabled) alarm.start()
if (timerRepository.alarmEnabled) alarm?.start()
if (timerRepository.vibrateEnabled) {
if (!vibrator.hasVibrator()) {
@@ -330,8 +332,8 @@ class TimerService : Service() {
fun stopAlarm() {
if (timerRepository.alarmEnabled) {
alarm.pause()
alarm.seekTo(0)
alarm?.pause()
alarm?.seekTo(0)
}
if (timerRepository.vibrateEnabled) {
@@ -351,6 +353,11 @@ class TimerService : Service() {
)
}
fun updateAlarmTone() {
alarm?.release()
alarm = MediaPlayer.create(this, timerRepository.alarmSoundUri)
}
suspend fun saveTimeToDb() {
when (timerState.value.timerMode) {
TimerMode.FOCUS -> statRepository.addFocusTime(
@@ -379,10 +386,11 @@ class TimerService : Service() {
job.cancel()
saveTimeToDb()
notificationManager.cancel(1)
alarm?.release()
}
}
enum class Actions {
TOGGLE, SKIP, RESET, STOP_ALARM
TOGGLE, SKIP, RESET, STOP_ALARM, UPDATE_ALARM_TONE
}
}

View File

@@ -7,9 +7,17 @@
package org.nsh07.pomodoro.ui.settingsScreen
import android.app.Activity
import android.content.Intent
import android.media.RingtoneManager
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.compose.animation.AnimatedVisibility
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.Column
@@ -54,21 +62,23 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.tooling.preview.Devices
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.net.toUri
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import org.nsh07.pomodoro.R
import org.nsh07.pomodoro.service.TimerService
import org.nsh07.pomodoro.ui.settingsScreen.viewModel.SettingsViewModel
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.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
@@ -79,6 +89,7 @@ fun SettingsScreenRoot(
modifier: Modifier = Modifier,
viewModel: SettingsViewModel = viewModel(factory = SettingsViewModel.Factory)
) {
val context = LocalContext.current
val focusTimeInputFieldState = rememberSaveable(saver = TextFieldState.Saver) {
viewModel.focusTimeTextFieldState
}
@@ -89,8 +100,9 @@ fun SettingsScreenRoot(
viewModel.longBreakTimeTextFieldState
}
val alarmEnabled = viewModel.alarmEnabled.collectAsStateWithLifecycle()
val vibrateEnabled = viewModel.vibrateEnabled.collectAsStateWithLifecycle()
val alarmEnabled by viewModel.alarmEnabled.collectAsStateWithLifecycle(true)
val vibrateEnabled by viewModel.vibrateEnabled.collectAsStateWithLifecycle(true)
val alarmSound by viewModel.alarmSound.collectAsStateWithLifecycle(viewModel.currentAlarmSound)
val sessionsSliderState = rememberSaveable(
saver = SliderState.Saver(
@@ -106,10 +118,18 @@ fun SettingsScreenRoot(
shortBreakTimeInputFieldState = shortBreakTimeInputFieldState,
longBreakTimeInputFieldState = longBreakTimeInputFieldState,
sessionsSliderState = sessionsSliderState,
alarmEnabled = alarmEnabled.value,
vibrateEnabled = vibrateEnabled.value,
alarmEnabled = alarmEnabled,
vibrateEnabled = vibrateEnabled,
alarmSound = alarmSound,
onAlarmEnabledChange = viewModel::saveAlarmEnabled,
onVibrateEnabledChange = viewModel::saveVibrateEnabled,
onAlarmSoundChanged = {
viewModel.saveAlarmSound(it)
Intent(context, TimerService::class.java).apply {
action = TimerService.Actions.RESET.toString()
context.startService(this)
}
},
modifier = modifier
)
}
@@ -123,8 +143,10 @@ private fun SettingsScreen(
sessionsSliderState: SliderState,
alarmEnabled: Boolean,
vibrateEnabled: Boolean,
alarmSound: String,
onAlarmEnabledChange: (Boolean) -> Unit,
onVibrateEnabledChange: (Boolean) -> Unit,
onAlarmSoundChanged: (Uri?) -> Unit,
modifier: Modifier = Modifier
) {
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
@@ -132,20 +154,46 @@ private fun SettingsScreen(
checkedIconColor = colorScheme.primary,
)
val context = LocalContext.current
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, "Alarm sound")
putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, alarmSound.toUri())
}
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",
description = "Ring alarm 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",
description = "Vibrate when a timer completes",
onClick = onVibrateEnabledChange
)
)
@@ -266,7 +314,27 @@ private fun SettingsScreen(
}
},
colors = listItemColors,
modifier = Modifier.clip(cardShape)
modifier = Modifier.clip(topListItemShape)
)
}
item {
ListItem(
leadingContent = {
Icon(painterResource(R.drawable.alarm), null)
},
headlineContent = { Text("Alarm sound") },
supportingContent = {
Text(
remember(alarmSound) {
RingtoneManager.getRingtone(context, alarmSound.toUri())
.getTitle(context)
}
)
},
colors = listItemColors,
modifier = Modifier
.clip(bottomListItemShape)
.clickable(onClick = { ringtonePickerLauncher.launch(intent) })
)
}
item { Spacer(Modifier.height(12.dp)) }
@@ -360,8 +428,10 @@ fun SettingsScreenPreview() {
sessionsSliderState = rememberSliderState(value = 3f, steps = 3, valueRange = 1f..5f),
alarmEnabled = true,
vibrateEnabled = true,
alarmSound = "null",
onAlarmEnabledChange = {},
onVibrateEnabledChange = {},
onAlarmSoundChanged = {},
modifier = Modifier.fillMaxSize()
)
}

View File

@@ -7,6 +7,7 @@
package org.nsh07.pomodoro.ui.settingsScreen.viewModel
import android.net.Uri
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.SliderState
@@ -19,10 +20,8 @@ import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import kotlinx.coroutines.Dispatchers
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.distinctUntilChanged
import kotlinx.coroutines.launch
import org.nsh07.pomodoro.TomatoApplication
import org.nsh07.pomodoro.data.AppPreferenceRepository
@@ -47,13 +46,14 @@ class SettingsViewModel(
onValueChangeFinished = ::updateSessionLength
)
private val _alarmEnabled: MutableStateFlow<Boolean> =
MutableStateFlow(timerRepository.alarmEnabled)
val alarmEnabled: StateFlow<Boolean> = _alarmEnabled.asStateFlow()
val currentAlarmSound = timerRepository.alarmSoundUri.toString()
private val _vibrateEnabled: MutableStateFlow<Boolean> =
MutableStateFlow(timerRepository.alarmEnabled)
val vibrateEnabled: StateFlow<Boolean> = _vibrateEnabled.asStateFlow()
val alarmSound =
preferenceRepository.getStringPreferenceFlow("alarm_sound").distinctUntilChanged()
val alarmEnabled =
preferenceRepository.getBooleanPreferenceFlow("alarm_enabled").distinctUntilChanged()
val vibrateEnabled =
preferenceRepository.getBooleanPreferenceFlow("vibrate_enabled").distinctUntilChanged()
init {
viewModelScope.launch(Dispatchers.IO) {
@@ -105,20 +105,25 @@ class SettingsViewModel(
fun saveAlarmEnabled(enabled: Boolean) {
viewModelScope.launch {
timerRepository.alarmEnabled = preferenceRepository
.saveIntPreference("alarm_enabled", if (enabled) 1 else 0) == 1
_alarmEnabled.value = enabled
preferenceRepository.saveBooleanPreference("alarm_enabled", enabled)
timerRepository.alarmEnabled = enabled
}
}
fun saveVibrateEnabled(enabled: Boolean) {
viewModelScope.launch {
timerRepository.vibrateEnabled = preferenceRepository
.saveIntPreference("vibrate_enabled", if (enabled) 1 else 0) == 1
_vibrateEnabled.value = enabled
preferenceRepository.saveBooleanPreference("vibrate_enabled", enabled)
timerRepository.vibrateEnabled = enabled
}
}
fun saveAlarmSound(uri: Uri?) {
viewModelScope.launch {
preferenceRepository.saveStringPreference("alarm_sound", uri.toString())
}
timerRepository.alarmSoundUri = uri
}
companion object {
val Factory: ViewModelProvider.Factory = viewModelFactory {
initializer {

View File

@@ -8,7 +8,9 @@
package org.nsh07.pomodoro.ui.timerScreen.viewModel
import android.app.Application
import android.provider.Settings
import androidx.compose.material3.ColorScheme
import androidx.core.net.toUri
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
@@ -77,17 +79,21 @@ class TimerViewModel(
timerRepository.sessionLength
)
timerRepository.alarmEnabled = (preferenceRepository.getIntPreference("alarm_enabled")
?: preferenceRepository.saveIntPreference(
"alarm_enabled",
1
)) == 1
timerRepository.alarmEnabled =
preferenceRepository.getBooleanPreference("alarm_enabled")
?: preferenceRepository.saveBooleanPreference("alarm_enabled", true)
timerRepository.vibrateEnabled =
(preferenceRepository.getIntPreference("vibrate_enabled")
?: preferenceRepository.saveIntPreference(
"vibrate_enabled",
1
)) == 1
preferenceRepository.getBooleanPreference("vibrate_enabled")
?: preferenceRepository.saveBooleanPreference("vibrate_enabled", true)
timerRepository.alarmSoundUri = (
preferenceRepository.getStringPreference("alarm_sound")
?: preferenceRepository.saveStringPreference(
"alarm_sound",
(Settings.System.DEFAULT_ALARM_ALERT_URI
?: Settings.System.DEFAULT_RINGTONE_URI).toString()
)
).toUri()
resetTimer()

View File

@@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#000000"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="@android:color/white"
android:pathData="M320,800h320v-120q0,-66 -47,-113t-113,-47q-66,0 -113,47t-47,113v120ZM480,440q66,0 113,-47t47,-113v-120L320,160v120q0,66 47,113t113,47ZM200,880q-17,0 -28.5,-11.5T160,840q0,-17 11.5,-28.5T200,800h40v-120q0,-61 28.5,-114.5T348,480q-51,-32 -79.5,-85.5T240,280v-120h-40q-17,0 -28.5,-11.5T160,120q0,-17 11.5,-28.5T200,80h560q17,0 28.5,11.5T800,120q0,17 -11.5,28.5T760,160h-40v120q0,61 -28.5,114.5T612,480q51,32 79.5,85.5T720,680v120h40q17,0 28.5,11.5T800,840q0,17 -11.5,28.5T760,880L200,880ZM480,800ZM480,160Z" />
</vector>

View File

@@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#000000"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="@android:color/white"
android:pathData="M200,880q-17,0 -28.5,-11.5T160,840q0,-17 11.5,-28.5T200,800h40v-120q0,-61 28.5,-114.5T348,480q-51,-32 -79.5,-85.5T240,280v-120h-40q-17,0 -28.5,-11.5T160,120q0,-17 11.5,-28.5T200,80h560q17,0 28.5,11.5T800,120q0,17 -11.5,28.5T760,160h-40v120q0,61 -28.5,114.5T612,480q51,32 79.5,85.5T720,680v120h40q17,0 28.5,11.5T800,840q0,17 -11.5,28.5T760,880L200,880Z" />
</vector>

View File

@@ -0,0 +1,9 @@
<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="M400,120q-17,0 -28.5,-11.5T360,80q0,-17 11.5,-28.5T400,40h160q17,0 28.5,11.5T600,80q0,17 -11.5,28.5T560,120L400,120ZM480,560q17,0 28.5,-11.5T520,520v-160q0,-17 -11.5,-28.5T480,320q-17,0 -28.5,11.5T440,360v160q0,17 11.5,28.5T480,560ZM480,880q-74,0 -139.5,-28.5T226,774q-49,-49 -77.5,-114.5T120,520q0,-74 28.5,-139.5T226,266q49,-49 114.5,-77.5T480,160q62,0 119,20t107,58l28,-28q11,-11 28,-11t28,11q11,11 11,28t-11,28l-28,28q38,50 58,107t20,119q0,74 -28.5,139.5T734,774q-49,49 -114.5,77.5T480,880Z" />
</vector>

View File

@@ -0,0 +1,9 @@
<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="M400,120q-17,0 -28.5,-11.5T360,80q0,-17 11.5,-28.5T400,40h160q17,0 28.5,11.5T600,80q0,17 -11.5,28.5T560,120L400,120ZM480,560q17,0 28.5,-11.5T520,520v-160q0,-17 -11.5,-28.5T480,320q-17,0 -28.5,11.5T440,360v160q0,17 11.5,28.5T480,560ZM480,880q-74,0 -139.5,-28.5T226,774q-49,-49 -77.5,-114.5T120,520q0,-74 28.5,-139.5T226,266q49,-49 114.5,-77.5T480,160q62,0 119,20t107,58l28,-28q11,-11 28,-11t28,11q11,11 11,28t-11,28l-28,28q38,50 58,107t20,119q0,74 -28.5,139.5T734,774q-49,49 -114.5,77.5T480,880ZM480,800q116,0 198,-82t82,-198q0,-116 -82,-198t-198,-82q-116,0 -198,82t-82,198q0,116 82,198t198,82ZM480,520Z" />
</vector>